All of lore.kernel.org
 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 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.