From: Richard Cheng <icheng@nvidia.com>
To: "Lucero Palau, Alejandro" <alejandro.lucero-palau@amd.com>
Cc: jic23@kernel.org, dave@stgolabs.net, dave.jiang@intel.com,
alison.schofield@intel.com, vishal.l.verma@intel.com,
iweiny@kernel.org, ming.li@zohomail.com, kaihengf@nvidia.com,
kobak@nvidia.com, newtonl@nvidia.com, kristinc@nvidia.com,
linux-cxl@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH v2 1/3] cxl/mem: Separate provider registration from region attachment
Date: Fri, 6 Nov 2026 14:25:35 +0800 [thread overview]
Message-ID: <au1yQOR0-vNj27NJ@MWDK4CY14F> (raw)
In-Reply-To: <fb16beb4-5816-45ed-ad74-3338ee698791@amd.com>
On Wed, Oct 14, 2026 at 06:17:44PM +0800, Lucero Palau, Alejandro wrote:
> Hi Richard,
>
>
> I think this is good stuff. Just some comments below.
>
> Thanks!
>
>
> On 07/10/2026 10:05, Richard Cheng wrote:
> > devm_cxl_probe_mem() requires a committed region when registering a
> > provider-owned memdev. If FW has not created a region
>
>
> This is mixing kernel parlance with BIOS potential actions like setting HDM
> decoders.
>
>
Hi Alejandro,
I see, I'll rephrase them to be more precise in v3.
> > , registration
> > fails before the provider can use the endpoint topology to provision
> > one.
> >
> > Make the attachment probe callback optional while retaining a non-NULL
> > descriptor to preserve provider ownership and handling of CXL link loss.
> > Install endpoint cleanup for FW-discovered provider regions even when
> > the provider never requests attachment.
> >
> > This establishes the registration and attachment APIs needed for
> > explicit region provisioning without introducing region creation or
> > allocation policy.
> >
> > Signed-off-by: Richard Cheng <icheng@nvidia.com>
> > ---
> > Changelog:
> >
> > v1 -> v2:
> > - Rework the patch into a registration/attachment refactor, following
> > Alejandro's request to separate region provisioning from memdev
> > registration.
> > - Add devm_cxl_register_mem() to establish a provider-owned memdev and
> > its endpoint without requiring a comitted region.
> > - Add devm_cxl_attach_mem_region() to obtain the HPA range of an
> > existing committed, single-target region after registration.
> > - Make the region probe callback optional while preserving provider
> > ownership, synchronous endpoint setup, and hdling of CXL link loss.
> > ---
> > drivers/cxl/core/region.c | 146 ++++++++++++++++++++++++++------------
> > drivers/cxl/cxlmem.h | 22 ++++--
> > drivers/cxl/mem.c | 69 +++++++++++++++++-
> > include/cxl/cxl.h | 2 +
> > 4 files changed, 188 insertions(+), 51 deletions(-)
> >
> > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> > index 5ef0ca0694ff..7a64a730587d 100644
> > --- a/drivers/cxl/core/region.c
> > +++ b/drivers/cxl/core/region.c
> > @@ -4111,67 +4111,123 @@ static int first_mapped_decoder(struct device *dev, const void *data)
> > return 0;
> > }
> > -/*
> > - * Runs in cxl_mem_probe context after successful endpoint probe, assumes the
> > - * simple case of single mapped decoder per memdev.
> > - */
> > -int cxl_memdev_attach_region(struct cxl_memdev *cxlmd)
> > +static int unregister_memdev_region(struct device *dev, void *data)
> > {
> > - struct cxl_attach_region *attach =
> > - container_of(cxlmd->attach, typeof(*attach), attach);
> > - struct cxl_port *endpoint = cxlmd->endpoint;
> > struct cxl_endpoint_decoder *cxled;
> > struct cxl_region *cxlr;
> > - int rc;
> > - /* hold endpoint lock to setup autoremove of the region */
> > + if (!is_endpoint_decoder(dev))
> > + return 0;
> > +
> > + cxled = to_cxl_endpoint_decoder(dev);
> > + scoped_guard(rwsem_read, &cxl_rwsem.region) {
> > + cxlr = cxled->cxld.region;
> > + if (!cxlr)
> > + return 0;
> > + get_device(&cxlr->dev);
> > + }
> > +
> > + /* Unregistration needs the region write lock. */
> > + endpoint_unregister_region(cxlr);
> > + return 0;
> > +}
> > +
> > +static void endpoint_unregister_regions(void *data)
> > +{
> > + struct cxl_port *endpoint = data;
> > +
> > + device_for_each_child(&endpoint->dev, NULL, unregister_memdev_region);
> > +}
> > +
> > +int cxl_memdev_setup_region_cleanup(struct cxl_memdev *cxlmd)
> > +{
> > + struct cxl_port *endpoint = cxlmd->endpoint;
> > +
> > + device_lock_assert(&cxlmd->dev);
> > + if (IS_ERR_OR_NULL(endpoint))
> > + return -ENXIO;
> > +
> > guard(device)(&endpoint->dev);
> > - if (!endpoint->dev.driver)
> > + if (!endpoint->dev.driver || endpoint->dead)
> > return -ENXIO;
> > - guard(rwsem_read)(&cxl_rwsem.region);
> > - guard(rwsem_read)(&cxl_rwsem.dpa);
> > /*
> > - * TODO auto-instantiate a region, for now assume this will find an
> > - * auto-region
> > + * Endpoint probe may discover provider-owned firmware regions even if
> > + * the provider never requests their HPA range. Run before decoder
> > + * teardown so those regions are unregistered, not just detached.
> > */
>
>
> I would put this as a description for the function instead of a comment
> here.
>
>
Sure thing.
> > - struct device *dev __free(put_device) =
> > - device_find_child(&endpoint->dev, NULL, first_mapped_decoder);
> > -
> > - if (!dev) {
> > - dev_dbg(cxlmd->cxlds->dev, "no region found for memdev %s\n",
> > - dev_name(&cxlmd->dev));
> > - return -ENXIO;
> > - }
> > + return devm_add_action_or_reset(&endpoint->dev,
> > + endpoint_unregister_regions, endpoint);
> > +}
> > +EXPORT_SYMBOL_FOR_MODULES(cxl_memdev_setup_region_cleanup, "cxl_mem");
> > - cxled = to_cxl_endpoint_decoder(dev);
> > - cxlr = cxled->cxld.region;
> > +/* Caller holds the memdev lock; attach to a single mapped decoder. */
> > +int cxl_memdev_attach_region(struct cxl_memdev *cxlmd, struct range *hpa_range)
> > +{
> > + struct cxl_port *endpoint = cxlmd->endpoint;
> > + struct cxl_endpoint_decoder *cxled;
> > + struct cxl_region *cxlr;
> > + int rc;
> > - if (cxlr->params.state < CXL_CONFIG_COMMIT) {
> > - dev_dbg(cxlmd->cxlds->dev,
> > - "region %s not committed for memdev %s\n",
> > - dev_name(&cxlr->dev), dev_name(&cxlmd->dev));
> > + device_lock_assert(&cxlmd->dev);
> > + if (IS_ERR_OR_NULL(endpoint))
> > return -ENXIO;
> > - }
> > - if (cxlr->params.nr_targets > 1) {
> > - dev_dbg(cxlmd->cxlds->dev,
> > - "Only attach to local non-interleaved region\n");
> > + /* hold endpoint lock to setup autoremove of the region */
> > + guard(device)(&endpoint->dev);
> > + if (!endpoint->dev.driver || endpoint->dead)
> > return -ENXIO;
> > - }
> > - /* Only teardown regions that pass validation, ignore the rest */
> > - get_device(&cxlr->dev);
> > - rc = devm_add_action_or_reset(&endpoint->dev,
> > - endpoint_unregister_region, cxlr);
> > - if (rc)
> > - return rc;
> > + scoped_guard(rwsem_read, &cxl_rwsem.region) {
> > + guard(rwsem_read)(&cxl_rwsem.dpa);
> > - attach->hpa_range = (struct range) {
> > - .start = cxlr->params.res->start,
> > - .end = cxlr->params.res->end,
> > - };
> > - return 0;
> > + struct device *dev __free(put_device) =
> > + device_find_child(&endpoint->dev, NULL,
> > + first_mapped_decoder);
> > + if (!dev) {
> > + dev_dbg(cxlmd->cxlds->dev,
> > + "no region found for memdev %s\n",
> > + dev_name(&cxlmd->dev));
> > + return -ENXIO;
> > + }
> > +
> > + cxled = to_cxl_endpoint_decoder(dev);
> > + cxlr = cxled->cxld.region;
> > + if (cxlr->params.state < CXL_CONFIG_COMMIT) {
> > + dev_dbg(cxlmd->cxlds->dev,
> > + "region %s not committed for memdev %s\n",
> > + dev_name(&cxlr->dev), dev_name(&cxlmd->dev));
> > + return -ENXIO;
> > + }
> > +
> > + if (cxlr->params.nr_targets > 1) {
> > + dev_dbg(cxlmd->cxlds->dev,
> > + "Only attach to local non-interleaved region\n");
> > + return -ENXIO;
> > + }
> > + if (!cxlr->params.res)
> > + return -ENXIO;
> > +
> > + /* Only teardown regions that pass validation, ignore the rest. */
> > + if (!devm_is_action_added(&endpoint->dev,
> > + endpoint_unregister_region, cxlr)) {
> > + get_device(&cxlr->dev);
> > + rc = devm_add_action(&endpoint->dev,
> > + endpoint_unregister_region, cxlr);
> > + if (rc)
> > + break;
> > + }
> > +
> > + *hpa_range = (struct range) {
> > + .start = cxlr->params.res->start,
> > + .end = cxlr->params.res->end,
> > + };
> > + return 0;
> > + }
> > +
> > + /* devm_add_action() failed; teardown needs the region write lock. */
> > + endpoint_unregister_region(cxlr);
> > + return rc;
> > }
> > EXPORT_SYMBOL_FOR_MODULES(cxl_memdev_attach_region, "cxl_mem");
> > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > index c401e3a1af06..8c050bc308bd 100644
> > --- a/drivers/cxl/cxlmem.h
> > +++ b/drivers/cxl/cxlmem.h
> > @@ -97,6 +97,13 @@ static inline bool is_cxl_endpoint(struct cxl_port *port)
> > return is_cxl_memdev(port->uport_dev);
> > }
> > +/**
> > + * struct cxl_memdev_attach - provider ownership and CXL link requirements
> > + * @probe: optional region probe callback, called with the memdev locked
> > + *
> > + * A non-NULL descriptor requires successful synchronous endpoint setup and
> > + * preserves provider ownership even when no region probe is requested.
> > + */
> > struct cxl_memdev_attach {
> > int (*probe)(struct cxl_memdev *cxlmd);
> > };
> > @@ -107,8 +114,8 @@ struct cxl_memdev_attach {
> > * @hpa_range: physical address range of the region
> > *
> > * For the common simple case of a CXL device with private (non-general purpose
> > - * / "accelerator") memory, enumerate firmware instantiated region, or
> > - * instantiate a region for the device's capacity. Destroy the region on detach.
> > + * / "accelerator") memory, enumerate a firmware-instantiated region and
> > + * report its range. Destroy the region on detach.
> > */
> > struct cxl_attach_region {
> > struct cxl_memdev_attach attach;
> > @@ -116,12 +123,19 @@ struct cxl_attach_region {
> > };
> > #ifdef CONFIG_CXL_REGION
> > -int cxl_memdev_attach_region(struct cxl_memdev *cxlmd);
> > +int cxl_memdev_attach_region(struct cxl_memdev *cxlmd, struct range *hpa_range);
> > +int cxl_memdev_setup_region_cleanup(struct cxl_memdev *cxlmd);
> > #else
> > -static inline int cxl_memdev_attach_region(struct cxl_memdev *cxlmd)
> > +static inline int cxl_memdev_attach_region(struct cxl_memdev *cxlmd,
> > + struct range *hpa_range)
> > {
> > return -EOPNOTSUPP;
> > }
> > +
> > +static inline int cxl_memdev_setup_region_cleanup(struct cxl_memdev *cxlmd)
> > +{
> > + return 0;
> > +}
> > #endif
> > struct cxl_memdev *devm_cxl_add_classdev(struct cxl_dev_state *cxlds);
> > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > index 3959ec963026..aa08d88ab104 100644
> > --- a/drivers/cxl/mem.c
> > +++ b/drivers/cxl/mem.c
> > @@ -172,7 +172,10 @@ static int cxl_mem_probe(struct device *dev)
> > }
> > if (cxlmd->attach) {
> > - rc = cxlmd->attach->probe(cxlmd);
> > + if (cxlmd->attach->probe)
> > + rc = cxlmd->attach->probe(cxlmd);
> > + else
> > + rc = cxl_memdev_setup_region_cleanup(cxlmd);
> > if (rc)
> > return rc;
> > }
> > @@ -215,6 +218,68 @@ struct cxl_memdev *devm_cxl_add_classdev(struct cxl_dev_state *cxlds)
> > }
> > EXPORT_SYMBOL_NS_GPL(devm_cxl_add_classdev, "CXL");
> > +/**
> > + * devm_cxl_register_mem - Register a provider-owned CXL memory device
> > + * @cxlds: CXL device state to associate with the memdev
> > + *
> > + * Establish the CXL port topology and endpoint synchronously, without requiring
> > + * a committed region. The provider retains ownership of its memory, including
> > + * any firmware-discovered regions, and must detach if the CXL link is lost.
> > + *
> > + * The parent of the resulting device and the devm context for allocations is
> > + * @cxlds->dev. Returns the registered memdev or an ERR_PTR() on failure.
> > + */
> > +struct cxl_memdev *devm_cxl_register_mem(struct cxl_dev_state *cxlds)
> > +{
> > + struct cxl_memdev_attach *attach;
> > +
> > + attach = devm_kzalloc(cxlds->dev, sizeof(*attach), GFP_KERNEL);
> > + if (!attach)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + return __devm_cxl_add_memdev(cxlds, attach);
> > +}
> > +EXPORT_SYMBOL_NS_GPL(devm_cxl_register_mem, "CXL");
> > +
> > +/**
> > + * devm_cxl_attach_mem_region - Attach a registered memdev to its region
> > + * @cxlmd: provider-owned memdev returned by devm_cxl_register_mem()
> > + * @hpa_range: CXL.mem physical address range result
> > + *
> > + * Attach to an existing committed, single-target region. This does not create
> > + * or program a region. Repeated attachment to the same region returns the same
> > + * range without adding another cleanup action. Failure leaves the memdev
> > + * registered so that the provider can decide how to proceed.
> > + *
> > + * The region is removed when the endpoint detaches. Returns zero on success or
> > + * a negative errno; @hpa_range is empty on failure.
> > + */
> > +int devm_cxl_attach_mem_region(struct cxl_memdev *cxlmd,
>
>
> is this devm prefix required here?
>
>
I do think so, the helper here can install an EP-managed region-unregistration
operation, so attachment has a managed lifetime side effect in addition to
just returning the HPA range. That's why I used the devm_ prefix.
Is that acceptable ?
Best regards,
Richard Cheng.
> > + struct range *hpa_range)
> > +{
> > + if (!hpa_range)
> > + return -EINVAL;
> > + *hpa_range = DEFINE_RANGE(0, -1);
> > +
> > + if (!cxlmd->attach)
> > + return -EINVAL;
> > +
> > + guard(device)(&cxlmd->dev);
> > + if (!cxlmd->dev.driver || !cxlmd->cxlds)
> > + return -ENXIO;
> > +
> > + return cxl_memdev_attach_region(cxlmd, hpa_range);
> > +}
> > +EXPORT_SYMBOL_NS_GPL(devm_cxl_attach_mem_region, "CXL");
> > +
> > +static int cxl_probe_mem_region(struct cxl_memdev *cxlmd)
> > +{
> > + struct cxl_attach_region *attach =
> > + container_of(cxlmd->attach, typeof(*attach), attach);
> > +
> > + return cxl_memdev_attach_region(cxlmd, &attach->hpa_range);
> > +}
> > +
> > /**
> > * devm_cxl_probe_mem - Add a CXL memory device and probe its region
> > * @cxlds: CXL device state to associate with the memdev
> > @@ -242,7 +307,7 @@ struct cxl_memdev *devm_cxl_probe_mem(struct cxl_dev_state *cxlds,
> > *attach = (struct cxl_attach_region) {
> > .attach = {
> > - .probe = cxl_memdev_attach_region,
> > + .probe = cxl_probe_mem_region,
> > },
> > .hpa_range = { 0, -1 },
> > };
> > diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
> > index 802b143de83d..3019e3ea5f09 100644
> > --- a/include/cxl/cxl.h
> > +++ b/include/cxl/cxl.h
> > @@ -224,6 +224,8 @@ struct cxl_dev_state *_devm_cxl_dev_state_create(struct device *dev,
> > sizeof(drv_struct), mbox); \
> > })
> > +struct cxl_memdev *devm_cxl_register_mem(struct cxl_dev_state *cxlds);
> > +int devm_cxl_attach_mem_region(struct cxl_memdev *cxlmd, struct range *hpa_range);
> > struct cxl_memdev *devm_cxl_probe_mem(struct cxl_dev_state *cxlds,
> > struct range *range);
next prev parent reply other threads:[~2026-11-06 6:25 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-07 9:05 [RFC PATCH v2 0/3] cxl: Provide explicit RAM region creation for Type-2 providers Richard Cheng
2026-10-07 9:05 ` [RFC PATCH v2 1/3] cxl/mem: Separate provider registration from region attachment Richard Cheng
2026-10-14 17:17 ` Lucero Palau, Alejandro
2026-11-06 6:25 ` Richard Cheng [this message]
2026-10-07 9:05 ` [RFC PATCH v2 2/3] cxl/mem: Add explicit RAM region creation for providers Richard Cheng
2026-10-15 12:55 ` Lucero Palau, Alejandro
2026-10-07 9:05 ` [RFC PATCH v2 3/3] cxl/test: Exercise explicit Type-2 RAM region creation Richard Cheng
2026-10-09 18:34 ` Dave Jiang
2026-10-14 15:34 ` [RFC PATCH v2 0/3] cxl: Provide explicit RAM region creation for Type-2 providers Lucero Palau, Alejandro
2026-10-14 16:23 ` Dave Jiang
2026-10-14 16:35 ` Lucero Palau, Alejandro
2026-10-14 17:16 ` Dave Jiang
2026-10-14 17:29 ` Lucero Palau, Alejandro
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=au1yQOR0-vNj27NJ@MWDK4CY14F \
--to=icheng@nvidia.com \
--cc=alejandro.lucero-palau@amd.com \
--cc=alison.schofield@intel.com \
--cc=dave.jiang@intel.com \
--cc=dave@stgolabs.net \
--cc=iweiny@kernel.org \
--cc=jic23@kernel.org \
--cc=kaihengf@nvidia.com \
--cc=kobak@nvidia.com \
--cc=kristinc@nvidia.com \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ming.li@zohomail.com \
--cc=newtonl@nvidia.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®