Re: [PATCH v7 1/2 RESEND] cxl/hdm: Allow zero sized HDM decoders

Jonathan Cameron <[email protected]>
Newsgroups org.kernel.vger.linux-cxl,org.kernel.vger.linux-kernel
Message-ID <20260722013822.57335034@jic23-huawei>
On Tue, 21 Jul 2026 16:57:14 +0800
Richard Cheng <[email protected]> wrote:

Hi Richard,

> CXL r4.0 §8.2.4.20.12 ("Committing Decoder Programming") and §14.13.10
> ("CXL HDM Decoder Zero Size Commit") permit committing an HDM decoder
> with size 0. BIOS may commit and lock such decoders so the OS cannot
> program regions through them, this is a design choice rather than a spec
> requirement.

Trivial but consistency in commit message formatting is good. I'd always use a
blank line between paragraphs.

> The kernel rejected these with -ENXIO during port enumeration and aborted
> the whole port, so affected systems showed nothing under 'cxl list'.
> 
> Treat empty decoders as first class instead of special casing them, back
> them with a kmalloc'd resource, since the resource tree can't represent
> an empty range, and keep the skip and hdm_end accounting intact. Guard
> the paths an empty decoder can't serve, e.g. region attach, DPA free, and
> poison queries.
> 
> Suggested-by: Dan Williams <[email protected]>
> Signed-off-by: Vishal Aslot <[email protected]>
> Signed-off-by: Richard Cheng <[email protected]>
> Reviewed-by: Dan Williams <[email protected]>

...

Main comment is of the 'whilst you are here' variety. I'm fine
if you want to ignore it.  I just found the existing code a little trickier
to read than the ideal and some of it gets shuffled round in here.

Either way
Reviewed-by: Jonathan Cameron <[email protected]>

> ---
>  drivers/cxl/core/hdm.c    | 52 ++++++++++++++++++++++++++-------------
>  drivers/cxl/core/mbox.c   |  3 +++
>  drivers/cxl/core/region.c | 49 +++++++++++++++++++++++-------------
>  drivers/cxl/cxl.h         | 10 ++++++++
>  drivers/cxl/port.c        |  3 +++
>  5 files changed, 83 insertions(+), 34 deletions(-)
> 
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 0c80b76a5f9b..ccbab2e21f06 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c

...


>  
> +static struct resource *cxl_dpa_request_region(struct resource *parent,
> +					       resource_size_t start,
> +					       resource_size_t n,
> +					       const char *name)
> +{
> +	if (!n) {
> +		struct resource *res = kmalloc_obj(*res);
> +
> +		if (!res)
> +			return NULL;
I'd format this a tiny bit differently but not that important.
The advantage is to keep the allocation and error check closely coupled.

		struct resource *res;

		res = malloc_obj(*res);
		if (!res)
			return NULL;

		*res = DEFINE_RES_NAMED(start, 0, name, IORESOURCE_MEM);

		return res;

> +		*res = DEFINE_RES_NAMED(start, 0, name, IORESOURCE_MEM);
> +		return res;
> +	}
> +
> +	return __request_region(parent, start, n, name, 0);
> +}

> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 1e211542b6b6..6c7d9a52707c 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -2115,7 +2115,7 @@ static int cxl_region_attach(struct cxl_region *cxlr,
>  		return -ENXIO;
>  	}
>  
> -	if (!cxled->dpa_res) {
> +	if (cxled_empty(cxled)) {
>  		dev_dbg(&cxlr->dev, "%s:%s: missing DPA allocation.\n",
>  			dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
>  		return -ENXIO;
> @@ -2959,28 +2959,35 @@ static int poison_by_decoder(struct device *dev, void *arg)
>  	if (!cxled->dpa_res)
>  		return rc;

Whilst touching code can we just return 0 for this and the one above.

The return values from this function are unusual so nice if we can make
them as obvious as possible!

>  
> -	cxlmd = cxled_to_memdev(cxled);
> -	cxlds = cxlmd->cxlds;
> -	mode = cxlds->part[cxled->part].mode;
> +	/*
> +	 * Handle the degenerate case of a device with only empty decoders. An
> +	 * empty decoder can still map a non-zero skip range, so advance the
> +	 * walk to commit_end either way.
> +	 */
> +	if (cxled->part >= 0) {
> +		cxlmd = cxled_to_memdev(cxled);
> +		cxlds = cxlmd->cxlds;
> +		mode = cxlds->part[cxled->part].mode;
> +
> +		if (cxled->skip) {
> +			offset = cxled->dpa_res->start - cxled->skip;
> +			length = cxled->skip;
> +			rc = cxl_mem_get_poison(cxlmd, offset, length, NULL);
> +			if (rc == -EFAULT && mode == CXL_PARTMODE_RAM)

Maybe similar to below. 

> +				rc = 0;
> +			if (rc)
> +				return rc;
> +		}
>  
> -	if (cxled->skip) {
> -		offset = cxled->dpa_res->start - cxled->skip;
> -		length = cxled->skip;
> -		rc = cxl_mem_get_poison(cxlmd, offset, length, NULL);
> +		offset = cxled->dpa_res->start;
> +		length = cxled->dpa_res->end - offset + 1;
> +		rc = cxl_mem_get_poison(cxlmd, offset, length, cxled->cxld.region);
>  		if (rc == -EFAULT && mode == CXL_PARTMODE_RAM)
>  			rc = 0;
>  		if (rc)
>  			return rc;

Maybe whilst we are here we can avoid the rc dance?

		if (rc && !(rc == -EFAULT && mode == CXL_PARTMODE_RAM))
			return rc;

>  	}
>  
> -	offset = cxled->dpa_res->start;
> -	length = cxled->dpa_res->end - offset + 1;
> -	rc = cxl_mem_get_poison(cxlmd, offset, length, cxled->cxld.region);
> -	if (rc == -EFAULT && mode == CXL_PARTMODE_RAM)
> -		rc = 0;
> -	if (rc)
> -		return rc;
> -
>  	/* Iterate until commit_end is reached */
>  	if (cxled->cxld.id == ctx->port->commit_end) {
>  		ctx->offset = cxled->dpa_res->end + 1;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.