diff mbox series

[02/26] cxl/core: Separate region mode from decoder mode

Message ID 20240324-dcd-type2-upstream-v1-2-b7b00d623625@intel.com
State Changes Requested
Headers show
Series DCD: Add support for Dynamic Capacity Devices (DCD) | expand

Commit Message

Ira Weiny March 24, 2024, 11:18 p.m. UTC
From: Navneet Singh <navneet.singh@intel.com>

Until now region modes and decoder modes were equivalent in that they
were either PMEM or RAM.  With the upcoming addition of Dynamic Capacity
regions (which will represent an array of device regions [better named
partitions] the index of which could be different on different
interleaved devices), the mode of an endpoint decoder and a region will
no longer be equivalent.

Define a new region mode enumeration and adjust the code for it.

Suggested-by: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
Signed-off-by: Navneet Singh <navneet.singh@intel.com>
Co-developed-by: Ira Weiny <ira.weiny@intel.com>
Signed-off-by: Ira Weiny <ira.weiny@intel.com>

---
Changes for v1
<none>
---
 drivers/cxl/core/region.c | 77 +++++++++++++++++++++++++++++++++++------------
 drivers/cxl/cxl.h         | 26 ++++++++++++++--
 2 files changed, 81 insertions(+), 22 deletions(-)

Comments

Jonathan Cameron March 25, 2024, 4:20 p.m. UTC | #1
On Sun, 24 Mar 2024 16:18:05 -0700
ira.weiny@intel.com wrote:

> From: Navneet Singh <navneet.singh@intel.com>
> 
> Until now region modes and decoder modes were equivalent in that they
> were either PMEM or RAM.  With the upcoming addition of Dynamic Capacity
> regions (which will represent an array of device regions [better named
> partitions] the index of which could be different on different
> interleaved devices), the mode of an endpoint decoder and a region will
> no longer be equivalent.
> 
> Define a new region mode enumeration and adjust the code for it.
> 
> Suggested-by: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
> Signed-off-by: Navneet Singh <navneet.singh@intel.com>
> Co-developed-by: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Ira Weiny <ira.weiny@intel.com>

I can't really remember the reasoning behind this split, but from a fresh
read it seems reasonable. Some trivial comments inline.

Jonathan

> 
> ---
> Changes for v1
> <none>
> ---
>  drivers/cxl/core/region.c | 77 +++++++++++++++++++++++++++++++++++------------
>  drivers/cxl/cxl.h         | 26 ++++++++++++++--
>  2 files changed, 81 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 4c7fd2d5cccb..1723d17f121e 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c


> @@ -2800,6 +2814,24 @@ static int match_region_by_range(struct device *dev, void *data)
>  	return rc;
>  }
>  
> +static enum cxl_region_mode
> +cxl_decoder_to_region_mode(enum cxl_decoder_mode mode)
> +{
> +	switch (mode) {
> +	case CXL_DECODER_NONE:
> +		return CXL_REGION_NONE;
> +	case CXL_DECODER_RAM:
> +		return CXL_REGION_RAM;
> +	case CXL_DECODER_PMEM:
> +		return CXL_REGION_PMEM;
> +	case CXL_DECODER_MIXED:
> +	default:
> +		return CXL_REGION_MIXED;
> +	}
> +

Dead code.

> +	return CXL_REGION_MIXED;
> +}
> +
>  /* Establish an empty region covering the given HPA range */
>  static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
>  					   struct cxl_endpoint_decoder *cxled)
> @@ -2808,12 +2840,17 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
>  	struct cxl_port *port = cxlrd_to_port(cxlrd);
>  	struct range *hpa = &cxled->cxld.hpa_range;
>  	struct cxl_region_params *p;
> +	enum cxl_region_mode mode;
>  	struct cxl_region *cxlr;
>  	struct resource *res;
>  	int rc;
>  
> +	if (cxled->mode == CXL_DECODER_DEAD)
> +		return ERR_PTR(-EINVAL);

Not a bad thing necessarily, but why do we now need this and didn't before?

