Skip to content

Commit d9412f0

Browse files
committed
Merge branch 'for-6.18/cxl-poison-inject' into cxl-for-next
Add support to allow expert users to inject and clear poison for the CXL subsystem by writing a System Physical Address (SPA) to a debugfs file.
2 parents 733c4e9 + c3dd676 commit d9412f0

9 files changed

Lines changed: 406 additions & 36 deletions

File tree

Documentation/ABI/testing/debugfs-cxl

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@ Description:
1919
is returned to the user. The inject_poison attribute is only
2020
visible for devices supporting the capability.
2121

22+
TEST-ONLY INTERFACE: This interface is intended for testing
23+
and validation purposes only. It is not a data repair mechanism
24+
and should never be used on production systems or live data.
25+
26+
DATA LOSS RISK: For CXL persistent memory (PMEM) devices,
27+
poison injection can result in permanent data loss. Injected
28+
poison may render data permanently inaccessible even after
29+
clearing, as the clear operation writes zeros and does not
30+
recover original data.
31+
32+
SYSTEM STABILITY RISK: For volatile memory, poison injection
33+
can cause kernel crashes, system instability, or unpredictable
34+
behavior if the poisoned addresses are accessed by running code
35+
or critical kernel structures.
2236

2337
What: /sys/kernel/debug/cxl/memX/clear_poison
2438
Date: April, 2023
@@ -35,6 +49,79 @@ Description:
3549
The clear_poison attribute is only visible for devices
3650
supporting the capability.
3751

52+
TEST-ONLY INTERFACE: This interface is intended for testing
53+
and validation purposes only. It is not a data repair mechanism
54+
and should never be used on production systems or live data.
55+
56+
CLEAR IS NOT DATA RECOVERY: This operation writes zeros to the
57+
specified address range and removes the address from the poison
58+
list. It does NOT recover or restore original data that may have
59+
been present before poison injection. Any original data at the
60+
cleared address is permanently lost and replaced with zeros.
61+
62+
CLEAR IS NOT A REPAIR MECHANISM: This interface is for testing
63+
purposes only and should not be used as a data repair tool.
64+
Clearing poison is fundamentally different from data recovery
65+
or error correction.
66+
67+
What: /sys/kernel/debug/cxl/regionX/inject_poison
68+
Date: August, 2025
69+
Contact: linux-cxl@vger.kernel.org
70+
Description:
71+
(WO) When a Host Physical Address (HPA) is written to this
72+
attribute, the region driver translates it to a Device
73+
Physical Address (DPA) and identifies the corresponding
74+
memdev. It then sends an inject poison command to that memdev
75+
at the translated DPA. Refer to the memdev ABI entry at:
76+
/sys/kernel/debug/cxl/memX/inject_poison for the detailed
77+
behavior. This attribute is only visible if all memdevs
78+
participating in the region support both inject and clear
79+
poison commands.
80+
81+
TEST-ONLY INTERFACE: This interface is intended for testing
82+
and validation purposes only. It is not a data repair mechanism
83+
and should never be used on production systems or live data.
84+
85+
DATA LOSS RISK: For CXL persistent memory (PMEM) devices,
86+
poison injection can result in permanent data loss. Injected
87+
poison may render data permanently inaccessible even after
88+
clearing, as the clear operation writes zeros and does not
89+
recover original data.
90+
91+
SYSTEM STABILITY RISK: For volatile memory, poison injection
92+
can cause kernel crashes, system instability, or unpredictable
93+
behavior if the poisoned addresses are accessed by running code
94+
or critical kernel structures.
95+
96+
What: /sys/kernel/debug/cxl/regionX/clear_poison
97+
Date: August, 2025
98+
Contact: linux-cxl@vger.kernel.org
99+
Description:
100+
(WO) When a Host Physical Address (HPA) is written to this
101+
attribute, the region driver translates it to a Device
102+
Physical Address (DPA) and identifies the corresponding
103+
memdev. It then sends a clear poison command to that memdev
104+
at the translated DPA. Refer to the memdev ABI entry at:
105+
/sys/kernel/debug/cxl/memX/clear_poison for the detailed
106+
behavior. This attribute is only visible if all memdevs
107+
participating in the region support both inject and clear
108+
poison commands.
109+
110+
TEST-ONLY INTERFACE: This interface is intended for testing
111+
and validation purposes only. It is not a data repair mechanism
112+
and should never be used on production systems or live data.
113+
114+
CLEAR IS NOT DATA RECOVERY: This operation writes zeros to the
115+
specified address range and removes the address from the poison
116+
list. It does NOT recover or restore original data that may have
117+
been present before poison injection. Any original data at the
118+
cleared address is permanently lost and replaced with zeros.
119+
120+
CLEAR IS NOT A REPAIR MECHANISM: This interface is for testing
121+
purposes only and should not be used as a data repair tool.
122+
Clearing poison is fundamentally different from data recovery
123+
or error correction.
124+
38125
What: /sys/kernel/debug/cxl/einj_types
39126
Date: January, 2024
40127
KernelVersion: v6.9

