From: sashiko-bot@kernel.org
To: "Anisa Su" <anisa.su887@gmail.com>, linux-cxl@vger.kernel.org
Cc: nvdimm@lists.linux.dev
Subject: Re: [PATCH v1 12/14] cxl/region: Read existing extents on region creation
Date: Wed, 04 Nov 2026 23:36:43 +0000 [thread overview]
Message-ID: <sashiko-outbox-198302@kernel.org> (raw)
In-Reply-To: <20261104230445.66625-13-anisa.su@samsung.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] cxl/mbox: per-extent DPA filtering in cxl_read_extent_list() splits tag groups
- [High] cxl/mbox: cxl_release_recovered() releases already-onlined DPA capacity
- [High] cxl/region: race in cxl_region_add_existing_extents() on concurrent detach
- [Medium] cxl/mbox: missing end < start check before range_contains() in extent scan
--
commit 6931a3e5d551f78995bded312bdac1337c547e3c
Author: Ira Weiny <iweiny@kernel.org>
cxl/region: Read existing extents on region creation
This patch reads pre-existing accepted dynamic capacity extents from the
device via CXL_MBOX_OP_GET_DC_EXTENT_LIST when probing a cxl_dax_region. It
filters extents by the endpoint decoder's DPA range, stages and onlines
recovered tag groups without sending an Add response, and gates new extent
add events until the initial scan completes.
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index c3236fe9e7c5e..a18b41ff5d232 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
[ ... ]
> @@ -1627,6 +1641,10 @@ static int cxl_realize_group(struct cxl_memdev_state *mds, const uuid_t *tag,
> mds->dc_chain.group = NULL;
> if (tag_group)
> discard_tag_group(tag_group);
> +err_release:
> + /* A fresh group is simply left out of the Add response */
> + if (existing)
> + cxl_release_recovered(mds, tag, group);
> return rc;
> }
>
> @@ -1686,11 +1704,14 @@ static int cxl_validate_group(struct cxl_memdev_state *mds, const uuid_t *tag,
> * list.
> */
> static int cxl_add_pending(struct cxl_memdev_state *mds,
> - struct list_head *pending)
> + struct list_head *pending, bool existing)
> {
[ ... ]
> @@ -1727,11 +1748,13 @@ static int cxl_add_pending(struct cxl_memdev_state *mds,
> shareable = cxl_group_is_shareable(mds, &group);
>
> if (cxl_validate_group(mds, &tag, &group, shareable)) {
> + if (existing)
> + cxl_release_recovered(mds, &tag, &group);
> drop_extent_group(&group);
> continue;
> }
>
> - cnt = cxl_realize_group(mds, &tag, &group, shareable);
> + cnt = cxl_realize_group(mds, &tag, &group, shareable, existing);
[Severity: High]
Could calling cxl_release_recovered() on all validation and realization
failures release DPA capacity that is already online on the host?
During recovery (existing == true), cxl_add_pending() processes groups
sequentially and onlines valid groups into cxlr_dax->dc_extents. If the
device's extent list contains a subsequent group whose tag is already
committed (cxl_tag_already_committed() returning true in
cxl_validate_group()) or whose extent DPA range duplicates or overlaps an
already-onlined dc_extent:
drivers/cxl/core/extent.c:cxl_add_extent() {
...
switch (cxlr_dax_classify_extent(cxlr_dax, cxled, &ext_range)) {
case CXL_EXT_DUPLICATE:
...
return -EEXIST;
case CXL_EXT_OVERLAP:
return -ENXIO;
case CXL_EXT_NEW:
break;
}
}
both error paths invoke cxl_release_recovered(), which sends
CXL_MBOX_OP_RELEASE_DC for every extent in the failing group. Wouldn't that
release physical capacity on the device that backs an active, already-onlined
dc_extent on the host?
> @@ -2439,6 +2471,180 @@ int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> }
> EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
>
> +/*
> + * Stage the device's extents inside @dpa_range on @existing. Returns
> + * -EAGAIN if the list changes while it is being read.
> + */
> +static int cxl_read_extent_list(struct cxl_memdev_state *mds,
> + const struct range *dpa_range,
> + struct list_head *existing)
> +{
[ ... ]
> + for (int i = 0; i < nr_returned ; i++) {
> + struct cxl_extent *extent = &extents->extent[i];
> + struct range ext_range;
> +
> + ext_range.start = le64_to_cpu(extent->start_dpa);
> + ext_range.end = ext_range.start +
> + le64_to_cpu(extent->length) - 1;
> + dev_dbg(dev, "Processing extent %d/%d %pra (%pUb) seq %u\n",
> + current_index + i, total_expected, &ext_range,
> + extent->uuid,
> + le16_to_cpu(extent->shared_extn_seq));
> + /* Another region's, or an unmapped partition's: not ours */
> + if (!range_contains(dpa_range, &ext_range))
> + continue;
> + rc = stage_pending_extent(existing, extent);
[Severity: Medium]
What happens in cxl_read_extent_list() if the device returns a malformed
extent where extent->length is 0 or start_dpa + length - 1 wraps around u64
such that ext_range.end < ext_range.start?
Because range_contains(r1, r2) only checks r1->start <= r2->start &&
r1->end >= r2->end, cxl_extent_dc_partition() explicitly guards against
ext_range->end < ext_range->start before calling range_contains(). Without a
similar check here, an extent with ext_range.start >= dpa_range->start (even
in a higher DPA partition) and a wrapped ext_range.end <= dpa_range->end
passes range_contains(dpa_range, &ext_range) and is staged onto existing.
If that malformed extent carries a UUID matching a valid tag group on this
region, extract_tag_group() will merge it into that group in
cxl_add_pending(), causing cxl_validate_group() to fail and
cxl_release_recovered() to release the entire valid group via
CXL_MBOX_OP_RELEASE_DC. Should cxl_read_extent_list() check
ext_range.end < ext_range.start before calling range_contains()?
[Severity: High]
Also in cxl_read_extent_list(), can filtering individual extents by
dpa_range before assembling tag groups split a multi-extent tag group that
spans or straddles the endpoint decoder's DPA boundary?
When cxl_read_extent_list() skips individual extents with
!range_contains(dpa_range, &ext_range), only the in-range subset of extents
is staged onto existing. When __cxl_process_extent_list() subsequently calls
cxl_add_pending(mds, &existing, true), extract_tag_group() forms each group
from only that pre-filtered subset:
1. For a non-shareable group (or a shareable group whose in-range extents
happen to carry shared_extn_seq 0..k-1), cxl_validate_group() and
cxl_realize_group() will succeed and online a truncated tag group,
committing the tag UUID host-wide so the remaining extents cannot be added
elsewhere.
2. For a shareable group whose in-range subset is missing earlier sequence
numbers, cxl_check_group_seq() fails and cxl_release_recovered() sends
CXL_MBOX_OP_RELEASE_DC for only the in-range subset, violating CXL r4.0
Section 8.2.10.9.9.4 which requires shared tag groups to be released as a
whole.
Should all extents be staged first (or grouped by tag first) so that a tag
group only partially contained in dpa_range is detected as a whole?
> diff --git a/drivers/cxl/core/region_dax.c b/drivers/cxl/core/region_dax.c
> index 790176a8aaddc..816386301b80e 100644
> --- a/drivers/cxl/core/region_dax.c
> +++ b/drivers/cxl/core/region_dax.c
[ ... ]
> @@ -79,6 +81,15 @@ static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
> cxlr_dax->hpa_range.start = p->res->start;
> cxlr_dax->hpa_range.end = p->res->end;
>
> + /*
> + * DC regions are not interleaved; the probe-time scan uses this decoder
> + * and may outlive its attachment to the region, so hold a reference.
> + */
> + if (cxlr->mode == CXL_PARTMODE_DYNAMIC_RAM)
> + cxlr_dax->cxled = p->targets[0];
> + if (cxlr_dax->cxled)
> + get_device(&cxlr_dax->cxled->cxld.dev);
> +
> dev = &cxlr_dax->dev;
> cxlr_dax->cxlr = cxlr;
> cxlr->cxlr_dax = cxlr_dax;
> @@ -153,6 +164,42 @@ DEFINE_FREE(unpublish_cxl_dax_region, struct cxl_dax_region *,
> put_device(&_T->dev);
> })
>
> +/*
> + * Runs from cxl_dax_region_probe(). The region's parameters may not be
> + * read here: a concurrent detach clears p->targets[] under cxl_rwsem.region
> + * before it waits for this probe. Use the decoder recorded at allocation
> + * instead; cxlr_dax holds a reference on it.
> + */
> +int cxl_region_add_existing_extents(struct cxl_dax_region *cxlr_dax)
> +{
> + struct cxl_endpoint_decoder *cxled = cxlr_dax->cxled;
> + int rc;
> +
> + rc = cxl_process_extent_list(cxled);
[Severity: High]
Can a concurrent region detach or reset while cxl_dax_region_probe() runs
cause cxl_region_add_existing_extents() to race with decoder reuse, memdev
teardown, or cache invalidation?
In cxl_decoder_detach(), __cxl_decoder_detach() runs under cxl_rwsem.region
write lock, disables the decoder, and clears cxled->cxld.region = NULL and
p->targets[0] = NULL before dropping cxl_rwsem.region and calling
device_release_driver(&detach->dev) -> cxlr_dax_unregister(), which waits on
device_lock(&cxlr_dax->dev) for cxl_dax_region_probe() to finish. Because
cxl_region_add_existing_extents() passes only cxled to
cxl_process_extent_list() and runs without cxl_rwsem.region held across the
scan, it looks like several races can occur once __cxl_decoder_detach()
drops cxl_rwsem.region:
1. If cxled is reattached to a new region (cxlr2 / cxlr_dax2) while the old
cxlr_dax1 probe is in cxl_read_extent_list(), cxl_add_pending() ->
cxl_realize_group() -> cxl_add_extent() -> cxl_validate_extent() resolves
start_dpa via cxl_dpa_to_region(cxlmd, start_dpa, &cxled) to cxlr_dax2
without checking that cxlr->cxlr_dax == cxlr_dax1. Because existing is
true, cxl_validate_extent() bypasses the extents_scanned gate and
online_tag_group() attaches devres actions to cxlr_dax2->dev before
cxlr_dax2 has probed, causing really_probe() on cxlr_dax2 to fail with
-EBUSY ("Resources present before probing") and leak the tag groups.
2. Similarly, cxled->dpa_res can be freed and reallocated to a different DPA
range via cxl_dpa_free() / cxl_dpa_alloc() before
__cxl_process_extent_list() (or one of its -EAGAIN retries) reads
cxled->dpa_res under cxl_rwsem.dpa, causing it to scan and release
extents in the new DPA range via cxl_release_recovered().
3. Once detach_target() clears cxled->cxld.region = NULL, a concurrent
memdev/PCI unbind running cxld_unregister() -> cxl_decoder_detach() sees
cxled->cxld.region == NULL and returns NULL without waiting for
cxlr_dax1's probe. Holding get_device(&cxled->cxld.dev) only pins the
decoder device, not cxlmd->cxlds, which cxl_memdev_shutdown() sets to
NULL before cxl_pci devres frees mds while cxl_process_extent_list() is
dereferencing cxled_to_mds(cxled).
4. During commit_store() reset (or cxl_decoder_detach()),
cxlr_dax_unpublish() clears cxlr->cxlr_dax = NULL before waiting for
cxl_dax_region_probe() (and in commit_store(), before
cxl_region_decode_reset() invalidates CPU caches). When the in-flight
scan sees !cxlr->cxlr_dax, cxl_validate_extent() returns -ENXIO and
cxl_realize_group() calls cxl_release_recovered(), sending
CXL_MBOX_OP_RELEASE_DC while CPU caches may still hold dirty lines or
before the region teardown has finished.
Should cxl_region_add_existing_extents() pass cxlr_dax into the scan, verify
under cxl_rwsem.region and cxl_rwsem.dpa that cxled->cxld.region ==
cxlr_dax->cxlr and cxlr->cxlr_dax == cxlr_dax, and avoid calling
cxl_release_recovered() when aborting due to a concurrent detach/unpublish?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261104230445.66625-1-anisa.su@samsung.com?part=12
next prev parent reply other threads:[~2026-11-04 23:36 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 23:02 [PATCH v1 00/14] DCD CXL Series: Dynamic Capacity extent handling Anisa Su
2026-11-04 23:02 ` [PATCH v1 01/14] cxl/core: Return endpoint decoder information from region search Anisa Su
2026-11-04 23:02 ` [PATCH v1 02/14] cxl/port: Add dynamic capacity options to endpoint decoder mode Anisa Su
2026-11-04 23:02 ` [PATCH v1 03/14] cxl/region: Add dynamic DAX region support Anisa Su
2026-11-04 23:21 ` sashiko-bot
2026-11-04 23:02 ` [PATCH v1 04/14] cxl/mem: Introduce DC event handlers Anisa Su
2026-11-04 23:18 ` sashiko-bot
2026-11-04 23:02 ` [PATCH v1 05/14] cxl/mem: Add 20 second timeout for stalled DC event chains Anisa Su
2026-11-04 23:13 ` sashiko-bot
2026-11-04 23:02 ` [PATCH v1 06/14] cxl/extent: Handle DC Add Capacity events Anisa Su
2026-11-04 23:34 ` sashiko-bot
2026-11-04 23:02 ` [PATCH v1 07/14] cxl/mem: Drop misaligned DCD extent groups Anisa Su
2026-11-04 23:02 ` [PATCH v1 08/14] cxl/extent: Validate DC extent partition Anisa Su
2026-11-04 23:02 ` [PATCH v1 09/14] cxl/mem: Enforce tag-group semantics Anisa Su
2026-11-04 23:02 ` [PATCH v1 10/14] cxl/extent: Enforce cross-region tag uniqueness Anisa Su
2026-11-04 23:17 ` sashiko-bot
2026-11-04 23:02 ` [PATCH v1 11/14] cxl/extent: Handle DC Release Capacity events Anisa Su
2026-11-04 23:28 ` sashiko-bot
2026-11-04 23:02 ` [PATCH v1 12/14] cxl/region: Read existing extents on region creation Anisa Su
2026-11-04 23:36 ` sashiko-bot [this message]
2026-11-04 23:02 ` [PATCH v1 13/14] cxl/mem: Trace Dynamic capacity Event Record Anisa Su
2026-11-04 23:02 ` [PATCH v1 14/14] Documentation/cxl: Document DCD extent handling Anisa Su
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=sashiko-outbox-198302@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=anisa.su887@gmail.com \
--cc=linux-cxl@vger.kernel.org \
--cc=nvdimm@lists.linux.dev \
--cc=sashiko-reviews@lists.linux.dev \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox