NVDIMM Device and Persistent Memory development
 help / color / mirror / Atom feed
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 11/14] cxl/extent: Handle DC Release Capacity events
Date: Wed, 04 Nov 2026 23:28:20 +0000	[thread overview]
Message-ID: <sashiko-outbox-198297@kernel.org> (raw)
In-Reply-To: <20261104230445.66625-12-anisa.su@samsung.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] cxl/extent: missing end < start range check in cxl_resolve_extent()
- [High] cxl/extent: cxl_resolve_extent() misses overlaps across decoder boundaries
- [High] cxl/extent: deferred xa_erase() and unpinned group in cxl_rm_pending()
- [High] cxl/extent: premature capacity release in cxl_rm_pending() during teardown
--

commit d32b383472a7ac06793ed19dcde742a2c32c23c3
Author: Ira Weiny <iweiny@kernel.org>

cxl/extent: Handle DC Release Capacity events

This commit replaces the Release Capacity ack-all stub with extent lookup and
tag group teardown, matching released extents by DPA range and tag and
acknowledging released capacity via CXL_MBOX_OP_RELEASE_DC.

> diff --git a/drivers/cxl/core/extent.c b/drivers/cxl/core/extent.c
> index 4a59db823af4f..1873a3dfe8e0b 100644
> --- a/drivers/cxl/core/extent.c
> +++ b/drivers/cxl/core/extent.c
> @@ -449,6 +449,109 @@ int cxl_add_extent(struct cxl_memdev_state *mds, struct cxl_extent *extent,
>  	return cxlr_add_extent(mds, cxlr_dax, dc_extent);
>  }
>  
> +/**
> + * cxl_resolve_extent() - locate the tag group a released extent belongs to
> + * @mds: memdev state
> + * @extent: extent from a DC Release-Capacity record
> + * @cxlrp: returns the region owning the group
> + * @groupp: returns the matching tag group
> + *
> + * Pure lookup, no side effects.  A record names the dc_extent that contains
> + * its range: the host cannot split an extent.
> + *
> + * Return: 0 when @extent matches an extent the host owns.  -ENXIO when the
> + * extent doesn't match anything on the host (no region, no dax region, or a
> + * range that has no overlap with host-owned extents), so the release can
> + * simply be acknowledged.  -EINVAL when it overlaps owned capacity without
> + * matching any single host-owned extent: it straddles multiple extents, or
> + * the tag doesn't match.
> + */
> +int cxl_resolve_extent(struct cxl_memdev_state *mds, struct cxl_extent *extent,
> +		       struct cxl_region **cxlrp,
> +		       struct cxl_dc_tag_group **groupp)
> +{
> +	u64 start_dpa = le64_to_cpu(extent->start_dpa);
> +	struct cxl_memdev *cxlmd = mds->cxlds.cxlmd;
> +	struct cxl_endpoint_decoder *cxled;
> +	struct cxl_dax_region *cxlr_dax;
> +	struct dc_extent *dc_extent;
> +	struct cxl_region *cxlr;
> +	struct range dpa_range;
> +	unsigned long idx;
> +	uuid_t tag;
> +
> +	*cxlrp = NULL;
> +	*groupp = NULL;
> +
> +	dpa_range = (struct range) {
> +		.start = start_dpa,
> +		.end = start_dpa + le64_to_cpu(extent->length) - 1,
> +	};

[Severity: High]
What happens in cxl_resolve_extent() if a DCD_RELEASE_CAPACITY event record
has extent->length == 0, or if start_dpa + le64_to_cpu(extent->length) wraps
around 64 bits so that dpa_range.end < dpa_range.start?

In cxl_extent_dc_partition(), this case is explicitly checked:

drivers/cxl/core/extent.c:cxl_extent_dc_partition() {
	if (ext_range->end < ext_range->start) {
		dev_err_ratelimited(dev,
				    "DC extent DPA %pra (%pU) has invalid length (firmware bug)\n",
				    ext_range, extent->uuid);
		return NULL;
	}
}

Because range_overlaps() and range_contains() assume start <= end, an
inverted dpa_range where both start and end fall inside a host-owned
dc_extent->dpa_range satisfies both range_overlaps(&dc_extent->dpa_range,
&dpa_range) and range_contains(&dc_extent->dpa_range, &dpa_range), tearing
down a live tag group if the tag matches.

Conversely, if dpa_range.end wraps below dc_extent->dpa_range.start,
range_overlaps() evaluates to false and cxl_resolve_extent() returns -ENXIO,
causing cxl_rm_pending() to queue the malformed extent onto acks and send it
in CXL_MBOX_OP_RELEASE_DC. Should cxl_resolve_extent() reject
dpa_range.end < dpa_range.start before checking for overlaps?

> +
> +	cxlr = cxl_dpa_to_region(cxlmd, start_dpa, &cxled);
> +	if (!cxlr)
> +		return -ENXIO;
> +
> +	cxlr_dax = cxlr->cxlr_dax;
> +	if (!cxlr_dax)
> +		return -ENXIO;

[Severity: High]
Can a Release Capacity extent whose start_dpa lies outside a decoder (or in a
preceding endpoint decoder) while dpa_range.end extends into a decoder and
overlaps a host-owned dc_extent be misclassified as -ENXIO here?

cxl_dpa_to_region(cxlmd, start_dpa, &cxled) only checks which endpoint
decoder contains the single address start_dpa, and the xarray walk below only
inspects extents where dc_extent->cxled == cxled without verifying that
dpa_range is contained within cxled->dpa_res.

If start_dpa is in an unmapped DPA gap before cxled->dpa_res->start (or in an
earlier decoder on cxlmd) and dpa_range.end overlaps a live dc_extent in a
subsequent decoder, cxl_resolve_extent() returns -ENXIO instead of -EINVAL.
The first loop in cxl_rm_pending() then moves the extent onto acks and sends
CXL_MBOX_OP_RELEASE_DC for the range while the host still owns the
overlapping capacity.

> +
> +	import_uuid(&tag, extent->uuid);
> +
> +	/* RCU keeps entries live during the walk; teardown happens later. */
> +	scoped_guard(rcu) {
> +		xa_for_each(&cxlr_dax->dc_extents, idx, dc_extent) {
> +			if (dc_extent->cxled != cxled)
> +				continue;
> +			if (!range_overlaps(&dc_extent->dpa_range, &dpa_range))
> +				continue;
> +			if (!range_contains(&dc_extent->dpa_range, &dpa_range))
> +				return -EINVAL;
> +			if (!uuid_equal(&dc_extent->group->uuid, &tag))
> +				return -EINVAL;
> +			*cxlrp = cxlr;
> +			*groupp = dc_extent->group;
> +			return 0;
> +		}
> +	}
> +	return -ENXIO;
> +}

