* [PATCH v4 0/2] cxl/edac: Fix event record lookups for memory repair @ 2026-11-02 12:32 Jongwon Lee 2026-11-02 12:32 ` [PATCH v4 1/2] cxl/edac: Read cached DRAM event records under the xarray lock Jongwon Lee 2026-11-02 12:32 ` [PATCH v4 2/2] cxl/edac: Mask DPA flags in the event record cache key Jongwon Lee 0 siblings, 2 replies; 9+ messages in thread From: Jongwon Lee @ 2026-11-02 12:32 UTC (permalink / raw) To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma, Dan Williams Cc: Ira Weiny, Li Ming, Richard Cheng, linux-cxl, linux-kernel Patch 1 fixes a use-after-free between the repair path and the event path. Sashiko's review of v2 pointed out that the repair path reads cached DRAM event records with no lock that keeps the event path from freeing them [1]. The race was already there in cxl_find_rec_dram() and cxl_mem_perform_sparing(). Patch 1 does the DRAM lookup under the xarray lock and copies the matching record out for the sparing path. Patch 2 is the DPA flag masking fix from v2. It also does the General Media lookup under the same lock, since v2 added a read of that record. I used xa_lock() instead of RCU freeing. The cached records are copies of the spec-defined event structures, so kfree_rcu() would need a wrapper struct with an rcu_head, and the lookups only compare a few fields. Taking device_lock() in the repair path, as the event path does, looked like it could deadlock against unbind, where the EDAC sysfs files are removed with device_lock() held. Changes in v4: - Patch 1: copy the whole struct cxl_event_dram out instead of only validity_flags (Richard Cheng) - Patch 2: make cxl_rec_key() and cxl_rec_not_repairable() static inline (Richard Cheng) - Cover letter: describe patch 1 first (Richard Cheng) Changes in v3: - New patch 1 for the record lifetime race (Sashiko) - Patch 2: General Media lookup under the xarray lock, returns bool Changes in v2: - Moved the Claude Code note below the --- (Dave Jiang) v3: https://lore.kernel.org/r/20261101013418.87326-1-ai@linux.com v2: https://lore.kernel.org/r/20261031161121.15948-1-ai@linux.com v1: https://lore.kernel.org/r/20261030072721.31970-1-ai@linux.com Testing, QEMU master (b574eaf) with a CXL type 3 device and a committed region, on top of d89e1d186e. Debug options: KASAN, PROVE_LOCKING, PROVE_RAW_LOCK_NESTING, PROVE_RCU, DEBUG_ATOMIC_SLEEP, DEBUG_LIST, UBSAN_BOUNDS, KMEMLEAK, hung task and soft lockup detection, booted with preempt=full. - Repair table: DRAM events at 0x1001, 0x2000 and 0x3002 and General Media events at 0x5001 and 0x6002, then a repair request on all five mem_repair devices for eight DPAs. With v4, 0x1000 and 0x2000 succeed for all five repair types and 0x5000 succeeds for PPR only. 0x3000 and 0x6000 (NOT_REPAIRABLE set), 0x7000 (no record), 0x1001 and 0x3002 are rejected with -EINVAL. Unpatched, 0x1000 and the PPR request at 0x5000 fail, and 0x1001 and 0x3002 are accepted. - Race test with a test-only mdelay(20): the host injects DRAM and General Media events with no gap, repeating some DPAs so that xa_store() replaces and frees records and adding enough distinct DPAs to reach the overflow path, while four guest loops request repairs on all five devices. Unpatched, delay after xa_load() and after the lookup in the sparing path: KASAN slab-use-after-free in cxl_do_mem_sparing() after 120 s, a 2-byte read of validity_flags in a record freed by cxl_store_rec_dram(). v4, delay after both xa_load() calls inside the locked section and after the lookup in the sparing path: 600 s, 11070 repair requests, 261973 events, no report. - The same race test without the delay: v4 for 1800 s, 56460 repair requests and 786023 events; unpatched for 900 s, 24150 repair requests and 301280 events. Neither kernel reported anything. - Unbind test: two repair loops and the event injection as above for 600 s while a loop unbinds cxl_mem, waits 2 s, binds it and waits 3 s. v4: 78 unbind/bind cycles, 7248 repair requests, no report, mem0 bound at the end. - kmemleak scan at the end of each run: no unreferenced objects. - Builds: W=1 after each patch, sparse, clang W=1 for x86_64 and arm64, and CONFIG_CXL_EDAC_MEM_REPAIR=n, with no new warnings. The series applies to cxl/next (cf15f9f236) and builds there. This series was developed with Claude Code. [1] https://lore.kernel.org/r/sashiko-outbox-192042@kernel.org Jongwon Lee (2): cxl/edac: Read cached DRAM event records under the xarray lock cxl/edac: Mask DPA flags in the event record cache key drivers/cxl/core/edac.c | 115 ++++++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 39 deletions(-) base-commit: cf15f9f2361dd4002203716b7b878393c0eee4bf -- 2.55.0 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 1/2] cxl/edac: Read cached DRAM event records under the xarray lock 2026-11-02 12:32 [PATCH v4 0/2] cxl/edac: Fix event record lookups for memory repair Jongwon Lee @ 2026-11-02 12:32 ` Jongwon Lee 2026-11-05 6:00 ` Richard Cheng 2026-11-05 7:42 ` Li Ming 2026-11-02 12:32 ` [PATCH v4 2/2] cxl/edac: Mask DPA flags in the event record cache key Jongwon Lee 1 sibling, 2 replies; 9+ messages in thread From: Jongwon Lee @ 2026-11-02 12:32 UTC (permalink / raw) To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma, Dan Williams Cc: Ira Weiny, Li Ming, Richard Cheng, linux-cxl, linux-kernel The event path caches DRAM event records in an xarray and frees a record when xa_store() replaces it, when it expires and when the cache overflows. It runs with device_lock() and cxl_rwsem held for read. The repair path looks records up with cxl_find_rec_dram() while holding only cxl_rwsem for read, so the two paths can run at the same time. cxl_find_rec_dram() reads the record after xa_load() returns, and cxl_mem_perform_sparing() reads validity_flags from the returned record again later. Both reads can hit a record the event path has just freed. Move the lookup into __cxl_find_rec_dram(), called with the xarray lock held, and make cxl_find_rec_dram() return whether a record matched and copy the matching record into a buffer the caller passes in. The PPR path only needs the match and passes NULL. No caller keeps a pointer to the record after the lock is dropped. The event path frees a record only after xa_store() or xa_erase() has removed it from the xarray, and both take the same lock. Fixes: 0b5ccb0de1e2 ("cxl/edac: Support for finding memory operation attributes from the current boot") Fixes: 588ca944c277 ("cxl/edac: Add CXL memory device memory sparing control feature") Reported-by: Sashiko <sashiko-bot@kernel.org> Closes: https://lore.kernel.org/r/sashiko-outbox-192042@kernel.org Signed-off-by: Jongwon Lee <ai@linux.com> --- drivers/cxl/core/edac.c | 72 ++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c index 34b81e8dfb..c5c8dda20e 100644 --- a/drivers/cxl/core/edac.c +++ b/drivers/cxl/core/edac.c @@ -938,18 +938,17 @@ cxl_find_rec_gen_media(struct cxl_memdev *cxlmd, return NULL; } +/* Caller holds the xarray lock. The record may be freed once it is dropped. */ static struct cxl_event_dram * -cxl_find_rec_dram(struct cxl_memdev *cxlmd, - struct cxl_mem_repair_attrbs *attrbs) +__cxl_find_rec_dram(struct xarray *rec_xarray, + struct cxl_mem_repair_attrbs *attrbs) { - struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; struct cxl_event_dram *rec; u16 validity_flags; - if (!array_rec) - return NULL; + lockdep_assert_held(&rec_xarray->xa_lock); - rec = xa_load(&array_rec->rec_dram, attrbs->dpa); + rec = xa_load(rec_xarray, attrbs->dpa); if (!rec) return NULL; @@ -1029,6 +1028,32 @@ cxl_find_rec_dram(struct cxl_memdev *cxlmd, return NULL; } +/* + * cxl_store_rec_dram() replaces and frees records while the repair path only + * holds cxl_rwsem for read, so look the record up and read it under the + * xarray lock, and hand back a copy of the record. + */ +static bool cxl_find_rec_dram(struct cxl_memdev *cxlmd, + struct cxl_mem_repair_attrbs *attrbs, + struct cxl_event_dram *out) +{ + struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; + struct cxl_event_dram *rec; + bool found; + + if (!array_rec) + return false; + + xa_lock(&array_rec->rec_dram); + rec = __cxl_find_rec_dram(&array_rec->rec_dram, attrbs); + found = rec; + if (rec && out) + *out = *rec; + xa_unlock(&array_rec->rec_dram); + + return found; +} + #define CXL_MAX_STORAGE_DAYS 10 #define CXL_MAX_STORAGE_TIME_SECS (CXL_MAX_STORAGE_DAYS * 24 * 60 * 60) @@ -1298,9 +1323,9 @@ cxl_mem_sparing_get_attrbs(struct cxl_mem_sparing_context *cxl_sparing_ctx) return 0; } -static struct cxl_event_dram * -cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd, - struct cxl_mem_sparing_context *ctx) +static bool cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd, + struct cxl_mem_sparing_context *ctx, + struct cxl_event_dram *rec) { struct cxl_mem_repair_attrbs attrbs = { 0 }; @@ -1332,10 +1357,10 @@ cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd, attrbs.repair_type = CXL_RANK_SPARING; break; default: - return NULL; + return false; } - return cxl_find_rec_dram(cxlmd, &attrbs); + return cxl_find_rec_dram(cxlmd, &attrbs, rec); } static int @@ -1344,7 +1369,8 @@ cxl_mem_perform_sparing(struct device *dev, { struct cxl_memdev *cxlmd = cxl_sparing_ctx->cxlmd; struct cxl_memdev_sparing_in_payload sparing_pi; - struct cxl_event_dram *rec = NULL; + struct cxl_event_dram rec; + bool rec_found = false; u16 validity_flags = 0; int ret; @@ -1362,11 +1388,14 @@ cxl_mem_perform_sparing(struct device *dev, return -EBUSY; } else { if (cxl_is_memdev_memory_online(cxlmd)) { - rec = cxl_mem_get_rec_dram(cxlmd, cxl_sparing_ctx); - if (!rec) + rec_found = cxl_mem_get_rec_dram(cxlmd, cxl_sparing_ctx, + &rec); + if (!rec_found) return -EINVAL; - if (!get_unaligned_le16(rec->media_hdr.validity_flags)) + validity_flags = + get_unaligned_le16(rec.media_hdr.validity_flags); + if (!validity_flags) return -EINVAL; } } @@ -1376,13 +1405,10 @@ cxl_mem_perform_sparing(struct device *dev, if (cxl_sparing_ctx->persist_mode) sparing_pi.flags |= CXL_SET_HARD_SPARING(1); - if (rec) - validity_flags = get_unaligned_le16(rec->media_hdr.validity_flags); - switch (cxl_sparing_ctx->repair_type) { case EDAC_REPAIR_CACHELINE_SPARING: sparing_pi.column = cpu_to_le16(cxl_sparing_ctx->column); - if (!rec || (validity_flags & CXL_DER_VALID_SUB_CHANNEL)) { + if (!rec_found || (validity_flags & CXL_DER_VALID_SUB_CHANNEL)) { sparing_pi.flags |= CXL_SET_SPARING_SUB_CHNL_VALID(1); sparing_pi.sub_channel = cxl_sparing_ctx->sub_channel; } @@ -1399,9 +1425,9 @@ cxl_mem_perform_sparing(struct device *dev, fallthrough; default: sparing_pi.channel = cxl_sparing_ctx->channel; - if ((rec && (validity_flags & CXL_DER_VALID_NIBBLE)) || - (!rec && (!cxl_sparing_ctx->nibble_mask || - (cxl_sparing_ctx->nibble_mask & 0xFFFFFF)))) { + if ((rec_found && (validity_flags & CXL_DER_VALID_NIBBLE)) || + (!rec_found && (!cxl_sparing_ctx->nibble_mask || + (cxl_sparing_ctx->nibble_mask & 0xFFFFFF)))) { sparing_pi.flags |= CXL_SET_SPARING_NIB_MASK_VALID(1); put_unaligned_le24(cxl_sparing_ctx->nibble_mask, sparing_pi.nibble_mask); @@ -1814,7 +1840,7 @@ static int cxl_mem_perform_ppr(struct cxl_ppr_context *cxl_ppr_ctx) attrbs.repair_type = CXL_PPR; attrbs.dpa = cxl_ppr_ctx->dpa; attrbs.nibble_mask = cxl_ppr_ctx->nibble_mask; - if (!cxl_find_rec_dram(cxlmd, &attrbs) && + if (!cxl_find_rec_dram(cxlmd, &attrbs, NULL) && !cxl_find_rec_gen_media(cxlmd, &attrbs)) return -EINVAL; } -- 2.55.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/2] cxl/edac: Read cached DRAM event records under the xarray lock 2026-11-02 12:32 ` [PATCH v4 1/2] cxl/edac: Read cached DRAM event records under the xarray lock Jongwon Lee @ 2026-11-05 6:00 ` Richard Cheng 2026-11-05 7:42 ` Li Ming 1 sibling, 0 replies; 9+ messages in thread From: Richard Cheng @ 2026-11-05 6:00 UTC (permalink / raw) To: Jongwon Lee Cc: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma, Dan Williams, Ira Weiny, Li Ming, linux-cxl, linux-kernel On Mon, Nov 02, 2026 at 09:32:34PM +0800, Jongwon Lee wrote: > The event path caches DRAM event records in an xarray and frees a record > when xa_store() replaces it, when it expires and when the cache > overflows. It runs with device_lock() and cxl_rwsem held for read. > > The repair path looks records up with cxl_find_rec_dram() while holding > only cxl_rwsem for read, so the two paths can run at the same time. > cxl_find_rec_dram() reads the record after xa_load() returns, and > cxl_mem_perform_sparing() reads validity_flags from the returned record > again later. Both reads can hit a record the event path has just freed. > > Move the lookup into __cxl_find_rec_dram(), called with the xarray lock > held, and make cxl_find_rec_dram() return whether a record matched and > copy the matching record into a buffer the caller passes in. The PPR > path only needs the match and passes NULL. No caller keeps a pointer to > the record after the lock is dropped. The event path frees a record > only after xa_store() or xa_erase() has removed it from the xarray, and > both take the same lock. > > Fixes: 0b5ccb0de1e2 ("cxl/edac: Support for finding memory operation attributes from the current boot") > Fixes: 588ca944c277 ("cxl/edac: Add CXL memory device memory sparing control feature") > Reported-by: Sashiko <sashiko-bot@kernel.org> > Closes: https://lore.kernel.org/r/sashiko-outbox-192042@kernel.org > Signed-off-by: Jongwon Lee <ai@linux.com> > --- > drivers/cxl/core/edac.c | 72 ++++++++++++++++++++++++++++------------- > 1 file changed, 49 insertions(+), 23 deletions(-) > > diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c > index 34b81e8dfb..c5c8dda20e 100644 > --- a/drivers/cxl/core/edac.c > +++ b/drivers/cxl/core/edac.c > @@ -938,18 +938,17 @@ cxl_find_rec_gen_media(struct cxl_memdev *cxlmd, > return NULL; > } > > +/* Caller holds the xarray lock. The record may be freed once it is dropped. */ > static struct cxl_event_dram * > -cxl_find_rec_dram(struct cxl_memdev *cxlmd, > - struct cxl_mem_repair_attrbs *attrbs) > +__cxl_find_rec_dram(struct xarray *rec_xarray, > + struct cxl_mem_repair_attrbs *attrbs) > { > - struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; > struct cxl_event_dram *rec; > u16 validity_flags; > > - if (!array_rec) > - return NULL; > + lockdep_assert_held(&rec_xarray->xa_lock); > > - rec = xa_load(&array_rec->rec_dram, attrbs->dpa); > + rec = xa_load(rec_xarray, attrbs->dpa); > if (!rec) > return NULL; > > @@ -1029,6 +1028,32 @@ cxl_find_rec_dram(struct cxl_memdev *cxlmd, > return NULL; > } > > +/* > + * cxl_store_rec_dram() replaces and frees records while the repair path only > + * holds cxl_rwsem for read, so look the record up and read it under the > + * xarray lock, and hand back a copy of the record. > + */ > +static bool cxl_find_rec_dram(struct cxl_memdev *cxlmd, > + struct cxl_mem_repair_attrbs *attrbs, > + struct cxl_event_dram *out) > +{ > + struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; > + struct cxl_event_dram *rec; > + bool found; > + > + if (!array_rec) > + return false; > + > + xa_lock(&array_rec->rec_dram); > + rec = __cxl_find_rec_dram(&array_rec->rec_dram, attrbs); > + found = rec; > + if (rec && out) > + *out = *rec; > + xa_unlock(&array_rec->rec_dram); > + > + return found; > +} > + > #define CXL_MAX_STORAGE_DAYS 10 > #define CXL_MAX_STORAGE_TIME_SECS (CXL_MAX_STORAGE_DAYS * 24 * 60 * 60) > > @@ -1298,9 +1323,9 @@ cxl_mem_sparing_get_attrbs(struct cxl_mem_sparing_context *cxl_sparing_ctx) > return 0; > } > > -static struct cxl_event_dram * > -cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd, > - struct cxl_mem_sparing_context *ctx) > +static bool cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd, > + struct cxl_mem_sparing_context *ctx, > + struct cxl_event_dram *rec) > { > struct cxl_mem_repair_attrbs attrbs = { 0 }; > > @@ -1332,10 +1357,10 @@ cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd, > attrbs.repair_type = CXL_RANK_SPARING; > break; > default: > - return NULL; > + return false; > } > > - return cxl_find_rec_dram(cxlmd, &attrbs); > + return cxl_find_rec_dram(cxlmd, &attrbs, rec); > } > > static int > @@ -1344,7 +1369,8 @@ cxl_mem_perform_sparing(struct device *dev, > { > struct cxl_memdev *cxlmd = cxl_sparing_ctx->cxlmd; > struct cxl_memdev_sparing_in_payload sparing_pi; > - struct cxl_event_dram *rec = NULL; > + struct cxl_event_dram rec; > + bool rec_found = false; > u16 validity_flags = 0; > int ret; > > @@ -1362,11 +1388,14 @@ cxl_mem_perform_sparing(struct device *dev, > return -EBUSY; > } else { > if (cxl_is_memdev_memory_online(cxlmd)) { > - rec = cxl_mem_get_rec_dram(cxlmd, cxl_sparing_ctx); > - if (!rec) > + rec_found = cxl_mem_get_rec_dram(cxlmd, cxl_sparing_ctx, > + &rec); > + if (!rec_found) > return -EINVAL; > > - if (!get_unaligned_le16(rec->media_hdr.validity_flags)) > + validity_flags = > + get_unaligned_le16(rec.media_hdr.validity_flags); > + if (!validity_flags) > return -EINVAL; > } > } > @@ -1376,13 +1405,10 @@ cxl_mem_perform_sparing(struct device *dev, > if (cxl_sparing_ctx->persist_mode) > sparing_pi.flags |= CXL_SET_HARD_SPARING(1); > > - if (rec) > - validity_flags = get_unaligned_le16(rec->media_hdr.validity_flags); > - > switch (cxl_sparing_ctx->repair_type) { > case EDAC_REPAIR_CACHELINE_SPARING: > sparing_pi.column = cpu_to_le16(cxl_sparing_ctx->column); > - if (!rec || (validity_flags & CXL_DER_VALID_SUB_CHANNEL)) { > + if (!rec_found || (validity_flags & CXL_DER_VALID_SUB_CHANNEL)) { > sparing_pi.flags |= CXL_SET_SPARING_SUB_CHNL_VALID(1); > sparing_pi.sub_channel = cxl_sparing_ctx->sub_channel; > } > @@ -1399,9 +1425,9 @@ cxl_mem_perform_sparing(struct device *dev, > fallthrough; > default: > sparing_pi.channel = cxl_sparing_ctx->channel; > - if ((rec && (validity_flags & CXL_DER_VALID_NIBBLE)) || > - (!rec && (!cxl_sparing_ctx->nibble_mask || > - (cxl_sparing_ctx->nibble_mask & 0xFFFFFF)))) { > + if ((rec_found && (validity_flags & CXL_DER_VALID_NIBBLE)) || > + (!rec_found && (!cxl_sparing_ctx->nibble_mask || > + (cxl_sparing_ctx->nibble_mask & 0xFFFFFF)))) { > sparing_pi.flags |= CXL_SET_SPARING_NIB_MASK_VALID(1); > put_unaligned_le24(cxl_sparing_ctx->nibble_mask, > sparing_pi.nibble_mask); > @@ -1814,7 +1840,7 @@ static int cxl_mem_perform_ppr(struct cxl_ppr_context *cxl_ppr_ctx) > attrbs.repair_type = CXL_PPR; > attrbs.dpa = cxl_ppr_ctx->dpa; > attrbs.nibble_mask = cxl_ppr_ctx->nibble_mask; > - if (!cxl_find_rec_dram(cxlmd, &attrbs) && > + if (!cxl_find_rec_dram(cxlmd, &attrbs, NULL) && > !cxl_find_rec_gen_media(cxlmd, &attrbs)) > return -EINVAL; > } > -- > 2.55.0 > Reviewed-by: Richard Cheng <icheng@nvidia.com> ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/2] cxl/edac: Read cached DRAM event records under the xarray lock 2026-11-02 12:32 ` [PATCH v4 1/2] cxl/edac: Read cached DRAM event records under the xarray lock Jongwon Lee 2026-11-05 6:00 ` Richard Cheng @ 2026-11-05 7:42 ` Li Ming 2026-11-05 8:42 ` Jongwon Lee 1 sibling, 1 reply; 9+ messages in thread From: Li Ming @ 2026-11-05 7:42 UTC (permalink / raw) To: Jongwon Lee, Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma, Dan Williams Cc: Ira Weiny, Richard Cheng, linux-cxl, linux-kernel 在 2026/11/2 20:32, Jongwon Lee 写道: > The event path caches DRAM event records in an xarray and frees a record > when xa_store() replaces it, when it expires and when the cache > overflows. It runs with device_lock() and cxl_rwsem held for read. > > The repair path looks records up with cxl_find_rec_dram() while holding > only cxl_rwsem for read, so the two paths can run at the same time. > cxl_find_rec_dram() reads the record after xa_load() returns, and > cxl_mem_perform_sparing() reads validity_flags from the returned record > again later. Both reads can hit a record the event path has just freed. > > Move the lookup into __cxl_find_rec_dram(), called with the xarray lock > held, and make cxl_find_rec_dram() return whether a record matched and > copy the matching record into a buffer the caller passes in. The PPR > path only needs the match and passes NULL. No caller keeps a pointer to > the record after the lock is dropped. The event path frees a record > only after xa_store() or xa_erase() has removed it from the xarray, and > both take the same lock. > > Fixes: 0b5ccb0de1e2 ("cxl/edac: Support for finding memory operation attributes from the current boot") > Fixes: 588ca944c277 ("cxl/edac: Add CXL memory device memory sparing control feature") > Reported-by: Sashiko <sashiko-bot@kernel.org> > Closes: https://lore.kernel.org/r/sashiko-outbox-192042@kernel.org > Signed-off-by: Jongwon Lee <ai@linux.com> Reviewed-by: Li Ming <ming.li@zohomail.com> Just one minor comment below. > --- > drivers/cxl/core/edac.c | 72 ++++++++++++++++++++++++++++------------- > 1 file changed, 49 insertions(+), 23 deletions(-) > > diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c > index 34b81e8dfb..c5c8dda20e 100644 > --- a/drivers/cxl/core/edac.c > +++ b/drivers/cxl/core/edac.c > @@ -938,18 +938,17 @@ cxl_find_rec_gen_media(struct cxl_memdev *cxlmd, > return NULL; > } > > +/* Caller holds the xarray lock. The record may be freed once it is dropped. */ > static struct cxl_event_dram * > -cxl_find_rec_dram(struct cxl_memdev *cxlmd, > - struct cxl_mem_repair_attrbs *attrbs) > +__cxl_find_rec_dram(struct xarray *rec_xarray, > + struct cxl_mem_repair_attrbs *attrbs) > { > - struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; > struct cxl_event_dram *rec; > u16 validity_flags; > > - if (!array_rec) > - return NULL; > + lockdep_assert_held(&rec_xarray->xa_lock); > > - rec = xa_load(&array_rec->rec_dram, attrbs->dpa); > + rec = xa_load(rec_xarray, attrbs->dpa); > if (!rec) > return NULL; > > @@ -1029,6 +1028,32 @@ cxl_find_rec_dram(struct cxl_memdev *cxlmd, > return NULL; > } > > +/* > + * cxl_store_rec_dram() replaces and frees records while the repair path only > + * holds cxl_rwsem for read, so look the record up and read it under the > + * xarray lock, and hand back a copy of the record. > + */ > +static bool cxl_find_rec_dram(struct cxl_memdev *cxlmd, > + struct cxl_mem_repair_attrbs *attrbs, > + struct cxl_event_dram *out) > +{ > + struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; > + struct cxl_event_dram *rec; > + bool found; > + > + if (!array_rec) > + return false; > + > + xa_lock(&array_rec->rec_dram); > + rec = __cxl_find_rec_dram(&array_rec->rec_dram, attrbs); > + found = rec; > + if (rec && out) > + *out = *rec; > + xa_unlock(&array_rec->rec_dram); > + > + return found; I think this "found" is not needed, "return rec;" or "return (rec != NULL);" is enough? > +} > + > #define CXL_MAX_STORAGE_DAYS 10 > #define CXL_MAX_STORAGE_TIME_SECS (CXL_MAX_STORAGE_DAYS * 24 * 60 * 60) > > @@ -1298,9 +1323,9 @@ cxl_mem_sparing_get_attrbs(struct cxl_mem_sparing_context *cxl_sparing_ctx) > return 0; > } > > -static struct cxl_event_dram * > -cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd, > - struct cxl_mem_sparing_context *ctx) > +static bool cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd, > + struct cxl_mem_sparing_context *ctx, > + struct cxl_event_dram *rec) > { > struct cxl_mem_repair_attrbs attrbs = { 0 }; > > @@ -1332,10 +1357,10 @@ cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd, > attrbs.repair_type = CXL_RANK_SPARING; > break; > default: > - return NULL; > + return false; > } > > - return cxl_find_rec_dram(cxlmd, &attrbs); > + return cxl_find_rec_dram(cxlmd, &attrbs, rec); > } > > static int > @@ -1344,7 +1369,8 @@ cxl_mem_perform_sparing(struct device *dev, > { > struct cxl_memdev *cxlmd = cxl_sparing_ctx->cxlmd; > struct cxl_memdev_sparing_in_payload sparing_pi; > - struct cxl_event_dram *rec = NULL; > + struct cxl_event_dram rec; > + bool rec_found = false; > u16 validity_flags = 0; > int ret; > > @@ -1362,11 +1388,14 @@ cxl_mem_perform_sparing(struct device *dev, > return -EBUSY; > } else { > if (cxl_is_memdev_memory_online(cxlmd)) { > - rec = cxl_mem_get_rec_dram(cxlmd, cxl_sparing_ctx); > - if (!rec) > + rec_found = cxl_mem_get_rec_dram(cxlmd, cxl_sparing_ctx, > + &rec); > + if (!rec_found) > return -EINVAL; > > - if (!get_unaligned_le16(rec->media_hdr.validity_flags)) > + validity_flags = > + get_unaligned_le16(rec.media_hdr.validity_flags); > + if (!validity_flags) > return -EINVAL; > } > } > @@ -1376,13 +1405,10 @@ cxl_mem_perform_sparing(struct device *dev, > if (cxl_sparing_ctx->persist_mode) > sparing_pi.flags |= CXL_SET_HARD_SPARING(1); > > - if (rec) > - validity_flags = get_unaligned_le16(rec->media_hdr.validity_flags); > - > switch (cxl_sparing_ctx->repair_type) { > case EDAC_REPAIR_CACHELINE_SPARING: > sparing_pi.column = cpu_to_le16(cxl_sparing_ctx->column); > - if (!rec || (validity_flags & CXL_DER_VALID_SUB_CHANNEL)) { > + if (!rec_found || (validity_flags & CXL_DER_VALID_SUB_CHANNEL)) { > sparing_pi.flags |= CXL_SET_SPARING_SUB_CHNL_VALID(1); > sparing_pi.sub_channel = cxl_sparing_ctx->sub_channel; > } > @@ -1399,9 +1425,9 @@ cxl_mem_perform_sparing(struct device *dev, > fallthrough; > default: > sparing_pi.channel = cxl_sparing_ctx->channel; > - if ((rec && (validity_flags & CXL_DER_VALID_NIBBLE)) || > - (!rec && (!cxl_sparing_ctx->nibble_mask || > - (cxl_sparing_ctx->nibble_mask & 0xFFFFFF)))) { > + if ((rec_found && (validity_flags & CXL_DER_VALID_NIBBLE)) || > + (!rec_found && (!cxl_sparing_ctx->nibble_mask || > + (cxl_sparing_ctx->nibble_mask & 0xFFFFFF)))) { > sparing_pi.flags |= CXL_SET_SPARING_NIB_MASK_VALID(1); > put_unaligned_le24(cxl_sparing_ctx->nibble_mask, > sparing_pi.nibble_mask); > @@ -1814,7 +1840,7 @@ static int cxl_mem_perform_ppr(struct cxl_ppr_context *cxl_ppr_ctx) > attrbs.repair_type = CXL_PPR; > attrbs.dpa = cxl_ppr_ctx->dpa; > attrbs.nibble_mask = cxl_ppr_ctx->nibble_mask; > - if (!cxl_find_rec_dram(cxlmd, &attrbs) && > + if (!cxl_find_rec_dram(cxlmd, &attrbs, NULL) && > !cxl_find_rec_gen_media(cxlmd, &attrbs)) > return -EINVAL; > } ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/2] cxl/edac: Read cached DRAM event records under the xarray lock 2026-11-05 7:42 ` Li Ming @ 2026-11-05 8:42 ` Jongwon Lee 0 siblings, 0 replies; 9+ messages in thread From: Jongwon Lee @ 2026-11-05 8:42 UTC (permalink / raw) To: Li Ming Cc: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma, Dan Williams, Ira Weiny, Richard Cheng, linux-cxl, linux-kernel Hi Ming, Thanks for the review. On 11/5/26 15:42, Li Ming wrote: >> + xa_lock(&array_rec->rec_dram); >> + rec = __cxl_find_rec_dram(&array_rec->rec_dram, attrbs); >> + found = rec; >> + if (rec && out) >> + *out = *rec; >> + xa_unlock(&array_rec->rec_dram); >> + >> + return found; > I think this "found" is not needed, "return rec;" or "return (rec != > NULL);" is enough? Right, the pointer is only compared after the unlock and never dereferenced, so found is not needed. I'll return rec != NULL in v5 and keep your and Richard's Reviewed-by. Thanks, Jongwon ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 2/2] cxl/edac: Mask DPA flags in the event record cache key 2026-11-02 12:32 [PATCH v4 0/2] cxl/edac: Fix event record lookups for memory repair Jongwon Lee 2026-11-02 12:32 ` [PATCH v4 1/2] cxl/edac: Read cached DRAM event records under the xarray lock Jongwon Lee @ 2026-11-02 12:32 ` Jongwon Lee 2026-11-05 6:00 ` Richard Cheng 2026-11-05 8:20 ` Li Ming 1 sibling, 2 replies; 9+ messages in thread From: Jongwon Lee @ 2026-11-02 12:32 UTC (permalink / raw) To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma, Dan Williams Cc: Ira Weiny, Li Ming, Richard Cheng, linux-cxl, linux-kernel The low bits of the DPA in a General Media or DRAM event record are flags: bit 0 is VOLATILE and bit 1 is NOT_REPAIRABLE. The trace events report the DPA with CXL_DPA_MASK applied, but cxl_store_rec_gen_media() and cxl_store_rec_dram() use the raw value as the xarray key. User space reads the DPA from the trace event and writes it to the mem_repair dpa attribute. If the record had any flag set, xa_load() misses it and the repair fails with -EINVAL. The raw key also lets a repair go through for a record the device marked NOT_REPAIRABLE, if the DPA is written with bit 1 set. Key the records by the masked DPA and skip records marked NOT_REPAIRABLE. cxl_find_rec_gen_media() now reads the record, so do the lookup under the xarray lock as cxl_find_rec_dram() does, and return a bool since its only caller tests the result. Fixes: 0b5ccb0de1e2 ("cxl/edac: Support for finding memory operation attributes from the current boot") Signed-off-by: Jongwon Lee <ai@linux.com> --- drivers/cxl/core/edac.c | 43 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c index c5c8dda20e..a5f198b561 100644 --- a/drivers/cxl/core/edac.c +++ b/drivers/cxl/core/edac.c @@ -918,24 +918,37 @@ struct cxl_mem_repair_attrbs { enum cxl_mem_repair_type repair_type; }; -static struct cxl_event_gen_media * -cxl_find_rec_gen_media(struct cxl_memdev *cxlmd, - struct cxl_mem_repair_attrbs *attrbs) +/* + * The low bits of the event record DPA are flags (VOLATILE, NOT_REPAIRABLE). + * Records are looked up by the DPA user space writes back, which comes from + * the trace event and has the flags masked off, so key them the same way. + */ +static inline unsigned long cxl_rec_key(const struct cxl_event_media_hdr *hdr) +{ + return le64_to_cpu(hdr->phys_addr) & CXL_DPA_MASK; +} + +static inline bool cxl_rec_not_repairable(const struct cxl_event_media_hdr *hdr) +{ + return le64_to_cpu(hdr->phys_addr) & CXL_DPA_NOT_REPAIRABLE; +} + +static bool cxl_find_rec_gen_media(struct cxl_memdev *cxlmd, + struct cxl_mem_repair_attrbs *attrbs) { struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; struct cxl_event_gen_media *rec; + bool found; - if (!array_rec) - return NULL; + if (!array_rec || attrbs->repair_type != CXL_PPR) + return false; + xa_lock(&array_rec->rec_gen_media); rec = xa_load(&array_rec->rec_gen_media, attrbs->dpa); - if (!rec) - return NULL; + found = rec && !cxl_rec_not_repairable(&rec->media_hdr); + xa_unlock(&array_rec->rec_gen_media); - if (attrbs->repair_type == CXL_PPR) - return rec; - - return NULL; + return found; } /* Caller holds the xarray lock. The record may be freed once it is dropped. */ @@ -949,7 +962,7 @@ __cxl_find_rec_dram(struct xarray *rec_xarray, lockdep_assert_held(&rec_xarray->xa_lock); rec = xa_load(rec_xarray, attrbs->dpa); - if (!rec) + if (!rec || cxl_rec_not_repairable(&rec->media_hdr)) return NULL; validity_flags = get_unaligned_le16(rec->media_hdr.validity_flags); @@ -1130,8 +1143,7 @@ int cxl_store_rec_gen_media(struct cxl_memdev *cxlmd, union cxl_event *evt) return -ENOMEM; old_rec = xa_store(&array_rec->rec_gen_media, - le64_to_cpu(rec->media_hdr.phys_addr), rec, - GFP_KERNEL); + cxl_rec_key(&rec->media_hdr), rec, GFP_KERNEL); if (xa_is_err(old_rec)) { kfree(rec); return xa_err(old_rec); @@ -1160,8 +1172,7 @@ int cxl_store_rec_dram(struct cxl_memdev *cxlmd, union cxl_event *evt) return -ENOMEM; old_rec = xa_store(&array_rec->rec_dram, - le64_to_cpu(rec->media_hdr.phys_addr), rec, - GFP_KERNEL); + cxl_rec_key(&rec->media_hdr), rec, GFP_KERNEL); if (xa_is_err(old_rec)) { kfree(rec); return xa_err(old_rec); -- 2.55.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/2] cxl/edac: Mask DPA flags in the event record cache key 2026-11-02 12:32 ` [PATCH v4 2/2] cxl/edac: Mask DPA flags in the event record cache key Jongwon Lee @ 2026-11-05 6:00 ` Richard Cheng 2026-11-05 8:20 ` Li Ming 1 sibling, 0 replies; 9+ messages in thread From: Richard Cheng @ 2026-11-05 6:00 UTC (permalink / raw) To: Jongwon Lee Cc: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma, Dan Williams, Ira Weiny, Li Ming, linux-cxl, linux-kernel On Mon, Nov 02, 2026 at 09:32:35PM +0800, Jongwon Lee wrote: > The low bits of the DPA in a General Media or DRAM event record are > flags: bit 0 is VOLATILE and bit 1 is NOT_REPAIRABLE. The trace events > report the DPA with CXL_DPA_MASK applied, but cxl_store_rec_gen_media() > and cxl_store_rec_dram() use the raw value as the xarray key. > > User space reads the DPA from the trace event and writes it to the > mem_repair dpa attribute. If the record had any flag set, xa_load() > misses it and the repair fails with -EINVAL. The raw key also lets a > repair go through for a record the device marked NOT_REPAIRABLE, if the > DPA is written with bit 1 set. > > Key the records by the masked DPA and skip records marked > NOT_REPAIRABLE. cxl_find_rec_gen_media() now reads the record, so do > the lookup under the xarray lock as cxl_find_rec_dram() does, and > return a bool since its only caller tests the result. > > Fixes: 0b5ccb0de1e2 ("cxl/edac: Support for finding memory operation attributes from the current boot") > Signed-off-by: Jongwon Lee <ai@linux.com> > --- > drivers/cxl/core/edac.c | 43 ++++++++++++++++++++++++++--------------- > 1 file changed, 27 insertions(+), 16 deletions(-) > > diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c > index c5c8dda20e..a5f198b561 100644 > --- a/drivers/cxl/core/edac.c > +++ b/drivers/cxl/core/edac.c > @@ -918,24 +918,37 @@ struct cxl_mem_repair_attrbs { > enum cxl_mem_repair_type repair_type; > }; > > -static struct cxl_event_gen_media * > -cxl_find_rec_gen_media(struct cxl_memdev *cxlmd, > - struct cxl_mem_repair_attrbs *attrbs) > +/* > + * The low bits of the event record DPA are flags (VOLATILE, NOT_REPAIRABLE). > + * Records are looked up by the DPA user space writes back, which comes from > + * the trace event and has the flags masked off, so key them the same way. > + */ > +static inline unsigned long cxl_rec_key(const struct cxl_event_media_hdr *hdr) > +{ > + return le64_to_cpu(hdr->phys_addr) & CXL_DPA_MASK; > +} > + > +static inline bool cxl_rec_not_repairable(const struct cxl_event_media_hdr *hdr) > +{ > + return le64_to_cpu(hdr->phys_addr) & CXL_DPA_NOT_REPAIRABLE; > +} > + > +static bool cxl_find_rec_gen_media(struct cxl_memdev *cxlmd, > + struct cxl_mem_repair_attrbs *attrbs) > { > struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; > struct cxl_event_gen_media *rec; > + bool found; > > - if (!array_rec) > - return NULL; > + if (!array_rec || attrbs->repair_type != CXL_PPR) > + return false; > > + xa_lock(&array_rec->rec_gen_media); > rec = xa_load(&array_rec->rec_gen_media, attrbs->dpa); > - if (!rec) > - return NULL; > + found = rec && !cxl_rec_not_repairable(&rec->media_hdr); > + xa_unlock(&array_rec->rec_gen_media); > > - if (attrbs->repair_type == CXL_PPR) > - return rec; > - > - return NULL; > + return found; > } > > /* Caller holds the xarray lock. The record may be freed once it is dropped. */ > @@ -949,7 +962,7 @@ __cxl_find_rec_dram(struct xarray *rec_xarray, > lockdep_assert_held(&rec_xarray->xa_lock); > > rec = xa_load(rec_xarray, attrbs->dpa); > - if (!rec) > + if (!rec || cxl_rec_not_repairable(&rec->media_hdr)) > return NULL; > > validity_flags = get_unaligned_le16(rec->media_hdr.validity_flags); > @@ -1130,8 +1143,7 @@ int cxl_store_rec_gen_media(struct cxl_memdev *cxlmd, union cxl_event *evt) > return -ENOMEM; > > old_rec = xa_store(&array_rec->rec_gen_media, > - le64_to_cpu(rec->media_hdr.phys_addr), rec, > - GFP_KERNEL); > + cxl_rec_key(&rec->media_hdr), rec, GFP_KERNEL); > if (xa_is_err(old_rec)) { > kfree(rec); > return xa_err(old_rec); > @@ -1160,8 +1172,7 @@ int cxl_store_rec_dram(struct cxl_memdev *cxlmd, union cxl_event *evt) > return -ENOMEM; > > old_rec = xa_store(&array_rec->rec_dram, > - le64_to_cpu(rec->media_hdr.phys_addr), rec, > - GFP_KERNEL); > + cxl_rec_key(&rec->media_hdr), rec, GFP_KERNEL); > if (xa_is_err(old_rec)) { > kfree(rec); > return xa_err(old_rec); > -- > 2.55.0 > Reviewed-by: Richard Cheng <icheng@nvidia.com> ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/2] cxl/edac: Mask DPA flags in the event record cache key 2026-11-02 12:32 ` [PATCH v4 2/2] cxl/edac: Mask DPA flags in the event record cache key Jongwon Lee 2026-11-05 6:00 ` Richard Cheng @ 2026-11-05 8:20 ` Li Ming 2026-11-05 8:42 ` Jongwon Lee 1 sibling, 1 reply; 9+ messages in thread From: Li Ming @ 2026-11-05 8:20 UTC (permalink / raw) To: Jongwon Lee, Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma, Dan Williams Cc: Ira Weiny, Richard Cheng, linux-cxl, linux-kernel 在 2026/11/2 20:32, Jongwon Lee 写道: > The low bits of the DPA in a General Media or DRAM event record are > flags: bit 0 is VOLATILE and bit 1 is NOT_REPAIRABLE. The trace events > report the DPA with CXL_DPA_MASK applied, but cxl_store_rec_gen_media() > and cxl_store_rec_dram() use the raw value as the xarray key. > > User space reads the DPA from the trace event and writes it to the > mem_repair dpa attribute. If the record had any flag set, xa_load() > misses it and the repair fails with -EINVAL. The raw key also lets a > repair go through for a record the device marked NOT_REPAIRABLE, if the > DPA is written with bit 1 set. > > Key the records by the masked DPA and skip records marked > NOT_REPAIRABLE. cxl_find_rec_gen_media() now reads the record, so do > the lookup under the xarray lock as cxl_find_rec_dram() does, and > return a bool since its only caller tests the result. > > Fixes: 0b5ccb0de1e2 ("cxl/edac: Support for finding memory operation attributes from the current boot") > Signed-off-by: Jongwon Lee <ai@linux.com> > --- > drivers/cxl/core/edac.c | 43 ++++++++++++++++++++++++++--------------- > 1 file changed, 27 insertions(+), 16 deletions(-) > > diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c > index c5c8dda20e..a5f198b561 100644 > --- a/drivers/cxl/core/edac.c > +++ b/drivers/cxl/core/edac.c > @@ -918,24 +918,37 @@ struct cxl_mem_repair_attrbs { > enum cxl_mem_repair_type repair_type; > }; > > -static struct cxl_event_gen_media * > -cxl_find_rec_gen_media(struct cxl_memdev *cxlmd, > - struct cxl_mem_repair_attrbs *attrbs) > +/* > + * The low bits of the event record DPA are flags (VOLATILE, NOT_REPAIRABLE). > + * Records are looked up by the DPA user space writes back, which comes from > + * the trace event and has the flags masked off, so key them the same way. > + */ > +static inline unsigned long cxl_rec_key(const struct cxl_event_media_hdr *hdr) > +{ > + return le64_to_cpu(hdr->phys_addr) & CXL_DPA_MASK; > +} This function actually returns the real DPA of the event record, I think the name should be cxl_rec_dpa()? xa_load() uses attrbs->dpa as the index, but xa_store() uses cxl_rec_key() as the index, that could make readers confused. > + > +static inline bool cxl_rec_not_repairable(const struct cxl_event_media_hdr *hdr) > +{ > + return le64_to_cpu(hdr->phys_addr) & CXL_DPA_NOT_REPAIRABLE; > +} > + > +static bool cxl_find_rec_gen_media(struct cxl_memdev *cxlmd, > + struct cxl_mem_repair_attrbs *attrbs) > { > struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; > struct cxl_event_gen_media *rec; > + bool found; > > - if (!array_rec) > - return NULL; > + if (!array_rec || attrbs->repair_type != CXL_PPR) > + return false; > > + xa_lock(&array_rec->rec_gen_media); > rec = xa_load(&array_rec->rec_gen_media, attrbs->dpa); > - if (!rec) > - return NULL; > + found = rec && !cxl_rec_not_repairable(&rec->media_hdr); > + xa_unlock(&array_rec->rec_gen_media); > > - if (attrbs->repair_type == CXL_PPR) > - return rec; > - > - return NULL; > + return found; > } I think the implementation of skipping unrepairable records should be moved into a separate patch, and this patch focuses on using real DPA as the index parameter of the xarray. My first feeling is that we should not simply ignore unrepairable records without any logs. But I am not very familiar with this area, not sure if we should report this repairable bit in trace log or output a kernel log while finding an unrepairable record. Let us see if maintainers have more comments on that. Ming > > /* Caller holds the xarray lock. The record may be freed once it is dropped. */ > @@ -949,7 +962,7 @@ __cxl_find_rec_dram(struct xarray *rec_xarray, > lockdep_assert_held(&rec_xarray->xa_lock); > > rec = xa_load(rec_xarray, attrbs->dpa); > - if (!rec) > + if (!rec || cxl_rec_not_repairable(&rec->media_hdr)) > return NULL; > > validity_flags = get_unaligned_le16(rec->media_hdr.validity_flags); > @@ -1130,8 +1143,7 @@ int cxl_store_rec_gen_media(struct cxl_memdev *cxlmd, union cxl_event *evt) > return -ENOMEM; > > old_rec = xa_store(&array_rec->rec_gen_media, > - le64_to_cpu(rec->media_hdr.phys_addr), rec, > - GFP_KERNEL); > + cxl_rec_key(&rec->media_hdr), rec, GFP_KERNEL); > if (xa_is_err(old_rec)) { > kfree(rec); > return xa_err(old_rec); > @@ -1160,8 +1172,7 @@ int cxl_store_rec_dram(struct cxl_memdev *cxlmd, union cxl_event *evt) > return -ENOMEM; > > old_rec = xa_store(&array_rec->rec_dram, > - le64_to_cpu(rec->media_hdr.phys_addr), rec, > - GFP_KERNEL); > + cxl_rec_key(&rec->media_hdr), rec, GFP_KERNEL); > if (xa_is_err(old_rec)) { > kfree(rec); > return xa_err(old_rec); ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/2] cxl/edac: Mask DPA flags in the event record cache key 2026-11-05 8:20 ` Li Ming @ 2026-11-05 8:42 ` Jongwon Lee 0 siblings, 0 replies; 9+ messages in thread From: Jongwon Lee @ 2026-11-05 8:42 UTC (permalink / raw) To: Li Ming Cc: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma, Dan Williams, Ira Weiny, Richard Cheng, linux-cxl, linux-kernel Hi Ming, Thanks for the careful review. On 11/5/26 16:20, Li Ming wrote: >> +static inline unsigned long cxl_rec_key(const struct cxl_event_media_hdr *hdr) >> +{ >> + return le64_to_cpu(hdr->phys_addr) & CXL_DPA_MASK; >> +} > This function actually returns the real DPA of the event record, I think > the name should be cxl_rec_dpa()? xa_load() uses attrbs->dpa as the > index, but xa_store() uses cxl_rec_key() as the index, that could make > readers confused. Agreed, cxl_rec_dpa() says what it returns. I'll rename it in v5. > I think the implementation of skipping unrepairable records should be > moved into a separate patch, and this patch focuses on using real DPA as > the index parameter of the xarray. Makes sense, I'll split it out. I would like to put that patch before the masking one, though. Once the key is masked, the DPA from the trace event matches a NOT_REPAIRABLE record, so if the masking went in first, those records could be repaired at that commit. > My first feeling is that we should not simply ignore unrepairable > records without any logs. But I am not very familiar with this area, not > sure if we should report this repairable bit in trace log or output a > kernel log while finding an unrepairable record. Good point. The trace events already carry the flag: cxl_general_media and cxl_dram print dpa_flags='NOT_REPAIRABLE' for such a record, so user space can see it before it requests a repair, and the request then fails with -EINVAL. If the maintainers would like a message on that path as well, I can add a dev_dbg() in v5. I'll wait a few days for other comments before sending v5. Thanks, Jongwon ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-11-05 8:42 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-02 12:32 [PATCH v4 0/2] cxl/edac: Fix event record lookups for memory repair Jongwon Lee 2026-11-02 12:32 ` [PATCH v4 1/2] cxl/edac: Read cached DRAM event records under the xarray lock Jongwon Lee 2026-11-05 6:00 ` Richard Cheng 2026-11-05 7:42 ` Li Ming 2026-11-05 8:42 ` Jongwon Lee 2026-11-02 12:32 ` [PATCH v4 2/2] cxl/edac: Mask DPA flags in the event record cache key Jongwon Lee 2026-11-05 6:00 ` Richard Cheng 2026-11-05 8:20 ` Li Ming 2026-11-05 8:42 ` Jongwon Lee
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox