]> www.infradead.org Git - users/hch/misc.git/commitdiff
cxl/region: Add inject and clear poison by region offset
authorAlison Schofield <alison.schofield@intel.com>
Mon, 4 Aug 2025 08:00:13 +0000 (01:00 -0700)
committerDave Jiang <dave.jiang@intel.com>
Tue, 12 Aug 2025 23:02:00 +0000 (16:02 -0700)
Add CXL region debugfs attributes to inject and clear poison based
on an offset into the region. These new interfaces allow users to
operate on poison at the region level without needing to resolve
Device Physical Addresses (DPA) or target individual memdevs.

The implementation uses a new helper, region_offset_to_dpa_result()
that applies decoder interleave logic, including XOR-based address
decoding when applicable. Note that XOR decodes rely on driver
internal xormaps which are not exposed to userspace. So, this support
is not only a simplification of poison operations that could be done
using existing per memdev operations, but also it enables this
functionality for XOR interleaved regions for the first time.

New debugfs attributes are added in /sys/kernel/debug/cxl/regionX/:
inject_poison and clear_poison. These are only exposed if all memdevs
participating in the region support both inject and clear commands,
ensuring consistent and reliable behavior across multi-device regions.

If tracing is enabled, these operations are logged as cxl_poison
events in /sys/kernel/tracing/trace.

The ABI documentation warns users of the significant risks that
come with using these capabilities.

A CXL Maturity Map update shows this user flow is now supported.

Signed-off-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/f3fd8628ab57ea79704fb2d645902cd499c066af.1754290144.git.alison.schofield@intel.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Documentation/ABI/testing/debugfs-cxl
Documentation/driver-api/cxl/maturity-map.rst
drivers/cxl/core/core.h
drivers/cxl/core/memdev.c
drivers/cxl/core/region.c

index e95e21f131e96bd18169f208183f92863010ab7a..2989d4da96c1b13de5c56a80c2cf687c955e76b0 100644 (file)
@@ -19,6 +19,20 @@ Description:
                is returned to the user. The inject_poison attribute is only
                visible for devices supporting the capability.
 
+               TEST-ONLY INTERFACE: This interface is intended for testing
+               and validation purposes only. It is not a data repair mechanism
+               and should never be used on production systems or live data.
+
+               DATA LOSS RISK: For CXL persistent memory (PMEM) devices,
+               poison injection can result in permanent data loss. Injected
+               poison may render data permanently inaccessible even after
+               clearing, as the clear operation writes zeros and does not
+               recover original data.
+
+               SYSTEM STABILITY RISK: For volatile memory, poison injection
+               can cause kernel crashes, system instability, or unpredictable
+               behavior if the poisoned addresses are accessed by running code
+               or critical kernel structures.
 
 What:          /sys/kernel/debug/cxl/memX/clear_poison
 Date:          April, 2023
@@ -35,6 +49,79 @@ Description:
                The clear_poison attribute is only visible for devices
                supporting the capability.
 