[Severity: High]
Could a transient kobject/sysfs reference on a member dc_extent->dev cause
cxl_resolve_extent() and cxl_rm_pending() to see already-unregistered
siblings or trigger a use-after-free on struct cxl_dc_tag_group?

When a tag group is torn down via rm_tag_group() -> unregister_tag_group(),
unregister_tag_group() calls device_unregister(&dc_extent->dev) on each
member, while the xarray removals and group->nr_extents-- happen in the
device release callback:

drivers/cxl/core/extent.c:dc_extent_release() {
	group = dc_extent->group;
	xa_erase(&group->cxlr_dax->dc_extents, dc_extent->dev.id);
	xa_erase(&group->dc_extents, dc_extent->seq_num);
	group->nr_extents--;
	if (!group->nr_extents)
		free_tag_group(group);
	kfree_rcu(dc_extent, rcu);
}

If a sysfs reader or udev holds a temporary reference on a sibling
dc_extent->dev when device_unregister() runs, dc_extent_release() is delayed:

1. The sibling dc_extent remains in cxlr_dax->dc_extents, so when the second
   loop in cxl_rm_pending() resolves the next sibling in the release chain,
   cxl_resolve_extent() returns 0 instead of -ENXIO.

2. When the external reference to dc_extent->dev is later dropped on another
   CPU via put_device(), dc_extent_release() decrements the non-atomic
   group->nr_extents without synchronization and frees group via
   free_tag_group() -> put_device(&group->dev) -> dc_tag_group_release() ->
   kfree(group) without RCU deferral, while cxl_resolve_extent() reads
   dc_extent->group->uuid under RCU and returns an unpinned dc_extent->group
   pointer.

[ ... ]

> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 0fa501f5800c9..c3236fe9e7c5e 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c

[ ... ]

> +static int cxl_rm_pending(struct cxl_memdev_state *mds)
> +{
> +	struct list_head *pending = &mds->dc_chain.pending_extents;
> +	struct device *dev = mds->cxlds.dev;
> +	struct cxl_extent_list_node *pos, *tmp;
> +	LIST_HEAD(acks);
> +	int cnt = 0, rc;
> +
> +	/* cxl_resolve_extent() -> cxl_dpa_to_region() walks every decoder's dpa_res */
> +	guard(rwsem_read)(&cxl_rwsem.region);
> +	guard(rwsem_read)(&cxl_rwsem.dpa);
> +
> +	/* Untracked first, before a teardown below makes a sibling look untracked */
> +	list_for_each_entry_safe(pos, tmp, pending, list) {
> +		struct cxl_dc_tag_group *group;
> +		struct cxl_region *cxlr;
> +
> +		if (cxl_resolve_extent(mds, pos->extent, &cxlr, &group) != -ENXIO)
> +			continue;
> +		list_move_tail(&pos->list, &acks);
> +		cnt++;
> +	}

[Severity: High]
If cxl_rm_pending() runs concurrently with region teardown, can this first
pass prematurely acknowledge a release before DAX teardown and CPU cache
invalidation complete?

During region teardown, cxlr_dax_unregister() clears cxlr->cxlr_dax and drops
cxl_rwsem.region before unbinding the DAX region driver:

drivers/cxl/core/region_dax.c:cxlr_dax_unregister() {
	cxlr_dax_unpublish(cxlr_dax);
	/* Unbind first: the driver's teardown empties untagged and the groups */
	device_release_driver(&cxlr_dax->dev);
	...
}

If cxl_rm_pending() acquires cxl_rwsem.region after cxlr_dax_unpublish() sets
cxlr->cxlr_dax = NULL (or after __cxl_decoder_detach() clears
cxled->cxld.region = NULL) but before device_release_driver(&cxlr_dax->dev)
finishes running dc_tag_group_teardown(), cxl_resolve_extent() sees
!cxlr_dax (or !cxlr) and returns -ENXIO.

Because this loop treats -ENXIO as untracked capacity, cxl_rm_pending()
queues the extent onto acks and sends CXL_MBOX_OP_RELEASE_DC immediately
(bypassing the !rm_tag_group(group) check in cxl_release_tag_group()), before
child DAX devices are torn down and before cxl_region_invalidate_memregion()
flushes CPU caches, and dc_tag_group_teardown() will then send a second
CXL_MBOX_OP_RELEASE_DC for the same group.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261104230445.66625-1-anisa.su@samsung.com?part=11

  reply	other threads:[~2026-11-04 23:28 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 [this message]
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
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-198297@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