> +
> +	mode = cxl_decoder_to_region_mode(cxled->mode);
>  	do {
> -		cxlr = __create_region(cxlrd, cxled->mode,
> +		cxlr = __create_region(cxlrd, mode,
>  				       atomic_read(&cxlrd->region_id));
>  	} while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY);


> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 003feebab79b..9a0cce1e6fca 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h


>  /*
>   * Track whether this decoder is reserved for region autodiscovery, or
>   * free for userspace provisioning.
> @@ -511,7 +532,8 @@ struct cxl_region_params {
>   * struct cxl_region - CXL region
>   * @dev: This region's device
>   * @id: This region's id. Id is globally unique across all regions
> - * @mode: Endpoint decoder allocation / access mode
> + * @mode: Region mode which defines which endpoint decoder mode the region is
mode or potentially modes?

If region is mixed, I guess that means endpoint could be pmem or ram in theory?
Don't think anyone has implemented anything yet, but is the potential there?


> + *        compatible with
Davidlohr Bueso March 25, 2024, 11:18 p.m. UTC | #2
On Sun, 24 Mar 2024, ira.weiny@intel.com wrote:

>From: Navneet Singh <navneet.singh@intel.com>
>
>Until now region modes and decoder modes were equivalent in that they
>were either PMEM or RAM.  With the upcoming addition of Dynamic Capacity
>regions (which will represent an array of device regions [better named
>partitions] the index of which could be different on different
>interleaved devices), the mode of an endpoint decoder and a region will
>no longer be equivalent.
>
>Define a new region mode enumeration and adjust the code for it.

Could this could also be picked up regardless of dcd?

Thanks,
Davidlohr
Ira Weiny March 28, 2024, 5:22 a.m. UTC | #3
Davidlohr Bueso wrote:
> On Sun, 24 Mar 2024, ira.weiny@intel.com wrote:
> 
> >From: Navneet Singh <navneet.singh@intel.com>
> >
> >Until now region modes and decoder modes were equivalent in that they
> >were either PMEM or RAM.  With the upcoming addition of Dynamic Capacity
> >regions (which will represent an array of device regions [better named
> >partitions] the index of which could be different on different
> >interleaved devices), the mode of an endpoint decoder and a region will
> >no longer be equivalent.
> >
> >Define a new region mode enumeration and adjust the code for it.
> 
> Could this could also be picked up regardless of dcd?

It could but there is no need for it without DCD.

I will work on re-ordering the cleanups if Dave will agree to take them
early.

Ira
Dave Jiang March 28, 2024, 8:09 p.m. UTC | #4
On 3/27/24 10:22 PM, Ira Weiny wrote:
> Davidlohr Bueso wrote:
>> On Sun, 24 Mar 2024, ira.weiny@intel.com wrote:
>>
>>> From: Navneet Singh <navneet.singh@intel.com>
>>>
>>> Until now region modes and decoder modes were equivalent in that they
>>> were either PMEM or RAM.  With the upcoming addition of Dynamic Capacity
>>> regions (which will represent an array of device regions [better named
>>> partitions] the index of which could be different on different
>>> interleaved devices), the mode of an endpoint decoder and a region will
>>> no longer be equivalent.
>>>
>>> Define a new region mode enumeration and adjust the code for it.
>>
>> Could this could also be picked up regardless of dcd?
> 
> It could but there is no need for it without DCD.
> 
> I will work on re-ordering the cleanups if Dave will agree to take them
> early.

There's no reason for the change unless it comes with DCD right? And probably no urgent need to taking it ahead then?
> 
> Ira
Ira Weiny April 2, 2024, 11:24 p.m. UTC | #5
Jonathan Cameron wrote:
> On Sun, 24 Mar 2024 16:18:05 -0700
> ira.weiny@intel.com wrote:

[snip]

> > 
> > ---
> > Changes for v1
> > <none>
> > ---
> >  drivers/cxl/core/region.c | 77 +++++++++++++++++++++++++++++++++++------------
> >  drivers/cxl/cxl.h         | 26 ++++++++++++++--
> >  2 files changed, 81 insertions(+), 22 deletions(-)
> > 
> > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> > index 4c7fd2d5cccb..1723d17f121e 100644
> > --- a/drivers/cxl/core/region.c
> > +++ b/drivers/cxl/core/region.c
> 
> 
> > @@ -2800,6 +2814,24 @@ static int match_region_by_range(struct device *dev, void *data)
> >  	return rc;
> >  }
> >  
> > +static enum cxl_region_mode
> > +cxl_decoder_to_region_mode(enum cxl_decoder_mode mode)
> > +{
> > +	switch (mode) {
> > +	case CXL_DECODER_NONE:
> > +		return CXL_REGION_NONE;
> > +	case CXL_DECODER_RAM:
> > +		return CXL_REGION_RAM;
> > +	case CXL_DECODER_PMEM:
> > +		return CXL_REGION_PMEM;
> > +	case CXL_DECODER_MIXED:
> > +	default:
> > +		return CXL_REGION_MIXED;
> > +	}
> > +
> 
> Dead code.

Fixed thanks.

> 
> > +	return CXL_REGION_MIXED;
> > +}
> > +
> >  /* Establish an empty region covering the given HPA range */
> >  static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
> >  					   struct cxl_endpoint_decoder *cxled)
> > @@ -2808,12 +2840,17 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
> >  	struct cxl_port *port = cxlrd_to_port(cxlrd);
> >  	struct range *hpa = &cxled->cxld.hpa_range;
> >  	struct cxl_region_params *p;
> > +	enum cxl_region_mode mode;
> >  	struct cxl_region *cxlr;
> >  	struct resource *res;
> >  	int rc;
> >  
> > +	if (cxled->mode == CXL_DECODER_DEAD)
> > +		return ERR_PTR(-EINVAL);
> 
> Not a bad thing necessarily, but why do we now need this and didn't before?