Documentation/driver-api/cxl/maturity-map.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ Accelerator
173173
User Flow Support
174174
-----------------
175175

176-
* [0] Inject & clear poison by HPA
176+
* [2] Inject & clear poison by region offset
177177

178178
Details
179179
=======

drivers/cxl/acpi.c

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ static const guid_t acpi_cxl_qtg_id_guid =
2020
GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071,
2121
0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52);
2222

23-
24-
static u64 cxl_xor_hpa_to_spa(struct cxl_root_decoder *cxlrd, u64 hpa)
23+
static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
2524
{
2625
struct cxl_cxims_data *cximsd = cxlrd->platform_data;
2726
int hbiw = cxlrd->cxlsd.nr_targets;
@@ -30,19 +29,23 @@ static u64 cxl_xor_hpa_to_spa(struct cxl_root_decoder *cxlrd, u64 hpa)
3029

3130
/* No xormaps for host bridge interleave ways of 1 or 3 */
3231
if (hbiw == 1 || hbiw == 3)
33-
return hpa;
32+
return addr;
3433

3534
/*
36-
* For root decoders using xormaps (hbiw: 2,4,6,8,12,16) restore
37-
* the position bit to its value before the xormap was applied at
38-
* HPA->DPA translation.
35+
* In regions using XOR interleave arithmetic the CXL HPA may not
36+
* be the same as the SPA. This helper performs the SPA->CXL HPA
37+
* or the CXL HPA->SPA translation. Since XOR is self-inverting,
38+
* so is this function.
39+
*
40+
* For root decoders using xormaps (hbiw: 2,4,6,8,12,16) applying the
41+
* xormaps will toggle a position bit.
3942
*
4043
* pos is the lowest set bit in an XORMAP
41-
* val is the XORALLBITS(HPA & XORMAP)
44+
* val is the XORALLBITS(addr & XORMAP)
4245
*
4346
* XORALLBITS: The CXL spec (3.1 Table 9-22) defines XORALLBITS
4447
* as an operation that outputs a single bit by XORing all the
45-
* bits in the input (hpa & xormap). Implement XORALLBITS using
48+
* bits in the input (addr & xormap). Implement XORALLBITS using
4649
* hweight64(). If the hamming weight is even the XOR of those
4750
* bits results in val==0, if odd the XOR result is val==1.
4851
*/
@@ -51,11 +54,11 @@ static u64 cxl_xor_hpa_to_spa(struct cxl_root_decoder *cxlrd, u64 hpa)
5154
if (!cximsd->xormaps[i])
5255
continue;
5356
pos = __ffs(cximsd->xormaps[i]);
54-
val = (hweight64(hpa & cximsd->xormaps[i]) & 1);
55-
hpa = (hpa & ~(1ULL << pos)) | (val << pos);
57+
val = (hweight64(addr & cximsd->xormaps[i]) & 1);
58+
addr = (addr & ~(1ULL << pos)) | (val << pos);
5659
}
5760

58-
return hpa;
61+
return addr;
5962
}
6063

6164
struct cxl_cxims_context {
@@ -472,8 +475,14 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
472475

473476
cxlrd->qos_class = cfmws->qtg_id;
474477

475-
if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_XOR)
476-
cxlrd->hpa_to_spa = cxl_xor_hpa_to_spa;
478+
if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_XOR) {
479+
cxlrd->ops = kzalloc(sizeof(*cxlrd->ops), GFP_KERNEL);
480+
if (!cxlrd->ops)
481+
return -ENOMEM;
482+
483+
cxlrd->ops->hpa_to_spa = cxl_apply_xor_maps;
484+
cxlrd->ops->spa_to_hpa = cxl_apply_xor_maps;
485+
}
477486

478487
rc = cxl_decoder_add(cxld, target_map);
479488
if (rc)

