[PATCH v19 03/22] cxl/mem: Introduce a memdev creation ->probe() operation

alejandro.lucero-palau
2025-10-06T10:02:56Z

From: Dan Williams <dan.j.williams@intel.com>

Allow for a driver to pass a routine to be called in cxl_mem_probe()
context. This ability mirrors the semantics of faux_device_create() and
allows for the caller to run CXL-topology-attach dependent logic on behalf
of the caller.

This capability is needed for CXL accelerator device drivers that need to
make decisions about enabling CXL dependent functionality in the device, or
falling back to PCIe-only operation.

The probe callback runs after the port topology is successfully attached
for the given memdev.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
drivers/cxl/core/memdev.c | 5 ++++-
drivers/cxl/cxlmem.h | 10 +++++++++-
drivers/cxl/mem.c | 34 +++++++++++++++++++++++++++++++---
drivers/cxl/pci.c | 2 +-
drivers/cxl/private.h | 3 ++-
tools/testing/cxl/test/mem.c | 2 +-
6 files changed, 48 insertions(+), 8 deletions(-)

diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index 2bef231008df..628f91c60c90 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -1012,7 +1012,8 @@ static const struct file_operations cxl_memdev_fops = {
.llseek = noop_llseek,
};

-struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds)
+struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds,
+ const struct cxl_memdev_ops *ops)
{
struct cxl_memdev *cxlmd;
struct device *dev;
@@ -1028,6 +1029,8 @@ struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds)
goto err;
cxlmd->id = rc;
cxlmd->depth = -1;
+ cxlmd->ops = ops;
+ cxlmd->endpoint = ERR_PTR(-ENXIO);
cxlmd->cxlds = cxlds;
cxlds->cxlmd = cxlmd;

diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 751478dfc410..82e8188c76a0 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -34,6 +34,10 @@
(FIELD_GET(CXLMDEV_RESET_NEEDED_MASK, status) != \
CXLMDEV_RESET_NEEDED_NOT)

+struct cxl_memdev_ops {
+ int (*probe)(struct cxl_memdev *cxlmd);
+};
+
/**
* struct cxl_memdev - CXL bus object representing a Type-3 Memory Device
* @dev: driver core device object
@@ -43,6 +47,7 @@
* @cxl_nvb: coordinate removal of @cxl_nvd if present
* @cxl_nvd: optional bridge to an nvdimm if the device supports pmem
* @endpoint: connection to the CXL port topology for this memory device
+ * @ops: incremental caller specific probe routine
* @id: id number of this memdev instance.
* @depth: endpoint port depth
* @scrub_cycle: current scrub cycle set for this device
@@ -59,6 +64,7 @@ struct cxl_memdev {
struct cxl_nvdimm_bridge *cxl_nvb;
struct cxl_nvdimm *cxl_nvd;
struct cxl_port *endpoint;
+ const struct cxl_memdev_ops *ops;
int id;
int depth;
u8 scrub_cycle;
@@ -96,7 +102,9 @@ static inline bool is_cxl_endpoint(struct cxl_port *port)
}

struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
- struct cxl_dev_state *cxlds);
+ struct cxl_dev_state *cxlds,
+ const struct cxl_memdev_ops *ops);
+
int devm_cxl_sanitize_setup_notifier(struct device *host,
struct cxl_memdev *cxlmd);
struct cxl_memdev_state;
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 56a1a4e14455..aeb2e3e8282a 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -145,6 +145,12 @@ static int cxl_mem_probe(struct device *dev)
return rc;
}

+ if (cxlmd->ops) {
+ rc = cxlmd->ops->probe(cxlmd);
+ if (rc)
+ return rc;
+ }
+
rc = devm_cxl_memdev_edac_register(cxlmd);
if (rc)
dev_dbg(dev, "CXL memdev EDAC registration failed rc=%d\n", rc);
@@ -170,15 +176,18 @@ static int cxl_mem_probe(struct device *dev)
* devm_cxl_add_memdev - Add a CXL memory device
* @host: devres alloc/release context and parent for the memdev
* @cxlds: CXL device state to associate with the memdev
+ * @ops: optional operations to run in cxl_mem::{probe,remove}() context
*
* Upon return the device will have had a chance to attach to the
* cxl_mem driver, but may fail if the CXL topology is not ready
* (hardware CXL link down, or software platform CXL root not attached)
+ *
*/
struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
- struct cxl_dev_state *cxlds)
+ struct cxl_dev_state *cxlds,
+ const struct cxl_memdev_ops *ops)
{
- struct cxl_memdev *cxlmd = cxl_memdev_alloc(cxlds);
+ struct cxl_memdev *cxlmd = cxl_memdev_alloc(cxlds, ops);
int rc;

if (IS_ERR(cxlmd))
@@ -190,7 +199,26 @@ struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
return ERR_PTR(rc);
}

- return devm_cxl_memdev_add_or_reset(host, cxlmd);
+ cxlmd = devm_cxl_memdev_add_or_reset(host, cxlmd);
+ if (IS_ERR(cxlmd))
+ return cxlmd;
+
+ /*
+ * If ops is provided fail if the driver is not attached upon
+ * return. The ->endpoint ERR_PTR may have a more precise error
+ * code to convey. Note that failure here could be the result of
+ * a race to teardown the CXL port topology. I.e.
+ * cxl_mem_probe() could have succeeded and then cxl_mem unbound
+ * before the lock is acquired.
+ */
+ guard(device)(&cxlmd->dev);
+ if (ops && !cxlmd->dev.driver) {
+ if (IS_ERR(cxlmd->endpoint))
+ return ERR_CAST(cxlmd->endpoint);
+ return ERR_PTR(-ENXIO);
+ }
+
+ return cxlmd;
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_memdev, "CXL");

diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 6803c2fb906b..0a3108d552c8 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -1007,7 +1007,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (rc)
dev_dbg(&pdev->dev, "No CXL Features discovered\n");

- cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);
+ cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds, NULL);
if (IS_ERR(cxlmd))
return PTR_ERR(cxlmd);

diff --git a/drivers/cxl/private.h b/drivers/cxl/private.h
index e15ff7f4b119..f8a5658e7090 100644
--- a/drivers/cxl/private.h
+++ b/drivers/cxl/private.h
@@ -8,7 +8,8 @@

#ifndef __CXL_PRIVATE_H__
#define __CXL_PRIVATE_H__
-struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds);
+struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds,
+ const struct cxl_memdev_ops *ops);
struct cxl_memdev *devm_cxl_memdev_add_or_reset(struct device *host,
struct cxl_memdev *cxlmd);
int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
index 0f1d91f57ba3..3d553661ca75 100644
--- a/tools/testing/cxl/test/mem.c
+++ b/tools/testing/cxl/test/mem.c
@@ -1768,7 +1768,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)

cxl_mock_add_event_logs(&mdata->mes);

- cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);
+ cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds, NULL);
if (IS_ERR(cxlmd))
return PTR_ERR(cxlmd);

--
2.34.1