From: Jongwon Lee <ai@linux.com>
To: Davidlohr Bueso <dave@stgolabs.net>,
Jonathan Cameron <jic23@kernel.org>,
Dave Jiang <dave.jiang@intel.com>,
Alison Schofield <alison.schofield@intel.com>,
Vishal Verma <vishal.l.verma@intel.com>,
Dan Williams <djbw@kernel.org>
Cc: Ira Weiny <iweiny@kernel.org>, Li Ming <ming.li@zohomail.com>,
Richard Cheng <icheng@nvidia.com>,
linux-cxl@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v3 1/2] cxl/edac: Read cached DRAM event records under the xarray lock
Date: Sun, 01 Nov 2026 10:34:19 +0900 [thread overview]
Message-ID: <20261101013418.87326-2-ai@linux.com> (raw)
In-Reply-To: <20261101013418.87326-1-ai@linux.com>
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 out the validity flags the sparing path needs. 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 | 69 +++++++++++++++++++++++++++--------------
1 file changed, 46 insertions(+), 23 deletions(-)
diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c
index 34b81e8dfb..f0c12871bd 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 what the caller needs.
+ */
+static bool cxl_find_rec_dram(struct cxl_memdev *cxlmd,
+ struct cxl_mem_repair_attrbs *attrbs,
+ u16 *validity_flags)
+{
+ 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 && validity_flags)
+ *validity_flags = get_unaligned_le16(rec->media_hdr.validity_flags);
+ 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,
+ u16 *validity_flags)
{
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, validity_flags);
}
static int
@@ -1344,7 +1369,7 @@ 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;
+ bool rec_found = false;
u16 validity_flags = 0;
int ret;
@@ -1362,11 +1387,12 @@ 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,
+ &validity_flags);
+ if (!rec_found)
return -EINVAL;
- if (!get_unaligned_le16(rec->media_hdr.validity_flags))
+ if (!validity_flags)
return -EINVAL;
}
}
@@ -1376,13 +1402,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 +1422,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 +1837,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
next prev parent reply other threads:[~2026-11-01 1:34 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-01 1:34 [PATCH v3 0/2] cxl/edac: Fix event record lookups for memory repair Jongwon Lee
2026-11-01 1:34 ` Jongwon Lee [this message]
2026-11-01 1:34 ` [PATCH v3 2/2] cxl/edac: Mask DPA flags in the event record cache key Jongwon Lee
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261101013418.87326-2-ai@linux.com \
--to=ai@linux.com \
--cc=alison.schofield@intel.com \
--cc=dave.jiang@intel.com \
--cc=dave@stgolabs.net \
--cc=djbw@kernel.org \
--cc=icheng@nvidia.com \
--cc=iweiny@kernel.org \
--cc=jic23@kernel.org \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ming.li@zohomail.com \
--cc=vishal.l.verma@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.