+               TEST-ONLY INTERFACE: This interface is intended for testing
+               and validation purposes only. It is not a data repair mechanism
+               and should never be used on production systems or live data.
+
+               CLEAR IS NOT DATA RECOVERY: This operation writes zeros to the
+               specified address range and removes the address from the poison
+               list. It does NOT recover or restore original data that may have
+               been present before poison injection. Any original data at the
+               cleared address is permanently lost and replaced with zeros.
+
+               CLEAR IS NOT A REPAIR MECHANISM: This interface is for testing
+               purposes only and should not be used as a data repair tool.
+               Clearing poison is fundamentally different from data recovery
+               or error correction.
+
+What:          /sys/kernel/debug/cxl/regionX/inject_poison
+Date:          August, 2025
+Contact:       linux-cxl@vger.kernel.org
+Description:
+               (WO) When a Host Physical Address (HPA) is written to this
+               attribute, the region driver translates it to a Device
+               Physical Address (DPA) and identifies the corresponding
+               memdev. It then sends an inject poison command to that memdev
+               at the translated DPA. Refer to the memdev ABI entry at:
+               /sys/kernel/debug/cxl/memX/inject_poison for the detailed
+               behavior. This attribute is only visible if all memdevs
+               participating in the region support both inject and clear
+               poison commands.
+
+               TEST-ONLY INTERFACE: This interface is intended for testing
+               and validation purposes only. It is not a data repair mechanism
+               and should never be used on production systems or live data.
+
+               DATA LOSS RISK: For CXL persistent memory (PMEM) devices,
+               poison injection can result in permanent data loss. Injected
+               poison may render data permanently inaccessible even after
+               clearing, as the clear operation writes zeros and does not
+               recover original data.
+
+               SYSTEM STABILITY RISK: For volatile memory, poison injection
+               can cause kernel crashes, system instability, or unpredictable
+               behavior if the poisoned addresses are accessed by running code
+               or critical kernel structures.
+
+What:          /sys/kernel/debug/cxl/regionX/clear_poison
+Date:          August, 2025
+Contact:       linux-cxl@vger.kernel.org
+Description:
+               (WO) When a Host Physical Address (HPA) is written to this
+               attribute, the region driver translates it to a Device
+               Physical Address (DPA) and identifies the corresponding
+               memdev. It then sends a clear poison command to that memdev
+               at the translated DPA. Refer to the memdev ABI entry at:
+               /sys/kernel/debug/cxl/memX/clear_poison for the detailed
+               behavior. This attribute is only visible if all memdevs
+               participating in the region support both inject and clear
+               poison commands.
+
+               TEST-ONLY INTERFACE: This interface is intended for testing
+               and validation purposes only. It is not a data repair mechanism
+               and should never be used on production systems or live data.
+
+               CLEAR IS NOT DATA RECOVERY: This operation writes zeros to the
+               specified address range and removes the address from the poison
+               list. It does NOT recover or restore original data that may have
+               been present before poison injection. Any original data at the
+               cleared address is permanently lost and replaced with zeros.
+
+               CLEAR IS NOT A REPAIR MECHANISM: This interface is for testing
+               purposes only and should not be used as a data repair tool.
+               Clearing poison is fundamentally different from data recovery
+               or error correction.
+
 What:          /sys/kernel/debug/cxl/einj_types
 Date:          January, 2024
 KernelVersion: v6.9
index 1330f3f52129a58fa8b09754212ae87fc4e60812..282c1102dd819c798d424484c6bd5ab0aec3ed89 100644 (file)
@@ -173,7 +173,7 @@ Accelerator
 User Flow Support
 -----------------
 
-* [0] Inject & clear poison by HPA
+* [2] Inject & clear poison by region offset
 
 Details
 =======
index 2669f251d677566c9d69484e4e9cb53226056380..eac8cc1bdaa0738174f22db4fe2e379aea040001 100644 (file)
@@ -135,6 +135,10 @@ enum cxl_poison_trace_type {
        CXL_POISON_TRACE_CLEAR,
 };
 
