diff mbox series

[ndctl,RFC] cxl/region: Add support to create dc region for dynamic capacity device

Message ID 20230511180757.2091508-1-fan.ni@samsung.com
State New, archived
Headers show
Series [ndctl,RFC] cxl/region: Add support to create dc region for dynamic capacity device | expand

Commit Message

Fan Ni May 11, 2023, 6:08 p.m. UTC
From: Fan Ni <nifan@outlook.com>

Add a new region type "dc" and function to support creating dc region
for dynamic capacity device.

Signed-off-by: Fan Ni <fan.ni@samsung.com>
---
 cxl/lib/libcxl.c   | 13 +++++++++++++
 cxl/lib/libcxl.sym |  5 +++++
 cxl/libcxl.h       |  5 +++++
 cxl/region.c       | 17 ++++++++++++++++-
 4 files changed, 39 insertions(+), 1 deletion(-)

Comments

Ben Cheatham May 11, 2023, 7:47 p.m. UTC | #1
Hi Fan,

I actually have a patch series that does this and adds cxl list support for DC regions that I've been 
sitting on. I was going to wait to send it out until the kernel DCD support was a bit more mature, 
but since you sent this out I thought I may as well put the list support out there so we don't
duplicate work. I've copy/pasted the patch below, feel free to add it to your patch/make modifications.

Subject: [PATCH] Add cxl list support for dynamic capacity regions

Add support for printing dynamic capacity regions to cxl list. Region
support is broken into two pieces: support for the cxl region, and
support for device level dc regions. The device level dc regions are
printed under the memdev in the list output since they are a memdev
attribute in sysfs. There are a maximum of eight of these regions,
only the regions that are present in sysfs are printed.

Example output:
(assume device has regions 0 and 1 populated, and region0 was created
 using create-region -t dc)
[
    {
	"memdevs":[
	  {
	    "memdev":"mem0"
	    "pmem_size":536870912,
	    "ram_size":1,
	    "serial":0,
	    "host":"0000:0d:00.0",
	    "dc_regions":[
	      {
		"region_nr":0,
		"size":268435456,
		"base":268435456,
		"blk_size":2097152
	      },
	      {
		"region_nr":1,
		"size":268435456,
		"base":536870912,
		"blk_size":2097152
	      }
	    ]
	  }
	]
    },
    {
	"regions":[
	  {
	    "region":"region0",
	    "resource":19595788288,
	    "size":536870912,
	    "type":"dc",
	    "interleave_ways":1,
	    "interleave_granularity":256,
	    "decode_state":"commit"
	  }
	]
    }
]

Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
---
 cxl/json.c         | 60 ++++++++++++++++++++++++++++++++++++++++++++++
 cxl/lib/libcxl.c   | 60 ++++++++++++++++++++++++++++++++++++++++++++++
 cxl/lib/libcxl.sym |  4 ++++
 cxl/lib/private.h  |  8 +++++++
 cxl/libcxl.h       |  7 ++++++
 5 files changed, 139 insertions(+)

diff --git a/cxl/json.c b/cxl/json.c
index 9a4b5c7..df155f8 100644
--- a/cxl/json.c
+++ b/cxl/json.c
@@ -480,6 +480,62 @@ err_jobj:
 	return NULL;
 }
 
+static void util_cxl_memdev_dc_regions_to_json(struct json_object *jdev, struct cxl_memdev *memdev,
+			       unsigned long flags)
+{
+	struct json_object *jdc_array, *jdc_region, *jobj;
+	u64 val;
+
+	jdc_array = json_object_new_array();
+	if (!jdc_array)
+		return;
+
+	for (int i = 0; i < CXL_MAX_DC_REGION; i++) {
+		jdc_region = json_object_new_object();
+		if (!jdc_region)
+			continue;
+
+		/*
+		 * Skip the region if the size is invalid, the rest is probably
+		 * also invalid. If size is 0, the region is most likely unset.
+		 */
+		val = cxl_memdev_get_dc_region_size(memdev, i);
+		if (val == ULLONG_MAX || val == 0) {
+			json_object_put(jdc_region);
+			continue;
+		}
+
+		jobj = json_object_new_int(i);
+		if (jobj)
+			json_object_object_add(jdc_region, "region_nr", jobj);
+
+		jobj = util_json_object_size(val, flags);
+		if (jobj) {
+			json_object_object_add(jdc_region, "size", jobj);
+		}
+
+		val = cxl_memdev_get_dc_region_base(memdev, i);
+		if (val < ULLONG_MAX) {
+			jobj = util_json_object_hex(val, flags);
+			if (jobj)
+				json_object_object_add(jdc_region, "base", jobj);
+		}
+
+		val = cxl_memdev_get_dc_region_blk_size(memdev, i);
+		if (val < ULLONG_MAX) {
+			jobj = util_json_object_size(val, flags);
+			if (jobj) {
+				json_object_object_add(jdc_region, "blk_size", jobj);
+			}
+		}
+
+
+		json_object_array_add(jdc_array, jdc_region);
+	}
+
+	json_object_object_add(jdev, "dc_regions", jdc_array);
+}
+
 struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
 		unsigned long flags)
 {
@@ -552,6 +608,10 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
 			json_object_object_add(jdev, "partition_info", jobj);
 	}
 
+	size = cxl_memdev_get_dc_size(memdev);
+	if (size)
+		util_cxl_memdev_dc_regions_to_json(jdev, memdev, flags);
+
 	json_object_set_userdata(jdev, memdev, NULL);
 	return jdev;
 }
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index ecdc242..0dcc79e 100644
--- a/cxl/lib/libcxl.c
+++ b/cxl/lib/libcxl.c
@@ -1211,6 +1211,37 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
 		goto err_read;
 	memdev->ram_size = strtoull(buf, NULL, 0);
 
+	sprintf(path, "%s/dc/size", cxlmem_base);
+	if (sysfs_read_attr(ctx, path, buf) < 0)
+		memdev->dc_size = 0;
+	else
+		memdev->dc_size = strtoull(buf, NULL, 0);
+
+	for (unsigned int i = 0; i < CXL_MAX_DC_REGION; i++) {
+		/*
+		 * if the first read fails, assume the region doesn't exist
+		 * and set the region to invalid values.
+		 */
+		sprintf(path, "%s/dc/base_region%d", cxlmem_base, i);
+		if (sysfs_read_attr(ctx, path, buf) < 0) {
+			memdev->dc_regions[i].base = ULLONG_MAX;
+			memdev->dc_regions[i].size = 0;
+			memdev->dc_regions[i].blk_size = 0;
+			continue;
+		}
+		memdev->dc_regions[i].base = strtoull(buf, NULL, 0);
+
+		sprintf(path, "%s/dc/size_region%d", cxlmem_base, i);
+		if (sysfs_read_attr(ctx, path, buf) < 0)
+			goto err_read;
+		memdev->dc_regions[i].size = strtoull(buf, NULL, 0);
+
+		sprintf(path,"%s/dc/blk_sz_region%d", cxlmem_base, i);
+		if (sysfs_read_attr(ctx, path, buf) < 0)
+			goto err_read;
+		memdev->dc_regions[i].blk_size = strtoull(buf, NULL, 0);
+	}
+
 	sprintf(path, "%s/payload_max", cxlmem_base);
 	if (sysfs_read_attr(ctx, path, buf) < 0)
 		goto err_read;
@@ -1369,6 +1400,35 @@ CXL_EXPORT unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev)
 	return memdev->ram_size;
 }
 
