diff mbox series

[RFC,3/4] cxl/port: introduce cxl_disable_port() function

Message ID 20231228060510.1178981-4-dongsheng.yang@easystack.cn
State New, archived
Headers show
Series cxl: introduce CXL Virtualization module | expand

Commit Message

Dongsheng Yang Dec. 28, 2023, 6:05 a.m. UTC
when we want to delete an port (e.g in cxlv), we want to
make sure there is no region attached to this port or
any child port. And more, we need to prevent region to
attach in port deleting.

cxl_disable_port() will return -EBUSY if there is any
region attached to this port or child port, otherwise it
will set any child endpoint decoder to CXL_DECODER_DEAD,
that means this port are going to be deleted, dont attach region
to it.

Signed-off-by: Dongsheng Yang <dongsheng.yang@easystack.cn>
---
 drivers/cxl/core/port.c | 80 +++++++++++++++++++++++++++++++++++++++++
 drivers/cxl/cxl.h       |  1 +
 2 files changed, 81 insertions(+)
diff mbox series

Patch

diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 8d2d54da45e5..59ab8fe2cff2 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -1508,6 +1508,86 @@  static void reap_dports(struct cxl_port *port)
 	}
 }
 
+/*
+ * Disable an endpoint decoder to prevent any more region attach.
+ */
+static int disable_decoder(struct device *device, void *data)
+{
+	struct cxl_endpoint_decoder *cxled;
+
+	if (!is_endpoint_decoder(device))
+		return 0;
+
+	cxled = to_cxl_endpoint_decoder(device);
+	cxled->mode = CXL_DECODER_DEAD;
+
+	return 0;
+}
+
+/*
+ * Disable a port, if it is an endpoint port, it will disable
+ * the related endpoint decoder, otherwise, disable all child ports.
+ */
+static int disable_port(struct device *device, void *data)
+{
+	struct cxl_port *port;
+	int ret;
+
+	if (!is_cxl_port(device))
+		return 0;
+
+	port = to_cxl_port(device);
+	if (is_cxl_endpoint(port)) {
+		ret = device_for_each_child(&port->dev, NULL, disable_decoder);
+	} else {
+		ret = device_for_each_child(&port->dev, NULL, disable_port);
+	}
+
+	return ret;
+}
+
+/*
+ * If there is any region attached to this port or child port, return -EBUSY.
+ */
+static int port_busy(struct device *device, void *data)
+{
+	struct cxl_port *port;
+
+	if (!is_cxl_port(device))
+		return 0;
+
+	port = to_cxl_port(device);
+	if (!xa_empty(&port->regions)) {
+		return -EBUSY;
+	}
+
+	return device_for_each_child(&port->dev, NULL, port_busy);
+}
+
+/*
+ * Disable any child endpoint decoder to prevent region attach,
+ * then we can delete this port safely.
+ *
+ * Returns -EBUSY if there is still region attached to this port
+ * or child port.
+ */
+int cxl_disable_port(struct cxl_port *port)
+{
+	int ret;
+
+	down_write(&cxl_region_rwsem);
+	if (port_busy(&port->dev, NULL)) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	ret = disable_port(&port->dev, NULL);
+unlock:
+	up_write(&cxl_region_rwsem);
+	return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_disable_port, CXL);
+
 struct detach_ctx {
 	struct cxl_memdev *cxlmd;
 	int depth;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 1397f66d943b..a1343449f35c 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -716,6 +716,7 @@  struct cxl_dport *devm_cxl_add_dport(struct cxl_port *port,
 struct cxl_dport *devm_cxl_add_rch_dport(struct cxl_port *port,
 					 struct device *dport_dev, int port_id,
 					 resource_size_t rcrb);
+int cxl_disable_port(struct cxl_port *port);
 
 #ifdef CONFIG_PCIEAER_CXL
 void cxl_setup_parent_dport(struct device *host, struct cxl_dport *dport);