summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Jiang <dave.jiang@intel.com>2026-06-12 13:47:53 -0700
committerDave Jiang <dave.jiang@intel.com>2026-06-12 13:47:53 -0700
commitf72af41a43e16276c46d44cf8a833cc0f9ba9d48 (patch)
tree1ccba38b40fc491fc85ce84a3c7b2d8bf41b1e94
parente53ef72033b30f8ff0bf76adf956d4a27d1a2675 (diff)
parent383f69656359191d2236ef5ec259984c844fde9a (diff)
downloadlwn-f72af41a43e16276c46d44cf8a833cc0f9ba9d48.tar.gz
lwn-f72af41a43e16276c46d44cf8a833cc0f9ba9d48.zip
Merge branch 'for-7.2/cxl-type2-attach-region' into cxl-for-next
cxl: Add dummy function for cxl_memdev_attach_region for !CONFIG_CXL_REGION cxl/region: Introduce devm_cxl_probe_mem() cxl/memdev: Introduce cxl_class_memdev_type cxl/memdev: Pin parents for entire memdev lifetime cxl/region: Resolve region deletion races cxl/region: Block region delete during region creation
-rw-r--r--drivers/cxl/core/core.h2
-rw-r--r--drivers/cxl/core/memdev.c20
-rw-r--r--drivers/cxl/core/port.c7
-rw-r--r--drivers/cxl/core/region.c194
-rw-r--r--drivers/cxl/cxl.h8
-rw-r--r--drivers/cxl/cxlmem.h34
-rw-r--r--drivers/cxl/mem.c95
-rw-r--r--drivers/cxl/pci.c2
-rw-r--r--include/cxl/cxl.h3
-rw-r--r--tools/testing/cxl/test/mem.c2
10 files changed, 304 insertions, 63 deletions
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 82ca3a476708..07555ae63859 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -52,6 +52,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
u64 dpa);
int devm_cxl_add_dax_region(struct cxl_region *cxlr);
int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
+void kill_regions(struct cxl_root_decoder *cxlrd);
#else
static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
@@ -81,6 +82,7 @@ static inline int cxl_region_init(void)
static inline void cxl_region_exit(void)
{
}
+static inline void kill_regions(struct cxl_root_decoder *cxlrd) { };
#define CXL_REGION_ATTR(x) NULL
#define CXL_REGION_TYPE(x) NULL
#define SET_CXL_REGION_ATTR(x)
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index 80e65690eb77..33a3d2e7b13a 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -25,9 +25,11 @@ static DEFINE_IDA(cxl_memdev_ida);
static void cxl_memdev_release(struct device *dev)
{
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
+ struct device *parent = dev->parent;
ida_free(&cxl_memdev_ida, cxlmd->id);
kfree(cxlmd);
+ put_device(parent);
}
static char *cxl_memdev_devnode(const struct device *dev, umode_t *mode, kuid_t *uid,
@@ -572,16 +574,23 @@ void cxl_memdev_update_perf(struct cxl_memdev *cxlmd)
}
EXPORT_SYMBOL_NS_GPL(cxl_memdev_update_perf, "CXL");
-static const struct device_type cxl_memdev_type = {
+static const struct device_type cxl_class_memdev_type = {
.name = "cxl_memdev",
.release = cxl_memdev_release,
.devnode = cxl_memdev_devnode,
.groups = cxl_memdev_attribute_groups,
};
+static const struct device_type cxl_memdev_type = {
+ .name = "cxl_memdev",
+ .release = cxl_memdev_release,
+ .devnode = cxl_memdev_devnode,
+};
+
bool is_cxl_memdev(const struct device *dev)
{
- return dev->type == &cxl_memdev_type;
+ return (dev->type == &cxl_class_memdev_type ||
+ dev->type == &cxl_memdev_type);
}
EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "CXL");
@@ -707,10 +716,13 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds,
dev = &cxlmd->dev;
device_initialize(dev);
lockdep_set_class(&dev->mutex, &cxl_memdev_key);
- dev->parent = cxlds->dev;
+ dev->parent = get_device(cxlds->dev);
dev->bus = &cxl_bus_type;
dev->devt = MKDEV(cxl_mem_major, cxlmd->id);
- dev->type = &cxl_memdev_type;
+ if (cxlds->type == CXL_DEVTYPE_DEVMEM)
+ dev->type = &cxl_memdev_type;
+ else
+ dev->type = &cxl_class_memdev_type;
device_set_pm_not_required(dev);
INIT_WORK(&cxlmd->detach_work, detach_memdev);
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index c5aacd7054f1..1215ee4f4035 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -458,6 +458,8 @@ static void cxl_root_decoder_release(struct device *dev)
if (atomic_read(&cxlrd->region_id) >= 0)
memregion_free(atomic_read(&cxlrd->region_id));
+ mutex_destroy(&cxlrd->regions_lock);
+ xa_destroy(&cxlrd->regions);
__cxl_decoder_release(&cxlrd->cxlsd.cxld);
kfree(cxlrd);
}
@@ -2016,7 +2018,8 @@ struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
return ERR_PTR(rc);
}
- mutex_init(&cxlrd->range_lock);
+ mutex_init(&cxlrd->regions_lock);
+ xa_init(&cxlrd->regions);
cxld = &cxlsd->cxld;
cxld->dev.type = &cxl_decoder_root_type;
@@ -2192,6 +2195,8 @@ static void cxld_unregister(void *dev)
if (is_endpoint_decoder(dev))
cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1,
DETACH_INVALIDATE);
+ if (is_root_decoder(dev))
+ kill_regions(to_cxl_root_decoder(dev));
device_unregister(dev);
}
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 66c328d1c14e..1e211542b6b6 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1148,6 +1148,19 @@ static int cxl_rr_assign_decoder(struct cxl_port *port, struct cxl_region *cxlr,
static void cxl_region_setup_flags(struct cxl_region *cxlr,
struct cxl_decoder *cxld)
{
+ if (is_endpoint_decoder(&cxld->dev)) {
+ struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(&cxld->dev);
+ struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
+
+ /*
+ * When a region's memdevs specify an @attach method the attach
+ * provider is responsible for dispositioning the region for
+ * both probe and userspace management
+ */
+ if (cxlmd->attach)
+ set_bit(CXL_REGION_F_LOCK, &cxlr->flags);
+ }
+
if (cxld->flags & CXL_DECODER_F_LOCK) {
set_bit(CXL_REGION_F_LOCK, &cxlr->flags);
clear_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags);
@@ -2546,12 +2559,13 @@ static struct cxl_region *to_cxl_region(struct device *dev)
return container_of(dev, struct cxl_region, dev);
}
-static void unregister_region(void *_cxlr)
+static void unregister_region(struct cxl_region *cxlr)
{
- struct cxl_region *cxlr = _cxlr;
+ struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
struct cxl_region_params *p = &cxlr->params;
int i;
+ xa_erase(&cxlrd->regions, cxlr->id);
device_del(&cxlr->dev);
/*
@@ -2568,6 +2582,17 @@ static void unregister_region(void *_cxlr)
put_device(&cxlr->dev);
}
+static void endpoint_unregister_region(void *_cxlr)
+{
+ struct cxl_region *cxlr = _cxlr;
+ struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
+
+ guard(mutex)(&cxlrd->regions_lock);
+ if (xa_load(&cxlrd->regions, cxlr->id))
+ unregister_region(cxlr);
+ put_device(&cxlr->dev);
+}
+
static struct lock_class_key cxl_region_key;
static struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int id)
@@ -2682,6 +2707,19 @@ static int cxl_region_calculate_adistance(struct notifier_block *nb,
return NOTIFY_STOP;
}
+/* unwind all remaining regions */
+void kill_regions(struct cxl_root_decoder *cxlrd)
+{
+ unsigned long index;
+ struct cxl_region *cxlr;
+
+ guard(mutex)(&cxlrd->regions_lock);
+ /* no more region creation */
+ cxlrd->dead = true;
+ xa_for_each(&cxlrd->regions, index, cxlr)
+ unregister_region(cxlr);
+}
+
/**
* devm_cxl_add_region - Adds a region to a decoder
* @cxlrd: root decoder
@@ -2720,14 +2758,15 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd,
if (rc)
goto err;
- rc = devm_add_action_or_reset(port->uport_dev, unregister_region, cxlr);
- if (rc)
+ rc = xa_insert(&cxlrd->regions, cxlr->id, cxlr, GFP_KERNEL);
+ if (rc) {
+ unregister_region(cxlr);
return ERR_PTR(rc);
+ }
dev_dbg(port->uport_dev, "%s: created %s\n",
dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev));
return cxlr;
-
err:
put_device(dev);
return ERR_PTR(rc);
@@ -2756,6 +2795,9 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd,
{
int rc;
+ if (cxlrd->dead)
+ return ERR_PTR(-ENXIO);
+
switch (mode) {
case CXL_PARTMODE_RAM:
case CXL_PARTMODE_PMEM:
@@ -2788,6 +2830,10 @@ static ssize_t create_region_store(struct device *dev, const char *buf,
if (rc != 1)
return -EINVAL;
+ ACQUIRE(mutex_intr, regions_lock)(&cxlrd->regions_lock);
+ if ((rc = ACQUIRE_ERR(mutex_intr, &regions_lock)))
+ return rc;
+
cxlr = __create_region(cxlrd, mode, id, CXL_DECODER_HOSTONLYMEM);
if (IS_ERR(cxlr))
return PTR_ERR(cxlr);
@@ -2827,33 +2873,27 @@ static ssize_t region_show(struct device *dev, struct device_attribute *attr,
}
DEVICE_ATTR_RO(region);
-static struct cxl_region *
-cxl_find_region_by_name(struct cxl_root_decoder *cxlrd, const char *name)
-{
- struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
- struct device *region_dev;
-
- region_dev = device_find_child_by_name(&cxld->dev, name);
- if (!region_dev)
- return ERR_PTR(-ENODEV);
-
- return to_cxl_region(region_dev);
-}
-
static ssize_t delete_region_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
- struct cxl_port *port = to_cxl_port(dev->parent);
struct cxl_region *cxlr;
+ int rc, id;
- cxlr = cxl_find_region_by_name(cxlrd, buf);
- if (IS_ERR(cxlr))
- return PTR_ERR(cxlr);
+ ACQUIRE(mutex_intr, regions_lock)(&cxlrd->regions_lock);
+ if ((rc = ACQUIRE_ERR(mutex_intr, &regions_lock)))
+ return rc;
- devm_release_action(port->uport_dev, unregister_region, cxlr);
- put_device(&cxlr->dev);
+ rc = sscanf(buf, "region%d\n", &id);
+ if (rc != 1)
+ return -EINVAL;
+
+ cxlr = xa_load(&cxlrd->regions, id);
+ if (!cxlr || !sysfs_streq(buf, dev_name(&cxlr->dev)))
+ return -ENODEV;
+
+ unregister_region(cxlr);
return len;
}
@@ -3718,7 +3758,6 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
{
struct cxl_endpoint_decoder *cxled = ctx->cxled;
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
- struct cxl_port *port = cxlrd_to_port(cxlrd);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
int rc, part = READ_ONCE(cxled->part);
struct cxl_region *cxlr;
@@ -3742,7 +3781,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
rc = __construct_region(cxlr, ctx);
if (rc) {
- devm_release_action(port->uport_dev, unregister_region, cxlr);
+ unregister_region(cxlr);
return ERR_PTR(rc);
}
@@ -3788,12 +3827,11 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
* for the HPA range, one does the construction and the others
* add to that.
*/
- mutex_lock(&cxlrd->range_lock);
+ guard(mutex)(&cxlrd->regions_lock);
struct cxl_region *cxlr __free(put_cxl_region) =
cxl_find_region_by_range(cxlrd, &ctx.hpa_range);
if (!cxlr)
cxlr = construct_region(cxlrd, &ctx);
- mutex_unlock(&cxlrd->range_lock);
rc = PTR_ERR_OR_ZERO(cxlr);
if (rc)
@@ -4055,6 +4093,103 @@ static int cxl_region_can_probe(struct cxl_region *cxlr)
return 0;
}
+static int first_mapped_decoder(struct device *dev, const void *data)
+{
+ struct cxl_endpoint_decoder *cxled;
+
+ if (!is_endpoint_decoder(dev))
+ return 0;
+
+ cxled = to_cxl_endpoint_decoder(dev);
+ if (cxled->cxld.region)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Runs in cxl_mem_probe context after successful endpoint probe, assumes the
+ * simple case of single mapped decoder per memdev.
+ */
+int cxl_memdev_attach_region(struct cxl_memdev *cxlmd)
+{
+ struct cxl_attach_region *attach =
+ container_of(cxlmd->attach, typeof(*attach), attach);
+ struct cxl_port *endpoint = cxlmd->endpoint;
+ struct cxl_endpoint_decoder *cxled;
+ struct cxl_region *cxlr;
+ int rc;
+
+ /* hold endpoint lock to setup autoremove of the region */
+ guard(device)(&endpoint->dev);
+ if (!endpoint->dev.driver)
+ return -ENXIO;
+ guard(rwsem_read)(&cxl_rwsem.region);
+ guard(rwsem_read)(&cxl_rwsem.dpa);
+
+ /*
+ * TODO auto-instantiate a region, for now assume this will find an
+ * auto-region
+ */
+ struct device *dev __free(put_device) =
+ device_find_child(&endpoint->dev, NULL, first_mapped_decoder);
+
+ if (!dev) {
+ dev_dbg(cxlmd->cxlds->dev, "no region found for memdev %s\n",
+ dev_name(&cxlmd->dev));
+ return -ENXIO;
+ }
+
+ cxled = to_cxl_endpoint_decoder(dev);
+ cxlr = cxled->cxld.region;
+
+ if (cxlr->params.state < CXL_CONFIG_COMMIT) {
+ dev_dbg(cxlmd->cxlds->dev,
+ "region %s not committed for memdev %s\n",
+ dev_name(&cxlr->dev), dev_name(&cxlmd->dev));
+ return -ENXIO;
+ }
+
+ if (cxlr->params.nr_targets > 1) {
+ dev_dbg(cxlmd->cxlds->dev,
+ "Only attach to local non-interleaved region\n");
+ return -ENXIO;
+ }
+
+ /* Only teardown regions that pass validation, ignore the rest */
+ get_device(&cxlr->dev);
+ rc = devm_add_action_or_reset(&endpoint->dev,
+ endpoint_unregister_region, cxlr);
+ if (rc)
+ return rc;
+
+ attach->hpa_range = (struct range) {
+ .start = cxlr->params.res->start,
+ .end = cxlr->params.res->end,
+ };
+ return 0;
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_memdev_attach_region, "cxl_mem");
+
+/*
+ * The presence of an attach method indicates that the region is designated for
+ * a purpose outside of CXL core memory expansion defaults.
+ */
+static bool cxl_region_has_memdev_attach(struct cxl_region *cxlr)
+{
+ struct cxl_region_params *p = &cxlr->params;
+
+ for (int i = 0; i < p->nr_targets; i++) {
+ struct cxl_endpoint_decoder *cxled = p->targets[i];
+ struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
+
+ if (cxlmd->attach)
+ return true;
+ }
+
+ return false;
+}
+
static int cxl_region_probe(struct device *dev)
{
struct cxl_region *cxlr = to_cxl_region(dev);
@@ -4086,6 +4221,9 @@ static int cxl_region_probe(struct device *dev)
if (rc)
return rc;
+ if (cxl_region_has_memdev_attach(cxlr))
+ return 0;
+
switch (cxlr->mode) {
case CXL_PARTMODE_PMEM:
rc = devm_cxl_region_edac_register(cxlr);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 4a884821ff7c..c0e5308e4d1b 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -371,7 +371,9 @@ struct cxl_rd_ops {
* @cache_size: extended linear cache size if exists, otherwise zero.
* @region_id: region id for next region provisioning event
* @platform_data: platform specific configuration data
- * @range_lock: sync region autodiscovery by address range
+ * @regions_lock: sync region discovery, construction, and deletion
+ * @regions: regions to remove at root decoder destruct time
+ * @dead: root decoder dead to region creation
* @qos_class: QoS performance class cookie
* @ops: CXL root decoder operations
* @cxlsd: base cxl switch decoder
@@ -381,7 +383,9 @@ struct cxl_root_decoder {
resource_size_t cache_size;
atomic_t region_id;
void *platform_data;
- struct mutex range_lock;
+ struct mutex regions_lock;
+ struct xarray regions;
+ bool dead;
int qos_class;
struct cxl_rd_ops ops;
struct cxl_switch_decoder cxlsd;
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 776c50d1db51..ed419d0c59f2 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -34,10 +34,6 @@
(FIELD_GET(CXLMDEV_RESET_NEEDED_MASK, status) != \
CXLMDEV_RESET_NEEDED_NOT)
-struct cxl_memdev_attach {
- int (*probe)(struct cxl_memdev *cxlmd);
-};
-
/**
* struct cxl_memdev - CXL bus object representing a Type-3 Memory Device
* @dev: driver core device object
@@ -101,10 +97,36 @@ static inline bool is_cxl_endpoint(struct cxl_port *port)
return is_cxl_memdev(port->uport_dev);
}
+struct cxl_memdev_attach {
+ int (*probe)(struct cxl_memdev *cxlmd);
+};
+
+/**
+ * struct cxl_attach_region - coordinate mapping a region at memdev registration
+ * @attach: common core attachment descriptor
+ * @hpa_range: physical address range of the region
+ *
+ * For the common simple case of a CXL device with private (non-general purpose
+ * / "accelerator") memory, enumerate firmware instantiated region, or
+ * instantiate a region for the device's capacity. Destroy the region on detach.
+ */
+struct cxl_attach_region {
+ struct cxl_memdev_attach attach;
+ struct range hpa_range;
+};
+
+#ifdef CONFIG_CXL_REGION
+int cxl_memdev_attach_region(struct cxl_memdev *cxlmd);
+#else
+static inline int cxl_memdev_attach_region(struct cxl_memdev *cxlmd)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+struct cxl_memdev *devm_cxl_add_classdev(struct cxl_dev_state *cxlds);
struct cxl_memdev *__devm_cxl_add_memdev(struct cxl_dev_state *cxlds,
const struct cxl_memdev_attach *attach);
-struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds,
- const struct cxl_memdev_attach *attach);
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 ab88eaa31d1d..798e5c369cfc 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -75,6 +75,26 @@ static int cxl_debugfs_poison_clear(void *data, u64 dpa)
DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_clear_fops, NULL,
cxl_debugfs_poison_clear, "%llx\n");
+static void cxl_memdev_poison_enable(struct cxl_memdev_state *mds,
+ struct cxl_memdev *cxlmd,
+ struct dentry *dentry)
+{
+ /*
+ * Avoid poison debugfs for DEVMEM aka accelerators as they rely on
+ * cxl_memdev_state.
+ */
+ if (!mds)
+ return;
+
+ if (test_bit(CXL_POISON_ENABLED_INJECT, mds->poison.enabled_cmds))
+ debugfs_create_file("inject_poison", 0200, dentry, cxlmd,
+ &cxl_poison_inject_fops);
+
+ if (test_bit(CXL_POISON_ENABLED_CLEAR, mds->poison.enabled_cmds))
+ debugfs_create_file("clear_poison", 0200, dentry, cxlmd,
+ &cxl_poison_clear_fops);
+}
+
static int cxl_mem_probe(struct device *dev)
{
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
@@ -102,12 +122,7 @@ static int cxl_mem_probe(struct device *dev)
dentry = cxl_debugfs_create_dir(dev_name(dev));
debugfs_create_devm_seqfile(dev, "dpamem", dentry, cxl_mem_dpa_show);
- if (test_bit(CXL_POISON_ENABLED_INJECT, mds->poison.enabled_cmds))
- debugfs_create_file("inject_poison", 0200, dentry, cxlmd,
- &cxl_poison_inject_fops);
- if (test_bit(CXL_POISON_ENABLED_CLEAR, mds->poison.enabled_cmds))
- debugfs_create_file("clear_poison", 0200, dentry, cxlmd,
- &cxl_poison_clear_fops);
+ cxl_memdev_poison_enable(mds, cxlmd, dentry);
rc = devm_add_action_or_reset(dev, remove_debugfs, dentry);
if (rc)
@@ -178,27 +193,59 @@ static int cxl_mem_probe(struct device *dev)
}
/**
- * devm_cxl_add_memdev - Add a CXL memory device
+ * devm_cxl_add_classdev - Add a CXL memory class-code device
* @cxlds: CXL device state to associate with the memdev
- * @attach: Caller depends on CXL topology attachment
*
* Upon return the device will have had a chance to attach to the
* cxl_mem driver, but may fail to attach if the CXL topology is not ready
* (hardware CXL link down, or software platform CXL root not attached).
*
- * When @attach is NULL it indicates the caller wants the memdev to remain
- * registered even if it does not immediately attach to the CXL hierarchy. When
- * @attach is provided a cxl_mem_probe() failure leads to failure of this routine.
+ * The parent of the resulting device and the devm context for allocations is
+ * @cxlds->dev.
+ */
+struct cxl_memdev *devm_cxl_add_classdev(struct cxl_dev_state *cxlds)
+{
+ return __devm_cxl_add_memdev(cxlds, NULL);
+}
+EXPORT_SYMBOL_NS_GPL(devm_cxl_add_classdev, "CXL");
+
+/**
+ * devm_cxl_probe_mem - Add a CXL memory device and probe its region
+ * @cxlds: CXL device state to associate with the memdev
+ * @hpa_range: CXL.mem physical address range result
+ *
+ * Upon return the device will have had a chance to attach to the
+ * cxl_mem driver, but may fail to attach if the CXL topology is not ready
+ * (hardware CXL link down, or software platform CXL root not attached).
+ *
+ * Failure to probe the memdev and/or setup a region for the memdev
+ * results in this function failing.
*
* The parent of the resulting device and the devm context for allocations is
* @cxlds->dev.
*/
-struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds,
- const struct cxl_memdev_attach *attach)
+struct cxl_memdev *devm_cxl_probe_mem(struct cxl_dev_state *cxlds,
+ struct range *hpa_range)
{
- return __devm_cxl_add_memdev(cxlds, attach);
+ struct cxl_attach_region *attach =
+ devm_kmalloc(cxlds->dev, sizeof(*attach), GFP_KERNEL);
+ struct cxl_memdev *cxlmd;
+
+ if (!attach)
+ return ERR_PTR(-ENOMEM);
+
+ *attach = (struct cxl_attach_region) {
+ .attach = {
+ .probe = cxl_memdev_attach_region,
+ },
+ .hpa_range = { 0, -1 },
+ };
+
+ cxlmd = __devm_cxl_add_memdev(cxlds, &attach->attach);
+ *hpa_range = attach->hpa_range;
+ return cxlmd;
}
-EXPORT_SYMBOL_NS_GPL(devm_cxl_add_memdev, "CXL");
+EXPORT_SYMBOL_NS_GPL(devm_cxl_probe_mem, "CXL");
static ssize_t trigger_poison_list_store(struct device *dev,
struct device_attribute *attr,
@@ -216,16 +263,24 @@ static ssize_t trigger_poison_list_store(struct device *dev,
}
static DEVICE_ATTR_WO(trigger_poison_list);
-static umode_t cxl_mem_visible(struct kobject *kobj, struct attribute *a, int n)
+static bool cxl_poison_attr_visible(struct kobject *kobj, struct attribute *a)
{
struct device *dev = kobj_to_dev(kobj);
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
- if (a == &dev_attr_trigger_poison_list.attr)
- if (!test_bit(CXL_POISON_ENABLED_LIST,
- mds->poison.enabled_cmds))
- return 0;
+ if (!mds ||
+ !test_bit(CXL_POISON_ENABLED_LIST, mds->poison.enabled_cmds))
+ return false;
+
+ return true;
+}
+
+static umode_t cxl_mem_visible(struct kobject *kobj, struct attribute *a, int n)
+{
+ if (a == &dev_attr_trigger_poison_list.attr &&
+ !cxl_poison_attr_visible(kobj, a))
+ return 0;
return a->mode;
}
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index bace662dc988..267c679b0b3c 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -878,7 +878,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(cxlds, NULL);
+ cxlmd = devm_cxl_add_classdev(cxlds);
if (IS_ERR(cxlmd))
return PTR_ERR(cxlmd);
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index fa7269154620..016c74fb747c 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -223,4 +223,7 @@ struct cxl_dev_state *_devm_cxl_dev_state_create(struct device *dev,
(drv_struct *)_devm_cxl_dev_state_create(parent, type, serial, dvsec, \
sizeof(drv_struct), mbox); \
})
+
+struct cxl_memdev *devm_cxl_probe_mem(struct cxl_dev_state *cxlds,
+ struct range *range);
#endif /* __CXL_CXL_H__ */
diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
index 739343cd5802..a1d170f88fee 100644
--- a/tools/testing/cxl/test/mem.c
+++ b/tools/testing/cxl/test/mem.c
@@ -1790,7 +1790,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
cxl_mock_add_event_logs(&mdata->mes);
- cxlmd = devm_cxl_add_memdev(cxlds, NULL);
+ cxlmd = devm_cxl_add_classdev(cxlds);
if (IS_ERR(cxlmd))
return PTR_ERR(cxlmd);