+CXL_EXPORT unsigned long long cxl_memdev_get_dc_size(struct cxl_memdev *memdev)
+{
+	return memdev->dc_size;
+}
+
+CXL_EXPORT unsigned long long cxl_memdev_get_dc_region_base(struct cxl_memdev *memdev,
+							    unsigned int region_nr)
+{
+	if (region_nr < CXL_MAX_DC_REGION)
+		return memdev->dc_regions[region_nr].base;
+	return ULLONG_MAX;
+}
+
+CXL_EXPORT unsigned long long cxl_memdev_get_dc_region_size(struct cxl_memdev *memdev,
+							    unsigned int region_nr)
+{
+	if (region_nr < CXL_MAX_DC_REGION)
+		return memdev->dc_regions[region_nr].size;
+	return ULLONG_MAX;
+}
+
+CXL_EXPORT unsigned long long cxl_memdev_get_dc_region_blk_size(struct cxl_memdev *memdev,
+							    unsigned int region_nr)
+{
+	if (region_nr < CXL_MAX_DC_REGION)
+		return memdev->dc_regions[region_nr].blk_size;
+	return ULLONG_MAX;
+}
+
 CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev)
 {
 	return memdev->firmware_version;
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
index 1434da6..e711273 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -254,4 +254,8 @@ global:
 LIBCXL_6 {
 global:
 	cxl_decoder_create_dc_region;
+	cxl_memdev_get_dc_size;
+	cxl_memdev_get_dc_region_base;
+	cxl_memdev_get_dc_region_size;
+	cxl_memdev_get_dc_region_blk_size;
 } LIBCXL_5;
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
index d49b560..968afaf 100644
--- a/cxl/lib/private.h
+++ b/cxl/lib/private.h
@@ -19,6 +19,12 @@ struct cxl_pmem {
 	char *dev_path;
 };
 
+struct cxl_dc_region {
+	u64 base;
+	u64 size;
+	u64 blk_size;
+};
+
 struct cxl_endpoint;
 struct cxl_memdev {
 	int id, major, minor;
@@ -32,12 +38,14 @@ struct cxl_memdev {
 	struct list_node list;
 	unsigned long long pmem_size;
 	unsigned long long ram_size;
+	unsigned long long dc_size;
 	int payload_max;
 	size_t lsa_size;
 	struct kmod_module *module;
 	struct cxl_pmem *pmem;
 	unsigned long long serial;
 	struct cxl_endpoint *endpoint;
+	struct cxl_dc_region dc_regions[CXL_MAX_DC_REGION];
 };
 
 struct cxl_dport {
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
index 1a54eee..c548abe 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -18,6 +18,8 @@ typedef unsigned char uuid_t[16];
 extern "C" {
 #endif
 
+#define CXL_MAX_DC_REGION 8
+
 struct cxl_ctx;
 struct cxl_ctx *cxl_ref(struct cxl_ctx *ctx);
 void cxl_unref(struct cxl_ctx *ctx);
@@ -47,6 +49,11 @@ int cxl_memdev_get_minor(struct cxl_memdev *memdev);
 struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev);
 unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev);
 unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev);
+unsigned long long cxl_memdev_get_dc_size(struct cxl_memdev *memdev);
+unsigned long long cxl_memdev_get_dc_region_size(struct cxl_memdev *memdev, unsigned int region_nr);
+unsigned long long cxl_memdev_get_dc_region_base(struct cxl_memdev *memdev, unsigned int region_nr);
+unsigned long long cxl_memdev_get_dc_region_blk_size(struct cxl_memdev *memdev,
+						     unsigned int region_nr);
 const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
 
 /* ABI spelling mistakes are forever */
Fan Ni May 11, 2023, 8:20 p.m. UTC | #2
On Thu, May 11, 2023 at 02:47:00PM -0500, Ben Cheatham wrote:

> Hi Fan,
> 
> I actually have a patch series that does this and adds cxl list support for DC regions that I've been 
> sitting on. I was going to wait to send it out until the kernel DCD support was a bit more mature, 
> but since you sent this out I thought I may as well put the list support out there so we don't
> duplicate work. I've copy/pasted the patch below, feel free to add it to your patch/make modifications.

Hi Ben,
Thanks for letting me know.

I sent out the RFC only for those who want to have a early try of
DCD since early eanbling of DCD at both qemu and kernel side is out.

We can wait until the kernel side code is out for review and decide who
will carry this on.
Personally, I am OK to have you or anyone else to carry it on as
long as we advance the feature and do not do duplicate work.

Thanks,
Fan

> 
> Subject: [PATCH] Add cxl list support for dynamic capacity regions
> 
> Add support for printing dynamic capacity regions to cxl list. Region
> support is broken into two pieces: support for the cxl region, and
> support for device level dc regions. The device level dc regions are
> printed under the memdev in the list output since they are a memdev
> attribute in sysfs. There are a maximum of eight of these regions,
> only the regions that are present in sysfs are printed.
> 
> Example output:
> (assume device has regions 0 and 1 populated, and region0 was created
>  using create-region -t dc)
> [
>     {
> 	"memdevs":[
> 	  {
> 	    "memdev":"mem0"
> 	    "pmem_size":536870912,
> 	    "ram_size":1,
> 	    "serial":0,
> 	    "host":"0000:0d:00.0",
> 	    "dc_regions":[
> 	      {
> 		"region_nr":0,
> 		"size":268435456,
> 		"base":268435456,
> 		"blk_size":2097152
> 	      },
> 	      {
> 		"region_nr":1,
> 		"size":268435456,
> 		"base":536870912,
> 		"blk_size":2097152
> 	      }
> 	    ]
> 	  }
> 	]
>     },
>     {
> 	"regions":[
> 	  {
> 	    "region":"region0",
> 	    "resource":19595788288,
> 	    "size":536870912,
> 	    "type":"dc",
> 	    "interleave_ways":1,
> 	    "interleave_granularity":256,
> 	    "decode_state":"commit"
> 	  }
> 	]
>     }
> ]
> 
> Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
> ---
>  cxl/json.c         | 60 ++++++++++++++++++++++++++++++++++++++++++++++
>  cxl/lib/libcxl.c   | 60 ++++++++++++++++++++++++++++++++++++++++++++++
>  cxl/lib/libcxl.sym |  4 ++++
>  cxl/lib/private.h  |  8 +++++++
>  cxl/libcxl.h       |  7 ++++++
>  5 files changed, 139 insertions(+)
> 
> diff --git a/cxl/json.c b/cxl/json.c
> index 9a4b5c7..df155f8 100644
> --- a/cxl/json.c
> +++ b/cxl/json.c
> @@ -480,6 +480,62 @@ err_jobj:
>  	return NULL;
>  }
>  
> +static void util_cxl_memdev_dc_regions_to_json(struct json_object *jdev, struct cxl_memdev *memdev,
> +			       unsigned long flags)
> +{
> +	struct json_object *jdc_array, *jdc_region, *jobj;
> +	u64 val;
> +
> +	jdc_array = json_object_new_array();
> +	if (!jdc_array)
> +		return;
> +
> +	for (int i = 0; i < CXL_MAX_DC_REGION; i++) {
> +		jdc_region = json_object_new_object();
> +		if (!jdc_region)
> +			continue;
> +
> +		/*
> +		 * Skip the region if the size is invalid, the rest is probably
> +		 * also invalid. If size is 0, the region is most likely unset.
> +		 */
> +		val = cxl_memdev_get_dc_region_size(memdev, i);
> +		if (val == ULLONG_MAX || val == 0) {
> +			json_object_put(jdc_region);
> +			continue;
> +		}
> +
> +		jobj = json_object_new_int(i);
> +		if (jobj)
> +			json_object_object_add(jdc_region, "region_nr", jobj);
> +
> +		jobj = util_json_object_size(val, flags);
> +		if (jobj) {
> +			json_object_object_add(jdc_region, "size", jobj);
> +		}
> +
> +		val = cxl_memdev_get_dc_region_base(memdev, i);
> +		if (val < ULLONG_MAX) {
> +			jobj = util_json_object_hex(val, flags);
> +			if (jobj)
> +				json_object_object_add(jdc_region, "base", jobj);
> +		}
> +
> +		val = cxl_memdev_get_dc_region_blk_size(memdev, i);
> +		if (val < ULLONG_MAX) {
> +			jobj = util_json_object_size(val, flags);
> +			if (jobj) {
> +				json_object_object_add(jdc_region, "blk_size", jobj);
> +			}
> +		}
> +
> +
> +		json_object_array_add(jdc_array, jdc_region);
> +	}
> +
> +	json_object_object_add(jdev, "dc_regions", jdc_array);
> +}
> +
>  struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
>  		unsigned long flags)
>  {
> @@ -552,6 +608,10 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
>  			json_object_object_add(jdev, "partition_info", jobj);
>  	}
>  
> +	size = cxl_memdev_get_dc_size(memdev);
> +	if (size)
> +		util_cxl_memdev_dc_regions_to_json(jdev, memdev, flags);
> +
>  	json_object_set_userdata(jdev, memdev, NULL);
>  	return jdev;
>  }
> diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
> index ecdc242..0dcc79e 100644
> --- a/cxl/lib/libcxl.c
> +++ b/cxl/lib/libcxl.c
> @@ -1211,6 +1211,37 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
>  		goto err_read;
>  	memdev->ram_size = strtoull(buf, NULL, 0);
>  
> +	sprintf(path, "%s/dc/size", cxlmem_base);
> +	if (sysfs_read_attr(ctx, path, buf) < 0)
> +		memdev->dc_size = 0;
> +	else
> +		memdev->dc_size = strtoull(buf, NULL, 0);
> +
> +	for (unsigned int i = 0; i < CXL_MAX_DC_REGION; i++) {
> +		/*
> +		 * if the first read fails, assume the region doesn't exist
> +		 * and set the region to invalid values.
> +		 */
> +		sprintf(path, "%s/dc/base_region%d", cxlmem_base, i);
> +		if (sysfs_read_attr(ctx, path, buf) < 0) {
> +			memdev->dc_regions[i].base = ULLONG_MAX;
> +			memdev->dc_regions[i].size = 0;
> +			memdev->dc_regions[i].blk_size = 0;
> +			continue;
> +		}
> +		memdev->dc_regions[i].base = strtoull(buf, NULL, 0);
> +
> +		sprintf(path, "%s/dc/size_region%d", cxlmem_base, i);
> +		if (sysfs_read_attr(ctx, path, buf) < 0)
> +			goto err_read;
> +		memdev->dc_regions[i].size = strtoull(buf, NULL, 0);
> +
> +		sprintf(path,"%s/dc/blk_sz_region%d", cxlmem_base, i);
> +		if (sysfs_read_attr(ctx, path, buf) < 0)
> +			goto err_read;
> +		memdev->dc_regions[i].blk_size = strtoull(buf, NULL, 0);
> +	}
> +
>  	sprintf(path, "%s/payload_max", cxlmem_base);
>  	if (sysfs_read_attr(ctx, path, buf) < 0)
>  		goto err_read;
> @@ -1369,6 +1400,35 @@ CXL_EXPORT unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev)
>  	return memdev->ram_size;
>  }
>  
> +CXL_EXPORT unsigned long long cxl_memdev_get_dc_size(struct cxl_memdev *memdev)
> +{
> +	return memdev->dc_size;
> +}
> +
> +CXL_EXPORT unsigned long long cxl_memdev_get_dc_region_base(struct cxl_memdev *memdev,
> +							    unsigned int region_nr)
> +{
> +	if (region_nr < CXL_MAX_DC_REGION)
> +		return memdev->dc_regions[region_nr].base;
> +	return ULLONG_MAX;
> +}
> +
> +CXL_EXPORT unsigned long long cxl_memdev_get_dc_region_size(struct cxl_memdev *memdev,
> +							    unsigned int region_nr)
> +{
> +	if (region_nr < CXL_MAX_DC_REGION)
> +		return memdev->dc_regions[region_nr].size;
> +	return ULLONG_MAX;
> +}
> +
> +CXL_EXPORT unsigned long long cxl_memdev_get_dc_region_blk_size(struct cxl_memdev *memdev,
> +							    unsigned int region_nr)
> +{
> +	if (region_nr < CXL_MAX_DC_REGION)
> +		return memdev->dc_regions[region_nr].blk_size;
> +	return ULLONG_MAX;
> +}
> +
>  CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev)
>  {
>  	return memdev->firmware_version;
> diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> index 1434da6..e711273 100644
> --- a/cxl/lib/libcxl.sym
> +++ b/cxl/lib/libcxl.sym
> @@ -254,4 +254,8 @@ global:
>  LIBCXL_6 {
>  global:
>  	cxl_decoder_create_dc_region;
> +	cxl_memdev_get_dc_size;
> +	cxl_memdev_get_dc_region_base;
> +	cxl_memdev_get_dc_region_size;
> +	cxl_memdev_get_dc_region_blk_size;
>  } LIBCXL_5;
> diff --git a/cxl/lib/private.h b/cxl/lib/private.h
> index d49b560..968afaf 100644
> --- a/cxl/lib/private.h
> +++ b/cxl/lib/private.h
> @@ -19,6 +19,12 @@ struct cxl_pmem {
>  	char *dev_path;
>  };
>  
> +struct cxl_dc_region {
> +	u64 base;
> +	u64 size;
> +	u64 blk_size;
> +};
> +
>  struct cxl_endpoint;
>  struct cxl_memdev {
>  	int id, major, minor;
> @@ -32,12 +38,14 @@ struct cxl_memdev {
>  	struct list_node list;
>  	unsigned long long pmem_size;
>  	unsigned long long ram_size;
> +	unsigned long long dc_size;
>  	int payload_max;
>  	size_t lsa_size;
>  	struct kmod_module *module;
>  	struct cxl_pmem *pmem;
>  	unsigned long long serial;
>  	struct cxl_endpoint *endpoint;
> +	struct cxl_dc_region dc_regions[CXL_MAX_DC_REGION];
>  };
>  
>  struct cxl_dport {
> diff --git a/cxl/libcxl.h b/cxl/libcxl.h
> index 1a54eee..c548abe 100644
> --- a/cxl/libcxl.h
> +++ b/cxl/libcxl.h
> @@ -18,6 +18,8 @@ typedef unsigned char uuid_t[16];
>  extern "C" {
>  #endif
>  
> +#define CXL_MAX_DC_REGION 8
> +
>  struct cxl_ctx;
>  struct cxl_ctx *cxl_ref(struct cxl_ctx *ctx);
>  void cxl_unref(struct cxl_ctx *ctx);
> @@ -47,6 +49,11 @@ int cxl_memdev_get_minor(struct cxl_memdev *memdev);
>  struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev);
>  unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev);
>  unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev);
> +unsigned long long cxl_memdev_get_dc_size(struct cxl_memdev *memdev);
> +unsigned long long cxl_memdev_get_dc_region_size(struct cxl_memdev *memdev, unsigned int region_nr);
> +unsigned long long cxl_memdev_get_dc_region_base(struct cxl_memdev *memdev, unsigned int region_nr);
> +unsigned long long cxl_memdev_get_dc_region_blk_size(struct cxl_memdev *memdev,
> +						     unsigned int region_nr);
>  const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
>  
>  /* ABI spelling mistakes are forever */
> -- 
> 2.34.1
diff mbox series

Patch

diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index 769cd8a..ecdc242 100644
--- a/cxl/lib/libcxl.c
+++ b/cxl/lib/libcxl.c
@@ -1886,6 +1886,8 @@  static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
 			decoder->mode = CXL_DECODER_MODE_RAM;
 		else if (strcmp(buf, "pmem") == 0)
 			decoder->mode = CXL_DECODER_MODE_PMEM;
+		else if (strcmp(buf, "dc") == 0)
+			decoder->mode = CXL_DECODER_MODE_DC;
 		else if (strcmp(buf, "mixed") == 0)
 			decoder->mode = CXL_DECODER_MODE_MIXED;
 		else if (strcmp(buf, "none") == 0)
@@ -2189,6 +2191,9 @@  CXL_EXPORT int cxl_decoder_set_mode(struct cxl_decoder *decoder,
 	case CXL_DECODER_MODE_RAM:
 		sprintf(buf, "ram");
 		break;
+	case CXL_DECODER_MODE_DC:
+		sprintf(buf, "dc");
+		break;
 	default:
 		err(ctx, "%s: unsupported mode: %d\n",
 		    cxl_decoder_get_devname(decoder), mode);
@@ -2314,6 +2319,8 @@  static struct cxl_region *cxl_decoder_create_region(struct cxl_decoder *decoder,
 		sprintf(path, "%s/create_pmem_region", decoder->dev_path);
 	else if (mode == CXL_DECODER_MODE_RAM)
 		sprintf(path, "%s/create_ram_region", decoder->dev_path);
+	else if (mode == CXL_DECODER_MODE_DC)
+		sprintf(path, "%s/create_dc_region", decoder->dev_path);
 
 	rc = sysfs_read_attr(ctx, path, buf);
 	if (rc < 0) {
@@ -2353,6 +2360,12 @@  static struct cxl_region *cxl_decoder_create_region(struct cxl_decoder *decoder,
 	return region;
 }
 
+CXL_EXPORT struct cxl_region *
+cxl_decoder_create_dc_region(struct cxl_decoder *decoder)
+{
+	return cxl_decoder_create_region(decoder, CXL_DECODER_MODE_DC);
+}
+
 CXL_EXPORT struct cxl_region *
 cxl_decoder_create_pmem_region(struct cxl_decoder *decoder)
 {
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
index c6545c7..1434da6 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -250,3 +250,8 @@  global:
 	cxl_region_get_daxctl_region;
 	cxl_port_get_parent_dport;
 } LIBCXL_4;
+
+LIBCXL_6 {
+global:
+	cxl_decoder_create_dc_region;
+} LIBCXL_5;
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
index 0218d73..1a54eee 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -151,6 +151,7 @@  cxl_decoder_get_max_available_extent(struct cxl_decoder *decoder);
 enum cxl_decoder_mode {
 	CXL_DECODER_MODE_NONE,
 	CXL_DECODER_MODE_MIXED,
+	CXL_DECODER_MODE_DC,
 	CXL_DECODER_MODE_PMEM,
 	CXL_DECODER_MODE_RAM,
 };
@@ -160,6 +161,7 @@  static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode)
 	static const char *names[] = {
 		[CXL_DECODER_MODE_NONE] = "none",
 		[CXL_DECODER_MODE_MIXED] = "mixed",
+		[CXL_DECODER_MODE_DC] = "dc",
 		[CXL_DECODER_MODE_PMEM] = "pmem",
 		[CXL_DECODER_MODE_RAM] = "ram",
 	};
@@ -178,6 +180,8 @@  cxl_decoder_mode_from_ident(const char *ident)
 		return CXL_DECODER_MODE_RAM;
 	else if (strcmp(ident, "pmem") == 0)
 		return CXL_DECODER_MODE_PMEM;
+	else if (strcmp(ident, "dc") == 0)
+		return CXL_DECODER_MODE_DC;
 	return CXL_DECODER_MODE_NONE;
 }
 
@@ -213,6 +217,7 @@  unsigned int
 cxl_decoder_get_interleave_granularity(struct cxl_decoder *decoder);
 unsigned int cxl_decoder_get_interleave_ways(struct cxl_decoder *decoder);
 struct cxl_region *cxl_decoder_get_region(struct cxl_decoder *decoder);
+struct cxl_region *cxl_decoder_create_dc_region(struct cxl_decoder *decoder);
 struct cxl_region *cxl_decoder_create_pmem_region(struct cxl_decoder *decoder);
 struct cxl_region *cxl_decoder_create_ram_region(struct cxl_decoder *decoder);
 struct cxl_decoder *cxl_decoder_get_by_name(struct cxl_ctx *ctx,
diff --git a/cxl/region.c b/cxl/region.c
index 07ce4a3..497562c 100644
--- a/cxl/region.c
+++ b/cxl/region.c
@@ -75,7 +75,7 @@  OPT_INTEGER('w', "ways", &param.ways, \
 OPT_INTEGER('g', "granularity", &param.granularity,  \
 	    "granularity of the interleave set"), \
 OPT_STRING('t', "type", &param.type, \
-	   "region type", "region type - 'pmem' or 'ram'"), \
+	   "region type", "region type - 'pmem' or 'ram' or 'dc'"), \
 OPT_STRING('U', "uuid", &param.uuid, \
 	   "region uuid", "uuid for the new region (default: autogenerate)"), \
 OPT_BOOLEAN('m', "memdevs", &param.memdevs, \
@@ -434,6 +434,14 @@  static int validate_decoder(struct cxl_decoder *decoder,
 			return -EINVAL;
 		}
 		break;
+	case CXL_DECODER_MODE_DC:
+		/* TODO: Do we need to have something like dc_capable?? */
+		if (!cxl_decoder_is_volatile_capable(decoder) &&
+				!cxl_decoder_is_pmem_capable(decoder)) {
+			log_err(&rl, "%s is not dc capable\n", devname);
+			return -EINVAL;
+		}
+		break;
 	default:
 		log_err(&rl, "unknown type: %s\n", param.type);
 		return -EINVAL;
@@ -640,6 +648,13 @@  static int create_region(struct cxl_ctx *ctx, int *count,
 				param.root_decoder);
 			return -ENXIO;
 		}
+	} else if (p->mode == CXL_DECODER_MODE_DC) {
+		region = cxl_decoder_create_dc_region(p->root_decoder);
+		if (!region) {
+			log_err(&rl, "failed to create region under %s\n",
+				param.root_decoder);
+			return -ENXIO;
+		}
 	} else {
 		log_err(&rl, "region type '%s' is not supported\n",
 			param.type);