drivers/cxl/core/core.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ enum cxl_poison_trace_type {
135135
CXL_POISON_TRACE_CLEAR,
136136
};
137137

138+
enum poison_cmd_enabled_bits;
139+
bool cxl_memdev_has_poison_cmd(struct cxl_memdev *cxlmd,
140+
enum poison_cmd_enabled_bits cmd);
141+
138142
long cxl_pci_get_latency(struct pci_dev *pdev);
139143
int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c);
140144
int cxl_update_hmat_access_coordinates(int nid, struct cxl_region *cxlr,

drivers/cxl/core/memdev.c

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ static ssize_t security_erase_store(struct device *dev,
200200
static struct device_attribute dev_attr_security_erase =
201201
__ATTR(erase, 0200, NULL, security_erase_store);
202202

203+
bool cxl_memdev_has_poison_cmd(struct cxl_memdev *cxlmd,
204+
enum poison_cmd_enabled_bits cmd)
205+
{
206+
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
207+
208+
return test_bit(cmd, mds->poison.enabled_cmds);
209+
}
210+
203211
static int cxl_get_poison_by_memdev(struct cxl_memdev *cxlmd)
204212
{
205213
struct cxl_dev_state *cxlds = cxlmd->cxlds;
@@ -276,7 +284,7 @@ static int cxl_validate_poison_dpa(struct cxl_memdev *cxlmd, u64 dpa)
276284
return 0;
277285
}
278286

279-
int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
287+
int cxl_inject_poison_locked(struct cxl_memdev *cxlmd, u64 dpa)
280288
{
281289
struct cxl_mailbox *cxl_mbox = &cxlmd->cxlds->cxl_mbox;
282290
struct cxl_mbox_inject_poison inject;
@@ -288,13 +296,8 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
288296
if (!IS_ENABLED(CONFIG_DEBUG_FS))
289297
return 0;
290298

291-
ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
292-
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
293-
return rc;
294-
295-
ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
296-
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
297-
return rc;
299+
lockdep_assert_held(&cxl_rwsem.dpa);
300+
lockdep_assert_held(&cxl_rwsem.region);
298301

299302
rc = cxl_validate_poison_dpa(cxlmd, dpa);
300303
if (rc)
@@ -324,9 +327,24 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
324327

325328
return 0;
326329
}
330+
331+
int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
332+
{
333+
int rc;
334+
335+
ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
336+
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
337+
return rc;
338+
339+
ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
340+
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
341+
return rc;
342+
343+
return cxl_inject_poison_locked(cxlmd, dpa);
344+
}
327345
EXPORT_SYMBOL_NS_GPL(cxl_inject_poison, "CXL");
328346

329-
int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
347+
int cxl_clear_poison_locked(struct cxl_memdev *cxlmd, u64 dpa)
330348
{
331349
struct cxl_mailbox *cxl_mbox = &cxlmd->cxlds->cxl_mbox;
332350
struct cxl_mbox_clear_poison clear;
@@ -338,13 +356,8 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
338356
if (!IS_ENABLED(CONFIG_DEBUG_FS))
339357
return 0;
340358

341-
ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
342-
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
343-
return rc;
344-
345-
ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
346-
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
347-
return rc;
359+
lockdep_assert_held(&cxl_rwsem.dpa);
360+
lockdep_assert_held(&cxl_rwsem.region);
348361

349362
rc = cxl_validate_poison_dpa(cxlmd, dpa);
350363
if (rc)
@@ -383,6 +396,21 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
383396

384397
return 0;
385398
}
399+
400+
int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
401+
{
402+
int rc;
403+
404+
ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
405+
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
406+
return rc;
407+
408+
ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
409+
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
410+
return rc;
411+
412+
return cxl_clear_poison_locked(cxlmd, dpa);
413+
}
386414
EXPORT_SYMBOL_NS_GPL(cxl_clear_poison, "CXL");
387415

388416
static struct attribute *cxl_memdev_attributes[] = {

drivers/cxl/core/port.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ static void cxl_root_decoder_release(struct device *dev)
450450
if (atomic_read(&cxlrd->region_id) >= 0)
451451
memregion_free(atomic_read(&cxlrd->region_id));
452452
__cxl_decoder_release(&cxlrd->cxlsd.cxld);
453+
kfree(cxlrd->ops);
453454
kfree(cxlrd);
454455
}
455456

0 commit comments

Comments
 (0)