Ah.  Because in devm_cxl_add_region() the mode of CXL_DECODER_DEAD used to
return -EINVAL.

There is no logical equivalent to decoder dead in the region mode (regions
don't need it).  So this correctly flags the error based on the decoder
mode rather than introduce a mode for regions which does not make sense.

I'll update the commit message because that is hard to see.

> 
> > +
> > +	mode = cxl_decoder_to_region_mode(cxled->mode);
> >  	do {
> > -		cxlr = __create_region(cxlrd, cxled->mode,
> > +		cxlr = __create_region(cxlrd, mode,
> >  				       atomic_read(&cxlrd->region_id));
> >  	} while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY);
> 
> 
> > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > index 003feebab79b..9a0cce1e6fca 100644
> > --- a/drivers/cxl/cxl.h
> > +++ b/drivers/cxl/cxl.h
> 
> 
> >  /*
> >   * Track whether this decoder is reserved for region autodiscovery, or
> >   * free for userspace provisioning.
> > @@ -511,7 +532,8 @@ struct cxl_region_params {
> >   * struct cxl_region - CXL region
> >   * @dev: This region's device
> >   * @id: This region's id. Id is globally unique across all regions
> > - * @mode: Endpoint decoder allocation / access mode
> > + * @mode: Region mode which defines which endpoint decoder mode the region is
> mode or potentially modes?
> 
> If region is mixed, I guess that means endpoint could be pmem or ram in theory?
> Don't think anyone has implemented anything yet, but is the potential there?

Yes the potential is there.  The endpoint decoder is set to
CXL_DECODER_MIXED in __cxl_dpa_reserve().  But I am unclear how that will
ever be executed except if the BIOS sets up a decoder to span ram/pmem.
In this case the rest of the stack is not going to work and will complain
about mixed mode.

Ok Dan clued me in.  Check out cxl_dpa_alloc().  Spanning partitions is
not allowed.

So the comment is targeted toward the 'normal' case even though the region
could be created incorrectly via BIOS.

Ira
Ira Weiny April 2, 2024, 11:25 p.m. UTC | #6
Davidlohr Bueso wrote:
> On Sun, 24 Mar 2024, ira.weiny@intel.com wrote:
> 
> >From: Navneet Singh <navneet.singh@intel.com>
> >
> >Until now region modes and decoder modes were equivalent in that they
> >were either PMEM or RAM.  With the upcoming addition of Dynamic Capacity
> >regions (which will represent an array of device regions [better named
> >partitions] the index of which could be different on different
> >interleaved devices), the mode of an endpoint decoder and a region will
> >no longer be equivalent.
> >
> >Define a new region mode enumeration and adjust the code for it.
> 
> Could this could also be picked up regardless of dcd?

It could be.  But there is no practical need for it without the addition of
DCD.  So I don't think it should be.

Ira
Ira Weiny April 2, 2024, 11:27 p.m. UTC | #7
Dave Jiang wrote:
> 
> 
> On 3/27/24 10:22 PM, Ira Weiny wrote:
> > Davidlohr Bueso wrote:
> >> On Sun, 24 Mar 2024, ira.weiny@intel.com wrote:
> >>
> >>> From: Navneet Singh <navneet.singh@intel.com>
> >>>
> >>> Until now region modes and decoder modes were equivalent in that they
> >>> were either PMEM or RAM.  With the upcoming addition of Dynamic Capacity
> >>> regions (which will represent an array of device regions [better named
> >>> partitions] the index of which could be different on different
> >>> interleaved devices), the mode of an endpoint decoder and a region will
> >>> no longer be equivalent.
> >>>
> >>> Define a new region mode enumeration and adjust the code for it.
> >>
> >> Could this could also be picked up regardless of dcd?
> > 
> > It could but there is no need for it without DCD.
> > 
> > I will work on re-ordering the cleanups if Dave will agree to take them
> > early.
> 
> There's no reason for the change unless it comes with DCD right? And probably no urgent need to taking it ahead then?
> > 

I think I just replied for a 2nd time to this...  yea.

LOL  I have to do better...

Ira
Alison Schofield April 10, 2024, 6:49 p.m. UTC | #8
On Sun, Mar 24, 2024 at 04:18:05PM -0700, Ira Weiny wrote:
> From: Navneet Singh <navneet.singh@intel.com>
> 
> Until now region modes and decoder modes were equivalent in that they
> were either PMEM or RAM.  With the upcoming addition of Dynamic Capacity
> regions (which will represent an array of device regions [better named
> partitions] the index of which could be different on different
> interleaved devices), the mode of an endpoint decoder and a region will
> no longer be equivalent.
> 
> Define a new region mode enumeration and adjust the code for it.
> 
> Suggested-by: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
> Signed-off-by: Navneet Singh <navneet.singh@intel.com>
> Co-developed-by: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> 
> ---
> Changes for v1
> <none>
> ---
>  drivers/cxl/core/region.c | 77 +++++++++++++++++++++++++++++++++++------------
>  drivers/cxl/cxl.h         | 26 ++++++++++++++--
>  2 files changed, 81 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 4c7fd2d5cccb..1723d17f121e 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -40,7 +40,7 @@ static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
>  	rc = down_read_interruptible(&cxl_region_rwsem);
>  	if (rc)
>  		return rc;
> -	if (cxlr->mode != CXL_DECODER_PMEM)
> +	if (cxlr->mode != CXL_REGION_PMEM)
>  		rc = sysfs_emit(buf, "\n");
>  	else
>  		rc = sysfs_emit(buf, "%pUb\n", &p->uuid);
> @@ -353,7 +353,7 @@ static umode_t cxl_region_visible(struct kobject *kobj, struct attribute *a,
>  	 * Support tooling that expects to find a 'uuid' attribute for all
>  	 * regions regardless of mode.
>  	 */
> -	if (a == &dev_attr_uuid.attr && cxlr->mode != CXL_DECODER_PMEM)
> +	if (a == &dev_attr_uuid.attr && cxlr->mode != CXL_REGION_PMEM)
>  		return 0444;
>  	return a->mode;
>  }
> @@ -516,7 +516,7 @@ static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
>  {
>  	struct cxl_region *cxlr = to_cxl_region(dev);
>  
> -	return sysfs_emit(buf, "%s\n", cxl_decoder_mode_name(cxlr->mode));
> +	return sysfs_emit(buf, "%s\n", cxl_region_mode_name(cxlr->mode));
>  }
>  static DEVICE_ATTR_RO(mode);
>  
> @@ -542,7 +542,7 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
>  
>  	/* ways, granularity and uuid (if PMEM) need to be set before HPA */
>  	if (!p->interleave_ways || !p->interleave_granularity ||
> -	    (cxlr->mode == CXL_DECODER_PMEM && uuid_is_null(&p->uuid)))
> +	    (cxlr->mode == CXL_REGION_PMEM && uuid_is_null(&p->uuid)))
>  		return -ENXIO;
>  
>  	div64_u64_rem(size, (u64)SZ_256M * p->interleave_ways, &remainder);
> @@ -1683,6 +1683,17 @@ static int cxl_region_sort_targets(struct cxl_region *cxlr)
>  	return rc;
>  }
>  
> +static bool cxl_modes_compatible(enum cxl_region_mode rmode,
> +				 enum cxl_decoder_mode dmode)
> +{

Perhaps is_region_mode_compatible() ?  

Seems we have precedence for asking these questions that have
boolean responses. I picked 'region' because it is the region
we are trying to construct.


> +	if (rmode == CXL_REGION_RAM && dmode == CXL_DECODER_RAM)
> +		return true;
> +	if (rmode == CXL_REGION_PMEM && dmode == CXL_DECODER_PMEM)
> +		return true;
> +
> +	return false;
> +}
> +
>  static int cxl_region_attach(struct cxl_region *cxlr,
>  			     struct cxl_endpoint_decoder *cxled, int pos)
>  {
> @@ -1693,9 +1704,11 @@ static int cxl_region_attach(struct cxl_region *cxlr,
>  	struct cxl_dport *dport;
>  	int rc = -ENXIO;
>  
> -	if (cxled->mode != cxlr->mode) {
> -		dev_dbg(&cxlr->dev, "%s region mode: %d mismatch: %d\n",
> -			dev_name(&cxled->cxld.dev), cxlr->mode, cxled->mode);
> +	if (!cxl_modes_compatible(cxlr->mode, cxled->mode)) {
> +		dev_dbg(&cxlr->dev, "%s region mode: %s mismatch decoder: %s\n",
> +			dev_name(&cxled->cxld.dev),
> +			cxl_region_mode_name(cxlr->mode),
> +			cxl_decoder_mode_name(cxled->mode));
>  		return -EINVAL;
>  	}

Does the above return bypass this next code segment (not in your diff):

       if (cxled->mode == CXL_DECODER_DEAD) {
                dev_dbg(&cxlr->dev, "%s dead\n", dev_name(&cxled->cxld.dev));
                return -ENODEV;
        }

It seems we are changing the return value on DEAD.

More below where a new check for DEAD is added ...

snip

>  /* Establish an empty region covering the given HPA range */
>  static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
>  					   struct cxl_endpoint_decoder *cxled)
> @@ -2808,12 +2840,17 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
>  	struct cxl_port *port = cxlrd_to_port(cxlrd);
>  	struct range *hpa = &cxled->cxld.hpa_range;
>  	struct cxl_region_params *p;
> +	enum cxl_region_mode mode;
>  	struct cxl_region *cxlr;
>  	struct resource *res;
>  	int rc;
>  
> +	if (cxled->mode == CXL_DECODER_DEAD)
> +		return ERR_PTR(-EINVAL);

I see this addition, but it is in a different place and with
a different return value. Help me understand that this is no
change in behavior.


> +
> +	mode = cxl_decoder_to_region_mode(cxled->mode);
>  	do {
> -		cxlr = __create_region(cxlrd, cxled->mode,
> +		cxlr = __create_region(cxlrd, mode,
>  				       atomic_read(&cxlrd->region_id));
>  	} while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY);
>  

snip

>  /*
>   * Track whether this decoder is reserved for region autodiscovery, or
>   * free for userspace provisioning.
> @@ -511,7 +532,8 @@ struct cxl_region_params {
>   * struct cxl_region - CXL region
>   * @dev: This region's device
>   * @id: This region's id. Id is globally unique across all regions
> - * @mode: Endpoint decoder allocation / access mode
> + * @mode: Region mode which defines which endpoint decoder mode the region is
> + *        compatible with

Maybe...
@mode: Region mode used for decoder compatibility check

snip to end

--Alison

>
Ira Weiny April 24, 2024, 5:58 p.m. UTC | #9
Dave Jiang wrote:
> 
> 
> On 3/27/24 10:22 PM, Ira Weiny wrote:
> > Davidlohr Bueso wrote:
> >> On Sun, 24 Mar 2024, ira.weiny@intel.com wrote:
> >>
> >>> From: Navneet Singh <navneet.singh@intel.com>
> >>>
> >>> Until now region modes and decoder modes were equivalent in that they
> >>> were either PMEM or RAM.  With the upcoming addition of Dynamic Capacity
> >>> regions (which will represent an array of device regions [better named
> >>> partitions] the index of which could be different on different
> >>> interleaved devices), the mode of an endpoint decoder and a region will
> >>> no longer be equivalent.
> >>>
> >>> Define a new region mode enumeration and adjust the code for it.
> >>
> >> Could this could also be picked up regardless of dcd?
> > 
> > It could but there is no need for it without DCD.
> > 
> > I will work on re-ordering the cleanups if Dave will agree to take them
> > early.
> 
> There's no reason for the change unless it comes with DCD right? And probably no urgent need to taking it ahead then?

No I don't think so.
Ira

> > 
> > Ira
diff mbox series

Patch

diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 4c7fd2d5cccb..1723d17f121e 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -40,7 +40,7 @@  static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
 	rc = down_read_interruptible(&cxl_region_rwsem);
 	if (rc)
 		return rc;
-	if (cxlr->mode != CXL_DECODER_PMEM)
+	if (cxlr->mode != CXL_REGION_PMEM)
 		rc = sysfs_emit(buf, "\n");
 	else
 		rc = sysfs_emit(buf, "%pUb\n", &p->uuid);
@@ -353,7 +353,7 @@  static umode_t cxl_region_visible(struct kobject *kobj, struct attribute *a,
 	 * Support tooling that expects to find a 'uuid' attribute for all
 	 * regions regardless of mode.
 	 */
-	if (a == &dev_attr_uuid.attr && cxlr->mode != CXL_DECODER_PMEM)
+	if (a == &dev_attr_uuid.attr && cxlr->mode != CXL_REGION_PMEM)
 		return 0444;
 	return a->mode;
 }
@@ -516,7 +516,7 @@  static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
 {
 	struct cxl_region *cxlr = to_cxl_region(dev);
 
-	return sysfs_emit(buf, "%s\n", cxl_decoder_mode_name(cxlr->mode));
+	return sysfs_emit(buf, "%s\n", cxl_region_mode_name(cxlr->mode));
 }
 static DEVICE_ATTR_RO(mode);
 
@@ -542,7 +542,7 @@  static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
 
 	/* ways, granularity and uuid (if PMEM) need to be set before HPA */
 	if (!p->interleave_ways || !p->interleave_granularity ||
-	    (cxlr->mode == CXL_DECODER_PMEM && uuid_is_null(&p->uuid)))
+	    (cxlr->mode == CXL_REGION_PMEM && uuid_is_null(&p->uuid)))
 		return -ENXIO;
 
 	div64_u64_rem(size, (u64)SZ_256M * p->interleave_ways, &remainder);