+enum poison_cmd_enabled_bits;
+bool cxl_memdev_has_poison_cmd(struct cxl_memdev *cxlmd,
+                              enum poison_cmd_enabled_bits cmd);
+
 long cxl_pci_get_latency(struct pci_dev *pdev);
 int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c);
 int cxl_update_hmat_access_coordinates(int nid, struct cxl_region *cxlr,
index 90d3390d9c7c666ac2d610d477ba5199c69ff2f0..e370d733e440013a6e5dbbbdf50f29dc49553ed2 100644 (file)
@@ -200,6 +200,14 @@ static ssize_t security_erase_store(struct device *dev,
 static struct device_attribute dev_attr_security_erase =
        __ATTR(erase, 0200, NULL, security_erase_store);
 
+bool cxl_memdev_has_poison_cmd(struct cxl_memdev *cxlmd,
+                              enum poison_cmd_enabled_bits cmd)
+{
+       struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
+
+       return test_bit(cmd, mds->poison.enabled_cmds);
+}
+
 static int cxl_get_poison_by_memdev(struct cxl_memdev *cxlmd)
 {
        struct cxl_dev_state *cxlds = cxlmd->cxlds;
index f492d1291e728e9dd8fabbd457d0a14784889a6b..1e2911af90605b60511c9d005ff938f7c16c2567 100644 (file)
@@ -2,6 +2,7 @@
 /* Copyright(c) 2022 Intel Corporation. All rights reserved. */
 #include <linux/memregion.h>
 #include <linux/genalloc.h>
+#include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/module.h>
 #include <linux/memory.h>
@@ -3003,9 +3004,8 @@ struct dpa_result {
        u64 dpa;
 };
 
-static int __maybe_unused region_offset_to_dpa_result(struct cxl_region *cxlr,
-                                                     u64 offset,
-                                                     struct dpa_result *result)
+static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
+                                      struct dpa_result *result)
 {
        struct cxl_region_params *p = &cxlr->params;
        struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
@@ -3648,6 +3648,105 @@ static void shutdown_notifiers(void *_cxlr)
        unregister_mt_adistance_algorithm(&cxlr->adist_notifier);
 }
 
+static void remove_debugfs(void *dentry)
+{
+       debugfs_remove_recursive(dentry);
+}
+
+static int validate_region_offset(struct cxl_region *cxlr, u64 offset)
+{
+       struct cxl_region_params *p = &cxlr->params;
+       resource_size_t region_size;
+       u64 hpa;
+
+       if (offset < p->cache_size) {
+               dev_err(&cxlr->dev,
+                       "Offset %#llx is within extended linear cache %#llx\n",
+                       offset, p->cache_size);
+               return -EINVAL;
+       }
+
+       region_size = resource_size(p->res);
+       if (offset >= region_size) {
+               dev_err(&cxlr->dev, "Offset %#llx exceeds region size %#llx\n",
+                       offset, region_size);
+               return -EINVAL;
+       }
+
+       hpa = p->res->start + offset;
+       if (hpa < p->res->start || hpa > p->res->end) {
+               dev_err(&cxlr->dev, "HPA %#llx not in region %pr\n", hpa,
+                       p->res);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int cxl_region_debugfs_poison_inject(void *data, u64 offset)
+{
+       struct dpa_result result = { .dpa = ULLONG_MAX, .cxlmd = NULL };
+       struct cxl_region *cxlr = data;
+       int rc;
+
+       ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
+       if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
+               return rc;
+
+       ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
+       if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
+               return rc;
+
+       if (validate_region_offset(cxlr, offset))
+               return -EINVAL;
+
+       rc = region_offset_to_dpa_result(cxlr, offset, &result);
+       if (rc || !result.cxlmd || result.dpa == ULLONG_MAX) {
+               dev_dbg(&cxlr->dev,
+                       "Failed to resolve DPA for region offset %#llx rc %d\n",
+                       offset, rc);
+
+               return rc ? rc : -EINVAL;
+       }
+
+       return cxl_inject_poison_locked(result.cxlmd, result.dpa);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_inject_fops, NULL,
+                        cxl_region_debugfs_poison_inject, "%llx\n");
+
+static int cxl_region_debugfs_poison_clear(void *data, u64 offset)
+{
+       struct dpa_result result = { .dpa = ULLONG_MAX, .cxlmd = NULL };
+       struct cxl_region *cxlr = data;
+       int rc;
+
+       ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
+       if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
+               return rc;
+
+       ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
+       if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
+               return rc;
+
+       if (validate_region_offset(cxlr, offset))
+               return -EINVAL;
+
+       rc = region_offset_to_dpa_result(cxlr, offset, &result);
+       if (rc || !result.cxlmd || result.dpa == ULLONG_MAX) {
+               dev_dbg(&cxlr->dev,
+                       "Failed to resolve DPA for region offset %#llx rc %d\n",
+                       offset, rc);
+
+               return rc ? rc : -EINVAL;
+       }
+
+       return cxl_clear_poison_locked(result.cxlmd, result.dpa);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_clear_fops, NULL,
+                        cxl_region_debugfs_poison_clear, "%llx\n");
+
 static int cxl_region_can_probe(struct cxl_region *cxlr)
 {
        struct cxl_region_params *p = &cxlr->params;
@@ -3677,6 +3776,7 @@ static int cxl_region_probe(struct device *dev)
 {
        struct cxl_region *cxlr = to_cxl_region(dev);
        struct cxl_region_params *p = &cxlr->params;
+       bool poison_supported = true;
        int rc;
 
        rc = cxl_region_can_probe(cxlr);
@@ -3700,6 +3800,31 @@ static int cxl_region_probe(struct device *dev)
        if (rc)
                return rc;
 
+       /* Create poison attributes if all memdevs support the capabilities */
+       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 (!cxl_memdev_has_poison_cmd(cxlmd, CXL_POISON_ENABLED_INJECT) ||
+                   !cxl_memdev_has_poison_cmd(cxlmd, CXL_POISON_ENABLED_CLEAR)) {
+                       poison_supported = false;
+                       break;
+               }
+       }
+
+       if (poison_supported) {
+               struct dentry *dentry;
+
+               dentry = cxl_debugfs_create_dir(dev_name(dev));
+               debugfs_create_file("inject_poison", 0200, dentry, cxlr,
+                                   &cxl_poison_inject_fops);
+               debugfs_create_file("clear_poison", 0200, dentry, cxlr,
+                                   &cxl_poison_clear_fops);
+               rc = devm_add_action_or_reset(dev, remove_debugfs, dentry);
+               if (rc)
+                       return rc;
+       }
+
        switch (cxlr->mode) {
        case CXL_PARTMODE_PMEM:
                rc = devm_cxl_region_edac_register(cxlr);