@@ -1683,6 +1683,17 @@  static int cxl_region_sort_targets(struct cxl_region *cxlr)
 	return rc;
 }
 
+static bool cxl_modes_compatible(enum cxl_region_mode rmode,
+				 enum cxl_decoder_mode dmode)
+{
+	if (rmode == CXL_REGION_RAM && dmode == CXL_DECODER_RAM)
+		return true;
+	if (rmode == CXL_REGION_PMEM && dmode == CXL_DECODER_PMEM)
+		return true;
+
+	return false;
+}
+
 static int cxl_region_attach(struct cxl_region *cxlr,
 			     struct cxl_endpoint_decoder *cxled, int pos)
 {
@@ -1693,9 +1704,11 @@  static int cxl_region_attach(struct cxl_region *cxlr,
 	struct cxl_dport *dport;
 	int rc = -ENXIO;
 
-	if (cxled->mode != cxlr->mode) {
-		dev_dbg(&cxlr->dev, "%s region mode: %d mismatch: %d\n",
-			dev_name(&cxled->cxld.dev), cxlr->mode, cxled->mode);
+	if (!cxl_modes_compatible(cxlr->mode, cxled->mode)) {
+		dev_dbg(&cxlr->dev, "%s region mode: %s mismatch decoder: %s\n",
+			dev_name(&cxled->cxld.dev),
+			cxl_region_mode_name(cxlr->mode),
+			cxl_decoder_mode_name(cxled->mode));
 		return -EINVAL;
 	}
 
@@ -2168,7 +2181,7 @@  static struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int i
  * devm_cxl_add_region - Adds a region to a decoder
  * @cxlrd: root decoder
  * @id: memregion id to create, or memregion_free() on failure
- * @mode: mode for the endpoint decoders of this region
+ * @mode: mode of this region
  * @type: select whether this is an expander or accelerator (type-2 or type-3)
  *
  * This is the second step of region initialization. Regions exist within an
@@ -2179,7 +2192,7 @@  static struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int i
  */
 static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd,
 					      int id,
-					      enum cxl_decoder_mode mode,
+					      enum cxl_region_mode mode,
 					      enum cxl_decoder_type type)
 {
 	struct cxl_port *port = to_cxl_port(cxlrd->cxlsd.cxld.dev.parent);
@@ -2188,11 +2201,12 @@  static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd,
 	int rc;
 
 	switch (mode) {
-	case CXL_DECODER_RAM:
-	case CXL_DECODER_PMEM:
+	case CXL_REGION_RAM:
+	case CXL_REGION_PMEM:
 		break;
 	default:
-		dev_err(&cxlrd->cxlsd.cxld.dev, "unsupported mode %d\n", mode);
+		dev_err(&cxlrd->cxlsd.cxld.dev, "unsupported mode %s\n",
+			cxl_region_mode_name(mode));
 		return ERR_PTR(-EINVAL);
 	}
 
@@ -2242,7 +2256,7 @@  static ssize_t create_ram_region_show(struct device *dev,
 }
 
 static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd,
-					  enum cxl_decoder_mode mode, int id)
+					  enum cxl_region_mode mode, int id)
 {
 	int rc;
 
@@ -2270,7 +2284,7 @@  static ssize_t create_pmem_region_store(struct device *dev,
 	if (rc != 1)
 		return -EINVAL;
 
-	cxlr = __create_region(cxlrd, CXL_DECODER_PMEM, id);
+	cxlr = __create_region(cxlrd, CXL_REGION_PMEM, id);
 	if (IS_ERR(cxlr))
 		return PTR_ERR(cxlr);
 
@@ -2290,7 +2304,7 @@  static ssize_t create_ram_region_store(struct device *dev,
 	if (rc != 1)
 		return -EINVAL;
 
-	cxlr = __create_region(cxlrd, CXL_DECODER_RAM, id);
+	cxlr = __create_region(cxlrd, CXL_REGION_RAM, id);
 	if (IS_ERR(cxlr))
 		return PTR_ERR(cxlr);
 
@@ -2800,6 +2814,24 @@  static int match_region_by_range(struct device *dev, void *data)
 	return rc;
 }
 
+static enum cxl_region_mode
+cxl_decoder_to_region_mode(enum cxl_decoder_mode mode)
+{
+	switch (mode) {
+	case CXL_DECODER_NONE:
+		return CXL_REGION_NONE;
+	case CXL_DECODER_RAM:
+		return CXL_REGION_RAM;
+	case CXL_DECODER_PMEM:
+		return CXL_REGION_PMEM;
+	case CXL_DECODER_MIXED:
+	default:
+		return CXL_REGION_MIXED;
+	}
+
+	return CXL_REGION_MIXED;
+}
+
 /* Establish an empty region covering the given HPA range */
 static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
 					   struct cxl_endpoint_decoder *cxled)
@@ -2808,12 +2840,17 @@  static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
 	struct cxl_port *port = cxlrd_to_port(cxlrd);
 	struct range *hpa = &cxled->cxld.hpa_range;
 	struct cxl_region_params *p;
+	enum cxl_region_mode mode;
 	struct cxl_region *cxlr;
 	struct resource *res;
 	int rc;
 
+	if (cxled->mode == CXL_DECODER_DEAD)
+		return ERR_PTR(-EINVAL);
+
+	mode = cxl_decoder_to_region_mode(cxled->mode);
 	do {
-		cxlr = __create_region(cxlrd, cxled->mode,
+		cxlr = __create_region(cxlrd, mode,
 				       atomic_read(&cxlrd->region_id));
 	} while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY);
 
@@ -2996,9 +3033,9 @@  static int cxl_region_probe(struct device *dev)
 		return rc;
 
 	switch (cxlr->mode) {
-	case CXL_DECODER_PMEM:
+	case CXL_REGION_PMEM:
 		return devm_cxl_add_pmem_region(cxlr);
-	case CXL_DECODER_RAM:
+	case CXL_REGION_RAM:
 		/*
 		 * The region can not be manged by CXL if any portion of
 		 * it is already online as 'System RAM'
@@ -3010,8 +3047,8 @@  static int cxl_region_probe(struct device *dev)
 			return 0;
 		return devm_cxl_add_dax_region(cxlr);
 	default:
-		dev_dbg(&cxlr->dev, "unsupported region mode: %d\n",
-			cxlr->mode);
+		dev_dbg(&cxlr->dev, "unsupported region mode: %s\n",
+			cxl_region_mode_name(cxlr->mode));
 		return -ENXIO;
 	}
 }
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 003feebab79b..9a0cce1e6fca 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -383,6 +383,27 @@  static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode)
 	return "mixed";
 }
 
+enum cxl_region_mode {
+	CXL_REGION_NONE,
+	CXL_REGION_RAM,
+	CXL_REGION_PMEM,
+	CXL_REGION_MIXED,
+};
+
+static inline const char *cxl_region_mode_name(enum cxl_region_mode mode)
+{
+	static const char * const names[] = {
+		[CXL_REGION_NONE] = "none",
+		[CXL_REGION_RAM] = "ram",
+		[CXL_REGION_PMEM] = "pmem",
+		[CXL_REGION_MIXED] = "mixed",
+	};
+
+	if (mode >= CXL_REGION_NONE && mode <= CXL_REGION_MIXED)
+		return names[mode];
+	return "mixed";
+}
+
 /*
  * Track whether this decoder is reserved for region autodiscovery, or
  * free for userspace provisioning.
@@ -511,7 +532,8 @@  struct cxl_region_params {
  * struct cxl_region - CXL region
  * @dev: This region's device
  * @id: This region's id. Id is globally unique across all regions
- * @mode: Endpoint decoder allocation / access mode
+ * @mode: Region mode which defines which endpoint decoder mode the region is
+ *        compatible with
  * @type: Endpoint decoder target type
  * @cxl_nvb: nvdimm bridge for coordinating @cxlr_pmem setup / shutdown
  * @cxlr_pmem: (for pmem regions) cached copy of the nvdimm bridge
@@ -521,7 +543,7 @@  struct cxl_region_params {
 struct cxl_region {
 	struct device dev;
 	int id;
-	enum cxl_decoder_mode mode;
+	enum cxl_region_mode mode;
 	enum cxl_decoder_type type;
 	struct cxl_nvdimm_bridge *cxl_nvb;
 	struct cxl_pmem_region *cxlr_pmem;