mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli
@ 2026-05-23  9:50 Anisa Su
  2026-05-23  9:50 ` [PATCH v6 1/7] " Anisa Su
                   ` (7 more replies)
  0 siblings, 8 replies; 31+ messages in thread
From: Anisa Su @ 2026-05-23  9:50 UTC (permalink / raw)
  To: linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Dave Jiang, Ira Weiny, Alison Schofield, John Groves,
	Gregory Price, Anisa Su

CXL Dynamic Capacity Device (DCD) support has continued to evolve in the
upstream kernel since Ira's v5 posting [1].  The kernel side has settled
on a uuid-driven claim model for sparse DAX devices: dax_resources carry
the tag delivered with each extent, and userspace selects which ones to
claim by writing a UUID to the dax device's sysfs 'uuid' attribute (or
"0" to claim a single untagged resource).  Size on a sparse region is
determined by the claim, not requested up-front.

This series brings cxl-cli and daxctl in line with that model and
extends cxl_test to exercise the new paths end-to-end.

The corresponding kernel patchset is here:
https://lore.kernel.org/linux-cxl/cover.1779528761.git.anisa.su@samsung.com/T/#t

Picked up unchanged from v5 (Ira):

  libcxl: Add Dynamic RAM A partition mode support
  cxl/region: Add cxl-cli support for dynamic RAM A
  libcxl: Add extent functionality to DC regions
  cxl/region: Add extent output to region query

New in v6:

  daxctl: Add --uuid option to create-device for DC DAX regions
    - Plumbs writes to the new dax 'uuid' sysfs attribute through a new
      daxctl_dev_set_uuid() helper (LIBDAXCTL_11).
    - --uuid is mutually exclusive with --size; pass "0" to claim a
      single untagged dax_resource.  An unmatched UUID surfaces ENOENT
      from the kernel and leaves the device at size 0.
    - Documents the option in the man page.

  cxl/test: Add Dynamic Capacity tests (rewritten on top of Ira's
  original patch to track the post-redesign kernel)
    - Routes untagged claims via --uuid "0" so daxctl exercises the
      kernel uuid_store path; tagged claims use real UUID strings.
    - Asserts that for DC regions, size-grow returns -EOPNOTSUPP (real grow is
      --uuid only) and that tag reuse across More-chains is rejected
      by the cross-More uniqueness gate.
    - Adds coverage for the new validators: test_uuid_no_match,
      test_uuid_no_match_seed_intact, test_uuid_show,
      test_cross_more_uniqueness, test_alignment_rejection.
    - Sharable-partition coverage (test_shared_extent_inject,
      test_seq_integrity_gap) is routed at runtime to a dedicated mock
      memdev that tools/testing/cxl stamps with serial 0xDCDC, so a
      single cxl_test module load exercises both regimes.
    - Localizes positional-arg assignments in every helper so functions
      no longer clobber caller globals (the previous behavior leaked
      the sharable memdev into later tests).
    - test_reject_overlapping arithmetic now lands an actual overlap
      inside the DC region (the prior math landed past the end).

Depends on the kernel DCD/sparse-DAX series; without it the new tests
will skip and 'cxl list -r N -Nu' will simply report no extents.

The branch is also available at:

  https://github.com/anisa-su993/anisa-ndctl/tree/dcd-2026-05-21

Based on pmem/pending commit:

  bbd403a test/cxl-sanitize: avoid sanitize submit/wait race

[1] https://lore.kernel.org/nvdimm/20250413-dcd-region2-v5-0-fbd753a2e0e8@intel.com/

---
Changes in v6:
- anisa: New patch — daxctl --uuid option + daxctl_dev_set_uuid() helper
- anisa: Rewrite cxl/test DCD tests against the post-redesign kernel
         (uuid sysfs claim, tag-group atomic release, cross-More
         uniqueness, alignment rejection, DC size-grow refusal)
- anisa: Rebase onto bbd403a (pmem/pending)
- Link to v5: https://lore.kernel.org/nvdimm/20250413-dcd-region2-v5-0-fbd753a2e0e8@intel.com/

Changes in v5:
- iweiny: Adjust all code to view only the dynamic RAM A partition
- Alison: s/tag/uuid/ in region query extent output
- Link to v4: https://patch.msgid.link/20241214-dcd-region2-v4-0-36550a97f8e2@intel.com

Anisa Su (1):
  daxctl: Add --uuid option to create-device for DC regions

Ira Weiny (6):
  ndctl: Dynamic Capacity additions for cxl-cli
  libcxl: Add Dynamic RAM A partition mode support
  cxl/region: Add cxl-cli support for dynamic RAM A
  libcxl: Add extent functionality to DC regions
  cxl/region: Add extent output to region query
  cxl/test: Add Dynamic Capacity tests

 Documentation/cxl/cxl-list.txt                |   29 +
 Documentation/cxl/lib/libcxl.txt              |   33 +-
 Documentation/daxctl/daxctl-create-device.txt |   12 +
 cxl/filter.h                                  |    3 +
 cxl/json.c                                    |   67 +
 cxl/json.h                                    |    3 +
 cxl/lib/libcxl.c                              |  181 +++
 cxl/lib/libcxl.sym                            |    9 +
 cxl/lib/private.h                             |   14 +
 cxl/libcxl.h                                  |   21 +-
 cxl/list.c                                    |    3 +
 cxl/memdev.c                                  |    4 +-
 cxl/region.c                                  |   27 +-
 daxctl/device.c                               |   72 +-
 daxctl/lib/libdaxctl.c                        |   44 +
 daxctl/lib/libdaxctl.sym                      |    5 +
 daxctl/libdaxctl.h                            |    1 +
 test/cxl-dcd.sh                               | 1267 +++++++++++++++++
 test/meson.build                              |    2 +
 util/json.h                                   |    1 +
 20 files changed, 1771 insertions(+), 27 deletions(-)
 create mode 100644 test/cxl-dcd.sh


base-commit: bbd403a03fa2a1551c1a10bbf78f32027c718758
-- 
2.43.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 1/7] ndctl: Dynamic Capacity additions for cxl-cli
  2026-05-23  9:50 [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Anisa Su
@ 2026-05-23  9:50 ` Anisa Su
  2026-06-08 23:18   ` Dave Jiang
  2026-05-23  9:50 ` [PATCH v6 2/7] libcxl: Add Dynamic RAM A partition mode support Anisa Su
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 31+ messages in thread
From: Anisa Su @ 2026-05-23  9:50 UTC (permalink / raw)
  To: linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Dave Jiang, Ira Weiny, Alison Schofield, John Groves,
	Gregory Price, Ira Weiny, Vishal Verma, Jonathan Cameron, Fan Ni,
	Sushant1 Kumar, Dan Williams

From: Ira Weiny <ira.weiny@intel.com>

This series can be found here:

	https://github.com/weiny2/ndctl/tree/dcd-region3-2025-04-13

CXL Dynamic Capacity Device (DCD) support is being discussed in the
upstream kernel.  cxl-cli requires modifications to interact with those
devices.

A new partition type 'dynamic_ram_a' has been added which cxl-cli
needs to know about.  Add support for the new decoder type.

With DCD regions may, or may not, have capacity.  The capacity is
communicated via extents.  Add region extent query capabilities.

Add cxl-test support.  cxl-testing allows for quick regression testing
as well as helping to design the cxl-cli interfaces.


To: "Alison Schofield" <alison.schofield@intel.com>
Cc: "Vishal Verma" <vishal.l.verma@intel.com>
Cc: "Jonathan Cameron" <jonathan.cameron@Huawei.com>
Cc: "Fan Ni" <fan.ni@samsung.com>
Cc: "Sushant1 Kumar" <sushant1.kumar@intel.com>
Cc: "Dan Williams" <dan.j.williams@intel.com>
Cc: "Dave Jiang" <dave.jiang@intel.com>
Cc: <linux-cxl@vger.kernel.org>
Cc: <nvdimm@lists.linux.dev>
Signed-off-by: Ira Weiny <ira.weiny@intel.com>

---
Changes in v5:
- iweiny: Adjust all code to view only the dynamic RAM A partition
- Alison: s/tag/uuid/ in region query extent output
- Link to v4: https://patch.msgid.link/20241214-dcd-region2-v4-0-36550a97f8e2@intel.com

--- b4-submit-tracking ---
# This section is used internally by b4 prep for tracking purposes.
{
  "series": {
    "revision": 5,
    "change-id": "20241030-dcd-region2-2d0149eb8efd",
    "prefixes": [],
    "history": {
      "v1": [
        "20241030-dcd-region2-v1-0-04600ba2b48e@intel.com"
      ],
      "v2": [
        "20241104-dcd-region2-v2-0-be057b479eeb@intel.com"
      ],
      "v3": [
        "20241115-dcd-region2-v3-0-585d480ccdab@intel.com"
      ],
      "v4": [
        "20241214-dcd-region2-v4-0-36550a97f8e2@intel.com"
      ]
    }
  }
}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 2/7] libcxl: Add Dynamic RAM A partition mode support
  2026-05-23  9:50 [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Anisa Su
  2026-05-23  9:50 ` [PATCH v6 1/7] " Anisa Su
@ 2026-05-23  9:50 ` Anisa Su
  2026-06-08 23:19   ` Dave Jiang
  2026-05-23  9:50 ` [PATCH v6 3/7] cxl/region: Add cxl-cli support for dynamic RAM A Anisa Su
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 31+ messages in thread
From: Anisa Su @ 2026-05-23  9:50 UTC (permalink / raw)
  To: linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Dave Jiang, Ira Weiny, Alison Schofield, John Groves,
	Gregory Price, Ira Weiny

From: Ira Weiny <ira.weiny@intel.com>

Dynamic capacity partitions are exposed as a singular dynamic ram
partition.

Add CXL library support to read this partition information.

Signed-off-by: Ira Weiny <ira.weiny@intel.com>
---
 Documentation/cxl/lib/libcxl.txt |  6 +++--
 cxl/lib/libcxl.c                 | 43 ++++++++++++++++++++++++++++++++
 cxl/lib/libcxl.sym               |  4 +++
 cxl/lib/private.h                |  3 +++
 cxl/libcxl.h                     | 10 +++++++-
 5 files changed, 63 insertions(+), 3 deletions(-)

diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
index 5c3ebd4..9921ac1 100644
--- a/Documentation/cxl/lib/libcxl.txt
+++ b/Documentation/cxl/lib/libcxl.txt
@@ -74,6 +74,7 @@ int cxl_memdev_get_major(struct cxl_memdev *memdev);
 int cxl_memdev_get_minor(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_dynamic_ram_a_size(struct cxl_memdev *memdev);
 const char *cxl_memdev_get_firmware_version(struct cxl_memdev *memdev);
 size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev);
 int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev);
@@ -93,8 +94,8 @@ The character device node for command submission can be found by default
 at /dev/cxl/mem%d, or created with a major / minor returned from
 cxl_memdev_get_{major,minor}().
 
-The 'pmem_size' and 'ram_size' attributes return the current
-provisioning of DPA (Device Physical Address / local capacity) in the
+The 'pmem_size', 'ram_size', and 'dynamic_ram_a_size' attributes return the
+current provisioning of DPA (Device Physical Address / local capacity) in the
 device.
 
 cxl_memdev_get_numa_node() returns the affinitized CPU node number if
@@ -453,6 +454,7 @@ enum cxl_decoder_mode {
 	CXL_DECODER_MODE_MIXED,
 	CXL_DECODER_MODE_PMEM,
 	CXL_DECODER_MODE_RAM,
+	CXL_DECODER_MODE_DYNAMIC_RAM_A,
 };
 enum cxl_decoder_mode cxl_decoder_get_mode(struct cxl_decoder *decoder);
 int cxl_decoder_set_mode(struct cxl_decoder *decoder, enum cxl_decoder_mode mode);
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index e55a7b4..be0bc03 100644
--- a/cxl/lib/libcxl.c
+++ b/cxl/lib/libcxl.c
@@ -501,6 +501,9 @@ CXL_EXPORT bool cxl_region_qos_class_mismatch(struct cxl_region *region)
 		} else if (region->mode == CXL_DECODER_MODE_PMEM) {
 			if (root_decoder->qos_class != memdev->pmem_qos_class)
 				return true;
+		} else if (region->mode == CXL_DECODER_MODE_DYNAMIC_RAM_A) {
+			if (root_decoder->qos_class != memdev->dynamic_ram_a_qos_class)
+				return true;
 		}
 	}
 
@@ -1426,6 +1429,10 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
 	if (sysfs_read_attr(ctx, path, buf) == 0)
 		memdev->ram_size = strtoull(buf, NULL, 0);
 
+	sprintf(path, "%s/dynamic_ram_a/size", cxlmem_base);
+	if (sysfs_read_attr(ctx, path, buf) == 0)
+		memdev->dynamic_ram_a_size = strtoull(buf, NULL, 0);
+
 	sprintf(path, "%s/pmem/qos_class", cxlmem_base);
 	if (sysfs_read_attr(ctx, path, buf) < 0)
 		memdev->pmem_qos_class = CXL_QOS_CLASS_NONE;
@@ -1438,6 +1445,12 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
 	else
 		memdev->ram_qos_class = atoi(buf);
 
+	sprintf(path, "%s/dynamic_ram_a/qos_class", cxlmem_base);
+	if (sysfs_read_attr(ctx, path, buf) < 0)
+		memdev->dynamic_ram_a_qos_class = CXL_QOS_CLASS_NONE;
+	else
+		memdev->dynamic_ram_a_qos_class = atoi(buf);
+
 	sprintf(path, "%s/payload_max", cxlmem_base);
 	if (sysfs_read_attr(ctx, path, buf) == 0) {
 		memdev->payload_max = strtoull(buf, NULL, 0);
@@ -1685,6 +1698,11 @@ 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_dynamic_ram_a_size(struct cxl_memdev *memdev)
+{
+	return memdev->dynamic_ram_a_size;
+}
+
 CXL_EXPORT int cxl_memdev_get_pmem_qos_class(struct cxl_memdev *memdev)
 {
 	return memdev->pmem_qos_class;
@@ -1695,6 +1713,11 @@ CXL_EXPORT int cxl_memdev_get_ram_qos_class(struct cxl_memdev *memdev)
 	return memdev->ram_qos_class;
 }
 
+CXL_EXPORT int cxl_memdev_get_dynamic_ram_a_qos_class(struct cxl_memdev *memdev)
+{
+	return memdev->dynamic_ram_a_qos_class;
+}
+
 CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev)
 {
 	return memdev->firmware_version;
@@ -2465,6 +2488,8 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
 			decoder->mode = CXL_DECODER_MODE_MIXED;
 		else if (strcmp(buf, "none") == 0)
 			decoder->mode = CXL_DECODER_MODE_NONE;
+		else if (strcmp(buf, "dynamic_ram_a") == 0)
+			decoder->mode = CXL_DECODER_MODE_DYNAMIC_RAM_A;
 		else
 			decoder->mode = CXL_DECODER_MODE_MIXED;
 	} else
@@ -2504,6 +2529,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
 	case CXL_PORT_SWITCH:
 		decoder->pmem_capable = true;
 		decoder->volatile_capable = true;
+		decoder->dynamic_ram_a_capable = true;
 		decoder->mem_capable = true;
 		decoder->accelmem_capable = true;
 		sprintf(path, "%s/locked", cxldecoder_base);
@@ -2528,6 +2554,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
 			{ "cap_type3", &decoder->mem_capable },
 			{ "cap_ram", &decoder->volatile_capable },
 			{ "cap_pmem", &decoder->pmem_capable },
+			{ "cap_dynamic_ram_a", &decoder->dynamic_ram_a_capable },
 			{ "locked", &decoder->locked },
 		};
 
@@ -2778,6 +2805,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_DYNAMIC_RAM_A:
+		sprintf(buf, "dynamic_ram_a");
+		break;
 	default:
 		err(ctx, "%s: unsupported mode: %d\n",
 		    cxl_decoder_get_devname(decoder), mode);
@@ -2829,6 +2859,11 @@ CXL_EXPORT bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder)
 	return decoder->volatile_capable;
 }
 
+CXL_EXPORT bool cxl_decoder_is_dynamic_ram_a_capable(struct cxl_decoder *decoder)
+{
+	return decoder->dynamic_ram_a_capable;
+}
+
 CXL_EXPORT bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder)
 {
 	return decoder->mem_capable;
@@ -2903,6 +2938,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_DYNAMIC_RAM_A)
+		sprintf(path, "%s/create_dynamic_ram_a_region", decoder->dev_path);
 
 	rc = sysfs_read_attr(ctx, path, buf);
 	if (rc < 0) {
@@ -2954,6 +2991,12 @@ cxl_decoder_create_ram_region(struct cxl_decoder *decoder)
 	return cxl_decoder_create_region(decoder, CXL_DECODER_MODE_RAM);
 }
 
+CXL_EXPORT struct cxl_region *
+cxl_decoder_create_dynamic_ram_a_region(struct cxl_decoder *decoder)
+{
+	return cxl_decoder_create_region(decoder, CXL_DECODER_MODE_DYNAMIC_RAM_A);
+}
+
 CXL_EXPORT int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder)
 {
 	return decoder->nr_targets;
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
index ed4429f..258bdd3 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -294,6 +294,10 @@ global:
 	cxl_memdev_get_fwctl;
 	cxl_fwctl_get_major;
 	cxl_fwctl_get_minor;
+	cxl_memdev_get_dynamic_ram_a_size;
+	cxl_memdev_get_dynamic_ram_a_qos_class;
+	cxl_decoder_is_dynamic_ram_a_capable;
+	cxl_decoder_create_dynamic_ram_a_region;
 } LIBECXL_8;
 
 LIBCXL_10 {
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
index d2d71fc..37b7b06 100644
--- a/cxl/lib/private.h
+++ b/cxl/lib/private.h
@@ -52,8 +52,10 @@ struct cxl_memdev {
 	struct list_node list;
 	unsigned long long pmem_size;
 	unsigned long long ram_size;
+	unsigned long long dynamic_ram_a_size;
 	int ram_qos_class;
 	int pmem_qos_class;
+	int dynamic_ram_a_qos_class;
 	int payload_max;
 	size_t lsa_size;
 	struct kmod_module *module;
@@ -159,6 +161,7 @@ struct cxl_decoder {
 	unsigned int interleave_granularity;
 	bool pmem_capable;
 	bool volatile_capable;
+	bool dynamic_ram_a_capable;
 	bool mem_capable;
 	bool accelmem_capable;
 	bool locked;
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
index e91af90..fd41122 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -75,8 +75,10 @@ struct cxl_fwctl *cxl_memdev_get_fwctl(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_dynamic_ram_a_size(struct cxl_memdev *memdev);
 int cxl_memdev_get_pmem_qos_class(struct cxl_memdev *memdev);
 int cxl_memdev_get_ram_qos_class(struct cxl_memdev *memdev);
+int cxl_memdev_get_dynamic_ram_a_qos_class(struct cxl_memdev *memdev);
 const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
 bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev);
 size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev);
@@ -210,6 +212,7 @@ enum cxl_decoder_mode {
 	CXL_DECODER_MODE_MIXED,
 	CXL_DECODER_MODE_PMEM,
 	CXL_DECODER_MODE_RAM,
+	CXL_DECODER_MODE_DYNAMIC_RAM_A,
 };
 
 static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode)
@@ -219,9 +222,10 @@ static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode)
 		[CXL_DECODER_MODE_MIXED] = "mixed",
 		[CXL_DECODER_MODE_PMEM] = "pmem",
 		[CXL_DECODER_MODE_RAM] = "ram",
+		[CXL_DECODER_MODE_DYNAMIC_RAM_A] = "dynamic_ram_a",
 	};
 
-	if (mode < CXL_DECODER_MODE_NONE || mode > CXL_DECODER_MODE_RAM)
+	if (mode < CXL_DECODER_MODE_NONE || mode > CXL_DECODER_MODE_DYNAMIC_RAM_A)
 		mode = CXL_DECODER_MODE_NONE;
 	return names[mode];
 }
@@ -235,6 +239,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, "dynamic_ram_a") == 0)
+		return CXL_DECODER_MODE_DYNAMIC_RAM_A;
 	return CXL_DECODER_MODE_NONE;
 }
 
@@ -264,6 +270,7 @@ cxl_decoder_get_target_type(struct cxl_decoder *decoder);
 bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder);
 bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder);
 bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder);
+bool cxl_decoder_is_dynamic_ram_a_capable(struct cxl_decoder *decoder);
 bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder);
 bool cxl_decoder_is_locked(struct cxl_decoder *decoder);
 unsigned int
@@ -272,6 +279,7 @@ 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_pmem_region(struct cxl_decoder *decoder);
 struct cxl_region *cxl_decoder_create_ram_region(struct cxl_decoder *decoder);
+struct cxl_region *cxl_decoder_create_dynamic_ram_a_region(struct cxl_decoder *decoder);
 struct cxl_decoder *cxl_decoder_get_by_name(struct cxl_ctx *ctx,
 					    const char *ident);
 struct cxl_memdev *cxl_decoder_get_memdev(struct cxl_decoder *decoder);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 3/7] cxl/region: Add cxl-cli support for dynamic RAM A
  2026-05-23  9:50 [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Anisa Su
  2026-05-23  9:50 ` [PATCH v6 1/7] " Anisa Su
  2026-05-23  9:50 ` [PATCH v6 2/7] libcxl: Add Dynamic RAM A partition mode support Anisa Su
@ 2026-05-23  9:50 ` Anisa Su
  2026-06-08 23:58   ` Dave Jiang
  2026-05-23  9:50 ` [PATCH v6 4/7] libcxl: Add extent functionality to DC regions Anisa Su
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 31+ messages in thread
From: Anisa Su @ 2026-05-23  9:50 UTC (permalink / raw)
  To: linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Dave Jiang, Ira Weiny, Alison Schofield, John Groves,
	Gregory Price, Ira Weiny

From: Ira Weiny <ira.weiny@intel.com>

A singular Dynamic RAM partition is exposed via the kernel.

Use this partition in cxl-cli.

Signed-off-by: Ira Weiny <ira.weiny@intel.com>

---
Changes:
[iweiny: New patch for decoder_ram_a]
---
 cxl/json.c   | 20 ++++++++++++++++++++
 cxl/memdev.c |  4 +++-
 cxl/region.c | 27 ++++++++++++++++++++++++---
 3 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/cxl/json.c b/cxl/json.c
index a925488..e94c809 100644
--- a/cxl/json.c
+++ b/cxl/json.c
@@ -620,6 +620,20 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
 		}
 	}
 
+	size = cxl_memdev_get_dynamic_ram_a_size(memdev);
+	if (size) {
+		jobj = util_json_object_size(size, flags);
+		if (jobj)
+			json_object_object_add(jdev, "dynamic_ram_a_size", jobj);
+
+		qos_class = cxl_memdev_get_dynamic_ram_a_qos_class(memdev);
+		if (qos_class != CXL_QOS_CLASS_NONE) {
+			jobj = json_object_new_int(qos_class);
+			if (jobj)
+				json_object_object_add(jdev, "dynamic_ram_a_qos_class", jobj);
+		}
+	}
+
 	if (flags & UTIL_JSON_HEALTH) {
 		jobj = util_cxl_memdev_health_to_json(memdev, flags);
 		if (jobj)
@@ -917,6 +931,12 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder,
 				json_object_object_add(
 					jdecoder, "volatile_capable", jobj);
 		}
+		if (cxl_decoder_is_dynamic_ram_a_capable(decoder)) {
+			jobj = json_object_new_boolean(true);
+			if (jobj)
+				json_object_object_add(
+					jdecoder, "dynamic_ram_a_capable", jobj);
+		}
 	}
 
 	if (cxl_port_is_root(port) &&
diff --git a/cxl/memdev.c b/cxl/memdev.c
index 6e44d15..bdcb008 100644
--- a/cxl/memdev.c
+++ b/cxl/memdev.c
@@ -269,8 +269,10 @@ static int __reserve_dpa(struct cxl_memdev *memdev,
 
 	if (mode == CXL_DECODER_MODE_RAM)
 		avail_dpa = cxl_memdev_get_ram_size(memdev);
-	else
+	else if (mode == CXL_DECODER_MODE_PMEM)
 		avail_dpa = cxl_memdev_get_pmem_size(memdev);
+	else
+		avail_dpa = cxl_memdev_get_dynamic_ram_a_size(memdev);
 
 	cxl_decoder_foreach(port, decoder) {
 		size = cxl_decoder_get_dpa_size(decoder);
diff --git a/cxl/region.c b/cxl/region.c
index 85d4d9b..3c935bf 100644
--- a/cxl/region.c
+++ b/cxl/region.c
@@ -303,7 +303,8 @@ static int parse_create_options(struct cxl_ctx *ctx, int count,
 
 	if (param.type) {
 		p->mode = cxl_decoder_mode_from_ident(param.type);
-		if (p->mode == CXL_DECODER_MODE_RAM && param.uuid) {
+		if ((p->mode == CXL_DECODER_MODE_RAM ||
+		     p->mode == CXL_DECODER_MODE_DYNAMIC_RAM_A) && param.uuid) {
 			log_err(&rl,
 				"can't set UUID for ram / volatile regions");
 			goto err;
@@ -417,6 +418,9 @@ static void collect_minsize(struct cxl_ctx *ctx, struct parsed_params *p)
 		case CXL_DECODER_MODE_PMEM:
 			size = cxl_memdev_get_pmem_size(memdev);
 			break;
+		case CXL_DECODER_MODE_DYNAMIC_RAM_A:
+			size = cxl_memdev_get_dynamic_ram_a_size(memdev);
+			break;
 		default:
 			/* Shouldn't ever get here */ ;
 		}
@@ -448,8 +452,10 @@ static int create_region_validate_qos_class(struct parsed_params *p)
 
 		if (p->mode == CXL_DECODER_MODE_RAM)
 			qos_class = cxl_memdev_get_ram_qos_class(memdev);
-		else
+		else if (p->mode == CXL_DECODER_MODE_PMEM)
 			qos_class = cxl_memdev_get_pmem_qos_class(memdev);
+		else
+			qos_class = cxl_memdev_get_dynamic_ram_a_qos_class(memdev);
 
 		/* No qos_class entries. Possibly no kernel support */
 		if (qos_class == CXL_QOS_CLASS_NONE)
@@ -488,6 +494,12 @@ static int validate_decoder(struct cxl_decoder *decoder,
 			return -EINVAL;
 		}
 		break;
+	case CXL_DECODER_MODE_DYNAMIC_RAM_A:
+		if (!cxl_decoder_is_dynamic_ram_a_capable(decoder)) {
+			log_err(&rl, "%s is not dynamic_ram_a capable\n", devname);
+			return -EINVAL;
+		}
+		break;
 	default:
 		log_err(&rl, "unknown type: %s\n", param.type);
 		return -EINVAL;
@@ -509,9 +521,11 @@ static void set_type_from_decoder(struct cxl_ctx *ctx, struct parsed_params *p)
 		return;
 
 	/*
-	 * default to pmem if both types are set, otherwise the single
+	 * default to pmem if all types are set, otherwise the single
 	 * capability dominates.
 	 */
+	if (cxl_decoder_is_dynamic_ram_a_capable(p->root_decoder))
+		p->mode = CXL_DECODER_MODE_DYNAMIC_RAM_A;
 	if (cxl_decoder_is_volatile_capable(p->root_decoder))
 		p->mode = CXL_DECODER_MODE_RAM;
 	if (cxl_decoder_is_pmem_capable(p->root_decoder))
@@ -699,6 +713,13 @@ static int create_region(struct cxl_ctx *ctx, int *count,
 				param.root_decoder);
 			return -ENXIO;
 		}
+	} else if (p->mode == CXL_DECODER_MODE_DYNAMIC_RAM_A) {
+		region = cxl_decoder_create_dynamic_ram_a_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);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 4/7] libcxl: Add extent functionality to DC regions
  2026-05-23  9:50 [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Anisa Su
                   ` (2 preceding siblings ...)
  2026-05-23  9:50 ` [PATCH v6 3/7] cxl/region: Add cxl-cli support for dynamic RAM A Anisa Su
@ 2026-05-23  9:50 ` Anisa Su
  2026-06-09  0:05   ` Dave Jiang
  2026-05-23  9:50 ` [PATCH v6 5/7] cxl/region: Add extent output to region query Anisa Su
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 31+ messages in thread
From: Anisa Su @ 2026-05-23  9:50 UTC (permalink / raw)
  To: linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Dave Jiang, Ira Weiny, Alison Schofield, John Groves,
	Gregory Price, Ira Weiny

From: Ira Weiny <ira.weiny@intel.com>

DCD regions have 0 or more extents.  The ability to list those and their
properties is useful to end users.

Add extent scanning and reporting functionality to libcxl.

Signed-off-by: Ira Weiny <ira.weiny@intel.com>

---
Changes:
[alison: s/tag/uuid/ for extents]
---
 Documentation/cxl/lib/libcxl.txt |  27 ++++++
 cxl/lib/libcxl.c                 | 138 +++++++++++++++++++++++++++++++
 cxl/lib/libcxl.sym               |   5 ++
 cxl/lib/private.h                |  11 +++
 cxl/libcxl.h                     |  11 +++
 5 files changed, 192 insertions(+)

diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
index 9921ac1..0ad294c 100644
--- a/Documentation/cxl/lib/libcxl.txt
+++ b/Documentation/cxl/lib/libcxl.txt
@@ -635,6 +635,33 @@ where its properties can be interrogated by daxctl. The helper
 cxl_region_get_daxctl_region() returns an 'struct daxctl_region *' that
 can be used with other libdaxctl APIs.
 
+EXTENTS
+-------
+
+=== EXTENT: Enumeration
+----
+struct cxl_region_extent;
+struct cxl_region_extent *cxl_extent_get_first(struct cxl_region *region);
+struct cxl_region_extent *cxl_extent_get_next(struct cxl_region_extent *extent);
+#define cxl_extent_foreach(region, extent) \
+        for (extent = cxl_extent_get_first(region); \
+             extent != NULL; \
+             extent = cxl_extent_get_next(extent))
+
+----
+
+=== EXTENT: Attributes
+----
+unsigned long long cxl_extent_get_offset(struct cxl_region_extent *extent);
+unsigned long long cxl_extent_get_length(struct cxl_region_extent *extent);
+void cxl_extent_get_uuid(struct cxl_region_extent *extent, uuid_t uuid);
+----
+
+Extents represent available memory within a dynamic capacity region.  Extent
+objects are available for informational purposes to aid in allocation of
+memory.
+
+
 include::../../copyright.txt[]
 
 SEE ALSO
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index be0bc03..c096666 100644
--- a/cxl/lib/libcxl.c
+++ b/cxl/lib/libcxl.c
@@ -635,6 +635,7 @@ static void *add_cxl_region(void *parent, int id, const char *cxlregion_base)
 	region->ctx = ctx;
 	region->decoder = decoder;
 	list_head_init(&region->mappings);
+	list_head_init(&region->extents);
 
 	region->dev_path = strdup(cxlregion_base);
 	if (!region->dev_path)
@@ -1257,6 +1258,143 @@ cxl_mapping_get_next(struct cxl_memdev_mapping *mapping)
 	return list_next(&region->mappings, mapping, list);
 }
 
+static void cxl_extents_init(struct cxl_region *region)
+{
+	const char *devname = cxl_region_get_devname(region);
+	struct cxl_ctx *ctx = cxl_region_get_ctx(region);
+	char *extent_path, *dax_region_path;
+	struct dirent *de;
+	DIR *dir = NULL;
+
+	if (region->extents_init)
+		return;
+	region->extents_init = 1;
+
+	dax_region_path = calloc(1, strlen(region->dev_path) + 64);
+	if (!dax_region_path) {
+		err(ctx, "%s: allocation failure\n", devname);
+		return;
+	}
+
+	extent_path = calloc(1, strlen(region->dev_path) + 100);
+	if (!extent_path) {
+		err(ctx, "%s: allocation failure\n", devname);
+		free(dax_region_path);
+		return;
+	}
+
+	sprintf(dax_region_path, "%s/dax_region%d",
+		region->dev_path, region->id);
+	dir = opendir(dax_region_path);
+	if (!dir) {
+		err(ctx, "no extents found (%s): %s\n",
+			strerror(errno), dax_region_path);
+		free(extent_path);
+		free(dax_region_path);
+		return;
+	}
+
+	while ((de = readdir(dir)) != NULL) {
+		struct cxl_region_extent *extent;
+		char buf[SYSFS_ATTR_SIZE];
+		u64 offset, length;
+		int id, region_id;
+
+		if (sscanf(de->d_name, "extent%d.%d", &region_id, &id) != 2)
+			continue;
+
+		sprintf(extent_path, "%s/extent%d.%d/offset",
+			dax_region_path, region_id, id);
+		if (sysfs_read_attr(ctx, extent_path, buf) < 0) {
+			err(ctx, "%s: failed to read extent%d.%d/offset\n",
+				devname, region_id, id);
+			continue;
+		}
+
+		offset = strtoull(buf, NULL, 0);
+		if (offset == ULLONG_MAX) {
+			err(ctx, "%s extent%d.%d: failed to read offset\n",
+				devname, region_id, id);
+			continue;
+		}
+
+		sprintf(extent_path, "%s/extent%d.%d/length",
+			dax_region_path, region_id, id);
+		if (sysfs_read_attr(ctx, extent_path, buf) < 0) {
+			err(ctx, "%s: failed to read extent%d.%d/length\n",
+				devname, region_id, id);
+			continue;
+		}
+
+		length = strtoull(buf, NULL, 0);
+		if (length == ULLONG_MAX) {
+			err(ctx, "%s extent%d.%d: failed to read length\n",
+				devname, region_id, id);
+			continue;
+		}
+
+		sprintf(extent_path, "%s/extent%d.%d/tag",
+			dax_region_path, region_id, id);
+		buf[0] = '\0';
+		if (sysfs_read_attr(ctx, extent_path, buf) != 0)
+			dbg(ctx, "%s extent%d.%d: failed to read uuid\n",
+				devname, region_id, id);
+
+		extent = calloc(1, sizeof(*extent));
+		if (!extent) {
+			err(ctx, "%s extent%d.%d: allocation failure\n",
+				devname, region_id, id);
+			continue;
+		}
+		if (strlen(buf) && uuid_parse(buf, extent->uuid) < 0)
+			err(ctx, "%s:%s\n", extent_path, buf);
+		extent->region = region;
+		extent->offset = offset;
+		extent->length = length;
+
+		list_node_init(&extent->list);
+		list_add(&region->extents, &extent->list);
+		dbg(ctx, "%s added extent%d.%d\n", devname, region_id, id);
+	}
+	free(dax_region_path);
+	free(extent_path);
+	closedir(dir);
+}
+
+CXL_EXPORT struct cxl_region_extent *
+cxl_extent_get_first(struct cxl_region *region)
+{
+	cxl_extents_init(region);
+
+	return list_top(&region->extents, struct cxl_region_extent, list);
+}
+
+CXL_EXPORT struct cxl_region_extent *
+cxl_extent_get_next(struct cxl_region_extent *extent)
+{
+	struct cxl_region *region = extent->region;
+
+	return list_next(&region->extents, extent, list);
+}
+
+CXL_EXPORT unsigned long long
+cxl_extent_get_offset(struct cxl_region_extent *extent)
+{
+	return extent->offset;
+}
+
+CXL_EXPORT unsigned long long
+cxl_extent_get_length(struct cxl_region_extent *extent)
+{
+	return extent->length;
+}
+
+CXL_EXPORT void
+cxl_extent_get_uuid(struct cxl_region_extent *extent, uuid_t uuid)
+{
+	memcpy(uuid, extent->uuid, sizeof(uuid_t));
+}
+
 CXL_EXPORT struct cxl_decoder *
 cxl_mapping_get_decoder(struct cxl_memdev_mapping *mapping)
 {
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
index 258bdd3..dcfe242 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -298,6 +298,11 @@ global:
 	cxl_memdev_get_dynamic_ram_a_qos_class;
 	cxl_decoder_is_dynamic_ram_a_capable;
 	cxl_decoder_create_dynamic_ram_a_region;
+	cxl_extent_get_first;
+	cxl_extent_get_next;
+	cxl_extent_get_offset;
+	cxl_extent_get_length;
+	cxl_extent_get_uuid;
 } LIBECXL_8;
 
 LIBCXL_10 {
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
index 37b7b06..c5f3bed 100644
--- a/cxl/lib/private.h
+++ b/cxl/lib/private.h
@@ -183,6 +183,7 @@ struct cxl_region {
 	struct cxl_decoder *decoder;
 	struct list_node list;
 	int mappings_init;
+	int extents_init;
 	struct cxl_ctx *ctx;
 	void *dev_buf;
 	size_t buf_len;
@@ -200,6 +201,7 @@ struct cxl_region {
 	struct daxctl_region *dax_region;
 	struct kmod_module *module;
 	struct list_head mappings;
+	struct list_head extents;
 };
 
 struct cxl_memdev_mapping {
@@ -209,6 +211,15 @@ struct cxl_memdev_mapping {
 	struct list_node list;
 };
 
+#define CXL_REGION_EXTENT_TAG 0x10
+struct cxl_region_extent {
+	struct cxl_region *region;
+	u64 offset;
+	u64 length;
+	uuid_t uuid;
+	struct list_node list;
+};
+
 enum cxl_cmd_query_status {
 	CXL_CMD_QUERY_NOT_RUN = 0,
 	CXL_CMD_QUERY_OK,
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
index fd41122..a60509f 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -394,6 +394,17 @@ unsigned int cxl_mapping_get_position(struct cxl_memdev_mapping *mapping);
              mapping != NULL; \
              mapping = cxl_mapping_get_next(mapping))
 
+struct cxl_region_extent;
+struct cxl_region_extent *cxl_extent_get_first(struct cxl_region *region);
+struct cxl_region_extent *cxl_extent_get_next(struct cxl_region_extent *extent);
+#define cxl_extent_foreach(region, extent) \
+        for (extent = cxl_extent_get_first(region); \
+             extent != NULL; \
+             extent = cxl_extent_get_next(extent))
+unsigned long long cxl_extent_get_offset(struct cxl_region_extent *extent);
+unsigned long long cxl_extent_get_length(struct cxl_region_extent *extent);
+void cxl_extent_get_uuid(struct cxl_region_extent *extent, uuid_t uuid);
+
 struct cxl_cmd;
 const char *cxl_cmd_get_devname(struct cxl_cmd *cmd);
 struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 5/7] cxl/region: Add extent output to region query
  2026-05-23  9:50 [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Anisa Su
                   ` (3 preceding siblings ...)
  2026-05-23  9:50 ` [PATCH v6 4/7] libcxl: Add extent functionality to DC regions Anisa Su
@ 2026-05-23  9:50 ` Anisa Su
  2026-06-09  0:08   ` Dave Jiang
  2026-05-23  9:50 ` [PATCH v6 6/7] daxctl: Add --uuid option to create-device for sparse regions Anisa Su
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 31+ messages in thread
From: Anisa Su @ 2026-05-23  9:50 UTC (permalink / raw)
  To: linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Dave Jiang, Ira Weiny, Alison Schofield, John Groves,
	Gregory Price, Ira Weiny

From: Ira Weiny <ira.weiny@intel.com>

DCD regions have 0 or more extents.  The ability to list those and their
properties is useful to end users.

Add an option for extent output to region queries.  An example of this
is:

	$ ./build/cxl/cxl list -r 8 -Nu
	{
	  "region":"region8",
	  ...
	  "type":"dc",
	  ...
	  "extents":[
	    {
	      "offset":"0x10000000",
	      "length":"64.00 MiB (67.11 MB)",
	      "tag":"00000000-0000-0000-0000-000000000000"
	    },
	    {
	      "offset":"0x8000000",
	      "length":"64.00 MiB (67.11 MB)",
	      "tag":"00000000-0000-0000-0000-000000000000"
	    }
	  ]
	}

Signed-off-by: Ira Weiny <ira.weiny@intel.com>

---
Changes:
[iweiny: s/tag/uuid/]
---
 Documentation/cxl/cxl-list.txt | 29 +++++++++++++++++++++
 cxl/filter.h                   |  3 +++
 cxl/json.c                     | 47 ++++++++++++++++++++++++++++++++++
 cxl/json.h                     |  3 +++
 cxl/list.c                     |  3 +++
 util/json.h                    |  1 +
 6 files changed, 86 insertions(+)

diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
index 193860b..7512687 100644
--- a/Documentation/cxl/cxl-list.txt
+++ b/Documentation/cxl/cxl-list.txt
@@ -426,6 +426,35 @@ OPTIONS
 }
 ----
 
+-N::
+--extents::
+	Append Dynamic Capacity extent information.
+----
+13:34:28 > ./build/cxl/cxl list -r 8 -Nu
+{
+  "region":"region8",
+  "resource":"0xf030000000",
+  "size":"512.00 MiB (536.87 MB)",
+  "type":"dc",
+  "interleave_ways":1,
+  "interleave_granularity":256,
+  "decode_state":"commit",
+  "extents":[
+    {
+      "offset":"0x10000000",
+      "length":"64.00 MiB (67.11 MB)",
+      "uuid":"00000000-0000-0000-0000-000000000000"
+    },
+    {
+      "offset":"0x8000000",
+      "length":"64.00 MiB (67.11 MB)",
+      "uuid":"00000000-0000-0000-0000-000000000000"
+    }
+  ]
+}
+----
+
+
 -r::
 --region::
 	Specify CXL region device name(s), or device id(s), to filter the listing.
diff --git a/cxl/filter.h b/cxl/filter.h
index 70463c4..30e7fe2 100644
--- a/cxl/filter.h
+++ b/cxl/filter.h
@@ -31,6 +31,7 @@ struct cxl_filter_params {
 	bool alert_config;
 	bool dax;
 	bool media_errors;
+	bool extents;
 	int verbose;
 	struct log_ctx ctx;
 };
@@ -93,6 +94,8 @@ static inline unsigned long cxl_filter_to_flags(struct cxl_filter_params *param)
 		flags |= UTIL_JSON_DAX | UTIL_JSON_DAX_DEVS;
 	if (param->media_errors)
 		flags |= UTIL_JSON_MEDIA_ERRORS;
+	if (param->extents)
+		flags |= UTIL_JSON_EXTENTS;
 	return flags;
 }
 
diff --git a/cxl/json.c b/cxl/json.c
index e94c809..7922b32 100644
--- a/cxl/json.c
+++ b/cxl/json.c
@@ -1022,6 +1022,50 @@ void util_cxl_mappings_append_json(struct json_object *jregion,
 	json_object_object_add(jregion, "mappings", jmappings);
 }
 
+void util_cxl_extents_append_json(struct json_object *jregion,
+				  struct cxl_region *region,
+				  unsigned long flags)
+{
+	struct json_object *jextents;
+	struct cxl_region_extent *extent;
+
+	jextents = json_object_new_array();
+	if (!jextents)
+		return;
+
+	cxl_extent_foreach(region, extent) {
+		struct json_object *jextent, *jobj;
+		unsigned long long val;
+		char uuid_str[40];
+		uuid_t uuid;
+
+		jextent = json_object_new_object();
+		if (!jextent)
+			continue;
+
+		val = cxl_extent_get_offset(extent);
+		jobj = util_json_object_hex(val, flags);
+		if (jobj)
+			json_object_object_add(jextent, "offset", jobj);
+
+		val = cxl_extent_get_length(extent);
+		jobj = util_json_object_size(val, flags);
+		if (jobj)
+			json_object_object_add(jextent, "length", jobj);
+
+		cxl_extent_get_uuid(extent, uuid);
+		uuid_unparse(uuid, uuid_str);
+		jobj = json_object_new_string(uuid_str);
+		if (jobj)
+			json_object_object_add(jextent, "uuid", jobj);
+
+		json_object_array_add(jextents, jextent);
+		json_object_set_userdata(jextent, extent, NULL);
+	}
+
+	json_object_object_add(jregion, "extents", jextents);
+}
+
 struct json_object *util_cxl_region_to_json(struct cxl_region *region,
 					     unsigned long flags)
 {
@@ -1126,6 +1170,9 @@ struct json_object *util_cxl_region_to_json(struct cxl_region *region,
 		}
 	}
 
+	if (flags & UTIL_JSON_EXTENTS)
+		util_cxl_extents_append_json(jregion, region, flags);
+
 	if (cxl_region_qos_class_mismatch(region)) {
 		jobj = json_object_new_boolean(true);
 		if (jobj)
diff --git a/cxl/json.h b/cxl/json.h
index eb7572b..f9c07ab 100644
--- a/cxl/json.h
+++ b/cxl/json.h
@@ -20,6 +20,9 @@ struct json_object *util_cxl_region_to_json(struct cxl_region *region,
 void util_cxl_mappings_append_json(struct json_object *jregion,
 				  struct cxl_region *region,
 				  unsigned long flags);
+void util_cxl_extents_append_json(struct json_object *jregion,
+				  struct cxl_region *region,
+				  unsigned long flags);
 void util_cxl_targets_append_json(struct json_object *jdecoder,
 				  struct cxl_decoder *decoder,
 				  const char *ident, const char *serial,
diff --git a/cxl/list.c b/cxl/list.c
index 0b25d78..47d1351 100644
--- a/cxl/list.c
+++ b/cxl/list.c
@@ -59,6 +59,8 @@ static const struct option options[] = {
 		    "include alert configuration information"),
 	OPT_BOOLEAN('L', "media-errors", &param.media_errors,
 		    "include media-error information "),
+	OPT_BOOLEAN('N', "extents", &param.extents,
+		    "include extent information (Dynamic Capacity regions only)"),
 	OPT_INCR('v', "verbose", &param.verbose, "increase output detail"),
 #ifdef ENABLE_DEBUG
 	OPT_BOOLEAN(0, "debug", &debug, "debug list walk"),
@@ -135,6 +137,7 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
 		param.decoders = true;
 		param.targets = true;
 		param.regions = true;
+		param.extents = true;
 		/*fallthrough*/
 	case 0:
 		break;
diff --git a/util/json.h b/util/json.h
index 560f845..79ae324 100644
--- a/util/json.h
+++ b/util/json.h
@@ -21,6 +21,7 @@ enum util_json_flags {
 	UTIL_JSON_TARGETS	= (1 << 11),
 	UTIL_JSON_PARTITION	= (1 << 12),
 	UTIL_JSON_ALERT_CONFIG	= (1 << 13),
+	UTIL_JSON_EXTENTS	= (1 << 14),
 };
 
 void util_display_json_array(FILE *f_out, struct json_object *jarray,
-- 
2.43.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 6/7] daxctl: Add --uuid option to create-device for sparse regions
  2026-05-23  9:50 [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Anisa Su
                   ` (4 preceding siblings ...)
  2026-05-23  9:50 ` [PATCH v6 5/7] cxl/region: Add extent output to region query Anisa Su
@ 2026-05-23  9:50 ` Anisa Su
  2026-06-09  0:12   ` Dave Jiang
  2026-05-23  9:50 ` [PATCH v6 7/7] cxl/test: Add Dynamic Capacity tests Anisa Su
  2026-06-05  5:43 ` [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Alison Schofield
  7 siblings, 1 reply; 31+ messages in thread
From: Anisa Su @ 2026-05-23  9:50 UTC (permalink / raw)
  To: linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Dave Jiang, Ira Weiny, Alison Schofield, John Groves,
	Gregory Price, Anisa Su, Anisa Su

Add a --uuid option to 'daxctl create-device' that writes the given
uuid to the new dax device's sysfs 'uuid' attribute.  On sparse (DCD)
regions this claims dax_resources whose tag matches and populates the
seed device with their capacity; size is determined by the claim, so
--uuid is mutually exclusive with --size.

Pass "0" to claim a single untagged dax_resource.  A claim that
matches no dax_resource leaves the device at size 0; the kernel
returns ENOENT.

Plumb the write through a new daxctl_dev_set_uuid() libdaxctl helper
(LIBDAXCTL_11) and document the option in the man page.

Signed-off-by: Anisa Su <anisa.su887@gmail.com>
---
 Documentation/daxctl/daxctl-create-device.txt | 12 ++++
 daxctl/device.c                               | 72 +++++++++++++------
 daxctl/lib/libdaxctl.c                        | 44 ++++++++++++
 daxctl/lib/libdaxctl.sym                      |  5 ++
 daxctl/libdaxctl.h                            |  1 +
 5 files changed, 114 insertions(+), 20 deletions(-)

diff --git a/Documentation/daxctl/daxctl-create-device.txt b/Documentation/daxctl/daxctl-create-device.txt
index b774b86..27b87d0 100644
--- a/Documentation/daxctl/daxctl-create-device.txt
+++ b/Documentation/daxctl/daxctl-create-device.txt
@@ -82,6 +82,18 @@ include::region-option.txt[]
 
 	The size must be a multiple of the region alignment.
 
+	Mutually exclusive with --uuid.
+
+--uuid=::
+	For dax devices on sparse (DCD) regions, claim dax_resource(s) whose
+	tag matches the given UUID.  The device's size is determined by the
+	claimed capacity, so --uuid cannot be combined with --size.
+
+	A non-null UUID claims every matching dax_resource in the parent
+	region.  The value "0" is shorthand for the null UUID and claims a
+	single untagged dax_resource.  A write that matches no dax_resource
+	fails with ENOENT and the device is left at size 0.
+
 -a::
 --align::
 	Applications that want to establish dax memory mappings with
diff --git a/daxctl/device.c b/daxctl/device.c
index a4e36b1..21a941e 100644
--- a/daxctl/device.c
+++ b/daxctl/device.c
@@ -30,6 +30,7 @@ static struct {
 	const char *size;
 	const char *align;
 	const char *input;
+	const char *uuid;
 	bool check_config;
 	bool no_online;
 	bool no_movable;
@@ -85,7 +86,9 @@ OPT_BOOLEAN('C', "check-config", &param.check_config, \
 #define CREATE_OPTIONS() \
 OPT_STRING('s', "size", &param.size, "size", "size to switch the device to"), \
 OPT_STRING('a', "align", &param.align, "align", "alignment to switch the device to"), \
-OPT_STRING('\0', "input", &param.input, "input", "input device JSON file")
+OPT_STRING('\0', "input", &param.input, "input", "input device JSON file"), \
+OPT_STRING('\0', "uuid", &param.uuid, "uuid", \
+	"claim sparse dax_resource(s) matching this uuid (\"0\" for untagged)")
 
 #define DESTROY_OPTIONS() \
 OPT_BOOLEAN('f', "force", &param.force, \
@@ -808,6 +811,22 @@ static int do_create(struct daxctl_region *region, long long val,
 	struct daxctl_dev *dev;
 	int i, rc = 0;
 	long long alloc = 0;
+	uuid_t uuid;
+
+	if (param.uuid) {
+		if (param.size) {
+			fprintf(stderr,
+				"--uuid and --size are mutually exclusive\n");
+			return -EINVAL;
+		}
+		if (strcmp(param.uuid, "0") == 0) {
+			uuid_clear(uuid);
+		} else if (uuid_parse(param.uuid, uuid) < 0) {
+			fprintf(stderr, "failed to parse uuid '%s'\n",
+				param.uuid);
+			return -EINVAL;
+		}
+	}
 
 	if (daxctl_region_create_dev(region))
 		return -ENOSPC;
@@ -816,33 +835,46 @@ static int do_create(struct daxctl_region *region, long long val,
 	if (!dev)
 		return -ENOSPC;
 
-	if (val == -1)
-		val = daxctl_region_get_available_size(region);
-
-	if (val <= 0)
-		return -ENOSPC;
-
 	if (align > 0) {
 		rc = daxctl_dev_set_align(dev, align);
 		if (rc < 0)
 			return rc;
 	}
 
-	/* @maps is ordered by page_offset */
-	for (i = 0; i < nmaps; i++) {
-		rc = daxctl_dev_set_mapping(dev, maps[i].start, maps[i].end);
-		if (rc < 0)
+	if (param.uuid) {
+		rc = daxctl_dev_set_uuid(dev, uuid);
+		if (rc < 0) {
+			fprintf(stderr,
+				"%s: failed to claim uuid '%s': %s\n",
+				daxctl_dev_get_devname(dev), param.uuid,
+				strerror(-rc));
 			return rc;
-		alloc += (maps[i].end - maps[i].start + 1);
-	}
-
-	if (nmaps > 0 && val > 0 && alloc != val) {
-		fprintf(stderr, "%s: allocated %lld but specified size %lld\n",
-			daxctl_dev_get_devname(dev), alloc, val);
+		}
 	} else {
-		rc = daxctl_dev_set_size(dev, val);
-		if (rc < 0)
-			return rc;
+		if (val == -1)
+			val = daxctl_region_get_available_size(region);
+
+		if (val <= 0)
+			return -ENOSPC;
+
+		/* @maps is ordered by page_offset */
+		for (i = 0; i < nmaps; i++) {
+			rc = daxctl_dev_set_mapping(dev, maps[i].start,
+						    maps[i].end);
+			if (rc < 0)
+				return rc;
+			alloc += (maps[i].end - maps[i].start + 1);
+		}
+
+		if (nmaps > 0 && val > 0 && alloc != val) {
+			fprintf(stderr,
+				"%s: allocated %lld but specified size %lld\n",
+				daxctl_dev_get_devname(dev), alloc, val);
+		} else {
+			rc = daxctl_dev_set_size(dev, val);
+			if (rc < 0)
+				return rc;
+		}
 	}
 
 	rc = daxctl_dev_enable_devdax(dev);
diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c
index 02ae7e5..fe07939 100644
--- a/daxctl/lib/libdaxctl.c
+++ b/daxctl/lib/libdaxctl.c
@@ -1107,6 +1107,50 @@ DAXCTL_EXPORT int daxctl_dev_set_size(struct daxctl_dev *dev, unsigned long long
 	return 0;
 }
 
+DAXCTL_EXPORT int daxctl_dev_set_uuid(struct daxctl_dev *dev, uuid_t uuid)
+{
+	struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev);
+	char buf[SYSFS_ATTR_SIZE];
+	char *path = dev->dev_buf;
+	int len = dev->buf_len;
+
+	if (snprintf(path, len, "%s/uuid", dev->dev_path) >= len) {
+		err(ctx, "%s: buffer too small!\n",
+				daxctl_dev_get_devname(dev));
+		return -ENXIO;
+	}
+
+	if (uuid_is_null(uuid))
+		sprintf(buf, "0\n");
+	else
+		uuid_unparse(uuid, buf);
+
+	if (sysfs_write_attr(ctx, path, buf) < 0) {
+		err(ctx, "%s: failed to set uuid\n",
+				daxctl_dev_get_devname(dev));
+		return -ENXIO;
+	}
+
+	/*
+	 * On a sparse region the kernel populates the device size as a
+	 * side effect of claiming the matching dax_resource(s); refresh
+	 * the cached size so callers see the post-claim value.
+	 */
+	if (snprintf(path, len, "%s/size", dev->dev_path) >= len) {
+		err(ctx, "%s: buffer too small!\n",
+				daxctl_dev_get_devname(dev));
+		return -ENXIO;
+	}
+	if (sysfs_read_attr(ctx, path, buf) < 0) {
+		err(ctx, "%s: failed to read back size\n",
+				daxctl_dev_get_devname(dev));
+		return -ENXIO;
+	}
+	dev->size = strtoull(buf, NULL, 0);
+
+	return 0;
+}
+
 DAXCTL_EXPORT unsigned long daxctl_dev_get_align(struct daxctl_dev *dev)
 {
 	return dev->align;
diff --git a/daxctl/lib/libdaxctl.sym b/daxctl/lib/libdaxctl.sym
index 3098811..16792eb 100644
--- a/daxctl/lib/libdaxctl.sym
+++ b/daxctl/lib/libdaxctl.sym
@@ -104,3 +104,8 @@ LIBDAXCTL_10 {
 global:
 	daxctl_dev_is_system_ram_capable;
 } LIBDAXCTL_9;
+
+LIBDAXCTL_11 {
+global:
+	daxctl_dev_set_uuid;
+} LIBDAXCTL_10;
diff --git a/daxctl/libdaxctl.h b/daxctl/libdaxctl.h
index 53c6bbd..cdd5995 100644
--- a/daxctl/libdaxctl.h
+++ b/daxctl/libdaxctl.h
@@ -63,6 +63,7 @@ int daxctl_dev_get_minor(struct daxctl_dev *dev);
 unsigned long long daxctl_dev_get_resource(struct daxctl_dev *dev);
 unsigned long long daxctl_dev_get_size(struct daxctl_dev *dev);
 int daxctl_dev_set_size(struct daxctl_dev *dev, unsigned long long size);
+int daxctl_dev_set_uuid(struct daxctl_dev *dev, uuid_t uuid);
 unsigned long daxctl_dev_get_align(struct daxctl_dev *dev);
 int daxctl_dev_set_align(struct daxctl_dev *dev, unsigned long align);
 int daxctl_dev_set_mapping(struct daxctl_dev *dev, unsigned long long start,
-- 
2.43.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v6 7/7] cxl/test: Add Dynamic Capacity tests
  2026-05-23  9:50 [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Anisa Su
                   ` (5 preceding siblings ...)
  2026-05-23  9:50 ` [PATCH v6 6/7] daxctl: Add --uuid option to create-device for sparse regions Anisa Su
@ 2026-05-23  9:50 ` Anisa Su
  2026-06-09  0:24   ` Dave Jiang
  2026-06-05  5:43 ` [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Alison Schofield
  7 siblings, 1 reply; 31+ messages in thread
From: Anisa Su @ 2026-05-23  9:50 UTC (permalink / raw)
  To: linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Dave Jiang, Ira Weiny, Alison Schofield, John Groves,
	Gregory Price, Ira Weiny, Anisa Su

From: Ira Weiny <ira.weiny@intel.com>

cxl_test provides a good way to ensure quick smoke and regression
testing.  The complexity of DCD and the new sparse DAX regions
required to use them benefits greatly with a series of smoke tests.

The only part of the kernel stack which must be bypassed is the actual
irq of DCD events.  However, the event processing itself can be tested
via cxl_test calling directly into the event processing.

In this way the rest of the stack; management of sparse regions, the
extent device lifetimes, and the dax device operations can be tested.

Add Dynamic Capacity Device tests for kernels which have DCD support.

Signed-off-by: Ira Weiny <ira.weiny@intel.com>
Signed-off-by: Anisa Su <anisa.su887@gmail.com>

---
Changes:
[anisa: align tests with kernel DCD redesign + add sharable coverage]

Rewrite tests against the post-redesign kernel (uuid sysfs claim,
sparse size-grow rejection, tag-group atomic release, cross-More
uniqueness):

  - Use real UUID strings; route untagged claims via --uuid "0" so
    daxctl exercises the kernel uuid_store path end to end.
  - Pre-existing-extent test handles the tagged pre2_ext (deadbeef)
    separately from the untagged pre_ext.
  - Spanning / aggregation tests drive both extents in one More-chain:
    cross-event tagged adds are dropped by the cross-More uniqueness
    gate, and untagged events become independent dax_resources so
    only one is claimed per --uuid "0".
  - test_dax_device_ops asserts that sparse size grow returns
    -EOPNOTSUPP after a shrink (real grow path is --uuid only).
  - test_reject_overlapping math now produces an actual overlap
    inside the DC region (the prior arithmetic landed past the end).

Add coverage for new validators:
  - test_uuid_no_match / test_uuid_no_match_seed_intact
  - test_uuid_show (reads back the dax-dev uuid sysfs attribute)
  - test_cross_more_uniqueness (rejects tag reuse across More-chains)
  - test_alignment_rejection (rejects misaligned extents)

Sharable-partition coverage (test_shared_extent_inject,
test_seq_integrity_gap) is routed at runtime to a dedicated mock
memdev that tools/testing/cxl stamps with serial 0xDCDC; the script
enumerates both regimes from one cxl_test module load.

Localize positional-arg assignments in every helper so functions
no longer clobber caller globals (the previous behavior leaked
the sharable memdev into later tests).

Update inject_extent to optionally pass shared_extn_seq (5th arg)
and remove_extent to carry the tag (now required by the mock's
delete sysfs).  Add inject_shared_extent for dc_inject_shared_extent.
---
 test/cxl-dcd.sh  | 1267 ++++++++++++++++++++++++++++++++++++++++++++++
 test/meson.build |    2 +
 2 files changed, 1269 insertions(+)
 create mode 100644 test/cxl-dcd.sh

diff --git a/test/cxl-dcd.sh b/test/cxl-dcd.sh
new file mode 100644
index 0000000..3194b00
--- /dev/null
+++ b/test/cxl-dcd.sh
@@ -0,0 +1,1267 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2024 Intel Corporation. All rights reserved.
+
+. "$(dirname "$0")/common"
+
+rc=77
+set -ex
+
+trap 'err $LINENO' ERR
+
+check_prereq "jq"
+
+rc=1
+
+dev_path="/sys/bus/platform/devices"
+cxl_path="/sys/bus/cxl/devices"
+
+# test extent tags (UUIDs).  pre_ext_tag matches the second pre-injected
+# extent tag in the kernel mock (tools/testing/cxl/test/mem.c).
+pre_ext_tag="deadbeef-cafe-baad-f00d-fedcba987654"
+test_tag_a="11111111-1111-1111-1111-111111111111"
+test_tag_b="22222222-2222-2222-2222-222222222222"
+unknown_tag="33333333-3333-3333-3333-333333333333"
+
+#
+# The test devices have 2G of non DC capacity.  A single DC reagion of 1G is
+# added beyond that.
+#
+# The testing centers around 3 extents.  Two are "pre-existing" on test load
+# called pre-ext and pre2-ext.  The other is created within this script alone
+# called base.
+
+#
+# | 2G non- |      DC region (1G)                                   |
+# |  DC cap |                                                       |
+# |  ...    |-------------------------------------------------------|
+# |         |--------|       |----------|      |----------|         |
+# |         | (base) |       |(pre-ext) |      |(pre2-ext)|         |
+
+dra_size=""
+
+base_dpa=0x80000000
+
+# base extent at dpa 2G - 64M long
+base_ext_offset=0x0
+base_ext_dpa=$(($base_dpa + $base_ext_offset))
+base_ext_length=0x4000000
+
+# pre existing extent base + 128M, 64M length
+# 0x00000088000000-0x0000008bffffff
+pre_ext_offset=0x8000000
+pre_ext_dpa=$(($base_dpa + $pre_ext_offset))
+pre_ext_length=0x4000000
+
+# pre2 existing extent base + 256M, 64M length
+# 0x00000090000000-0x00000093ffffff
+pre2_ext_offset=0x10000000
+pre2_ext_dpa=$(($base_dpa + $pre2_ext_offset))
+pre2_ext_length=0x4000000
+
+mem=""
+bus=""
+device=""
+decoder=""
+
+# ========================================================================
+# Support functions
+# ========================================================================
+
+create_dcd_region()
+{
+	local mem="$1"
+	local decoder="$2"
+	local reg_size_string=""
+	local region
+	if [ "$3" != "" ]; then
+		reg_size_string="-s $3"
+	fi
+
+	# create region
+	region=$($CXL create-region -t dynamic_ram_a -d "$decoder" -m "$mem" ${reg_size_string} | jq -r ".region")
+
+	if [[ ! $region ]]; then
+		echo "create-region failed for $decoder / $mem"
+		err "$LINENO"
+	fi
+
+	echo ${region}
+}
+
+check_region()
+{
+	local search=$1
+	local region_size=$2
+	local result
+
+	result=$($CXL list -r "$search" | jq -r ".[].region")
+	if [ "$result" != "$search" ]; then
+		echo "check region failed to find $search"
+		err "$LINENO"
+	fi
+
+	result=$($CXL list -r "$search" | jq -r ".[].size")
+	if [ "$result" != "$region_size" ]; then
+		echo "check region failed invalid size $result != $region_size"
+		err "$LINENO"
+	fi
+}
+
+check_not_region()
+{
+	local search=$1
+	local result
+
+	result=$($CXL list -r "$search" | jq -r ".[].region")
+	if [ "$result" == "$search" ]; then
+		echo "check not region failed; $search found"
+		err "$LINENO"
+	fi
+}
+
+destroy_region()
+{
+	local region=$1
+	$CXL disable-region $region
+	$CXL destroy-region $region
+}
+
+inject_extent()
+{
+	local device="$1"
+	local dpa="$2"
+	local length="$3"
+	local tag="$4"
+	local seq="$6"
+	local more="0"
+	local cmd
+
+	if [ "$5" != "" ]; then
+		more="1"
+	fi
+
+	cmd="${dpa}:${length}:${tag}:${more}"
+	if [ -n "$seq" ]; then
+		cmd="${cmd}:${seq}"
+	fi
+	echo ${cmd} > "${dev_path}/${device}/dc_inject_extent"
+}
+
+# Shared-extent inject targets a *sharable* DC partition.  The mock
+# stamps the sentinel serial onto a single cxl_mem instance, and the
+# script routes shared-extent tests to that memdev via $sharable_device.
+inject_shared_extent()
+{
+	local device="$1"
+	local dpa="$2"
+	local length="$3"
+	local tag="$4"
+	local seq="$6"
+	local more="0"
+	local cmd
+
+	if [ "$5" != "" ]; then
+		more="1"
+	fi
+
+	cmd="${dpa}:${length}:${tag}:${more}"
+	if [ -n "$seq" ]; then
+		cmd="${cmd}:${seq}"
+	fi
+	echo ${cmd} > "${dev_path}/${device}/dc_inject_shared_extent"
+}
+
+remove_extent()
+{
+	local device="$1"
+	local dpa="$2"
+	local length="$3"
+	local tag="$4"
+
+	echo ${dpa}:${length}:${tag} > "${dev_path}/${device}/dc_del_extent"
+}
+
+create_dax_dev()
+{
+	local reg="$1"
+	local dax_dev
+
+	dax_dev=$($DAXCTL create-device -r $reg | jq -er '.[].chardev')
+
+	echo ${dax_dev}
+}
+
+create_dax_dev_with_uuid()
+{
+	local reg="$1"
+	local uuid="$2"
+	local dax_dev
+
+	dax_dev=$($DAXCTL create-device -r $reg --uuid "$uuid" \
+			| jq -er '.[].chardev')
+
+	echo ${dax_dev}
+}
+
+fail_create_dax_dev_with_uuid()
+{
+	local reg="$1"
+	local uuid="$2"
+	local create_rc
+
+	set +e
+	$DAXCTL create-device -r $reg --uuid "$uuid"
+	create_rc=$?
+	set -e
+	if [ $create_rc -eq 0 ]; then
+		echo "FAIL create-device with uuid $uuid unexpectedly succeeded"
+		err "$LINENO"
+	fi
+}
+
+fail_create_dax_dev()
+{
+	local reg="$1"
+	local result
+
+	set +e
+	result=$($DAXCTL create-device -r $reg)
+	set -e
+	if [ "$result" == "0" ]; then
+		echo "FAIL device created"
+		err "$LINENO"
+	fi
+}
+
+# Try to resize a sparse dax device via reconfigure -s; must fail because
+# the kernel rejects any non-zero size_store on a dynamic dax region with
+# -EOPNOTSUPP (drivers/dax/bus.c:dev_dax_resize).  The only valid resize is
+# to size 0, which is the path `daxctl destroy-device` takes.
+fail_resize_dax_dev()
+{
+	local dev="$1"
+	local new_size="$2"
+	local resize_rc
+
+	$DAXCTL disable-device $dev
+	set +e
+	$DAXCTL reconfigure-device $dev -s $new_size
+	resize_rc=$?
+	set -e
+	# Re-enable regardless so subsequent checks/cleanup work.
+	$DAXCTL enable-device $dev
+	if [ $resize_rc -eq 0 ]; then
+		echo "FAIL resize of $dev to $new_size unexpectedly succeeded"
+		err "$LINENO"
+	fi
+}
+
+# Read /sys/bus/dax/devices/<dax>/uuid and compare to $expected.  "0"
+# matches the null UUID (kernel emits "0\n" when the resource is empty,
+# untagged, or non-DCD).
+check_dax_uuid()
+{
+	local dax_dev="$1"
+	local expected="$2"
+	local uuid_path got want
+
+	uuid_path="/sys/bus/dax/devices/${dax_dev}/uuid"
+	if [ ! -e "$uuid_path" ]; then
+		echo "FAIL no uuid attribute at $uuid_path"
+		err "$LINENO"
+	fi
+	got=$(cat "$uuid_path" | tr -d '[:space:]')
+	want=$(echo "$expected" | tr -d '[:space:]')
+	if [ "$got" != "$want" ]; then
+		echo "FAIL uuid show on $dax_dev: got '$got' want '$want'"
+		err "$LINENO"
+	fi
+}
+
+destroy_dax_dev()
+{
+	local dev="$1"
+
+	$DAXCTL disable-device $dev
+	$DAXCTL destroy-device $dev
+}
+
+check_dax_dev()
+{
+	local search="$1"
+	local size=$(($2))
+	local result
+
+	result=$($DAXCTL list -d $search | jq -er '.[].chardev')
+	if [ "$result" != "$search" ]; then
+		echo "check dax device failed to find $search"
+		err "$LINENO"
+	fi
+	result=$($DAXCTL list -d $search | jq -er '.[].size')
+	if [ "$result" -ne "$size" ]; then
+		echo "check dax device failed incorrect size $result; exp $size"
+		err "$LINENO"
+	fi
+}
+
+# check that the dax device is not there.
+check_not_dax_dev()
+{
+	local reg="$1"
+	local search="$2"
+	local result
+	result=$($DAXCTL list -r $reg -D | jq -r '.[].chardev')
+	if [ "$result" == "$search" ]; then
+		echo "FAIL found $search"
+		err "$LINENO"
+	fi
+}
+
+check_extent()
+{
+	local region=$1
+	local offset=$(($2))
+	local length=$(($3))
+	local result
+
+	result=$($CXL list -r "$region" -N | jq -r ".[].extents[] | select(.offset == ${offset}) | .length")
+	if [[ $result != $length ]]; then
+		echo "FAIL region $1 could not find extent @ $offset ($length)"
+		err "$LINENO"
+	fi
+}
+
+check_extent_cnt()
+{
+	local region=$1
+	local count=$(($2))
+	local result
+
+	result=$($CXL list -r $region -N | jq -r '.[].extents[].offset' | wc -l)
+	if [[ $result != $count ]]; then
+		echo "FAIL region $1: found wrong number of extents $result; expect $count"
+		err "$LINENO"
+	fi
+}
+
+
+# ========================================================================
+# Tests
+# ========================================================================
+
+# testing pre existing extents must be called first as the extents were created
+# by cxl-test being loaded.  The mock fixture is one untagged extent at
+# pre_ext_dpa and one tagged (pre_ext_tag) extent at pre2_ext_dpa, so the
+# untagged extent is claimed via --uuid "0" and the tagged one via its UUID.
+test_pre_existing_extents()
+{
+	echo ""
+	echo "Test: pre-existing extents (untagged + tagged)"
+	echo ""
+	region=$(create_dcd_region ${mem} ${decoder})
+
+	# | 2G non- |      DC region                                        |
+	# |  DC cap |                                                       |
+	# |  ...    |-------------------------------------------------------|
+	# |         |                   |----------|         |----------|   |
+	# |         |                   |(pre-ext) |         |(pre2-ext)|   |
+	# |         |                   | untagged |         | tagged   |   |
+	check_region ${region} ${dra_size}
+	check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
+	check_extent ${region} ${pre2_ext_offset} ${pre2_ext_length}
+
+	# Untagged claim picks up the untagged pre-extent only
+	dax_dev_u=$(create_dax_dev_with_uuid ${region} "0")
+	check_dax_dev ${dax_dev_u} $pre_ext_length
+
+	# Tagged claim picks up the tagged pre2-extent
+	dax_dev_t=$(create_dax_dev_with_uuid ${region} "$pre_ext_tag")
+	check_dax_dev ${dax_dev_t} $pre2_ext_length
+
+	destroy_dax_dev ${dax_dev_u}
+	destroy_dax_dev ${dax_dev_t}
+
+	# Release events must carry the tag identity so cxl_rm_extent's
+	# uuid_equal lookup matches the right region_extent.
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	remove_extent ${device} $pre2_ext_dpa $pre2_ext_length "$pre_ext_tag"
+	check_extent_cnt ${region} 0
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_remove_extent_under_dax_device()
+{
+	# Remove the pre-created test extent out from under dax device
+	# stack should hold ref until dax device deleted
+	echo ""
+	echo "Test: Remove extent from under DAX dev"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+	# | 2G non- |      DC region                                        |
+	# |  DC cap |                                                       |
+	# |  ...    |-------------------------------------------------------|
+	# |         |                                                       |
+	# |         |                                                       |
+
+	
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	# |         |                   |----------|                        |
+	# |         |                   |(pre-ext) |                        |
+	check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
+
+	# Sparse seed starts at size 0; --uuid "0" is the only sizing path.
+	dax_dev=$(create_dax_dev_with_uuid ${region} "0")
+	# |         |                   |----------|                        |
+	# |         |                   |(pre-ext) |                        |
+	# |         |                   | daxX.1   |                        |
+	check_extent_cnt ${region} 1
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	# In-use extents are not released (dax-layer EBUSY defers release).
+	check_dax_dev ${dax_dev} $pre_ext_length
+
+	check_extent_cnt ${region} 1
+	destroy_dax_dev ${dax_dev}
+	# |         |                   |----------|                        |
+	# |         |                   |(pre-ext) |                        |
+	check_not_dax_dev ${region} ${dax_dev}
+
+	check_extent_cnt ${region} 1
+	# Re-issuing the release after the dax device dropped its hold
+	# completes the deferred release.
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	# |         |                                                       |
+	# |         |                                                       |
+	check_extent_cnt ${region} 0
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_remove_extents_in_use()
+{
+	echo ""
+	echo "Test: Remove extents under sparse dax device"
+	echo ""
+	# Tagged release events are deferred while a dax device pins the
+	# tag group; extent count stays at 2.
+	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 2
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 2
+}
+
+test_create_dax_dev_spanning_two_extents()
+{
+	echo ""
+	echo "Test: Create dax device spanning 2 extents in a tagged group"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+	# | 2G non- |      DC region                                        |
+	# |  DC cap |                                                       |
+	# |  ...    |-------------------------------------------------------|
+	#
+	# A single dax device spanning two extents is only possible when
+	# both extents belong to the same tagged More-chain.  Cross-event
+	# tagged adds are rejected by the cross-More uniqueness gate;
+	# untagged adds become independent dax_resources and only one is
+	# claimed per --uuid "0".  Drive both extents in one More-chain:
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a" 1
+	check_extent_cnt ${region} 0   # held off until More=0 closes chain
+	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+	check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
+	check_extent ${region} ${base_ext_offset} ${base_ext_length}
+	# |         |--------|          |----------|                        |
+	# |         | (base) |          |(pre-ext) |     tag_a              |
+
+	check_extent_cnt ${region} 2
+	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
+	# |         |--------|          |----------|                        |
+	# |         | (base) |          |(pre-ext) |                        |
+	# |         | daxX.1 |          | daxX.1   |                        |
+
+	echo "Checking if dev dax is spanning sparse extents"
+	ext_sum_length="$(($base_ext_length + $pre_ext_length))"
+	check_dax_dev ${dax_dev} $ext_sum_length
+
+	test_remove_extents_in_use
+
+	destroy_dax_dev ${dax_dev}
+	check_not_dax_dev ${region} ${dax_dev}
+
+	# In-use extents were not released.  Check they can be removed after
+	# the dax device is removed.
+	check_extent_cnt ${region} 2
+	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 0
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_inject_tag_support()
+{
+	echo ""
+	echo "Test: inject untagged + tagged extents, claim each via --uuid"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+
+	# untagged extent
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
+	# tagged extent (different DPA so both can land)
+	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+
+	# both extents should be accepted
+	check_extent_cnt ${region} 2
+
+	# claim the tagged extent into one dax device
+	dax_dev_a=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
+	check_dax_dev ${dax_dev_a} $base_ext_length
+
+	# claim the untagged extent into a second dax device via "0"
+	dax_dev_0=$(create_dax_dev_with_uuid ${region} "0")
+	check_dax_dev ${dax_dev_0} $pre_ext_length
+
+	destroy_dax_dev ${dax_dev_a}
+	destroy_dax_dev ${dax_dev_0}
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 0
+
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_uuid_no_match()
+{
+	echo ""
+	echo "Test: daxctl create-device --uuid with no matching extent fails"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+
+	# inject only one tagged extent; ask daxctl to claim a different uuid
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 1
+
+	fail_create_dax_dev_with_uuid ${region} "$unknown_tag"
+
+	# the extent should still be claimable via its real tag
+	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
+	check_dax_dev ${dax_dev} $pre_ext_length
+	destroy_dax_dev ${dax_dev}
+
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 0
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_uuid_aggregation()
+{
+	echo ""
+	echo "Test: multiple extents in a single More-chain aggregate under one tag"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+
+	# Both extents must arrive in the same More-chain to land in the
+	# same tag group.  Re-using a tag across More-chains hits the
+	# cross-More uniqueness gate and the second event would be dropped.
+	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a" 1
+	check_extent_cnt ${region} 0
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 2
+
+	# a single --uuid claim should pick up both extents
+	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
+	expected=$(($base_ext_length + $pre_ext_length))
+	check_dax_dev ${dax_dev} $expected
+
+	destroy_dax_dev ${dax_dev}
+	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+
+test_partial_extent_remove ()
+{
+	echo ""
+	echo "Test: partial extent remove"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+
+	# | 2G non- |      DC region                                        |
+	# |  DC cap |                                                       |
+	# |  ...    |-------------------------------------------------------|
+
+	inject_extent ${device} $base_ext_dpa $base_ext_length ""
+	# |  ...    |-------------------------------------------------------|
+	# |         |--------|                                              |
+	# |         | (base) |                                              |
+
+	dax_dev=$(create_dax_dev_with_uuid ${region} "0")
+
+	# |  ...    |-------------------------------------------------------|
+	# |         |--------|                                              |
+	# |         | (base) |                                              |
+	# |         | daxX.1 |                                              |
+
+	partial_ext_dpa="$(($base_ext_dpa + ($base_ext_length / 2)))"
+	partial_ext_length="$(($base_ext_length / 2))"
+	echo "Removing Partial : $partial_ext_dpa $partial_ext_length"
+
+	# |         |    |---|                                              |
+	#                  Partial
+
+	remove_extent ${device} $partial_ext_dpa $partial_ext_length ""
+	# In-use extents are not released.
+	check_extent_cnt ${region} 1
+
+	# |  ...    |-------------------------------------------------------|
+	# |         |--------|                                              |
+	# |         | (base) |                                              |
+	# |         | daxX.1 |                                              |
+
+	destroy_dax_dev ${dax_dev}
+	check_not_dax_dev ${region} ${dax_dev}
+
+	# |  ...    |-------------------------------------------------------|
+	# |         |--------|                                              |
+	# |         | (base) |                                              |
+
+	# Partial release event re-targets the whole containing region_extent.
+	check_extent_cnt ${region} 1
+	remove_extent ${device} $partial_ext_dpa $partial_ext_length ""
+	# |         |    |---|                                              |
+	#                  Partial
+	check_extent_cnt ${region} 0
+
+	# |  ...    |-------------------------------------------------------|
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_multiple_extent_remove ()
+{
+	# Per-tag-group release: a release event whose DPA range straddles
+	# multiple region_extents in the same tag group should target them
+	# atomically.  Set up two extents in one tagged More-chain.
+	echo ""
+	echo "Test: per-group release event covering two extents"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+
+	# | 2G non- |      DC region                                        |
+	# |  DC cap |                                                       |
+	# |  ...    |-------------------------------------------------------|
+
+	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a" 1
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	# |  ...    |-------------------------------------------------------|
+	# |         |--------|          |-------------------|               |
+	# |         | (base) |          | (pre)-existing    |   tag_a       |
+
+	check_extent_cnt ${region} 2
+	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
+
+	# |         | daxX.1 |          | daxX.1            |   tag_a       |
+
+	# A release event addressed at any DPA inside the tag group releases
+	# the whole group atomically.  Use base_ext_dpa so the kernel finds
+	# the tag group's region_extent on that DPA.
+	echo "Issuing tag-group release at $base_ext_dpa"
+	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+
+	# In-use extents are not released (dax-layer EBUSY defer).
+	check_extent_cnt ${region} 2
+
+	destroy_dax_dev ${dax_dev}
+	check_not_dax_dev ${region} ${dax_dev}
+
+	# |  ...    |-------------------------------------------------------|
+	# |         |--------|          |-------------------|               |
+	# |         | (base) |          | (pre)-existing    |               |
+
+	# Now that the dax dev is gone, re-issue the release(s) for each
+	# extent.  Each release event targets the containing region_extent.
+	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 0
+
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_destroy_region_without_extent_removal ()
+{
+	echo ""
+	echo "Test: Destroy region without extent removal"
+	echo ""
+	
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	inject_extent ${device} $base_ext_dpa $base_ext_length ""
+	check_extent_cnt ${region} 2
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_destroy_with_extent_and_dax ()
+{
+	echo ""
+	echo "Test: Destroy region with extents and dax devices"
+	echo ""
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+	check_extent_cnt ${region} 0
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	
+	# | 2G non- |      DC region                                        |
+	# |  DC cap |                                                       |
+	# |  ...    |-------------------------------------------------------|
+	# |         |                   |----------|                        |
+	# |         |                   |(pre-ext) |                        |
+
+	check_extent_cnt ${region} 1
+	dax_dev=$(create_dax_dev_with_uuid ${region} "0")
+	# |         |                   |<dax_dev> |                        |
+	check_dax_dev ${dax_dev} ${pre_ext_length}
+	destroy_region ${region}
+	check_not_region ${region}
+
+	# | 2G non- |      DC region                                        |
+	# |  DC cap |                                                       |
+	# |  ...    |-------------------------------------------------------|
+	# |         |                                                       |
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+	check_extent_cnt ${region} 0
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_dax_device_ops ()
+{
+	echo ""
+	echo "Test: Fail sparse dax dev creation without space"
+	echo ""
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
+
+	# | 2G non- |      DC region                                        |
+	# |  DC cap |                                                       |
+	# |  ...    |-------------------------------------------------------|
+	# |         |                   |-------------------|               |
+	# |         |                   | (pre)-existing    |               |
+
+	check_extent_cnt ${region} 1
+
+	# |         |                   | daxX.1            |               |
+
+	dax_dev=$(create_dax_dev_with_uuid ${region} "0")
+	check_dax_dev ${dax_dev} $pre_ext_length
+	# No more untagged dax_resource avail: untagged claim returns -ENOENT
+	fail_create_dax_dev_with_uuid ${region} "0"
+	# Plain create-device on sparse: size grow is -EOPNOTSUPP
+	fail_create_dax_dev ${region}
+
+	# DC dax devices cannot be resized to a non-zero size.  The
+	# kernel rejects any size_store with -EOPNOTSUPP unless size == 0,
+	# in which case dev_dax_shrink releases every range -- the same path
+	# `daxctl destroy-device` takes.
+	echo ""
+	echo "Test: Resize of sparse dax device to non-zero is rejected"
+	echo ""
+	half=$(($pre_ext_length / 2))
+	fail_resize_dax_dev ${dax_dev} $half
+	check_dax_dev ${dax_dev} $pre_ext_length
+
+	# Destroy (size=0) is the only valid resize.  After it the
+	# dax_resource has avail again, so --uuid "0" can claim it back
+	# into a new dax device.
+	# |         |                   | daxX.2            |               |
+	echo ""
+	echo "Test: Destroy (size=0) of sparse dax device releases the resource"
+	echo ""
+	destroy_dax_dev ${dax_dev}
+	check_not_dax_dev ${region} ${dax_dev}
+	dax_dev2=$(create_dax_dev_with_uuid ${region} "0")
+	check_dax_dev ${dax_dev2} $pre_ext_length
+
+	destroy_region ${region}
+	check_not_region ${region}
+
+	# Multi-extent device must come from a single tagged group; cross-
+	# event untagged adds land in independent dax_resources and cannot
+	# be claimed as one dax device.  The no-resize rule applies equally
+	# across the tagged group.
+	echo ""
+	echo "Test: Resize of tagged multi-extent dax device is rejected"
+	echo ""
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a" 1
+	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+
+	# | 2G non- |      DC region                                        |
+	# |  DC cap |                                                       |
+	# |  ...    |-------------------------------------------------------|
+	# |         |--------|          |-------------------|               |
+	# |         | (base) |          | (pre)-existing    |    tag_a      |
+
+	check_extent_cnt ${region} 2
+	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
+	ext_sum_length="$(($base_ext_length + $pre_ext_length))"
+	check_dax_dev ${dax_dev} $ext_sum_length
+
+	# Any non-zero resize (here a shrink to 32M, which would sit inside
+	# the first extent of the group) is rejected.
+	fail_resize_dax_dev ${dax_dev} 33554432 # 32MB
+	check_dax_dev ${dax_dev} $ext_sum_length
+
+	# Only size=0 / destroy is valid for the tagged group too.
+	destroy_dax_dev ${dax_dev}
+	check_not_dax_dev ${region} ${dax_dev}
+
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_reject_overlapping ()
+{
+	echo ""
+	echo "Test: Rejecting overlapping extents"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
+
+	# | 2G non- |      DC region                                        |
+	# |  DC cap |                                                       |
+	# |  ...    |-------------------------------------------------------|
+	# |         |                   |-------------------|               |
+	# |         |                   | (pre)-existing    |               |
+
+	check_extent_cnt ${region} 1
+
+	# Attempt overlapping extent: start halfway through pre_ext, same
+	# length so it straddles the end of pre_ext.
+	#
+	# |         |                   |---------|---------|               |
+	# |         |                   |(pre-ext)| overlap |               |
+
+	partial_ext_dpa="$(($pre_ext_dpa + ($pre_ext_length / 2)))"
+	partial_ext_length=$pre_ext_length
+	inject_extent ${device} $partial_ext_dpa $partial_ext_length ""
+
+	# Should only see the original ext
+	check_extent_cnt ${region} 1
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_two_regions()
+{
+	echo ""
+	echo "Test: create 2 regions in the same DC partition"
+	echo ""
+	region_size=$(($dra_size / 2))
+	region=$(create_dcd_region ${mem} ${decoder} ${region_size})
+	check_region ${region} ${region_size}
+	
+	region_two=$(create_dcd_region ${mem} ${decoder} ${region_size})
+	check_region ${region_two} ${region_size}
+	
+	destroy_region ${region_two}
+	check_not_region ${region_two}
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_more_bit()
+{
+	echo ""
+	echo "Test: More bit"
+	echo ""
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length "" 1
+	# More bit should hold off surfacing extent until the more bit is 0
+	check_extent_cnt ${region} 0
+	inject_extent ${device} $base_ext_dpa $base_ext_length ""
+	check_extent_cnt ${region} 2
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_driver_tear_down()
+{
+	echo ""
+	echo "Test: driver remove tear down"
+	echo ""
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
+	dax_dev=$(create_dax_dev_with_uuid ${region} "0")
+	# remove driver releases extents
+	modprobe -r dax_cxl
+	check_extent_cnt ${region} 0
+}
+
+test_driver_bring_up()
+{
+	# leave region up, driver removed.
+	echo ""
+	echo "Test: no driver inject ok"
+	echo ""
+	check_region ${region} ${dra_size}
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	check_extent_cnt ${region} 1
+
+	modprobe dax_cxl
+	check_extent_cnt ${region} 1
+
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_driver_reload()
+{
+	test_driver_tear_down
+	test_driver_bring_up
+}
+
+# Verify the dax dev's uuid sysfs attribute reflects the claim source:
+# "0" for untagged seed/claim, the tag string for tagged claims.
+test_uuid_show()
+{
+	echo ""
+	echo "Test: dax dev uuid attribute reflects the claim source"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+
+	# Pre-claim the seed device's uuid (before any extent injected)
+	# must read back as "0".  -i includes the idle seed at size 0.
+	seed=$($DAXCTL list -r ${region} -D -i | jq -r '.[].chardev')
+	check_dax_uuid ${seed} "0"
+
+	# Untagged extent + claim: uuid stays "0"
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	dax_u=$(create_dax_dev_with_uuid ${region} "0")
+	check_dax_uuid ${dax_u} "0"
+
+	# Tagged extent + claim: uuid reads back as the tag
+	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+	dax_t=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
+	check_dax_uuid ${dax_t} "$test_tag_a"
+
+	destroy_dax_dev ${dax_u}
+	destroy_dax_dev ${dax_t}
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
+	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+# --uuid <unknown> on a sparse region must return -ENOENT before any
+# size is committed.  The region's available_size must be unchanged and
+# the underlying extent must still be claimable via its real tag.
+test_uuid_no_match_seed_intact()
+{
+	echo ""
+	echo "Test: --uuid mismatch does not consume any extent space"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 1
+
+	# Capture region available size before the failed claim
+	avail_before=$($DAXCTL list -r ${region} | jq -r '.[].available_size')
+
+	fail_create_dax_dev_with_uuid ${region} "$unknown_tag"
+
+	avail_after=$($DAXCTL list -r ${region} | jq -r '.[].available_size')
+	if [ "$avail_before" != "$avail_after" ]; then
+		echo "FAIL avail size changed by unmatched --uuid: $avail_before -> $avail_after"
+		err "$LINENO"
+	fi
+
+	# The real tag still claims successfully
+	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
+	check_dax_dev ${dax_dev} $pre_ext_length
+
+	destroy_dax_dev ${dax_dev}
+	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+# A second tagged Add-event reusing a previously committed tag must be
+# dropped by the cross-More uniqueness gate (firmware-bug path).  Skipped
+# for the null UUID.
+test_cross_more_uniqueness()
+{
+	echo ""
+	echo "Test: cross-More uniqueness drops second event with same tag"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+
+	# First Add-event (single extent) commits tag_a at base_ext_dpa.
+	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 1
+
+	# Second Add-event reuses tag_a at a different DPA: must be dropped.
+	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
+	check_extent_cnt ${region} 1
+
+	# The original tag_a allocation is still usable.
+	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
+	check_dax_dev ${dax_dev} $base_ext_length
+
+	destroy_dax_dev ${dax_dev}
+	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+# Sharable-partition test: two extents in one tagged More-chain with
+# device-stamped seq 1..2.  Uses the sharable memdev (serial 0xDCDC).
+test_shared_extent_inject()
+{
+	echo ""
+	echo "Test: shared extent inject on sharable partition"
+	echo ""
+
+	region=$(create_dcd_region ${sharable_mem} ${sharable_decoder})
+	check_region ${region} ${sharable_dra_size}
+
+	inject_shared_extent ${sharable_device} $base_ext_dpa $base_ext_length \
+		"$test_tag_b" 1 1
+	check_extent_cnt ${region} 0
+	inject_shared_extent ${sharable_device} $pre_ext_dpa $pre_ext_length \
+		"$test_tag_b" "" 2
+	check_extent_cnt ${region} 2
+
+	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_b")
+	expected=$(($base_ext_length + $pre_ext_length))
+	check_dax_dev ${dax_dev} $expected
+
+	destroy_dax_dev ${dax_dev}
+	remove_extent ${sharable_device} $base_ext_dpa $base_ext_length "$test_tag_b"
+	remove_extent ${sharable_device} $pre_ext_dpa $pre_ext_length "$test_tag_b"
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+# Sharable extents must arrive with a dense 1..n shared_extn_seq.  A gap
+# (1, 3) lets the cxl side accept the extents but uuid_claim_tagged
+# refuses the group on its density check.  Uses the sharable memdev.
+test_seq_integrity_gap()
+{
+	echo ""
+	echo "Test: sharable extents with seq gap (1,3) refused on claim"
+	echo ""
+
+	region=$(create_dcd_region ${sharable_mem} ${sharable_decoder})
+	check_region ${region} ${sharable_dra_size}
+
+	inject_shared_extent ${sharable_device} $base_ext_dpa $base_ext_length \
+		"$test_tag_b" 1 1
+	check_extent_cnt ${region} 0
+	inject_shared_extent ${sharable_device} $pre_ext_dpa $pre_ext_length \
+		"$test_tag_b" "" 3
+
+	if [ "$(jq -r '.[].extents | length' <($CXL list -r ${region} -N))" = "2" ]; then
+		fail_create_dax_dev_with_uuid ${region} "$test_tag_b"
+		remove_extent ${sharable_device} $base_ext_dpa $base_ext_length "$test_tag_b"
+		remove_extent ${sharable_device} $pre_ext_dpa $pre_ext_length "$test_tag_b"
+	fi
+	check_extent_cnt ${region} 0
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+# CXL_DCD_EXTENT_ALIGN is 2M; an extent that is not 2M-aligned must drop
+# the whole group.  Inject a 64M extent at a 1M-offset DPA.
+test_alignment_rejection()
+{
+	echo ""
+	echo "Test: misaligned extent drops the group"
+	echo ""
+
+	region=$(create_dcd_region ${mem} ${decoder})
+	check_region ${region} ${dra_size}
+
+	# 1M past base — not 2M aligned
+	mis_dpa=$(($base_ext_dpa + 0x100000))
+	inject_extent ${device} $mis_dpa $base_ext_length ""
+	check_extent_cnt ${region} 0
+
+	destroy_region ${region}
+	check_not_region ${region}
+}
+
+test_event_reporting()
+{
+	# Test event reporting
+	# results expected
+	num_dcd_events_expected=2
+
+	echo "Test: Prep event trace"
+	echo "" > /sys/kernel/tracing/trace
+	echo 1 > /sys/kernel/tracing/events/cxl/enable
+	echo 1 > /sys/kernel/tracing/tracing_on
+
+	inject_extent ${device} $base_ext_dpa $base_ext_length ""
+	remove_extent ${device} $base_ext_dpa $base_ext_length
+
+	echo 0 > /sys/kernel/tracing/tracing_on
+
+	echo "Test: Events seen"
+	trace_out=$(cat /sys/kernel/tracing/trace)
+
+	# Look for DCD events
+	num_dcd_events=$(grep -c "cxl_dynamic_capacity" <<< "${trace_out}")
+	echo "     LOG     (Expected) : (Found)"
+	echo "     DCD events    ($num_dcd_events_expected) : $num_dcd_events"
+
+	if [ "$num_dcd_events" -ne $num_dcd_events_expected ]; then
+		err "$LINENO"
+	fi
+}
+
+
+# ========================================================================
+# main()
+# ========================================================================
+
+modprobe -r cxl_test
+modprobe cxl_test
+
+# The mock stamps a single cxl_mem instance with this serial (0xDCDC).
+# That memdev's DC partition is advertised as sharable in CDAT and is
+# the only one that can host the shared-extent tests.  All other DCD
+# memdevs stay non-sharable and host the rest of the suite.
+MOCK_DC_SHARABLE_SERIAL=56540
+
+readarray -t memdevs < <("$CXL" list -b cxl_test -Mi | jq -r '.[].memdev')
+
+sharable_mem=""
+sharable_decoder=""
+sharable_bus=""
+sharable_device=""
+sharable_dra_size=""
+
+for cand in ${memdevs[@]}; do
+	cand_dra=$($CXL list -m $cand | jq -r '.[].dynamic_ram_a_size')
+	if [ "$cand_dra" == "null" ]; then
+		continue
+	fi
+	cand_decoder=$($CXL list -b cxl_test -D -d root -m "$cand" |
+		  jq -r ".[] |
+		  select(.volatile_capable == true) |
+		  select(.nr_targets == 1) |
+		  select(.max_available_extent >= ${cand_dra}) |
+		  .decoder")
+	if [[ -z "$cand_decoder" ]]; then
+		continue
+	fi
+	cand_serial=$($CXL list -m $cand | jq -r '.[].serial')
+	cand_bus=`"$CXL" list -b cxl_test -m ${cand} | jq -r '.[].bus'`
+	cand_device=$($CXL list -m $cand | jq -r '.[].host')
+
+	if [ "$cand_serial" == "$MOCK_DC_SHARABLE_SERIAL" ]; then
+		if [ -z "$sharable_mem" ]; then
+			sharable_mem="$cand"
+			sharable_decoder="$cand_decoder"
+			sharable_bus="$cand_bus"
+			sharable_device="$cand_device"
+			sharable_dra_size="$cand_dra"
+		fi
+	else
+		if [ -z "$mem" ]; then
+			mem="$cand"
+			decoder="$cand_decoder"
+			bus="$cand_bus"
+			device="$cand_device"
+			dra_size="$cand_dra"
+		fi
+	fi
+
+	if [ -n "$mem" ] && [ -n "$sharable_mem" ]; then
+		break
+	fi
+done
+
+echo "TEST: non-sharable bus:${bus} decoder:${decoder} mem:${mem} device:${device} size:${dra_size}"
+echo "TEST: sharable     bus:${sharable_bus} decoder:${sharable_decoder} mem:${sharable_mem} device:${sharable_device} size:${sharable_dra_size}"
+
+if [ "$decoder" == "" ] || [ "$device" == "" ] || [ "$dra_size" == "" ]; then
+	echo "No non-sharable mem device/decoder found with DCD support"
+	exit 77
+fi
+
+if [ "$sharable_mem" == "" ]; then
+	echo "No sharable mem device found (mock did not stamp MOCK_DC_SHARABLE_SERIAL)"
+	exit 77
+fi
+
+# testing pre existing extents must be called first as the extents were created
+# by cxl-test being loaded
+test_pre_existing_extents
+test_remove_extent_under_dax_device
+test_create_dax_dev_spanning_two_extents
+test_inject_tag_support
+test_uuid_no_match
+test_uuid_no_match_seed_intact
+test_uuid_aggregation
+test_uuid_show
+# These two run on the sharable memdev (serial $MOCK_DC_SHARABLE_SERIAL).
+test_shared_extent_inject
+test_seq_integrity_gap
+test_cross_more_uniqueness
+test_alignment_rejection
+test_partial_extent_remove
+test_multiple_extent_remove
+test_destroy_region_without_extent_removal
+test_destroy_with_extent_and_dax
+test_dax_device_ops
+test_reject_overlapping
+test_two_regions
+test_more_bit
+test_driver_reload
+test_event_reporting
+
+modprobe -r cxl_test
+
+check_dmesg "$LINENO"
+
+exit 0
diff --git a/test/meson.build b/test/meson.build
index e0e2193..cd06bf8 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -171,6 +171,7 @@ cxl_translate = find_program('cxl-translate.sh')
 cxl_elc = find_program('cxl-elc.sh')
 cxl_dax_hmem = find_program('cxl-dax-hmem.sh')
 cxl_region_replay = find_program('cxl-region-replay.sh')
+cxl_dcd = find_program('cxl-dcd.sh')
 
 tests = [
   [ 'libndctl',               libndctl,		  'ndctl' ],
@@ -207,6 +208,7 @@ tests = [
   [ 'cxl-elc.sh',             cxl_elc,            'cxl'   ],
   [ 'cxl-dax-hmem.sh',        cxl_dax_hmem,       'cxl'   ],
   [ 'cxl-region-replay.sh',   cxl_region_replay,  'cxl'   ],
+  [ 'cxl-dcd.sh',             cxl_dcd,            'cxl'   ],
 ]
 
 if get_option('destructive').enabled()
-- 
2.43.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli
  2026-05-23  9:50 [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Anisa Su
                   ` (6 preceding siblings ...)
  2026-05-23  9:50 ` [PATCH v6 7/7] cxl/test: Add Dynamic Capacity tests Anisa Su
@ 2026-06-05  5:43 ` Alison Schofield
  2026-06-08  8:11   ` Anisa Su
  2026-06-17  7:10   ` Alison Schofield
  7 siblings, 2 replies; 31+ messages in thread
From: Alison Schofield @ 2026-06-05  5:43 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, linux-kernel, nvdimm, Dan Williams, Jonathan Cameron,
	Davidlohr Bueso, Dave Jiang, Ira Weiny, John Groves,
	Gregory Price, Anisa Su

On Sat, May 23, 2026 at 02:50:35AM -0700, Anisa Su wrote:
> CXL Dynamic Capacity Device (DCD) support has continued to evolve in the
> upstream kernel since Ira's v5 posting [1].  The kernel side has settled
> on a uuid-driven claim model for sparse DAX devices: dax_resources carry
> the tag delivered with each extent, and userspace selects which ones to
> claim by writing a UUID to the dax device's sysfs 'uuid' attribute (or
> "0" to claim a single untagged resource).  Size on a sparse region is
> determined by the claim, not requested up-front.
> 
> This series brings cxl-cli and daxctl in line with that model and
> extends cxl_test to exercise the new paths end-to-end.

Hi Anisa,

I just now picked this up with the kernel side and took it for a quick
test drive. Based on what's been touched, first meaningful finding is
all the DAX unit tests pass, and then for CXL unit tests, all but these
2 pass: cxl-security.sh and cxl-dcd.sh

Please let me know if there are known problems with either of those
before I explore further.

Question below about dependency....

> 
> The corresponding kernel patchset is here:
> https://lore.kernel.org/linux-cxl/cover.1779528761.git.anisa.su@samsung.com/T/#t
> 
> Picked up unchanged from v5 (Ira):
> 
>   libcxl: Add Dynamic RAM A partition mode support
>   cxl/region: Add cxl-cli support for dynamic RAM A
>   libcxl: Add extent functionality to DC regions
>   cxl/region: Add extent output to region query
> 
> New in v6:
> 
>   daxctl: Add --uuid option to create-device for DC DAX regions
>     - Plumbs writes to the new dax 'uuid' sysfs attribute through a new
>       daxctl_dev_set_uuid() helper (LIBDAXCTL_11).
>     - --uuid is mutually exclusive with --size; pass "0" to claim a
>       single untagged dax_resource.  An unmatched UUID surfaces ENOENT
>       from the kernel and leaves the device at size 0.
>     - Documents the option in the man page.
> 
>   cxl/test: Add Dynamic Capacity tests (rewritten on top of Ira's
>   original patch to track the post-redesign kernel)
>     - Routes untagged claims via --uuid "0" so daxctl exercises the
>       kernel uuid_store path; tagged claims use real UUID strings.
>     - Asserts that for DC regions, size-grow returns -EOPNOTSUPP (real grow is
>       --uuid only) and that tag reuse across More-chains is rejected
>       by the cross-More uniqueness gate.
>     - Adds coverage for the new validators: test_uuid_no_match,
>       test_uuid_no_match_seed_intact, test_uuid_show,
>       test_cross_more_uniqueness, test_alignment_rejection.
>     - Sharable-partition coverage (test_shared_extent_inject,
>       test_seq_integrity_gap) is routed at runtime to a dedicated mock
>       memdev that tools/testing/cxl stamps with serial 0xDCDC, so a
>       single cxl_test module load exercises both regimes.
>     - Localizes positional-arg assignments in every helper so functions
>       no longer clobber caller globals (the previous behavior leaked
>       the sharable memdev into later tests).
>     - test_reject_overlapping arithmetic now lands an actual overlap
>       inside the DC region (the prior math landed past the end).
> 
> Depends on the kernel DCD/sparse-DAX series; without it the new tests
> will skip and 'cxl list -r N -Nu' will simply report no extents.

What is this dependency- DCD/sparse-DAX series ?

> 
> The branch is also available at:
> 
>   https://github.com/anisa-su993/anisa-ndctl/tree/dcd-2026-05-21
> 
> Based on pmem/pending commit:
> 
>   bbd403a test/cxl-sanitize: avoid sanitize submit/wait race
> 
> [1] https://lore.kernel.org/nvdimm/20250413-dcd-region2-v5-0-fbd753a2e0e8@intel.com/
> 
> ---
> Changes in v6:
> - anisa: New patch — daxctl --uuid option + daxctl_dev_set_uuid() helper
> - anisa: Rewrite cxl/test DCD tests against the post-redesign kernel
>          (uuid sysfs claim, tag-group atomic release, cross-More
>          uniqueness, alignment rejection, DC size-grow refusal)
> - anisa: Rebase onto bbd403a (pmem/pending)
> - Link to v5: https://lore.kernel.org/nvdimm/20250413-dcd-region2-v5-0-fbd753a2e0e8@intel.com/
> 
> Changes in v5:
> - iweiny: Adjust all code to view only the dynamic RAM A partition
> - Alison: s/tag/uuid/ in region query extent output
> - Link to v4: https://patch.msgid.link/20241214-dcd-region2-v4-0-36550a97f8e2@intel.com
> 
> Anisa Su (1):
>   daxctl: Add --uuid option to create-device for DC regions
> 
> Ira Weiny (6):
>   ndctl: Dynamic Capacity additions for cxl-cli
>   libcxl: Add Dynamic RAM A partition mode support
>   cxl/region: Add cxl-cli support for dynamic RAM A
>   libcxl: Add extent functionality to DC regions
>   cxl/region: Add extent output to region query
>   cxl/test: Add Dynamic Capacity tests
> 
>  Documentation/cxl/cxl-list.txt                |   29 +
>  Documentation/cxl/lib/libcxl.txt              |   33 +-
>  Documentation/daxctl/daxctl-create-device.txt |   12 +
>  cxl/filter.h                                  |    3 +
>  cxl/json.c                                    |   67 +
>  cxl/json.h                                    |    3 +
>  cxl/lib/libcxl.c                              |  181 +++
>  cxl/lib/libcxl.sym                            |    9 +
>  cxl/lib/private.h                             |   14 +
>  cxl/libcxl.h                                  |   21 +-
>  cxl/list.c                                    |    3 +
>  cxl/memdev.c                                  |    4 +-
>  cxl/region.c                                  |   27 +-
>  daxctl/device.c                               |   72 +-
>  daxctl/lib/libdaxctl.c                        |   44 +
>  daxctl/lib/libdaxctl.sym                      |    5 +
>  daxctl/libdaxctl.h                            |    1 +
>  test/cxl-dcd.sh                               | 1267 +++++++++++++++++
>  test/meson.build                              |    2 +
>  util/json.h                                   |    1 +
>  20 files changed, 1771 insertions(+), 27 deletions(-)
>  create mode 100644 test/cxl-dcd.sh
> 
> 
> base-commit: bbd403a03fa2a1551c1a10bbf78f32027c718758
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli
  2026-06-05  5:43 ` [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Alison Schofield
@ 2026-06-08  8:11   ` Anisa Su
  2026-06-17  7:10   ` Alison Schofield
  1 sibling, 0 replies; 31+ messages in thread
From: Anisa Su @ 2026-06-08  8:11 UTC (permalink / raw)
  To: Alison Schofield
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Dave Jiang, Ira Weiny,
	John Groves, Gregory Price

On Thu, Jun 04, 2026 at 10:43:10PM -0700, Alison Schofield wrote:
> On Sat, May 23, 2026 at 02:50:35AM -0700, Anisa Su wrote:
> > CXL Dynamic Capacity Device (DCD) support has continued to evolve in the
> > upstream kernel since Ira's v5 posting [1].  The kernel side has settled
> > on a uuid-driven claim model for sparse DAX devices: dax_resources carry
> > the tag delivered with each extent, and userspace selects which ones to
> > claim by writing a UUID to the dax device's sysfs 'uuid' attribute (or
> > "0" to claim a single untagged resource).  Size on a sparse region is
> > determined by the claim, not requested up-front.
> > 
> > This series brings cxl-cli and daxctl in line with that model and
> > extends cxl_test to exercise the new paths end-to-end.
> 
> Hi Anisa,
> 
> I just now picked this up with the kernel side and took it for a quick
> test drive. Based on what's been touched, first meaningful finding is
> all the DAX unit tests pass, and then for CXL unit tests, all but these
> 2 pass: cxl-security.sh and cxl-dcd.sh
> 
> Please let me know if there are known problems with either of those
> before I explore further.

Oh hmm... I would expect cxl-dcd.sh to pass. Let me know what error you
saw? But it's totally possible that I mucked something up with some last
minute change. Let me try it again on my side...

For cxl-security.sh, sorry I was bad and didn't run all of the other CXL
unit tests. Pin of shame for me ;(◞‸ ◟)

Let me figure out how to get the NDCTL test runner working and it should
not be an issue moving forward
> 
> Question below about dependency....
> 
> > 
> > The corresponding kernel patchset is here:
> > https://lore.kernel.org/linux-cxl/cover.1779528761.git.anisa.su@samsung.com/T/#t
> > 
> > Picked up unchanged from v5 (Ira):
> > 
> >   libcxl: Add Dynamic RAM A partition mode support
> >   cxl/region: Add cxl-cli support for dynamic RAM A
> >   libcxl: Add extent functionality to DC regions
> >   cxl/region: Add extent output to region query
> > 
> > New in v6:
> > 
> >   daxctl: Add --uuid option to create-device for DC DAX regions
> >     - Plumbs writes to the new dax 'uuid' sysfs attribute through a new
> >       daxctl_dev_set_uuid() helper (LIBDAXCTL_11).
> >     - --uuid is mutually exclusive with --size; pass "0" to claim a
> >       single untagged dax_resource.  An unmatched UUID surfaces ENOENT
> >       from the kernel and leaves the device at size 0.
> >     - Documents the option in the man page.
> > 
> >   cxl/test: Add Dynamic Capacity tests (rewritten on top of Ira's
> >   original patch to track the post-redesign kernel)
> >     - Routes untagged claims via --uuid "0" so daxctl exercises the
> >       kernel uuid_store path; tagged claims use real UUID strings.
> >     - Asserts that for DC regions, size-grow returns -EOPNOTSUPP (real grow is
> >       --uuid only) and that tag reuse across More-chains is rejected
> >       by the cross-More uniqueness gate.
> >     - Adds coverage for the new validators: test_uuid_no_match,
> >       test_uuid_no_match_seed_intact, test_uuid_show,
> >       test_cross_more_uniqueness, test_alignment_rejection.
> >     - Sharable-partition coverage (test_shared_extent_inject,
> >       test_seq_integrity_gap) is routed at runtime to a dedicated mock
> >       memdev that tools/testing/cxl stamps with serial 0xDCDC, so a
> >       single cxl_test module load exercises both regimes.
> >     - Localizes positional-arg assignments in every helper so functions
> >       no longer clobber caller globals (the previous behavior leaked
> >       the sharable memdev into later tests).
> >     - test_reject_overlapping arithmetic now lands an actual overlap
> >       inside the DC region (the prior math landed past the end).
> > 
> > Depends on the kernel DCD/sparse-DAX series; without it the new tests
> > will skip and 'cxl list -r N -Nu' will simply report no extents.
> 
> What is this dependency- DCD/sparse-DAX series ?
> 
Sorry, I just meant the kernel support for DCD. Poorly worded here. 'sparse'
DAX is just the terminology for DAX regions backed by DCD.
Well, in the kernel patchset, I renamed it to
"DC DAX" to make the association clearer, but then forgot to reflect it
on this side (𖦹ᯅ_𖦹)

Thanks for testing!
Anisa
> > 
> > The branch is also available at:
> > 
> >   https://github.com/anisa-su993/anisa-ndctl/tree/dcd-2026-05-21
> > 
> > Based on pmem/pending commit:
> > 
> >   bbd403a test/cxl-sanitize: avoid sanitize submit/wait race
> > 
> > [1] https://lore.kernel.org/nvdimm/20250413-dcd-region2-v5-0-fbd753a2e0e8@intel.com/
> > 
> > ---
> > Changes in v6:
> > - anisa: New patch — daxctl --uuid option + daxctl_dev_set_uuid() helper
> > - anisa: Rewrite cxl/test DCD tests against the post-redesign kernel
> >          (uuid sysfs claim, tag-group atomic release, cross-More
> >          uniqueness, alignment rejection, DC size-grow refusal)
> > - anisa: Rebase onto bbd403a (pmem/pending)
> > - Link to v5: https://lore.kernel.org/nvdimm/20250413-dcd-region2-v5-0-fbd753a2e0e8@intel.com/
> > 
> > Changes in v5:
> > - iweiny: Adjust all code to view only the dynamic RAM A partition
> > - Alison: s/tag/uuid/ in region query extent output
> > - Link to v4: https://patch.msgid.link/20241214-dcd-region2-v4-0-36550a97f8e2@intel.com
> > 
> > Anisa Su (1):
> >   daxctl: Add --uuid option to create-device for DC regions
> > 
> > Ira Weiny (6):
> >   ndctl: Dynamic Capacity additions for cxl-cli
> >   libcxl: Add Dynamic RAM A partition mode support
> >   cxl/region: Add cxl-cli support for dynamic RAM A
> >   libcxl: Add extent functionality to DC regions
> >   cxl/region: Add extent output to region query
> >   cxl/test: Add Dynamic Capacity tests
> > 
> >  Documentation/cxl/cxl-list.txt                |   29 +
> >  Documentation/cxl/lib/libcxl.txt              |   33 +-
> >  Documentation/daxctl/daxctl-create-device.txt |   12 +
> >  cxl/filter.h                                  |    3 +
> >  cxl/json.c                                    |   67 +
> >  cxl/json.h                                    |    3 +
> >  cxl/lib/libcxl.c                              |  181 +++
> >  cxl/lib/libcxl.sym                            |    9 +
> >  cxl/lib/private.h                             |   14 +
> >  cxl/libcxl.h                                  |   21 +-
> >  cxl/list.c                                    |    3 +
> >  cxl/memdev.c                                  |    4 +-
> >  cxl/region.c                                  |   27 +-
> >  daxctl/device.c                               |   72 +-
> >  daxctl/lib/libdaxctl.c                        |   44 +
> >  daxctl/lib/libdaxctl.sym                      |    5 +
> >  daxctl/libdaxctl.h                            |    1 +
> >  test/cxl-dcd.sh                               | 1267 +++++++++++++++++
> >  test/meson.build                              |    2 +
> >  util/json.h                                   |    1 +
> >  20 files changed, 1771 insertions(+), 27 deletions(-)
> >  create mode 100644 test/cxl-dcd.sh
> > 
> > 
> > base-commit: bbd403a03fa2a1551c1a10bbf78f32027c718758
> > -- 
> > 2.43.0
> > 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 1/7] ndctl: Dynamic Capacity additions for cxl-cli
  2026-05-23  9:50 ` [PATCH v6 1/7] " Anisa Su
@ 2026-06-08 23:18   ` Dave Jiang
  0 siblings, 0 replies; 31+ messages in thread
From: Dave Jiang @ 2026-06-08 23:18 UTC (permalink / raw)
  To: Anisa Su, linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Ira Weiny, Alison Schofield, John Groves, Gregory Price,
	Ira Weiny, Vishal Verma, Jonathan Cameron, Fan Ni,
	Sushant1 Kumar, Dan Williams



On 5/23/26 2:50 AM, Anisa Su wrote:
> From: Ira Weiny <ira.weiny@intel.com>
> 
> This series can be found here:
> 
> 	https://github.com/weiny2/ndctl/tree/dcd-region3-2025-04-13
> 
> CXL Dynamic Capacity Device (DCD) support is being discussed in the
> upstream kernel.  cxl-cli requires modifications to interact with those
> devices.
> 
> A new partition type 'dynamic_ram_a' has been added which cxl-cli
> needs to know about.  Add support for the new decoder type.
> 
> With DCD regions may, or may not, have capacity.  The capacity is
> communicated via extents.  Add region extent query capabilities.
> 
> Add cxl-test support.  cxl-testing allows for quick regression testing
> as well as helping to design the cxl-cli interfaces.
> 
> 
> To: "Alison Schofield" <alison.schofield@intel.com>
> Cc: "Vishal Verma" <vishal.l.verma@intel.com>
> Cc: "Jonathan Cameron" <jonathan.cameron@Huawei.com>
> Cc: "Fan Ni" <fan.ni@samsung.com>
> Cc: "Sushant1 Kumar" <sushant1.kumar@intel.com>
> Cc: "Dan Williams" <dan.j.williams@intel.com>
> Cc: "Dave Jiang" <dave.jiang@intel.com>
> Cc: <linux-cxl@vger.kernel.org>
> Cc: <nvdimm@lists.linux.dev>
> Signed-off-by: Ira Weiny <ira.weiny@intel.com>

Drop this? Seems b4 metadata and not a patch.


> 
> ---
> Changes in v5:
> - iweiny: Adjust all code to view only the dynamic RAM A partition
> - Alison: s/tag/uuid/ in region query extent output
> - Link to v4: https://patch.msgid.link/20241214-dcd-region2-v4-0-36550a97f8e2@intel.com
> 
> --- b4-submit-tracking ---
> # This section is used internally by b4 prep for tracking purposes.
> {
>   "series": {
>     "revision": 5,
>     "change-id": "20241030-dcd-region2-2d0149eb8efd",
>     "prefixes": [],
>     "history": {
>       "v1": [
>         "20241030-dcd-region2-v1-0-04600ba2b48e@intel.com"
>       ],
>       "v2": [
>         "20241104-dcd-region2-v2-0-be057b479eeb@intel.com"
>       ],
>       "v3": [
>         "20241115-dcd-region2-v3-0-585d480ccdab@intel.com"
>       ],
>       "v4": [
>         "20241214-dcd-region2-v4-0-36550a97f8e2@intel.com"
>       ]
>     }
>   }
> }


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 2/7] libcxl: Add Dynamic RAM A partition mode support
  2026-05-23  9:50 ` [PATCH v6 2/7] libcxl: Add Dynamic RAM A partition mode support Anisa Su
@ 2026-06-08 23:19   ` Dave Jiang
  2026-06-10  3:51     ` Richard Cheng
  2026-06-25  9:07     ` Anisa Su
  0 siblings, 2 replies; 31+ messages in thread
From: Dave Jiang @ 2026-06-08 23:19 UTC (permalink / raw)
  To: Anisa Su, linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Ira Weiny, Alison Schofield, John Groves, Gregory Price,
	Ira Weiny



On 5/23/26 2:50 AM, Anisa Su wrote:
> From: Ira Weiny <ira.weiny@intel.com>
> 
> Dynamic capacity partitions are exposed as a singular dynamic ram
> partition.
> 
> Add CXL library support to read this partition information.
> 
> Signed-off-by: Ira Weiny <ira.weiny@intel.com>

Missing Anisa sign off.

Can probably squash this and the next commit so the usage is shown for the reviewer.

DJ

> ---
>  Documentation/cxl/lib/libcxl.txt |  6 +++--
>  cxl/lib/libcxl.c                 | 43 ++++++++++++++++++++++++++++++++
>  cxl/lib/libcxl.sym               |  4 +++
>  cxl/lib/private.h                |  3 +++
>  cxl/libcxl.h                     | 10 +++++++-
>  5 files changed, 63 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
> index 5c3ebd4..9921ac1 100644
> --- a/Documentation/cxl/lib/libcxl.txt
> +++ b/Documentation/cxl/lib/libcxl.txt
> @@ -74,6 +74,7 @@ int cxl_memdev_get_major(struct cxl_memdev *memdev);
>  int cxl_memdev_get_minor(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_dynamic_ram_a_size(struct cxl_memdev *memdev);
>  const char *cxl_memdev_get_firmware_version(struct cxl_memdev *memdev);
>  size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev);
>  int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev);
> @@ -93,8 +94,8 @@ The character device node for command submission can be found by default
>  at /dev/cxl/mem%d, or created with a major / minor returned from
>  cxl_memdev_get_{major,minor}().
>  
> -The 'pmem_size' and 'ram_size' attributes return the current
> -provisioning of DPA (Device Physical Address / local capacity) in the
> +The 'pmem_size', 'ram_size', and 'dynamic_ram_a_size' attributes return the
> +current provisioning of DPA (Device Physical Address / local capacity) in the
>  device.
>  
>  cxl_memdev_get_numa_node() returns the affinitized CPU node number if
> @@ -453,6 +454,7 @@ enum cxl_decoder_mode {
>  	CXL_DECODER_MODE_MIXED,
>  	CXL_DECODER_MODE_PMEM,
>  	CXL_DECODER_MODE_RAM,
> +	CXL_DECODER_MODE_DYNAMIC_RAM_A,
>  };
>  enum cxl_decoder_mode cxl_decoder_get_mode(struct cxl_decoder *decoder);
>  int cxl_decoder_set_mode(struct cxl_decoder *decoder, enum cxl_decoder_mode mode);
> diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
> index e55a7b4..be0bc03 100644
> --- a/cxl/lib/libcxl.c
> +++ b/cxl/lib/libcxl.c
> @@ -501,6 +501,9 @@ CXL_EXPORT bool cxl_region_qos_class_mismatch(struct cxl_region *region)
>  		} else if (region->mode == CXL_DECODER_MODE_PMEM) {
>  			if (root_decoder->qos_class != memdev->pmem_qos_class)
>  				return true;
> +		} else if (region->mode == CXL_DECODER_MODE_DYNAMIC_RAM_A) {
> +			if (root_decoder->qos_class != memdev->dynamic_ram_a_qos_class)
> +				return true;
>  		}
>  	}
>  
> @@ -1426,6 +1429,10 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
>  	if (sysfs_read_attr(ctx, path, buf) == 0)
>  		memdev->ram_size = strtoull(buf, NULL, 0);
>  
> +	sprintf(path, "%s/dynamic_ram_a/size", cxlmem_base);
> +	if (sysfs_read_attr(ctx, path, buf) == 0)
> +		memdev->dynamic_ram_a_size = strtoull(buf, NULL, 0);
> +
>  	sprintf(path, "%s/pmem/qos_class", cxlmem_base);
>  	if (sysfs_read_attr(ctx, path, buf) < 0)
>  		memdev->pmem_qos_class = CXL_QOS_CLASS_NONE;
> @@ -1438,6 +1445,12 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
>  	else
>  		memdev->ram_qos_class = atoi(buf);
>  
> +	sprintf(path, "%s/dynamic_ram_a/qos_class", cxlmem_base);
> +	if (sysfs_read_attr(ctx, path, buf) < 0)
> +		memdev->dynamic_ram_a_qos_class = CXL_QOS_CLASS_NONE;
> +	else
> +		memdev->dynamic_ram_a_qos_class = atoi(buf);
> +
>  	sprintf(path, "%s/payload_max", cxlmem_base);
>  	if (sysfs_read_attr(ctx, path, buf) == 0) {
>  		memdev->payload_max = strtoull(buf, NULL, 0);
> @@ -1685,6 +1698,11 @@ 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_dynamic_ram_a_size(struct cxl_memdev *memdev)
> +{
> +	return memdev->dynamic_ram_a_size;
> +}
> +
>  CXL_EXPORT int cxl_memdev_get_pmem_qos_class(struct cxl_memdev *memdev)
>  {
>  	return memdev->pmem_qos_class;
> @@ -1695,6 +1713,11 @@ CXL_EXPORT int cxl_memdev_get_ram_qos_class(struct cxl_memdev *memdev)
>  	return memdev->ram_qos_class;
>  }
>  
> +CXL_EXPORT int cxl_memdev_get_dynamic_ram_a_qos_class(struct cxl_memdev *memdev)
> +{
> +	return memdev->dynamic_ram_a_qos_class;
> +}
> +
>  CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev)
>  {
>  	return memdev->firmware_version;
> @@ -2465,6 +2488,8 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
>  			decoder->mode = CXL_DECODER_MODE_MIXED;
>  		else if (strcmp(buf, "none") == 0)
>  			decoder->mode = CXL_DECODER_MODE_NONE;
> +		else if (strcmp(buf, "dynamic_ram_a") == 0)
> +			decoder->mode = CXL_DECODER_MODE_DYNAMIC_RAM_A;
>  		else
>  			decoder->mode = CXL_DECODER_MODE_MIXED;
>  	} else
> @@ -2504,6 +2529,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
>  	case CXL_PORT_SWITCH:
>  		decoder->pmem_capable = true;
>  		decoder->volatile_capable = true;
> +		decoder->dynamic_ram_a_capable = true;
>  		decoder->mem_capable = true;
>  		decoder->accelmem_capable = true;
>  		sprintf(path, "%s/locked", cxldecoder_base);
> @@ -2528,6 +2554,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
>  			{ "cap_type3", &decoder->mem_capable },
>  			{ "cap_ram", &decoder->volatile_capable },
>  			{ "cap_pmem", &decoder->pmem_capable },
> +			{ "cap_dynamic_ram_a", &decoder->dynamic_ram_a_capable },
>  			{ "locked", &decoder->locked },
>  		};
>  
> @@ -2778,6 +2805,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_DYNAMIC_RAM_A:
> +		sprintf(buf, "dynamic_ram_a");
> +		break;
>  	default:
>  		err(ctx, "%s: unsupported mode: %d\n",
>  		    cxl_decoder_get_devname(decoder), mode);
> @@ -2829,6 +2859,11 @@ CXL_EXPORT bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder)
>  	return decoder->volatile_capable;
>  }
>  
> +CXL_EXPORT bool cxl_decoder_is_dynamic_ram_a_capable(struct cxl_decoder *decoder)
> +{
> +	return decoder->dynamic_ram_a_capable;
> +}
> +
>  CXL_EXPORT bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder)
>  {
>  	return decoder->mem_capable;
> @@ -2903,6 +2938,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_DYNAMIC_RAM_A)
> +		sprintf(path, "%s/create_dynamic_ram_a_region", decoder->dev_path);
>  
>  	rc = sysfs_read_attr(ctx, path, buf);
>  	if (rc < 0) {
> @@ -2954,6 +2991,12 @@ cxl_decoder_create_ram_region(struct cxl_decoder *decoder)
>  	return cxl_decoder_create_region(decoder, CXL_DECODER_MODE_RAM);
>  }
>  
> +CXL_EXPORT struct cxl_region *
> +cxl_decoder_create_dynamic_ram_a_region(struct cxl_decoder *decoder)
> +{
> +	return cxl_decoder_create_region(decoder, CXL_DECODER_MODE_DYNAMIC_RAM_A);
> +}
> +
>  CXL_EXPORT int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder)
>  {
>  	return decoder->nr_targets;
> diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> index ed4429f..258bdd3 100644
> --- a/cxl/lib/libcxl.sym
> +++ b/cxl/lib/libcxl.sym
> @@ -294,6 +294,10 @@ global:
>  	cxl_memdev_get_fwctl;
>  	cxl_fwctl_get_major;
>  	cxl_fwctl_get_minor;
> +	cxl_memdev_get_dynamic_ram_a_size;
> +	cxl_memdev_get_dynamic_ram_a_qos_class;
> +	cxl_decoder_is_dynamic_ram_a_capable;
> +	cxl_decoder_create_dynamic_ram_a_region;
>  } LIBECXL_8;
>  
>  LIBCXL_10 {
> diff --git a/cxl/lib/private.h b/cxl/lib/private.h
> index d2d71fc..37b7b06 100644
> --- a/cxl/lib/private.h
> +++ b/cxl/lib/private.h
> @@ -52,8 +52,10 @@ struct cxl_memdev {
>  	struct list_node list;
>  	unsigned long long pmem_size;
>  	unsigned long long ram_size;
> +	unsigned long long dynamic_ram_a_size;
>  	int ram_qos_class;
>  	int pmem_qos_class;
> +	int dynamic_ram_a_qos_class;
>  	int payload_max;
>  	size_t lsa_size;
>  	struct kmod_module *module;
> @@ -159,6 +161,7 @@ struct cxl_decoder {
>  	unsigned int interleave_granularity;
>  	bool pmem_capable;
>  	bool volatile_capable;
> +	bool dynamic_ram_a_capable;
>  	bool mem_capable;
>  	bool accelmem_capable;
>  	bool locked;
> diff --git a/cxl/libcxl.h b/cxl/libcxl.h
> index e91af90..fd41122 100644
> --- a/cxl/libcxl.h
> +++ b/cxl/libcxl.h
> @@ -75,8 +75,10 @@ struct cxl_fwctl *cxl_memdev_get_fwctl(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_dynamic_ram_a_size(struct cxl_memdev *memdev);
>  int cxl_memdev_get_pmem_qos_class(struct cxl_memdev *memdev);
>  int cxl_memdev_get_ram_qos_class(struct cxl_memdev *memdev);
> +int cxl_memdev_get_dynamic_ram_a_qos_class(struct cxl_memdev *memdev);
>  const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
>  bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev);
>  size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev);
> @@ -210,6 +212,7 @@ enum cxl_decoder_mode {
>  	CXL_DECODER_MODE_MIXED,
>  	CXL_DECODER_MODE_PMEM,
>  	CXL_DECODER_MODE_RAM,
> +	CXL_DECODER_MODE_DYNAMIC_RAM_A,
>  };
>  
>  static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode)
> @@ -219,9 +222,10 @@ static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode)
>  		[CXL_DECODER_MODE_MIXED] = "mixed",
>  		[CXL_DECODER_MODE_PMEM] = "pmem",
>  		[CXL_DECODER_MODE_RAM] = "ram",
> +		[CXL_DECODER_MODE_DYNAMIC_RAM_A] = "dynamic_ram_a",
>  	};
>  
> -	if (mode < CXL_DECODER_MODE_NONE || mode > CXL_DECODER_MODE_RAM)
> +	if (mode < CXL_DECODER_MODE_NONE || mode > CXL_DECODER_MODE_DYNAMIC_RAM_A)
>  		mode = CXL_DECODER_MODE_NONE;
>  	return names[mode];
>  }
> @@ -235,6 +239,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, "dynamic_ram_a") == 0)
> +		return CXL_DECODER_MODE_DYNAMIC_RAM_A;
>  	return CXL_DECODER_MODE_NONE;
>  }
>  
> @@ -264,6 +270,7 @@ cxl_decoder_get_target_type(struct cxl_decoder *decoder);
>  bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder);
>  bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder);
>  bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder);
> +bool cxl_decoder_is_dynamic_ram_a_capable(struct cxl_decoder *decoder);
>  bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder);
>  bool cxl_decoder_is_locked(struct cxl_decoder *decoder);
>  unsigned int
> @@ -272,6 +279,7 @@ 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_pmem_region(struct cxl_decoder *decoder);
>  struct cxl_region *cxl_decoder_create_ram_region(struct cxl_decoder *decoder);
> +struct cxl_region *cxl_decoder_create_dynamic_ram_a_region(struct cxl_decoder *decoder);
>  struct cxl_decoder *cxl_decoder_get_by_name(struct cxl_ctx *ctx,
>  					    const char *ident);
>  struct cxl_memdev *cxl_decoder_get_memdev(struct cxl_decoder *decoder);


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 3/7] cxl/region: Add cxl-cli support for dynamic RAM A
  2026-05-23  9:50 ` [PATCH v6 3/7] cxl/region: Add cxl-cli support for dynamic RAM A Anisa Su
@ 2026-06-08 23:58   ` Dave Jiang
  0 siblings, 0 replies; 31+ messages in thread
From: Dave Jiang @ 2026-06-08 23:58 UTC (permalink / raw)
  To: Anisa Su, linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Ira Weiny, Alison Schofield, John Groves, Gregory Price,
	Ira Weiny



On 5/23/26 2:50 AM, Anisa Su wrote:
> From: Ira Weiny <ira.weiny@intel.com>
> 
> A singular Dynamic RAM partition is exposed via the kernel.
> 
> Use this partition in cxl-cli.
> 
> Signed-off-by: Ira Weiny <ira.weiny@intel.com>

Missing Anisa sign off

Reviewed-by: Dave Jiang <dave.jiang@intel.com>



> 
> ---
> Changes:
> [iweiny: New patch for decoder_ram_a]
> ---
>  cxl/json.c   | 20 ++++++++++++++++++++
>  cxl/memdev.c |  4 +++-
>  cxl/region.c | 27 ++++++++++++++++++++++++---
>  3 files changed, 47 insertions(+), 4 deletions(-)
> 
> diff --git a/cxl/json.c b/cxl/json.c
> index a925488..e94c809 100644
> --- a/cxl/json.c
> +++ b/cxl/json.c
> @@ -620,6 +620,20 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
>  		}
>  	}
>  
> +	size = cxl_memdev_get_dynamic_ram_a_size(memdev);
> +	if (size) {
> +		jobj = util_json_object_size(size, flags);
> +		if (jobj)
> +			json_object_object_add(jdev, "dynamic_ram_a_size", jobj);
> +
> +		qos_class = cxl_memdev_get_dynamic_ram_a_qos_class(memdev);
> +		if (qos_class != CXL_QOS_CLASS_NONE) {
> +			jobj = json_object_new_int(qos_class);
> +			if (jobj)
> +				json_object_object_add(jdev, "dynamic_ram_a_qos_class", jobj);
> +		}
> +	}
> +
>  	if (flags & UTIL_JSON_HEALTH) {
>  		jobj = util_cxl_memdev_health_to_json(memdev, flags);
>  		if (jobj)
> @@ -917,6 +931,12 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder,
>  				json_object_object_add(
>  					jdecoder, "volatile_capable", jobj);
>  		}
> +		if (cxl_decoder_is_dynamic_ram_a_capable(decoder)) {
> +			jobj = json_object_new_boolean(true);
> +			if (jobj)
> +				json_object_object_add(
> +					jdecoder, "dynamic_ram_a_capable", jobj);
> +		}
>  	}
>  
>  	if (cxl_port_is_root(port) &&
> diff --git a/cxl/memdev.c b/cxl/memdev.c
> index 6e44d15..bdcb008 100644
> --- a/cxl/memdev.c
> +++ b/cxl/memdev.c
> @@ -269,8 +269,10 @@ static int __reserve_dpa(struct cxl_memdev *memdev,
>  
>  	if (mode == CXL_DECODER_MODE_RAM)
>  		avail_dpa = cxl_memdev_get_ram_size(memdev);
> -	else
> +	else if (mode == CXL_DECODER_MODE_PMEM)
>  		avail_dpa = cxl_memdev_get_pmem_size(memdev);
> +	else
> +		avail_dpa = cxl_memdev_get_dynamic_ram_a_size(memdev);
>  
>  	cxl_decoder_foreach(port, decoder) {
>  		size = cxl_decoder_get_dpa_size(decoder);
> diff --git a/cxl/region.c b/cxl/region.c
> index 85d4d9b..3c935bf 100644
> --- a/cxl/region.c
> +++ b/cxl/region.c
> @@ -303,7 +303,8 @@ static int parse_create_options(struct cxl_ctx *ctx, int count,
>  
>  	if (param.type) {
>  		p->mode = cxl_decoder_mode_from_ident(param.type);
> -		if (p->mode == CXL_DECODER_MODE_RAM && param.uuid) {
> +		if ((p->mode == CXL_DECODER_MODE_RAM ||
> +		     p->mode == CXL_DECODER_MODE_DYNAMIC_RAM_A) && param.uuid) {
>  			log_err(&rl,
>  				"can't set UUID for ram / volatile regions");
>  			goto err;
> @@ -417,6 +418,9 @@ static void collect_minsize(struct cxl_ctx *ctx, struct parsed_params *p)
>  		case CXL_DECODER_MODE_PMEM:
>  			size = cxl_memdev_get_pmem_size(memdev);
>  			break;
> +		case CXL_DECODER_MODE_DYNAMIC_RAM_A:
> +			size = cxl_memdev_get_dynamic_ram_a_size(memdev);
> +			break;
>  		default:
>  			/* Shouldn't ever get here */ ;
>  		}
> @@ -448,8 +452,10 @@ static int create_region_validate_qos_class(struct parsed_params *p)
>  
>  		if (p->mode == CXL_DECODER_MODE_RAM)
>  			qos_class = cxl_memdev_get_ram_qos_class(memdev);
> -		else
> +		else if (p->mode == CXL_DECODER_MODE_PMEM)
>  			qos_class = cxl_memdev_get_pmem_qos_class(memdev);
> +		else
> +			qos_class = cxl_memdev_get_dynamic_ram_a_qos_class(memdev);
>  
>  		/* No qos_class entries. Possibly no kernel support */
>  		if (qos_class == CXL_QOS_CLASS_NONE)
> @@ -488,6 +494,12 @@ static int validate_decoder(struct cxl_decoder *decoder,
>  			return -EINVAL;
>  		}
>  		break;
> +	case CXL_DECODER_MODE_DYNAMIC_RAM_A:
> +		if (!cxl_decoder_is_dynamic_ram_a_capable(decoder)) {
> +			log_err(&rl, "%s is not dynamic_ram_a capable\n", devname);
> +			return -EINVAL;
> +		}
> +		break;
>  	default:
>  		log_err(&rl, "unknown type: %s\n", param.type);
>  		return -EINVAL;
> @@ -509,9 +521,11 @@ static void set_type_from_decoder(struct cxl_ctx *ctx, struct parsed_params *p)
>  		return;
>  
>  	/*
> -	 * default to pmem if both types are set, otherwise the single
> +	 * default to pmem if all types are set, otherwise the single
>  	 * capability dominates.
>  	 */
> +	if (cxl_decoder_is_dynamic_ram_a_capable(p->root_decoder))
> +		p->mode = CXL_DECODER_MODE_DYNAMIC_RAM_A;
>  	if (cxl_decoder_is_volatile_capable(p->root_decoder))
>  		p->mode = CXL_DECODER_MODE_RAM;
>  	if (cxl_decoder_is_pmem_capable(p->root_decoder))
> @@ -699,6 +713,13 @@ static int create_region(struct cxl_ctx *ctx, int *count,
>  				param.root_decoder);
>  			return -ENXIO;
>  		}
> +	} else if (p->mode == CXL_DECODER_MODE_DYNAMIC_RAM_A) {
> +		region = cxl_decoder_create_dynamic_ram_a_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);


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 4/7] libcxl: Add extent functionality to DC regions
  2026-05-23  9:50 ` [PATCH v6 4/7] libcxl: Add extent functionality to DC regions Anisa Su
@ 2026-06-09  0:05   ` Dave Jiang
  2026-06-25  9:18     ` Anisa Su
  0 siblings, 1 reply; 31+ messages in thread
From: Dave Jiang @ 2026-06-09  0:05 UTC (permalink / raw)
  To: Anisa Su, linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Ira Weiny, Alison Schofield, John Groves, Gregory Price,
	Ira Weiny



On 5/23/26 2:50 AM, Anisa Su wrote:
> From: Ira Weiny <ira.weiny@intel.com>
> 
> DCD regions have 0 or more extents.  The ability to list those and their
> properties is useful to end users.
> 
> Add extent scanning and reporting functionality to libcxl.
> 
> Signed-off-by: Ira Weiny <ira.weiny@intel.com>

Missing Anisa sign-off


> 
> ---
> Changes:
> [alison: s/tag/uuid/ for extents]
> ---
>  Documentation/cxl/lib/libcxl.txt |  27 ++++++
>  cxl/lib/libcxl.c                 | 138 +++++++++++++++++++++++++++++++
>  cxl/lib/libcxl.sym               |   5 ++
>  cxl/lib/private.h                |  11 +++
>  cxl/libcxl.h                     |  11 +++
>  5 files changed, 192 insertions(+)
> 
> diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
> index 9921ac1..0ad294c 100644
> --- a/Documentation/cxl/lib/libcxl.txt
> +++ b/Documentation/cxl/lib/libcxl.txt
> @@ -635,6 +635,33 @@ where its properties can be interrogated by daxctl. The helper
>  cxl_region_get_daxctl_region() returns an 'struct daxctl_region *' that
>  can be used with other libdaxctl APIs.
>  
> +EXTENTS
> +-------
> +
> +=== EXTENT: Enumeration
> +----
> +struct cxl_region_extent;
> +struct cxl_region_extent *cxl_extent_get_first(struct cxl_region *region);
> +struct cxl_region_extent *cxl_extent_get_next(struct cxl_region_extent *extent);
> +#define cxl_extent_foreach(region, extent) \
> +        for (extent = cxl_extent_get_first(region); \
> +             extent != NULL; \
> +             extent = cxl_extent_get_next(extent))
> +
> +----
> +
> +=== EXTENT: Attributes
> +----
> +unsigned long long cxl_extent_get_offset(struct cxl_region_extent *extent);
> +unsigned long long cxl_extent_get_length(struct cxl_region_extent *extent);
> +void cxl_extent_get_uuid(struct cxl_region_extent *extent, uuid_t uuid);
> +----
> +
> +Extents represent available memory within a dynamic capacity region.  Extent
> +objects are available for informational purposes to aid in allocation of
> +memory.
> +
> +
>  include::../../copyright.txt[]
>  
>  SEE ALSO
> diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
> index be0bc03..c096666 100644
> --- a/cxl/lib/libcxl.c
> +++ b/cxl/lib/libcxl.c
> @@ -635,6 +635,7 @@ static void *add_cxl_region(void *parent, int id, const char *cxlregion_base)
>  	region->ctx = ctx;
>  	region->decoder = decoder;
>  	list_head_init(&region->mappings);
> +	list_head_init(&region->extents);
>  
>  	region->dev_path = strdup(cxlregion_base);
>  	if (!region->dev_path)
> @@ -1257,6 +1258,143 @@ cxl_mapping_get_next(struct cxl_memdev_mapping *mapping)
>  	return list_next(&region->mappings, mapping, list);
>  }
>  
> +static void cxl_extents_init(struct cxl_region *region)
> +{
> +	const char *devname = cxl_region_get_devname(region);
> +	struct cxl_ctx *ctx = cxl_region_get_ctx(region);
> +	char *extent_path, *dax_region_path;
> +	struct dirent *de;
> +	DIR *dir = NULL;
> +
> +	if (region->extents_init)
> +		return;
> +	region->extents_init = 1;
> +
> +	dax_region_path = calloc(1, strlen(region->dev_path) + 64);
> +	if (!dax_region_path) {
> +		err(ctx, "%s: allocation failure\n", devname);
> +		return;
> +	}
> +
> +	extent_path = calloc(1, strlen(region->dev_path) + 100);
> +	if (!extent_path) {
> +		err(ctx, "%s: allocation failure\n", devname);
> +		free(dax_region_path);
> +		return;
> +	}
> +
> +	sprintf(dax_region_path, "%s/dax_region%d",
> +		region->dev_path, region->id);
> +	dir = opendir(dax_region_path);
> +	if (!dir) {
> +		err(ctx, "no extents found (%s): %s\n",
> +			strerror(errno), dax_region_path);
> +		free(extent_path);
> +		free(dax_region_path);
> +		return;
> +	}
> +
> +	while ((de = readdir(dir)) != NULL) {
> +		struct cxl_region_extent *extent;
> +		char buf[SYSFS_ATTR_SIZE];
> +		u64 offset, length;
> +		int id, region_id;
> +
> +		if (sscanf(de->d_name, "extent%d.%d", &region_id, &id) != 2)
> +			continue;
> +
> +		sprintf(extent_path, "%s/extent%d.%d/offset",
> +			dax_region_path, region_id, id);
> +		if (sysfs_read_attr(ctx, extent_path, buf) < 0) {
> +			err(ctx, "%s: failed to read extent%d.%d/offset\n",
> +				devname, region_id, id);
> +			continue;
> +		}
> +
> +		offset = strtoull(buf, NULL, 0);
> +		if (offset == ULLONG_MAX) {
> +			err(ctx, "%s extent%d.%d: failed to read offset\n",
> +				devname, region_id, id);
> +			continue;
> +		}
> +
> +		sprintf(extent_path, "%s/extent%d.%d/length",
> +			dax_region_path, region_id, id);
> +		if (sysfs_read_attr(ctx, extent_path, buf) < 0) {
> +			err(ctx, "%s: failed to read extent%d.%d/length\n",
> +				devname, region_id, id);
> +			continue;
> +		}
> +
> +		length = strtoull(buf, NULL, 0);
> +		if (length == ULLONG_MAX) {
> +			err(ctx, "%s extent%d.%d: failed to read length\n",
> +				devname, region_id, id);
> +			continue;
> +		}
> +
> +		sprintf(extent_path, "%s/extent%d.%d/tag",
> +			dax_region_path, region_id, id);
> +		buf[0] = '\0';
> +		if (sysfs_read_attr(ctx, extent_path, buf) != 0)
> +			dbg(ctx, "%s extent%d.%d: failed to read uuid\n",
> +				devname, region_id, id);
> +
> +		extent = calloc(1, sizeof(*extent));
> +		if (!extent) {
> +			err(ctx, "%s extent%d.%d: allocation failure\n",
> +				devname, region_id, id);
> +			continue;
> +		}
> +		if (strlen(buf) && uuid_parse(buf, extent->uuid) < 0)
> +			err(ctx, "%s:%s\n", extent_path, buf);
> +		extent->region = region;
> +		extent->offset = offset;
> +		extent->length = length;
> +
> +		list_node_init(&extent->list);
> +		list_add(&region->extents, &extent->list);

free_region() never frees any of the extents allocated and added here and thus leak the memory when region is freed.


> +		dbg(ctx, "%s added extent%d.%d\n", devname, region_id, id);
> +	}
> +	free(dax_region_path);
> +	free(extent_path);
> +	closedir(dir);
> +}
> +
> +CXL_EXPORT struct cxl_region_extent *
> +cxl_extent_get_first(struct cxl_region *region)
> +{
> +	cxl_extents_init(region);
> +
> +	return list_top(&region->extents, struct cxl_region_extent, list);
> +}
> +
> +CXL_EXPORT struct cxl_region_extent *
> +cxl_extent_get_next(struct cxl_region_extent *extent)
> +{
> +	struct cxl_region *region = extent->region;
> +
> +	return list_next(&region->extents, extent, list);
> +}
> +
> +CXL_EXPORT unsigned long long
> +cxl_extent_get_offset(struct cxl_region_extent *extent)
> +{
> +	return extent->offset;
> +}
> +
> +CXL_EXPORT unsigned long long
> +cxl_extent_get_length(struct cxl_region_extent *extent)
> +{
> +	return extent->length;
> +}
> +
> +CXL_EXPORT void
> +cxl_extent_get_uuid(struct cxl_region_extent *extent, uuid_t uuid)
> +{
> +	memcpy(uuid, extent->uuid, sizeof(uuid_t));
> +}
> +
>  CXL_EXPORT struct cxl_decoder *
>  cxl_mapping_get_decoder(struct cxl_memdev_mapping *mapping)
>  {
> diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> index 258bdd3..dcfe242 100644
> --- a/cxl/lib/libcxl.sym
> +++ b/cxl/lib/libcxl.sym
> @@ -298,6 +298,11 @@ global:
>  	cxl_memdev_get_dynamic_ram_a_qos_class;
>  	cxl_decoder_is_dynamic_ram_a_capable;
>  	cxl_decoder_create_dynamic_ram_a_region;
> +	cxl_extent_get_first;
> +	cxl_extent_get_next;
> +	cxl_extent_get_offset;
> +	cxl_extent_get_length;
> +	cxl_extent_get_uuid;
>  } LIBECXL_8;
>  
>  LIBCXL_10 {
> diff --git a/cxl/lib/private.h b/cxl/lib/private.h
> index 37b7b06..c5f3bed 100644
> --- a/cxl/lib/private.h
> +++ b/cxl/lib/private.h
> @@ -183,6 +183,7 @@ struct cxl_region {
>  	struct cxl_decoder *decoder;
>  	struct list_node list;
>  	int mappings_init;
> +	int extents_init;
>  	struct cxl_ctx *ctx;
>  	void *dev_buf;
>  	size_t buf_len;
> @@ -200,6 +201,7 @@ struct cxl_region {
>  	struct daxctl_region *dax_region;
>  	struct kmod_module *module;
>  	struct list_head mappings;
> +	struct list_head extents;
>  };
>  
>  struct cxl_memdev_mapping {
> @@ -209,6 +211,15 @@ struct cxl_memdev_mapping {
>  	struct list_node list;
>  };
>  
> +#define CXL_REGION_EXTENT_TAG 0x10

defined but never used

DJ

> +struct cxl_region_extent {
> +	struct cxl_region *region;
> +	u64 offset;
> +	u64 length;
> +	uuid_t uuid;
> +	struct list_node list;
> +};
> +
>  enum cxl_cmd_query_status {
>  	CXL_CMD_QUERY_NOT_RUN = 0,
>  	CXL_CMD_QUERY_OK,
> diff --git a/cxl/libcxl.h b/cxl/libcxl.h
> index fd41122..a60509f 100644
> --- a/cxl/libcxl.h
> +++ b/cxl/libcxl.h
> @@ -394,6 +394,17 @@ unsigned int cxl_mapping_get_position(struct cxl_memdev_mapping *mapping);
>               mapping != NULL; \
>               mapping = cxl_mapping_get_next(mapping))
>  
> +struct cxl_region_extent;
> +struct cxl_region_extent *cxl_extent_get_first(struct cxl_region *region);
> +struct cxl_region_extent *cxl_extent_get_next(struct cxl_region_extent *extent);
> +#define cxl_extent_foreach(region, extent) \
> +        for (extent = cxl_extent_get_first(region); \
> +             extent != NULL; \
> +             extent = cxl_extent_get_next(extent))
> +unsigned long long cxl_extent_get_offset(struct cxl_region_extent *extent);
> +unsigned long long cxl_extent_get_length(struct cxl_region_extent *extent);
> +void cxl_extent_get_uuid(struct cxl_region_extent *extent, uuid_t uuid);
> +
>  struct cxl_cmd;
>  const char *cxl_cmd_get_devname(struct cxl_cmd *cmd);
>  struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode);


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 5/7] cxl/region: Add extent output to region query
  2026-05-23  9:50 ` [PATCH v6 5/7] cxl/region: Add extent output to region query Anisa Su
@ 2026-06-09  0:08   ` Dave Jiang
  2026-06-10  3:55     ` Richard Cheng
  2026-06-16 10:45     ` Anisa Su
  0 siblings, 2 replies; 31+ messages in thread
From: Dave Jiang @ 2026-06-09  0:08 UTC (permalink / raw)
  To: Anisa Su, linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Ira Weiny, Alison Schofield, John Groves, Gregory Price,
	Ira Weiny



On 5/23/26 2:50 AM, Anisa Su wrote:
> From: Ira Weiny <ira.weiny@intel.com>
> 
> DCD regions have 0 or more extents.  The ability to list those and their
> properties is useful to end users.
> 
> Add an option for extent output to region queries.  An example of this
> is:
> 
> 	$ ./build/cxl/cxl list -r 8 -Nu
> 	{
> 	  "region":"region8",
> 	  ...
> 	  "type":"dc",
> 	  ...
> 	  "extents":[
> 	    {
> 	      "offset":"0x10000000",
> 	      "length":"64.00 MiB (67.11 MB)",
> 	      "tag":"00000000-0000-0000-0000-000000000000"

I think the code emits "uuid". Update commit log.
> 	    },
> 	    {
> 	      "offset":"0x8000000",
> 	      "length":"64.00 MiB (67.11 MB)",
> 	      "tag":"00000000-0000-0000-0000-000000000000"

same here

DJ

> 	    }
> 	  ]
> 	}
> 
> Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> 
> ---
> Changes:
> [iweiny: s/tag/uuid/]
> ---
>  Documentation/cxl/cxl-list.txt | 29 +++++++++++++++++++++
>  cxl/filter.h                   |  3 +++
>  cxl/json.c                     | 47 ++++++++++++++++++++++++++++++++++
>  cxl/json.h                     |  3 +++
>  cxl/list.c                     |  3 +++
>  util/json.h                    |  1 +
>  6 files changed, 86 insertions(+)
> 
> diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
> index 193860b..7512687 100644
> --- a/Documentation/cxl/cxl-list.txt
> +++ b/Documentation/cxl/cxl-list.txt
> @@ -426,6 +426,35 @@ OPTIONS
>  }
>  ----
>  
> +-N::
> +--extents::
> +	Append Dynamic Capacity extent information.
> +----
> +13:34:28 > ./build/cxl/cxl list -r 8 -Nu
> +{
> +  "region":"region8",
> +  "resource":"0xf030000000",
> +  "size":"512.00 MiB (536.87 MB)",
> +  "type":"dc",
> +  "interleave_ways":1,
> +  "interleave_granularity":256,
> +  "decode_state":"commit",
> +  "extents":[
> +    {
> +      "offset":"0x10000000",
> +      "length":"64.00 MiB (67.11 MB)",
> +      "uuid":"00000000-0000-0000-0000-000000000000"
> +    },
> +    {
> +      "offset":"0x8000000",
> +      "length":"64.00 MiB (67.11 MB)",
> +      "uuid":"00000000-0000-0000-0000-000000000000"
> +    }
> +  ]
> +}
> +----
> +
> +
>  -r::
>  --region::
>  	Specify CXL region device name(s), or device id(s), to filter the listing.
> diff --git a/cxl/filter.h b/cxl/filter.h
> index 70463c4..30e7fe2 100644
> --- a/cxl/filter.h
> +++ b/cxl/filter.h
> @@ -31,6 +31,7 @@ struct cxl_filter_params {
>  	bool alert_config;
>  	bool dax;
>  	bool media_errors;
> +	bool extents;
>  	int verbose;
>  	struct log_ctx ctx;
>  };
> @@ -93,6 +94,8 @@ static inline unsigned long cxl_filter_to_flags(struct cxl_filter_params *param)
>  		flags |= UTIL_JSON_DAX | UTIL_JSON_DAX_DEVS;
>  	if (param->media_errors)
>  		flags |= UTIL_JSON_MEDIA_ERRORS;
> +	if (param->extents)
> +		flags |= UTIL_JSON_EXTENTS;
>  	return flags;
>  }
>  
> diff --git a/cxl/json.c b/cxl/json.c
> index e94c809..7922b32 100644
> --- a/cxl/json.c
> +++ b/cxl/json.c
> @@ -1022,6 +1022,50 @@ void util_cxl_mappings_append_json(struct json_object *jregion,
>  	json_object_object_add(jregion, "mappings", jmappings);
>  }
>  
> +void util_cxl_extents_append_json(struct json_object *jregion,
> +				  struct cxl_region *region,
> +				  unsigned long flags)
> +{
> +	struct json_object *jextents;
> +	struct cxl_region_extent *extent;
> +
> +	jextents = json_object_new_array();
> +	if (!jextents)
> +		return;
> +
> +	cxl_extent_foreach(region, extent) {
> +		struct json_object *jextent, *jobj;
> +		unsigned long long val;
> +		char uuid_str[40];
> +		uuid_t uuid;
> +
> +		jextent = json_object_new_object();
> +		if (!jextent)
> +			continue;
> +
> +		val = cxl_extent_get_offset(extent);
> +		jobj = util_json_object_hex(val, flags);
> +		if (jobj)
> +			json_object_object_add(jextent, "offset", jobj);
> +
> +		val = cxl_extent_get_length(extent);
> +		jobj = util_json_object_size(val, flags);
> +		if (jobj)
> +			json_object_object_add(jextent, "length", jobj);
> +
> +		cxl_extent_get_uuid(extent, uuid);
> +		uuid_unparse(uuid, uuid_str);
> +		jobj = json_object_new_string(uuid_str);
> +		if (jobj)
> +			json_object_object_add(jextent, "uuid", jobj);
> +
> +		json_object_array_add(jextents, jextent);
> +		json_object_set_userdata(jextent, extent, NULL);
> +	}
> +
> +	json_object_object_add(jregion, "extents", jextents);
> +}
> +
>  struct json_object *util_cxl_region_to_json(struct cxl_region *region,
>  					     unsigned long flags)
>  {
> @@ -1126,6 +1170,9 @@ struct json_object *util_cxl_region_to_json(struct cxl_region *region,
>  		}
>  	}
>  
> +	if (flags & UTIL_JSON_EXTENTS)
> +		util_cxl_extents_append_json(jregion, region, flags);
> +
>  	if (cxl_region_qos_class_mismatch(region)) {
>  		jobj = json_object_new_boolean(true);
>  		if (jobj)
> diff --git a/cxl/json.h b/cxl/json.h
> index eb7572b..f9c07ab 100644
> --- a/cxl/json.h
> +++ b/cxl/json.h
> @@ -20,6 +20,9 @@ struct json_object *util_cxl_region_to_json(struct cxl_region *region,
>  void util_cxl_mappings_append_json(struct json_object *jregion,
>  				  struct cxl_region *region,
>  				  unsigned long flags);
> +void util_cxl_extents_append_json(struct json_object *jregion,
> +				  struct cxl_region *region,
> +				  unsigned long flags);
>  void util_cxl_targets_append_json(struct json_object *jdecoder,
>  				  struct cxl_decoder *decoder,
>  				  const char *ident, const char *serial,
> diff --git a/cxl/list.c b/cxl/list.c
> index 0b25d78..47d1351 100644
> --- a/cxl/list.c
> +++ b/cxl/list.c
> @@ -59,6 +59,8 @@ static const struct option options[] = {
>  		    "include alert configuration information"),
>  	OPT_BOOLEAN('L', "media-errors", &param.media_errors,
>  		    "include media-error information "),
> +	OPT_BOOLEAN('N', "extents", &param.extents,
> +		    "include extent information (Dynamic Capacity regions only)"),
>  	OPT_INCR('v', "verbose", &param.verbose, "increase output detail"),
>  #ifdef ENABLE_DEBUG
>  	OPT_BOOLEAN(0, "debug", &debug, "debug list walk"),
> @@ -135,6 +137,7 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
>  		param.decoders = true;
>  		param.targets = true;
>  		param.regions = true;
> +		param.extents = true;
>  		/*fallthrough*/
>  	case 0:
>  		break;
> diff --git a/util/json.h b/util/json.h
> index 560f845..79ae324 100644
> --- a/util/json.h
> +++ b/util/json.h
> @@ -21,6 +21,7 @@ enum util_json_flags {
>  	UTIL_JSON_TARGETS	= (1 << 11),
>  	UTIL_JSON_PARTITION	= (1 << 12),
>  	UTIL_JSON_ALERT_CONFIG	= (1 << 13),
> +	UTIL_JSON_EXTENTS	= (1 << 14),
>  };
>  
>  void util_display_json_array(FILE *f_out, struct json_object *jarray,


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 6/7] daxctl: Add --uuid option to create-device for sparse regions
  2026-05-23  9:50 ` [PATCH v6 6/7] daxctl: Add --uuid option to create-device for sparse regions Anisa Su
@ 2026-06-09  0:12   ` Dave Jiang
  2026-06-25  9:30     ` Anisa Su
  0 siblings, 1 reply; 31+ messages in thread
From: Dave Jiang @ 2026-06-09  0:12 UTC (permalink / raw)
  To: Anisa Su, linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Ira Weiny, Alison Schofield, John Groves, Gregory Price,
	Anisa Su



On 5/23/26 2:50 AM, Anisa Su wrote:
> Add a --uuid option to 'daxctl create-device' that writes the given
> uuid to the new dax device's sysfs 'uuid' attribute.  On sparse (DCD)
> regions this claims dax_resources whose tag matches and populates the
> seed device with their capacity; size is determined by the claim, so
> --uuid is mutually exclusive with --size.
> 
> Pass "0" to claim a single untagged dax_resource.  A claim that
> matches no dax_resource leaves the device at size 0; the kernel
> returns ENOENT.
> 
> Plumb the write through a new daxctl_dev_set_uuid() libdaxctl helper
> (LIBDAXCTL_11) and document the option in the man page.
> 
> Signed-off-by: Anisa Su <anisa.su887@gmail.com>
> ---
>  Documentation/daxctl/daxctl-create-device.txt | 12 ++++
>  daxctl/device.c                               | 72 +++++++++++++------
>  daxctl/lib/libdaxctl.c                        | 44 ++++++++++++
>  daxctl/lib/libdaxctl.sym                      |  5 ++
>  daxctl/libdaxctl.h                            |  1 +
>  5 files changed, 114 insertions(+), 20 deletions(-)
> 
> diff --git a/Documentation/daxctl/daxctl-create-device.txt b/Documentation/daxctl/daxctl-create-device.txt
> index b774b86..27b87d0 100644
> --- a/Documentation/daxctl/daxctl-create-device.txt
> +++ b/Documentation/daxctl/daxctl-create-device.txt
> @@ -82,6 +82,18 @@ include::region-option.txt[]
>  
>  	The size must be a multiple of the region alignment.
>  
> +	Mutually exclusive with --uuid.
> +
> +--uuid=::
> +	For dax devices on sparse (DCD) regions, claim dax_resource(s) whose
> +	tag matches the given UUID.  The device's size is determined by the
> +	claimed capacity, so --uuid cannot be combined with --size.
> +
> +	A non-null UUID claims every matching dax_resource in the parent
> +	region.  The value "0" is shorthand for the null UUID and claims a
> +	single untagged dax_resource.  A write that matches no dax_resource
> +	fails with ENOENT and the device is left at size 0.
> +
>  -a::
>  --align::
>  	Applications that want to establish dax memory mappings with
> diff --git a/daxctl/device.c b/daxctl/device.c
> index a4e36b1..21a941e 100644
> --- a/daxctl/device.c
> +++ b/daxctl/device.c
> @@ -30,6 +30,7 @@ static struct {
>  	const char *size;
>  	const char *align;
>  	const char *input;
> +	const char *uuid;
>  	bool check_config;
>  	bool no_online;
>  	bool no_movable;
> @@ -85,7 +86,9 @@ OPT_BOOLEAN('C', "check-config", &param.check_config, \
>  #define CREATE_OPTIONS() \
>  OPT_STRING('s', "size", &param.size, "size", "size to switch the device to"), \
>  OPT_STRING('a', "align", &param.align, "align", "alignment to switch the device to"), \
> -OPT_STRING('\0', "input", &param.input, "input", "input device JSON file")
> +OPT_STRING('\0', "input", &param.input, "input", "input device JSON file"), \
> +OPT_STRING('\0', "uuid", &param.uuid, "uuid", \
> +	"claim sparse dax_resource(s) matching this uuid (\"0\" for untagged)")
>  
>  #define DESTROY_OPTIONS() \
>  OPT_BOOLEAN('f', "force", &param.force, \
> @@ -808,6 +811,22 @@ static int do_create(struct daxctl_region *region, long long val,
>  	struct daxctl_dev *dev;
>  	int i, rc = 0;
>  	long long alloc = 0;
> +	uuid_t uuid;
> +
> +	if (param.uuid) {
> +		if (param.size) {
> +			fprintf(stderr,
> +				"--uuid and --size are mutually exclusive\n");
> +			return -EINVAL;
> +		}
> +		if (strcmp(param.uuid, "0") == 0) {
> +			uuid_clear(uuid);
> +		} else if (uuid_parse(param.uuid, uuid) < 0) {
> +			fprintf(stderr, "failed to parse uuid '%s'\n",
> +				param.uuid);
> +			return -EINVAL;
> +		}
> +	}
>  
>  	if (daxctl_region_create_dev(region))
>  		return -ENOSPC;
> @@ -816,33 +835,46 @@ static int do_create(struct daxctl_region *region, long long val,
>  	if (!dev)
>  		return -ENOSPC;
>  
> -	if (val == -1)
> -		val = daxctl_region_get_available_size(region);
> -
> -	if (val <= 0)
> -		return -ENOSPC;
> -
>  	if (align > 0) {
>  		rc = daxctl_dev_set_align(dev, align);
>  		if (rc < 0)
>  			return rc;
>  	}
>  
> -	/* @maps is ordered by page_offset */
> -	for (i = 0; i < nmaps; i++) {
> -		rc = daxctl_dev_set_mapping(dev, maps[i].start, maps[i].end);
> -		if (rc < 0)
> +	if (param.uuid) {
> +		rc = daxctl_dev_set_uuid(dev, uuid);
> +		if (rc < 0) {
> +			fprintf(stderr,
> +				"%s: failed to claim uuid '%s': %s\n",
> +				daxctl_dev_get_devname(dev), param.uuid,
> +				strerror(-rc));
>  			return rc;
> -		alloc += (maps[i].end - maps[i].start + 1);
> -	}
> -
> -	if (nmaps > 0 && val > 0 && alloc != val) {
> -		fprintf(stderr, "%s: allocated %lld but specified size %lld\n",
> -			daxctl_dev_get_devname(dev), alloc, val);
> +		}
>  	} else {
> -		rc = daxctl_dev_set_size(dev, val);
> -		if (rc < 0)
> -			return rc;
> +		if (val == -1)
> +			val = daxctl_region_get_available_size(region);
> +
> +		if (val <= 0)
> +			return -ENOSPC;
> +
> +		/* @maps is ordered by page_offset */
> +		for (i = 0; i < nmaps; i++) {
> +			rc = daxctl_dev_set_mapping(dev, maps[i].start,
> +						    maps[i].end);
> +			if (rc < 0)
> +				return rc;
> +			alloc += (maps[i].end - maps[i].start + 1);
> +		}
> +
> +		if (nmaps > 0 && val > 0 && alloc != val) {
> +			fprintf(stderr,
> +				"%s: allocated %lld but specified size %lld\n",
> +				daxctl_dev_get_devname(dev), alloc, val);
> +		} else {
> +			rc = daxctl_dev_set_size(dev, val);
> +			if (rc < 0)
> +				return rc;
> +		}
>  	}
>  
>  	rc = daxctl_dev_enable_devdax(dev);
> diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c
> index 02ae7e5..fe07939 100644
> --- a/daxctl/lib/libdaxctl.c
> +++ b/daxctl/lib/libdaxctl.c
> @@ -1107,6 +1107,50 @@ DAXCTL_EXPORT int daxctl_dev_set_size(struct daxctl_dev *dev, unsigned long long
>  	return 0;
>  }
>  
> +DAXCTL_EXPORT int daxctl_dev_set_uuid(struct daxctl_dev *dev, uuid_t uuid)
> +{
> +	struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev);
> +	char buf[SYSFS_ATTR_SIZE];
> +	char *path = dev->dev_buf;
> +	int len = dev->buf_len;
> +
> +	if (snprintf(path, len, "%s/uuid", dev->dev_path) >= len) {
> +		err(ctx, "%s: buffer too small!\n",
> +				daxctl_dev_get_devname(dev));
> +		return -ENXIO;

snprintf() returns negative errno, propogate

> +	}
> +
> +	if (uuid_is_null(uuid))
> +		sprintf(buf, "0\n");
> +	else
> +		uuid_unparse(uuid, buf);
> +
> +	if (sysfs_write_attr(ctx, path, buf) < 0) {
> +		err(ctx, "%s: failed to set uuid\n",
> +				daxctl_dev_get_devname(dev));
> +		return -ENXIO;

propogate the errno from sysfs_write_attr()

> +	}
> +
> +	/*
> +	 * On a sparse region the kernel populates the device size as a
> +	 * side effect of claiming the matching dax_resource(s); refresh
> +	 * the cached size so callers see the post-claim value.
> +	 */
> +	if (snprintf(path, len, "%s/size", dev->dev_path) >= len) {
> +		err(ctx, "%s: buffer too small!\n",
> +				daxctl_dev_get_devname(dev));
> +		return -ENXIO;

propogate negative return value from snprintf()

> +	}
> +	if (sysfs_read_attr(ctx, path, buf) < 0) {
> +		err(ctx, "%s: failed to read back size\n",
> +				daxctl_dev_get_devname(dev));
> +		return -ENXIO;

propgate negative errno from sysfs_read_attr()

> +	}
> +	dev->size = strtoull(buf, NULL, 0);
> +
> +	return 0;
> +}
> +
>  DAXCTL_EXPORT unsigned long daxctl_dev_get_align(struct daxctl_dev *dev)
>  {
>  	return dev->align;
> diff --git a/daxctl/lib/libdaxctl.sym b/daxctl/lib/libdaxctl.sym
> index 3098811..16792eb 100644
> --- a/daxctl/lib/libdaxctl.sym
> +++ b/daxctl/lib/libdaxctl.sym
> @@ -104,3 +104,8 @@ LIBDAXCTL_10 {
>  global:
>  	daxctl_dev_is_system_ram_capable;
>  } LIBDAXCTL_9;
> +
> +LIBDAXCTL_11 {
> +global:
> +	daxctl_dev_set_uuid;
> +} LIBDAXCTL_10;
> diff --git a/daxctl/libdaxctl.h b/daxctl/libdaxctl.h
> index 53c6bbd..cdd5995 100644
> --- a/daxctl/libdaxctl.h
> +++ b/daxctl/libdaxctl.h
> @@ -63,6 +63,7 @@ int daxctl_dev_get_minor(struct daxctl_dev *dev);
>  unsigned long long daxctl_dev_get_resource(struct daxctl_dev *dev);
>  unsigned long long daxctl_dev_get_size(struct daxctl_dev *dev);
>  int daxctl_dev_set_size(struct daxctl_dev *dev, unsigned long long size);
> +int daxctl_dev_set_uuid(struct daxctl_dev *dev, uuid_t uuid);
>  unsigned long daxctl_dev_get_align(struct daxctl_dev *dev);
>  int daxctl_dev_set_align(struct daxctl_dev *dev, unsigned long align);
>  int daxctl_dev_set_mapping(struct daxctl_dev *dev, unsigned long long start,


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 7/7] cxl/test: Add Dynamic Capacity tests
  2026-05-23  9:50 ` [PATCH v6 7/7] cxl/test: Add Dynamic Capacity tests Anisa Su
@ 2026-06-09  0:24   ` Dave Jiang
  2026-06-25  9:34     ` Anisa Su
  0 siblings, 1 reply; 31+ messages in thread
From: Dave Jiang @ 2026-06-09  0:24 UTC (permalink / raw)
  To: Anisa Su, linux-cxl, linux-kernel
  Cc: nvdimm, Dan Williams, Jonathan Cameron, Davidlohr Bueso,
	Ira Weiny, Alison Schofield, John Groves, Gregory Price,
	Ira Weiny



On 5/23/26 2:50 AM, Anisa Su wrote:
> From: Ira Weiny <ira.weiny@intel.com>
> 
> cxl_test provides a good way to ensure quick smoke and regression
> testing.  The complexity of DCD and the new sparse DAX regions
> required to use them benefits greatly with a series of smoke tests.
> 
> The only part of the kernel stack which must be bypassed is the actual
> irq of DCD events.  However, the event processing itself can be tested
> via cxl_test calling directly into the event processing.
> 
> In this way the rest of the stack; management of sparse regions, the
> extent device lifetimes, and the dax device operations can be tested.
> 
> Add Dynamic Capacity Device tests for kernels which have DCD support.
> 
> Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Anisa Su <anisa.su887@gmail.com>

A nit below. Otherwise

Reviewed-by: Dave Jiang <dave.jiang@intel.com>


> 
> ---
> Changes:
> [anisa: align tests with kernel DCD redesign + add sharable coverage]
> 
> Rewrite tests against the post-redesign kernel (uuid sysfs claim,
> sparse size-grow rejection, tag-group atomic release, cross-More
> uniqueness):
> 
>   - Use real UUID strings; route untagged claims via --uuid "0" so
>     daxctl exercises the kernel uuid_store path end to end.
>   - Pre-existing-extent test handles the tagged pre2_ext (deadbeef)
>     separately from the untagged pre_ext.
>   - Spanning / aggregation tests drive both extents in one More-chain:
>     cross-event tagged adds are dropped by the cross-More uniqueness
>     gate, and untagged events become independent dax_resources so
>     only one is claimed per --uuid "0".
>   - test_dax_device_ops asserts that sparse size grow returns
>     -EOPNOTSUPP after a shrink (real grow path is --uuid only).
>   - test_reject_overlapping math now produces an actual overlap
>     inside the DC region (the prior arithmetic landed past the end).
> 
> Add coverage for new validators:
>   - test_uuid_no_match / test_uuid_no_match_seed_intact
>   - test_uuid_show (reads back the dax-dev uuid sysfs attribute)
>   - test_cross_more_uniqueness (rejects tag reuse across More-chains)
>   - test_alignment_rejection (rejects misaligned extents)
> 
> Sharable-partition coverage (test_shared_extent_inject,
> test_seq_integrity_gap) is routed at runtime to a dedicated mock
> memdev that tools/testing/cxl stamps with serial 0xDCDC; the script
> enumerates both regimes from one cxl_test module load.
> 
> Localize positional-arg assignments in every helper so functions
> no longer clobber caller globals (the previous behavior leaked
> the sharable memdev into later tests).
> 
> Update inject_extent to optionally pass shared_extn_seq (5th arg)
> and remove_extent to carry the tag (now required by the mock's
> delete sysfs).  Add inject_shared_extent for dc_inject_shared_extent.
> ---
>  test/cxl-dcd.sh  | 1267 ++++++++++++++++++++++++++++++++++++++++++++++
>  test/meson.build |    2 +
>  2 files changed, 1269 insertions(+)
>  create mode 100644 test/cxl-dcd.sh
> 
> diff --git a/test/cxl-dcd.sh b/test/cxl-dcd.sh
> new file mode 100644
> index 0000000..3194b00
> --- /dev/null
> +++ b/test/cxl-dcd.sh
> @@ -0,0 +1,1267 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (C) 2024 Intel Corporation. All rights reserved.
> +
> +. "$(dirname "$0")/common"
> +
> +rc=77
> +set -ex
> +
> +trap 'err $LINENO' ERR
> +
> +check_prereq "jq"
> +
> +rc=1
> +
> +dev_path="/sys/bus/platform/devices"
> +cxl_path="/sys/bus/cxl/devices"
> +
> +# test extent tags (UUIDs).  pre_ext_tag matches the second pre-injected
> +# extent tag in the kernel mock (tools/testing/cxl/test/mem.c).
> +pre_ext_tag="deadbeef-cafe-baad-f00d-fedcba987654"
> +test_tag_a="11111111-1111-1111-1111-111111111111"
> +test_tag_b="22222222-2222-2222-2222-222222222222"
> +unknown_tag="33333333-3333-3333-3333-333333333333"
> +
> +#
> +# The test devices have 2G of non DC capacity.  A single DC reagion of 1G is
> +# added beyond that.
> +#
> +# The testing centers around 3 extents.  Two are "pre-existing" on test load
> +# called pre-ext and pre2-ext.  The other is created within this script alone
> +# called base.
> +
> +#
> +# | 2G non- |      DC region (1G)                                   |
> +# |  DC cap |                                                       |
> +# |  ...    |-------------------------------------------------------|
> +# |         |--------|       |----------|      |----------|         |
> +# |         | (base) |       |(pre-ext) |      |(pre2-ext)|         |
> +
> +dra_size=""
> +
> +base_dpa=0x80000000
> +
> +# base extent at dpa 2G - 64M long
> +base_ext_offset=0x0
> +base_ext_dpa=$(($base_dpa + $base_ext_offset))
> +base_ext_length=0x4000000
> +
> +# pre existing extent base + 128M, 64M length
> +# 0x00000088000000-0x0000008bffffff
> +pre_ext_offset=0x8000000
> +pre_ext_dpa=$(($base_dpa + $pre_ext_offset))
> +pre_ext_length=0x4000000
> +
> +# pre2 existing extent base + 256M, 64M length
> +# 0x00000090000000-0x00000093ffffff
> +pre2_ext_offset=0x10000000
> +pre2_ext_dpa=$(($base_dpa + $pre2_ext_offset))
> +pre2_ext_length=0x4000000
> +
> +mem=""
> +bus=""
> +device=""
> +decoder=""
> +
> +# ========================================================================
> +# Support functions
> +# ========================================================================
> +
> +create_dcd_region()
> +{
> +	local mem="$1"
> +	local decoder="$2"
> +	local reg_size_string=""
> +	local region
> +	if [ "$3" != "" ]; then
> +		reg_size_string="-s $3"
> +	fi
> +
> +	# create region
> +	region=$($CXL create-region -t dynamic_ram_a -d "$decoder" -m "$mem" ${reg_size_string} | jq -r ".region")
> +
> +	if [[ ! $region ]]; then
> +		echo "create-region failed for $decoder / $mem"
> +		err "$LINENO"
> +	fi
> +
> +	echo ${region}
> +}
> +
> +check_region()
> +{
> +	local search=$1
> +	local region_size=$2
> +	local result
> +
> +	result=$($CXL list -r "$search" | jq -r ".[].region")
> +	if [ "$result" != "$search" ]; then
> +		echo "check region failed to find $search"
> +		err "$LINENO"
> +	fi
> +
> +	result=$($CXL list -r "$search" | jq -r ".[].size")
> +	if [ "$result" != "$region_size" ]; then
> +		echo "check region failed invalid size $result != $region_size"
> +		err "$LINENO"
> +	fi
> +}
> +
> +check_not_region()
> +{
> +	local search=$1
> +	local result
> +
> +	result=$($CXL list -r "$search" | jq -r ".[].region")
> +	if [ "$result" == "$search" ]; then
> +		echo "check not region failed; $search found"
> +		err "$LINENO"
> +	fi
> +}
> +
> +destroy_region()
> +{
> +	local region=$1
> +	$CXL disable-region $region
> +	$CXL destroy-region $region
> +}
> +
> +inject_extent()
> +{
> +	local device="$1"
> +	local dpa="$2"
> +	local length="$3"
> +	local tag="$4"
> +	local seq="$6"
> +	local more="0"
> +	local cmd
> +
> +	if [ "$5" != "" ]; then
> +		more="1"
> +	fi
> +
> +	cmd="${dpa}:${length}:${tag}:${more}"
> +	if [ -n "$seq" ]; then
> +		cmd="${cmd}:${seq}"
> +	fi
> +	echo ${cmd} > "${dev_path}/${device}/dc_inject_extent"
> +}
> +
> +# Shared-extent inject targets a *sharable* DC partition.  The mock
> +# stamps the sentinel serial onto a single cxl_mem instance, and the
> +# script routes shared-extent tests to that memdev via $sharable_device.
> +inject_shared_extent()
> +{
> +	local device="$1"
> +	local dpa="$2"
> +	local length="$3"
> +	local tag="$4"
> +	local seq="$6"
> +	local more="0"
> +	local cmd
> +
> +	if [ "$5" != "" ]; then
> +		more="1"
> +	fi
> +
> +	cmd="${dpa}:${length}:${tag}:${more}"
> +	if [ -n "$seq" ]; then
> +		cmd="${cmd}:${seq}"
> +	fi
> +	echo ${cmd} > "${dev_path}/${device}/dc_inject_shared_extent"
> +}
> +
> +remove_extent()
> +{
> +	local device="$1"
> +	local dpa="$2"
> +	local length="$3"
> +	local tag="$4"
> +
> +	echo ${dpa}:${length}:${tag} > "${dev_path}/${device}/dc_del_extent"
> +}
> +
> +create_dax_dev()
> +{
> +	local reg="$1"
> +	local dax_dev
> +
> +	dax_dev=$($DAXCTL create-device -r $reg | jq -er '.[].chardev')
> +
> +	echo ${dax_dev}
> +}
> +
> +create_dax_dev_with_uuid()
> +{
> +	local reg="$1"
> +	local uuid="$2"
> +	local dax_dev
> +
> +	dax_dev=$($DAXCTL create-device -r $reg --uuid "$uuid" \
> +			| jq -er '.[].chardev')
> +
> +	echo ${dax_dev}
> +}
> +
> +fail_create_dax_dev_with_uuid()
> +{
> +	local reg="$1"
> +	local uuid="$2"
> +	local create_rc
> +
> +	set +e
> +	$DAXCTL create-device -r $reg --uuid "$uuid"
> +	create_rc=$?
> +	set -e
> +	if [ $create_rc -eq 0 ]; then
> +		echo "FAIL create-device with uuid $uuid unexpectedly succeeded"
> +		err "$LINENO"
> +	fi
> +}
> +
> +fail_create_dax_dev()
> +{
> +	local reg="$1"
> +	local result
> +
> +	set +e
> +	result=$($DAXCTL create-device -r $reg)
> +	set -e
> +	if [ "$result" == "0" ]; then
> +		echo "FAIL device created"
> +		err "$LINENO"
> +	fi
> +}
> +
> +# Try to resize a sparse dax device via reconfigure -s; must fail because
> +# the kernel rejects any non-zero size_store on a dynamic dax region with
> +# -EOPNOTSUPP (drivers/dax/bus.c:dev_dax_resize).  The only valid resize is
> +# to size 0, which is the path `daxctl destroy-device` takes.
> +fail_resize_dax_dev()
> +{
> +	local dev="$1"
> +	local new_size="$2"
> +	local resize_rc
> +
> +	$DAXCTL disable-device $dev
> +	set +e
> +	$DAXCTL reconfigure-device $dev -s $new_size
> +	resize_rc=$?
> +	set -e
> +	# Re-enable regardless so subsequent checks/cleanup work.
> +	$DAXCTL enable-device $dev
> +	if [ $resize_rc -eq 0 ]; then
> +		echo "FAIL resize of $dev to $new_size unexpectedly succeeded"
> +		err "$LINENO"
> +	fi
> +}
> +
> +# Read /sys/bus/dax/devices/<dax>/uuid and compare to $expected.  "0"
> +# matches the null UUID (kernel emits "0\n" when the resource is empty,
> +# untagged, or non-DCD).
> +check_dax_uuid()
> +{
> +	local dax_dev="$1"
> +	local expected="$2"
> +	local uuid_path got want
> +
> +	uuid_path="/sys/bus/dax/devices/${dax_dev}/uuid"
> +	if [ ! -e "$uuid_path" ]; then
> +		echo "FAIL no uuid attribute at $uuid_path"
> +		err "$LINENO"
> +	fi
> +	got=$(cat "$uuid_path" | tr -d '[:space:]')
> +	want=$(echo "$expected" | tr -d '[:space:]')
> +	if [ "$got" != "$want" ]; then
> +		echo "FAIL uuid show on $dax_dev: got '$got' want '$want'"
> +		err "$LINENO"
> +	fi
> +}
> +
> +destroy_dax_dev()
> +{
> +	local dev="$1"
> +
> +	$DAXCTL disable-device $dev
> +	$DAXCTL destroy-device $dev
> +}
> +
> +check_dax_dev()
> +{
> +	local search="$1"
> +	local size=$(($2))
> +	local result
> +
> +	result=$($DAXCTL list -d $search | jq -er '.[].chardev')
> +	if [ "$result" != "$search" ]; then
> +		echo "check dax device failed to find $search"
> +		err "$LINENO"
> +	fi
> +	result=$($DAXCTL list -d $search | jq -er '.[].size')
> +	if [ "$result" -ne "$size" ]; then
> +		echo "check dax device failed incorrect size $result; exp $size"
> +		err "$LINENO"
> +	fi
> +}
> +
> +# check that the dax device is not there.
> +check_not_dax_dev()
> +{
> +	local reg="$1"
> +	local search="$2"
> +	local result
> +	result=$($DAXCTL list -r $reg -D | jq -r '.[].chardev')
> +	if [ "$result" == "$search" ]; then
> +		echo "FAIL found $search"
> +		err "$LINENO"
> +	fi
> +}
> +
> +check_extent()
> +{
> +	local region=$1
> +	local offset=$(($2))
> +	local length=$(($3))
> +	local result
> +
> +	result=$($CXL list -r "$region" -N | jq -r ".[].extents[] | select(.offset == ${offset}) | .length")
> +	if [[ $result != $length ]]; then
> +		echo "FAIL region $1 could not find extent @ $offset ($length)"
> +		err "$LINENO"
> +	fi
> +}
> +
> +check_extent_cnt()
> +{
> +	local region=$1
> +	local count=$(($2))
> +	local result
> +
> +	result=$($CXL list -r $region -N | jq -r '.[].extents[].offset' | wc -l)
> +	if [[ $result != $count ]]; then
> +		echo "FAIL region $1: found wrong number of extents $result; expect $count"
> +		err "$LINENO"
> +	fi
> +}
> +
> +
> +# ========================================================================
> +# Tests
> +# ========================================================================
> +
> +# testing pre existing extents must be called first as the extents were created
> +# by cxl-test being loaded.  The mock fixture is one untagged extent at
> +# pre_ext_dpa and one tagged (pre_ext_tag) extent at pre2_ext_dpa, so the
> +# untagged extent is claimed via --uuid "0" and the tagged one via its UUID.
> +test_pre_existing_extents()
> +{
> +	echo ""
> +	echo "Test: pre-existing extents (untagged + tagged)"
> +	echo ""
> +	region=$(create_dcd_region ${mem} ${decoder})
> +
> +	# | 2G non- |      DC region                                        |
> +	# |  DC cap |                                                       |
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |                   |----------|         |----------|   |
> +	# |         |                   |(pre-ext) |         |(pre2-ext)|   |
> +	# |         |                   | untagged |         | tagged   |   |
> +	check_region ${region} ${dra_size}
> +	check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
> +	check_extent ${region} ${pre2_ext_offset} ${pre2_ext_length}
> +
> +	# Untagged claim picks up the untagged pre-extent only
> +	dax_dev_u=$(create_dax_dev_with_uuid ${region} "0")
> +	check_dax_dev ${dax_dev_u} $pre_ext_length
> +
> +	# Tagged claim picks up the tagged pre2-extent
> +	dax_dev_t=$(create_dax_dev_with_uuid ${region} "$pre_ext_tag")
> +	check_dax_dev ${dax_dev_t} $pre2_ext_length
> +
> +	destroy_dax_dev ${dax_dev_u}
> +	destroy_dax_dev ${dax_dev_t}
> +
> +	# Release events must carry the tag identity so cxl_rm_extent's
> +	# uuid_equal lookup matches the right region_extent.
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	remove_extent ${device} $pre2_ext_dpa $pre2_ext_length "$pre_ext_tag"
> +	check_extent_cnt ${region} 0
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_remove_extent_under_dax_device()
> +{
> +	# Remove the pre-created test extent out from under dax device
> +	# stack should hold ref until dax device deleted
> +	echo ""
> +	echo "Test: Remove extent from under DAX dev"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +	# | 2G non- |      DC region                                        |
> +	# |  DC cap |                                                       |
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |                                                       |
> +	# |         |                                                       |
> +
> +	
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	# |         |                   |----------|                        |
> +	# |         |                   |(pre-ext) |                        |
> +	check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
> +
> +	# Sparse seed starts at size 0; --uuid "0" is the only sizing path.
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "0")
> +	# |         |                   |----------|                        |
> +	# |         |                   |(pre-ext) |                        |
> +	# |         |                   | daxX.1   |                        |
> +	check_extent_cnt ${region} 1
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	# In-use extents are not released (dax-layer EBUSY defers release).
> +	check_dax_dev ${dax_dev} $pre_ext_length
> +
> +	check_extent_cnt ${region} 1
> +	destroy_dax_dev ${dax_dev}
> +	# |         |                   |----------|                        |
> +	# |         |                   |(pre-ext) |                        |
> +	check_not_dax_dev ${region} ${dax_dev}
> +
> +	check_extent_cnt ${region} 1
> +	# Re-issuing the release after the dax device dropped its hold
> +	# completes the deferred release.
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	# |         |                                                       |
> +	# |         |                                                       |
> +	check_extent_cnt ${region} 0
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_remove_extents_in_use()
> +{
> +	echo ""
> +	echo "Test: Remove extents under sparse dax device"
> +	echo ""
> +	# Tagged release events are deferred while a dax device pins the
> +	# tag group; extent count stays at 2.
> +	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 2
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 2
> +}
> +
> +test_create_dax_dev_spanning_two_extents()
> +{
> +	echo ""
> +	echo "Test: Create dax device spanning 2 extents in a tagged group"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +	# | 2G non- |      DC region                                        |
> +	# |  DC cap |                                                       |
> +	# |  ...    |-------------------------------------------------------|
> +	#
> +	# A single dax device spanning two extents is only possible when
> +	# both extents belong to the same tagged More-chain.  Cross-event
> +	# tagged adds are rejected by the cross-More uniqueness gate;
> +	# untagged adds become independent dax_resources and only one is
> +	# claimed per --uuid "0".  Drive both extents in one More-chain:
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a" 1
> +	check_extent_cnt ${region} 0   # held off until More=0 closes chain
> +	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +	check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
> +	check_extent ${region} ${base_ext_offset} ${base_ext_length}
> +	# |         |--------|          |----------|                        |
> +	# |         | (base) |          |(pre-ext) |     tag_a              |
> +
> +	check_extent_cnt ${region} 2
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> +	# |         |--------|          |----------|                        |
> +	# |         | (base) |          |(pre-ext) |                        |
> +	# |         | daxX.1 |          | daxX.1   |                        |
> +
> +	echo "Checking if dev dax is spanning sparse extents"
> +	ext_sum_length="$(($base_ext_length + $pre_ext_length))"
> +	check_dax_dev ${dax_dev} $ext_sum_length
> +
> +	test_remove_extents_in_use
> +
> +	destroy_dax_dev ${dax_dev}
> +	check_not_dax_dev ${region} ${dax_dev}
> +
> +	# In-use extents were not released.  Check they can be removed after
> +	# the dax device is removed.
> +	check_extent_cnt ${region} 2
> +	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 0
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_inject_tag_support()
> +{
> +	echo ""
> +	echo "Test: inject untagged + tagged extents, claim each via --uuid"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +
> +	# untagged extent
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
> +	# tagged extent (different DPA so both can land)
> +	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +
> +	# both extents should be accepted
> +	check_extent_cnt ${region} 2
> +
> +	# claim the tagged extent into one dax device
> +	dax_dev_a=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> +	check_dax_dev ${dax_dev_a} $base_ext_length
> +
> +	# claim the untagged extent into a second dax device via "0"
> +	dax_dev_0=$(create_dax_dev_with_uuid ${region} "0")
> +	check_dax_dev ${dax_dev_0} $pre_ext_length
> +
> +	destroy_dax_dev ${dax_dev_a}
> +	destroy_dax_dev ${dax_dev_0}
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 0
> +
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_uuid_no_match()
> +{
> +	echo ""
> +	echo "Test: daxctl create-device --uuid with no matching extent fails"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +
> +	# inject only one tagged extent; ask daxctl to claim a different uuid
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 1
> +
> +	fail_create_dax_dev_with_uuid ${region} "$unknown_tag"
> +
> +	# the extent should still be claimable via its real tag
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> +	check_dax_dev ${dax_dev} $pre_ext_length
> +	destroy_dax_dev ${dax_dev}
> +
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 0
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_uuid_aggregation()
> +{
> +	echo ""
> +	echo "Test: multiple extents in a single More-chain aggregate under one tag"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +
> +	# Both extents must arrive in the same More-chain to land in the
> +	# same tag group.  Re-using a tag across More-chains hits the
> +	# cross-More uniqueness gate and the second event would be dropped.
> +	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a" 1
> +	check_extent_cnt ${region} 0
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 2
> +
> +	# a single --uuid claim should pick up both extents
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> +	expected=$(($base_ext_length + $pre_ext_length))
> +	check_dax_dev ${dax_dev} $expected
> +
> +	destroy_dax_dev ${dax_dev}
> +	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +
> +test_partial_extent_remove ()
> +{
> +	echo ""
> +	echo "Test: partial extent remove"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +
> +	# | 2G non- |      DC region                                        |
> +	# |  DC cap |                                                       |
> +	# |  ...    |-------------------------------------------------------|
> +
> +	inject_extent ${device} $base_ext_dpa $base_ext_length ""
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |--------|                                              |
> +	# |         | (base) |                                              |
> +
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "0")
> +
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |--------|                                              |
> +	# |         | (base) |                                              |
> +	# |         | daxX.1 |                                              |
> +
> +	partial_ext_dpa="$(($base_ext_dpa + ($base_ext_length / 2)))"
> +	partial_ext_length="$(($base_ext_length / 2))"
> +	echo "Removing Partial : $partial_ext_dpa $partial_ext_length"
> +
> +	# |         |    |---|                                              |
> +	#                  Partial
> +
> +	remove_extent ${device} $partial_ext_dpa $partial_ext_length ""
> +	# In-use extents are not released.
> +	check_extent_cnt ${region} 1
> +
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |--------|                                              |
> +	# |         | (base) |                                              |
> +	# |         | daxX.1 |                                              |
> +
> +	destroy_dax_dev ${dax_dev}
> +	check_not_dax_dev ${region} ${dax_dev}
> +
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |--------|                                              |
> +	# |         | (base) |                                              |
> +
> +	# Partial release event re-targets the whole containing region_extent.
> +	check_extent_cnt ${region} 1
> +	remove_extent ${device} $partial_ext_dpa $partial_ext_length ""
> +	# |         |    |---|                                              |
> +	#                  Partial
> +	check_extent_cnt ${region} 0
> +
> +	# |  ...    |-------------------------------------------------------|
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_multiple_extent_remove ()
> +{
> +	# Per-tag-group release: a release event whose DPA range straddles
> +	# multiple region_extents in the same tag group should target them
> +	# atomically.  Set up two extents in one tagged More-chain.
> +	echo ""
> +	echo "Test: per-group release event covering two extents"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +
> +	# | 2G non- |      DC region                                        |
> +	# |  DC cap |                                                       |
> +	# |  ...    |-------------------------------------------------------|
> +
> +	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a" 1
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |--------|          |-------------------|               |
> +	# |         | (base) |          | (pre)-existing    |   tag_a       |
> +
> +	check_extent_cnt ${region} 2
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> +
> +	# |         | daxX.1 |          | daxX.1            |   tag_a       |
> +
> +	# A release event addressed at any DPA inside the tag group releases
> +	# the whole group atomically.  Use base_ext_dpa so the kernel finds
> +	# the tag group's region_extent on that DPA.
> +	echo "Issuing tag-group release at $base_ext_dpa"
> +	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +
> +	# In-use extents are not released (dax-layer EBUSY defer).
> +	check_extent_cnt ${region} 2
> +
> +	destroy_dax_dev ${dax_dev}
> +	check_not_dax_dev ${region} ${dax_dev}
> +
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |--------|          |-------------------|               |
> +	# |         | (base) |          | (pre)-existing    |               |
> +
> +	# Now that the dax dev is gone, re-issue the release(s) for each
> +	# extent.  Each release event targets the containing region_extent.
> +	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 0
> +
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_destroy_region_without_extent_removal ()
> +{
> +	echo ""
> +	echo "Test: Destroy region without extent removal"
> +	echo ""
> +	
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	inject_extent ${device} $base_ext_dpa $base_ext_length ""
> +	check_extent_cnt ${region} 2
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_destroy_with_extent_and_dax ()
> +{
> +	echo ""
> +	echo "Test: Destroy region with extents and dax devices"
> +	echo ""
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +	check_extent_cnt ${region} 0
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	
> +	# | 2G non- |      DC region                                        |
> +	# |  DC cap |                                                       |
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |                   |----------|                        |
> +	# |         |                   |(pre-ext) |                        |
> +
> +	check_extent_cnt ${region} 1
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "0")
> +	# |         |                   |<dax_dev> |                        |
> +	check_dax_dev ${dax_dev} ${pre_ext_length}
> +	destroy_region ${region}
> +	check_not_region ${region}
> +
> +	# | 2G non- |      DC region                                        |
> +	# |  DC cap |                                                       |
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |                                                       |
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +	check_extent_cnt ${region} 0
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_dax_device_ops ()
> +{
> +	echo ""
> +	echo "Test: Fail sparse dax dev creation without space"
> +	echo ""
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +
> +	# | 2G non- |      DC region                                        |
> +	# |  DC cap |                                                       |
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |                   |-------------------|               |
> +	# |         |                   | (pre)-existing    |               |
> +
> +	check_extent_cnt ${region} 1
> +
> +	# |         |                   | daxX.1            |               |
> +
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "0")
> +	check_dax_dev ${dax_dev} $pre_ext_length
> +	# No more untagged dax_resource avail: untagged claim returns -ENOENT
> +	fail_create_dax_dev_with_uuid ${region} "0"
> +	# Plain create-device on sparse: size grow is -EOPNOTSUPP
> +	fail_create_dax_dev ${region}
> +
> +	# DC dax devices cannot be resized to a non-zero size.  The
> +	# kernel rejects any size_store with -EOPNOTSUPP unless size == 0,
> +	# in which case dev_dax_shrink releases every range -- the same path
> +	# `daxctl destroy-device` takes.
> +	echo ""
> +	echo "Test: Resize of sparse dax device to non-zero is rejected"
> +	echo ""
> +	half=$(($pre_ext_length / 2))
> +	fail_resize_dax_dev ${dax_dev} $half
> +	check_dax_dev ${dax_dev} $pre_ext_length
> +
> +	# Destroy (size=0) is the only valid resize.  After it the
> +	# dax_resource has avail again, so --uuid "0" can claim it back
> +	# into a new dax device.
> +	# |         |                   | daxX.2            |               |
> +	echo ""
> +	echo "Test: Destroy (size=0) of sparse dax device releases the resource"
> +	echo ""
> +	destroy_dax_dev ${dax_dev}
> +	check_not_dax_dev ${region} ${dax_dev}
> +	dax_dev2=$(create_dax_dev_with_uuid ${region} "0")
> +	check_dax_dev ${dax_dev2} $pre_ext_length
> +
> +	destroy_region ${region}
> +	check_not_region ${region}
> +
> +	# Multi-extent device must come from a single tagged group; cross-
> +	# event untagged adds land in independent dax_resources and cannot
> +	# be claimed as one dax device.  The no-resize rule applies equally
> +	# across the tagged group.
> +	echo ""
> +	echo "Test: Resize of tagged multi-extent dax device is rejected"
> +	echo ""
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a" 1
> +	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +
> +	# | 2G non- |      DC region                                        |
> +	# |  DC cap |                                                       |
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |--------|          |-------------------|               |
> +	# |         | (base) |          | (pre)-existing    |    tag_a      |
> +
> +	check_extent_cnt ${region} 2
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> +	ext_sum_length="$(($base_ext_length + $pre_ext_length))"
> +	check_dax_dev ${dax_dev} $ext_sum_length
> +
> +	# Any non-zero resize (here a shrink to 32M, which would sit inside
> +	# the first extent of the group) is rejected.
> +	fail_resize_dax_dev ${dax_dev} 33554432 # 32MB
> +	check_dax_dev ${dax_dev} $ext_sum_length
> +
> +	# Only size=0 / destroy is valid for the tagged group too.
> +	destroy_dax_dev ${dax_dev}
> +	check_not_dax_dev ${region} ${dax_dev}
> +
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_reject_overlapping ()
> +{
> +	echo ""
> +	echo "Test: Rejecting overlapping extents"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +
> +	# | 2G non- |      DC region                                        |
> +	# |  DC cap |                                                       |
> +	# |  ...    |-------------------------------------------------------|
> +	# |         |                   |-------------------|               |
> +	# |         |                   | (pre)-existing    |               |
> +
> +	check_extent_cnt ${region} 1
> +
> +	# Attempt overlapping extent: start halfway through pre_ext, same
> +	# length so it straddles the end of pre_ext.
> +	#
> +	# |         |                   |---------|---------|               |
> +	# |         |                   |(pre-ext)| overlap |               |
> +
> +	partial_ext_dpa="$(($pre_ext_dpa + ($pre_ext_length / 2)))"
> +	partial_ext_length=$pre_ext_length
> +	inject_extent ${device} $partial_ext_dpa $partial_ext_length ""
> +
> +	# Should only see the original ext
> +	check_extent_cnt ${region} 1
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_two_regions()
> +{
> +	echo ""
> +	echo "Test: create 2 regions in the same DC partition"
> +	echo ""
> +	region_size=$(($dra_size / 2))
> +	region=$(create_dcd_region ${mem} ${decoder} ${region_size})
> +	check_region ${region} ${region_size}
> +	
> +	region_two=$(create_dcd_region ${mem} ${decoder} ${region_size})
> +	check_region ${region_two} ${region_size}
> +	
> +	destroy_region ${region_two}
> +	check_not_region ${region_two}
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_more_bit()
> +{
> +	echo ""
> +	echo "Test: More bit"
> +	echo ""
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length "" 1
> +	# More bit should hold off surfacing extent until the more bit is 0
> +	check_extent_cnt ${region} 0
> +	inject_extent ${device} $base_ext_dpa $base_ext_length ""
> +	check_extent_cnt ${region} 2
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_driver_tear_down()
> +{
> +	echo ""
> +	echo "Test: driver remove tear down"
> +	echo ""
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "0")
> +	# remove driver releases extents
> +	modprobe -r dax_cxl
> +	check_extent_cnt ${region} 0
> +}
> +
> +test_driver_bring_up()
> +{
> +	# leave region up, driver removed.
> +	echo ""
> +	echo "Test: no driver inject ok"
> +	echo ""
> +	check_region ${region} ${dra_size}
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	check_extent_cnt ${region} 1
> +
> +	modprobe dax_cxl
> +	check_extent_cnt ${region} 1
> +
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_driver_reload()
> +{
> +	test_driver_tear_down
> +	test_driver_bring_up
> +}
> +
> +# Verify the dax dev's uuid sysfs attribute reflects the claim source:
> +# "0" for untagged seed/claim, the tag string for tagged claims.
> +test_uuid_show()
> +{
> +	echo ""
> +	echo "Test: dax dev uuid attribute reflects the claim source"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +
> +	# Pre-claim the seed device's uuid (before any extent injected)
> +	# must read back as "0".  -i includes the idle seed at size 0.
> +	seed=$($DAXCTL list -r ${region} -D -i | jq -r '.[].chardev')
> +	check_dax_uuid ${seed} "0"
> +
> +	# Untagged extent + claim: uuid stays "0"
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	dax_u=$(create_dax_dev_with_uuid ${region} "0")
> +	check_dax_uuid ${dax_u} "0"
> +
> +	# Tagged extent + claim: uuid reads back as the tag
> +	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +	dax_t=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> +	check_dax_uuid ${dax_t} "$test_tag_a"
> +
> +	destroy_dax_dev ${dax_u}
> +	destroy_dax_dev ${dax_t}
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +# --uuid <unknown> on a sparse region must return -ENOENT before any
> +# size is committed.  The region's available_size must be unchanged and
> +# the underlying extent must still be claimable via its real tag.
> +test_uuid_no_match_seed_intact()
> +{
> +	echo ""
> +	echo "Test: --uuid mismatch does not consume any extent space"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 1
> +
> +	# Capture region available size before the failed claim
> +	avail_before=$($DAXCTL list -r ${region} | jq -r '.[].available_size')
> +
> +	fail_create_dax_dev_with_uuid ${region} "$unknown_tag"
> +
> +	avail_after=$($DAXCTL list -r ${region} | jq -r '.[].available_size')
> +	if [ "$avail_before" != "$avail_after" ]; then
> +		echo "FAIL avail size changed by unmatched --uuid: $avail_before -> $avail_after"
> +		err "$LINENO"
> +	fi
> +
> +	# The real tag still claims successfully
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> +	check_dax_dev ${dax_dev} $pre_ext_length
> +
> +	destroy_dax_dev ${dax_dev}
> +	remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +# A second tagged Add-event reusing a previously committed tag must be
> +# dropped by the cross-More uniqueness gate (firmware-bug path).  Skipped
> +# for the null UUID.
> +test_cross_more_uniqueness()
> +{
> +	echo ""
> +	echo "Test: cross-More uniqueness drops second event with same tag"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +
> +	# First Add-event (single extent) commits tag_a at base_ext_dpa.
> +	inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 1
> +
> +	# Second Add-event reuses tag_a at a different DPA: must be dropped.
> +	inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> +	check_extent_cnt ${region} 1
> +
> +	# The original tag_a allocation is still usable.
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> +	check_dax_dev ${dax_dev} $base_ext_length
> +
> +	destroy_dax_dev ${dax_dev}
> +	remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +# Sharable-partition test: two extents in one tagged More-chain with
> +# device-stamped seq 1..2.  Uses the sharable memdev (serial 0xDCDC).
> +test_shared_extent_inject()
> +{
> +	echo ""
> +	echo "Test: shared extent inject on sharable partition"
> +	echo ""
> +
> +	region=$(create_dcd_region ${sharable_mem} ${sharable_decoder})
> +	check_region ${region} ${sharable_dra_size}
> +
> +	inject_shared_extent ${sharable_device} $base_ext_dpa $base_ext_length \
> +		"$test_tag_b" 1 1
> +	check_extent_cnt ${region} 0
> +	inject_shared_extent ${sharable_device} $pre_ext_dpa $pre_ext_length \
> +		"$test_tag_b" "" 2
> +	check_extent_cnt ${region} 2
> +
> +	dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_b")
> +	expected=$(($base_ext_length + $pre_ext_length))
> +	check_dax_dev ${dax_dev} $expected
> +
> +	destroy_dax_dev ${dax_dev}
> +	remove_extent ${sharable_device} $base_ext_dpa $base_ext_length "$test_tag_b"
> +	remove_extent ${sharable_device} $pre_ext_dpa $pre_ext_length "$test_tag_b"
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +# Sharable extents must arrive with a dense 1..n shared_extn_seq.  A gap
> +# (1, 3) lets the cxl side accept the extents but uuid_claim_tagged
> +# refuses the group on its density check.  Uses the sharable memdev.
> +test_seq_integrity_gap()
> +{
> +	echo ""
> +	echo "Test: sharable extents with seq gap (1,3) refused on claim"
> +	echo ""
> +
> +	region=$(create_dcd_region ${sharable_mem} ${sharable_decoder})
> +	check_region ${region} ${sharable_dra_size}
> +
> +	inject_shared_extent ${sharable_device} $base_ext_dpa $base_ext_length \
> +		"$test_tag_b" 1 1
> +	check_extent_cnt ${region} 0
> +	inject_shared_extent ${sharable_device} $pre_ext_dpa $pre_ext_length \
> +		"$test_tag_b" "" 3
> +
> +	if [ "$(jq -r '.[].extents | length' <($CXL list -r ${region} -N))" = "2" ]; then
> +		fail_create_dax_dev_with_uuid ${region} "$test_tag_b"
> +		remove_extent ${sharable_device} $base_ext_dpa $base_ext_length "$test_tag_b"
> +		remove_extent ${sharable_device} $pre_ext_dpa $pre_ext_length "$test_tag_b"
> +	fi
> +	check_extent_cnt ${region} 0
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +# CXL_DCD_EXTENT_ALIGN is 2M; an extent that is not 2M-aligned must drop
> +# the whole group.  Inject a 64M extent at a 1M-offset DPA.
> +test_alignment_rejection()
> +{
> +	echo ""
> +	echo "Test: misaligned extent drops the group"
> +	echo ""
> +
> +	region=$(create_dcd_region ${mem} ${decoder})
> +	check_region ${region} ${dra_size}
> +
> +	# 1M past base — not 2M aligned
> +	mis_dpa=$(($base_ext_dpa + 0x100000))
> +	inject_extent ${device} $mis_dpa $base_ext_length ""
> +	check_extent_cnt ${region} 0
> +
> +	destroy_region ${region}
> +	check_not_region ${region}
> +}
> +
> +test_event_reporting()
> +{
> +	# Test event reporting
> +	# results expected
> +	num_dcd_events_expected=2
> +
> +	echo "Test: Prep event trace"
> +	echo "" > /sys/kernel/tracing/trace
> +	echo 1 > /sys/kernel/tracing/events/cxl/enable
> +	echo 1 > /sys/kernel/tracing/tracing_on
> +
> +	inject_extent ${device} $base_ext_dpa $base_ext_length ""
> +	remove_extent ${device} $base_ext_dpa $base_ext_length

Missing the tag arg

DJ

> +
> +	echo 0 > /sys/kernel/tracing/tracing_on
> +
> +	echo "Test: Events seen"
> +	trace_out=$(cat /sys/kernel/tracing/trace)
> +
> +	# Look for DCD events
> +	num_dcd_events=$(grep -c "cxl_dynamic_capacity" <<< "${trace_out}")
> +	echo "     LOG     (Expected) : (Found)"
> +	echo "     DCD events    ($num_dcd_events_expected) : $num_dcd_events"
> +
> +	if [ "$num_dcd_events" -ne $num_dcd_events_expected ]; then
> +		err "$LINENO"
> +	fi
> +}
> +
> +
> +# ========================================================================
> +# main()
> +# ========================================================================
> +
> +modprobe -r cxl_test
> +modprobe cxl_test
> +
> +# The mock stamps a single cxl_mem instance with this serial (0xDCDC).
> +# That memdev's DC partition is advertised as sharable in CDAT and is
> +# the only one that can host the shared-extent tests.  All other DCD
> +# memdevs stay non-sharable and host the rest of the suite.
> +MOCK_DC_SHARABLE_SERIAL=56540
> +
> +readarray -t memdevs < <("$CXL" list -b cxl_test -Mi | jq -r '.[].memdev')
> +
> +sharable_mem=""
> +sharable_decoder=""
> +sharable_bus=""
> +sharable_device=""
> +sharable_dra_size=""
> +
> +for cand in ${memdevs[@]}; do
> +	cand_dra=$($CXL list -m $cand | jq -r '.[].dynamic_ram_a_size')
> +	if [ "$cand_dra" == "null" ]; then
> +		continue
> +	fi
> +	cand_decoder=$($CXL list -b cxl_test -D -d root -m "$cand" |
> +		  jq -r ".[] |
> +		  select(.volatile_capable == true) |
> +		  select(.nr_targets == 1) |
> +		  select(.max_available_extent >= ${cand_dra}) |
> +		  .decoder")
> +	if [[ -z "$cand_decoder" ]]; then
> +		continue
> +	fi
> +	cand_serial=$($CXL list -m $cand | jq -r '.[].serial')
> +	cand_bus=`"$CXL" list -b cxl_test -m ${cand} | jq -r '.[].bus'`
> +	cand_device=$($CXL list -m $cand | jq -r '.[].host')
> +
> +	if [ "$cand_serial" == "$MOCK_DC_SHARABLE_SERIAL" ]; then
> +		if [ -z "$sharable_mem" ]; then
> +			sharable_mem="$cand"
> +			sharable_decoder="$cand_decoder"
> +			sharable_bus="$cand_bus"
> +			sharable_device="$cand_device"
> +			sharable_dra_size="$cand_dra"
> +		fi
> +	else
> +		if [ -z "$mem" ]; then
> +			mem="$cand"
> +			decoder="$cand_decoder"
> +			bus="$cand_bus"
> +			device="$cand_device"
> +			dra_size="$cand_dra"
> +		fi
> +	fi
> +
> +	if [ -n "$mem" ] && [ -n "$sharable_mem" ]; then
> +		break
> +	fi
> +done
> +
> +echo "TEST: non-sharable bus:${bus} decoder:${decoder} mem:${mem} device:${device} size:${dra_size}"
> +echo "TEST: sharable     bus:${sharable_bus} decoder:${sharable_decoder} mem:${sharable_mem} device:${sharable_device} size:${sharable_dra_size}"
> +
> +if [ "$decoder" == "" ] || [ "$device" == "" ] || [ "$dra_size" == "" ]; then
> +	echo "No non-sharable mem device/decoder found with DCD support"
> +	exit 77
> +fi
> +
> +if [ "$sharable_mem" == "" ]; then
> +	echo "No sharable mem device found (mock did not stamp MOCK_DC_SHARABLE_SERIAL)"
> +	exit 77
> +fi
> +
> +# testing pre existing extents must be called first as the extents were created
> +# by cxl-test being loaded
> +test_pre_existing_extents
> +test_remove_extent_under_dax_device
> +test_create_dax_dev_spanning_two_extents
> +test_inject_tag_support
> +test_uuid_no_match
> +test_uuid_no_match_seed_intact
> +test_uuid_aggregation
> +test_uuid_show
> +# These two run on the sharable memdev (serial $MOCK_DC_SHARABLE_SERIAL).
> +test_shared_extent_inject
> +test_seq_integrity_gap
> +test_cross_more_uniqueness
> +test_alignment_rejection
> +test_partial_extent_remove
> +test_multiple_extent_remove
> +test_destroy_region_without_extent_removal
> +test_destroy_with_extent_and_dax
> +test_dax_device_ops
> +test_reject_overlapping
> +test_two_regions
> +test_more_bit
> +test_driver_reload
> +test_event_reporting
> +
> +modprobe -r cxl_test
> +
> +check_dmesg "$LINENO"
> +
> +exit 0
> diff --git a/test/meson.build b/test/meson.build
> index e0e2193..cd06bf8 100644
> --- a/test/meson.build
> +++ b/test/meson.build
> @@ -171,6 +171,7 @@ cxl_translate = find_program('cxl-translate.sh')
>  cxl_elc = find_program('cxl-elc.sh')
>  cxl_dax_hmem = find_program('cxl-dax-hmem.sh')
>  cxl_region_replay = find_program('cxl-region-replay.sh')
> +cxl_dcd = find_program('cxl-dcd.sh')
>  
>  tests = [
>    [ 'libndctl',               libndctl,		  'ndctl' ],
> @@ -207,6 +208,7 @@ tests = [
>    [ 'cxl-elc.sh',             cxl_elc,            'cxl'   ],
>    [ 'cxl-dax-hmem.sh',        cxl_dax_hmem,       'cxl'   ],
>    [ 'cxl-region-replay.sh',   cxl_region_replay,  'cxl'   ],
> +  [ 'cxl-dcd.sh',             cxl_dcd,            'cxl'   ],
>  ]
>  
>  if get_option('destructive').enabled()


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 2/7] libcxl: Add Dynamic RAM A partition mode support
  2026-06-08 23:19   ` Dave Jiang
@ 2026-06-10  3:51     ` Richard Cheng
  2026-06-10 16:55       ` Dave Jiang
  2026-06-25  9:07     ` Anisa Su
  1 sibling, 1 reply; 31+ messages in thread
From: Richard Cheng @ 2026-06-10  3:51 UTC (permalink / raw)
  To: Dave Jiang
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Ira Weiny, Alison Schofield,
	John Groves, Gregory Price, Ira Weiny

On Mon, Jun 08, 2026 at 04:19:47PM +0800, Dave Jiang wrote:
> 
> 
> On 5/23/26 2:50 AM, Anisa Su wrote:
> > From: Ira Weiny <ira.weiny@intel.com>
> > 
> > Dynamic capacity partitions are exposed as a singular dynamic ram
> > partition.
> > 
> > Add CXL library support to read this partition information.
> > 
> > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> 
> Missing Anisa sign off.
> 
> Can probably squash this and the next commit so the usage is shown for the reviewer.
> 
> DJ
> 
> > ---
> >  Documentation/cxl/lib/libcxl.txt |  6 +++--
> >  cxl/lib/libcxl.c                 | 43 ++++++++++++++++++++++++++++++++
> >  cxl/lib/libcxl.sym               |  4 +++
> >  cxl/lib/private.h                |  3 +++
> >  cxl/libcxl.h                     | 10 +++++++-
> >  5 files changed, 63 insertions(+), 3 deletions(-)
> > 
> > diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
> > index 5c3ebd4..9921ac1 100644
> > --- a/Documentation/cxl/lib/libcxl.txt
> > +++ b/Documentation/cxl/lib/libcxl.txt
> > @@ -74,6 +74,7 @@ int cxl_memdev_get_major(struct cxl_memdev *memdev);
> >  int cxl_memdev_get_minor(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_dynamic_ram_a_size(struct cxl_memdev *memdev);
> >  const char *cxl_memdev_get_firmware_version(struct cxl_memdev *memdev);
> >  size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev);
> >  int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev);
> > @@ -93,8 +94,8 @@ The character device node for command submission can be found by default
> >  at /dev/cxl/mem%d, or created with a major / minor returned from
> >  cxl_memdev_get_{major,minor}().
> >  
> > -The 'pmem_size' and 'ram_size' attributes return the current
> > -provisioning of DPA (Device Physical Address / local capacity) in the
> > +The 'pmem_size', 'ram_size', and 'dynamic_ram_a_size' attributes return the
> > +current provisioning of DPA (Device Physical Address / local capacity) in the
> >  device.
> >  
> >  cxl_memdev_get_numa_node() returns the affinitized CPU node number if
> > @@ -453,6 +454,7 @@ enum cxl_decoder_mode {
> >  	CXL_DECODER_MODE_MIXED,
> >  	CXL_DECODER_MODE_PMEM,
> >  	CXL_DECODER_MODE_RAM,
> > +	CXL_DECODER_MODE_DYNAMIC_RAM_A,
> >  };
> >  enum cxl_decoder_mode cxl_decoder_get_mode(struct cxl_decoder *decoder);
> >  int cxl_decoder_set_mode(struct cxl_decoder *decoder, enum cxl_decoder_mode mode);
> > diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
> > index e55a7b4..be0bc03 100644
> > --- a/cxl/lib/libcxl.c
> > +++ b/cxl/lib/libcxl.c
> > @@ -501,6 +501,9 @@ CXL_EXPORT bool cxl_region_qos_class_mismatch(struct cxl_region *region)
> >  		} else if (region->mode == CXL_DECODER_MODE_PMEM) {
> >  			if (root_decoder->qos_class != memdev->pmem_qos_class)
> >  				return true;
> > +		} else if (region->mode == CXL_DECODER_MODE_DYNAMIC_RAM_A) {
> > +			if (root_decoder->qos_class != memdev->dynamic_ram_a_qos_class)
> > +				return true;
> >  		}
> >  	}
> >  
> > @@ -1426,6 +1429,10 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
> >  	if (sysfs_read_attr(ctx, path, buf) == 0)
> >  		memdev->ram_size = strtoull(buf, NULL, 0);
> >  
> > +	sprintf(path, "%s/dynamic_ram_a/size", cxlmem_base);
> > +	if (sysfs_read_attr(ctx, path, buf) == 0)
> > +		memdev->dynamic_ram_a_size = strtoull(buf, NULL, 0);
> > +
> >  	sprintf(path, "%s/pmem/qos_class", cxlmem_base);
> >  	if (sysfs_read_attr(ctx, path, buf) < 0)
> >  		memdev->pmem_qos_class = CXL_QOS_CLASS_NONE;
> > @@ -1438,6 +1445,12 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
> >  	else
> >  		memdev->ram_qos_class = atoi(buf);
> >  
> > +	sprintf(path, "%s/dynamic_ram_a/qos_class", cxlmem_base);
> > +	if (sysfs_read_attr(ctx, path, buf) < 0)
> > +		memdev->dynamic_ram_a_qos_class = CXL_QOS_CLASS_NONE;
> > +	else
> > +		memdev->dynamic_ram_a_qos_class = atoi(buf);
> > +
> >  	sprintf(path, "%s/payload_max", cxlmem_base);
> >  	if (sysfs_read_attr(ctx, path, buf) == 0) {
> >  		memdev->payload_max = strtoull(buf, NULL, 0);
> > @@ -1685,6 +1698,11 @@ 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_dynamic_ram_a_size(struct cxl_memdev *memdev)
> > +{
> > +	return memdev->dynamic_ram_a_size;
> > +}
> > +
> >  CXL_EXPORT int cxl_memdev_get_pmem_qos_class(struct cxl_memdev *memdev)
> >  {
> >  	return memdev->pmem_qos_class;
> > @@ -1695,6 +1713,11 @@ CXL_EXPORT int cxl_memdev_get_ram_qos_class(struct cxl_memdev *memdev)
> >  	return memdev->ram_qos_class;
> >  }
> >  
> > +CXL_EXPORT int cxl_memdev_get_dynamic_ram_a_qos_class(struct cxl_memdev *memdev)
> > +{
> > +	return memdev->dynamic_ram_a_qos_class;
> > +}
> > +
> >  CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev)
> >  {
> >  	return memdev->firmware_version;
> > @@ -2465,6 +2488,8 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
> >  			decoder->mode = CXL_DECODER_MODE_MIXED;
> >  		else if (strcmp(buf, "none") == 0)
> >  			decoder->mode = CXL_DECODER_MODE_NONE;
> > +		else if (strcmp(buf, "dynamic_ram_a") == 0)
> > +			decoder->mode = CXL_DECODER_MODE_DYNAMIC_RAM_A;
> >  		else
> >  			decoder->mode = CXL_DECODER_MODE_MIXED;
> >  	} else
> > @@ -2504,6 +2529,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
> >  	case CXL_PORT_SWITCH:
> >  		decoder->pmem_capable = true;
> >  		decoder->volatile_capable = true;
> > +		decoder->dynamic_ram_a_capable = true;
> >  		decoder->mem_capable = true;
> >  		decoder->accelmem_capable = true;
> >  		sprintf(path, "%s/locked", cxldecoder_base);
> > @@ -2528,6 +2554,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
> >  			{ "cap_type3", &decoder->mem_capable },
> >  			{ "cap_ram", &decoder->volatile_capable },
> >  			{ "cap_pmem", &decoder->pmem_capable },
> > +			{ "cap_dynamic_ram_a", &decoder->dynamic_ram_a_capable },
> >  			{ "locked", &decoder->locked },
> >  		};
> >  
> > @@ -2778,6 +2805,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_DYNAMIC_RAM_A:
> > +		sprintf(buf, "dynamic_ram_a");
> > +		break;
> >  	default:
> >  		err(ctx, "%s: unsupported mode: %d\n",
> >  		    cxl_decoder_get_devname(decoder), mode);
> > @@ -2829,6 +2859,11 @@ CXL_EXPORT bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder)
> >  	return decoder->volatile_capable;
> >  }
> >  
> > +CXL_EXPORT bool cxl_decoder_is_dynamic_ram_a_capable(struct cxl_decoder *decoder)
> > +{
> > +	return decoder->dynamic_ram_a_capable;
> > +}
> > +
> >  CXL_EXPORT bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder)
> >  {
> >  	return decoder->mem_capable;
> > @@ -2903,6 +2938,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_DYNAMIC_RAM_A)
> > +		sprintf(path, "%s/create_dynamic_ram_a_region", decoder->dev_path);
> >  
> >  	rc = sysfs_read_attr(ctx, path, buf);
> >  	if (rc < 0) {
> > @@ -2954,6 +2991,12 @@ cxl_decoder_create_ram_region(struct cxl_decoder *decoder)
> >  	return cxl_decoder_create_region(decoder, CXL_DECODER_MODE_RAM);
> >  }
> >  
> > +CXL_EXPORT struct cxl_region *
> > +cxl_decoder_create_dynamic_ram_a_region(struct cxl_decoder *decoder)
> > +{
> > +	return cxl_decoder_create_region(decoder, CXL_DECODER_MODE_DYNAMIC_RAM_A);
> > +}
> > +
> >  CXL_EXPORT int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder)
> >  {
> >  	return decoder->nr_targets;
> > diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> > index ed4429f..258bdd3 100644
> > --- a/cxl/lib/libcxl.sym
> > +++ b/cxl/lib/libcxl.sym
> > @@ -294,6 +294,10 @@ global:
> >  	cxl_memdev_get_fwctl;
> >  	cxl_fwctl_get_major;
> >  	cxl_fwctl_get_minor;
> > +	cxl_memdev_get_dynamic_ram_a_size;
> > +	cxl_memdev_get_dynamic_ram_a_qos_class;
> > +	cxl_decoder_is_dynamic_ram_a_capable;
> > +	cxl_decoder_create_dynamic_ram_a_region;
> >  } LIBECXL_8;
> >  
> >  LIBCXL_10 {

Shouldn't new exported symbols go in a fresh top-level node ?
Something like LIBCXL_12 ? please note that Patch 4 has the same
issue.

Please let me know if I'm wrong or misunderstand anything.

Best regards,
Richard Cheng.

> > diff --git a/cxl/lib/private.h b/cxl/lib/private.h
> > index d2d71fc..37b7b06 100644
> > --- a/cxl/lib/private.h
> > +++ b/cxl/lib/private.h
> > @@ -52,8 +52,10 @@ struct cxl_memdev {
> >  	struct list_node list;
> >  	unsigned long long pmem_size;
> >  	unsigned long long ram_size;
> > +	unsigned long long dynamic_ram_a_size;
> >  	int ram_qos_class;
> >  	int pmem_qos_class;
> > +	int dynamic_ram_a_qos_class;
> >  	int payload_max;
> >  	size_t lsa_size;
> >  	struct kmod_module *module;
> > @@ -159,6 +161,7 @@ struct cxl_decoder {
> >  	unsigned int interleave_granularity;
> >  	bool pmem_capable;
> >  	bool volatile_capable;
> > +	bool dynamic_ram_a_capable;
> >  	bool mem_capable;
> >  	bool accelmem_capable;
> >  	bool locked;
> > diff --git a/cxl/libcxl.h b/cxl/libcxl.h
> > index e91af90..fd41122 100644
> > --- a/cxl/libcxl.h
> > +++ b/cxl/libcxl.h
> > @@ -75,8 +75,10 @@ struct cxl_fwctl *cxl_memdev_get_fwctl(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_dynamic_ram_a_size(struct cxl_memdev *memdev);
> >  int cxl_memdev_get_pmem_qos_class(struct cxl_memdev *memdev);
> >  int cxl_memdev_get_ram_qos_class(struct cxl_memdev *memdev);
> > +int cxl_memdev_get_dynamic_ram_a_qos_class(struct cxl_memdev *memdev);
> >  const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
> >  bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev);
> >  size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev);
> > @@ -210,6 +212,7 @@ enum cxl_decoder_mode {
> >  	CXL_DECODER_MODE_MIXED,
> >  	CXL_DECODER_MODE_PMEM,
> >  	CXL_DECODER_MODE_RAM,
> > +	CXL_DECODER_MODE_DYNAMIC_RAM_A,
> >  };
> >  
> >  static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode)
> > @@ -219,9 +222,10 @@ static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode)
> >  		[CXL_DECODER_MODE_MIXED] = "mixed",
> >  		[CXL_DECODER_MODE_PMEM] = "pmem",
> >  		[CXL_DECODER_MODE_RAM] = "ram",
> > +		[CXL_DECODER_MODE_DYNAMIC_RAM_A] = "dynamic_ram_a",
> >  	};
> >  
> > -	if (mode < CXL_DECODER_MODE_NONE || mode > CXL_DECODER_MODE_RAM)
> > +	if (mode < CXL_DECODER_MODE_NONE || mode > CXL_DECODER_MODE_DYNAMIC_RAM_A)
> >  		mode = CXL_DECODER_MODE_NONE;
> >  	return names[mode];
> >  }
> > @@ -235,6 +239,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, "dynamic_ram_a") == 0)
> > +		return CXL_DECODER_MODE_DYNAMIC_RAM_A;
> >  	return CXL_DECODER_MODE_NONE;
> >  }
> >  
> > @@ -264,6 +270,7 @@ cxl_decoder_get_target_type(struct cxl_decoder *decoder);
> >  bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder);
> >  bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder);
> >  bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder);
> > +bool cxl_decoder_is_dynamic_ram_a_capable(struct cxl_decoder *decoder);
> >  bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder);
> >  bool cxl_decoder_is_locked(struct cxl_decoder *decoder);
> >  unsigned int
> > @@ -272,6 +279,7 @@ 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_pmem_region(struct cxl_decoder *decoder);
> >  struct cxl_region *cxl_decoder_create_ram_region(struct cxl_decoder *decoder);
> > +struct cxl_region *cxl_decoder_create_dynamic_ram_a_region(struct cxl_decoder *decoder);
> >  struct cxl_decoder *cxl_decoder_get_by_name(struct cxl_ctx *ctx,
> >  					    const char *ident);
> >  struct cxl_memdev *cxl_decoder_get_memdev(struct cxl_decoder *decoder);
> 
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 5/7] cxl/region: Add extent output to region query
  2026-06-09  0:08   ` Dave Jiang
@ 2026-06-10  3:55     ` Richard Cheng
  2026-06-16 10:47       ` Anisa Su
  2026-06-16 10:45     ` Anisa Su
  1 sibling, 1 reply; 31+ messages in thread
From: Richard Cheng @ 2026-06-10  3:55 UTC (permalink / raw)
  To: Dave Jiang
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Ira Weiny, Alison Schofield,
	John Groves, Gregory Price, Ira Weiny

On Mon, Jun 08, 2026 at 05:08:19PM +0800, Dave Jiang wrote:
> 
> 
> On 5/23/26 2:50 AM, Anisa Su wrote:
> > From: Ira Weiny <ira.weiny@intel.com>
> > 
> > DCD regions have 0 or more extents.  The ability to list those and their
> > properties is useful to end users.
> > 
> > Add an option for extent output to region queries.  An example of this
> > is:
> > 
> > 	$ ./build/cxl/cxl list -r 8 -Nu
> > 	{
> > 	  "region":"region8",
> > 	  ...
> > 	  "type":"dc",
> > 	  ...
> > 	  "extents":[
> > 	    {
> > 	      "offset":"0x10000000",
> > 	      "length":"64.00 MiB (67.11 MB)",
> > 	      "tag":"00000000-0000-0000-0000-000000000000"
> 
> I think the code emits "uuid". Update commit log.
> > 	    },
> > 	    {
> > 	      "offset":"0x8000000",
> > 	      "length":"64.00 MiB (67.11 MB)",
> > 	      "tag":"00000000-0000-0000-0000-000000000000"
> 
> same here
> 
> DJ
> 
> > 	    }
> > 	  ]
> > 	}
> > 
> > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> > 
> > ---
> > Changes:
> > [iweiny: s/tag/uuid/]
> > ---
> >  Documentation/cxl/cxl-list.txt | 29 +++++++++++++++++++++
> >  cxl/filter.h                   |  3 +++
> >  cxl/json.c                     | 47 ++++++++++++++++++++++++++++++++++
> >  cxl/json.h                     |  3 +++
> >  cxl/list.c                     |  3 +++
> >  util/json.h                    |  1 +
> >  6 files changed, 86 insertions(+)
> > 
> > diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
> > index 193860b..7512687 100644
> > --- a/Documentation/cxl/cxl-list.txt
> > +++ b/Documentation/cxl/cxl-list.txt
> > @@ -426,6 +426,35 @@ OPTIONS
> >  }
> >  ----
> >  
> > +-N::
> > +--extents::
> > +	Append Dynamic Capacity extent information.
> > +----
> > +13:34:28 > ./build/cxl/cxl list -r 8 -Nu
> > +{
> > +  "region":"region8",
> > +  "resource":"0xf030000000",
> > +  "size":"512.00 MiB (536.87 MB)",
> > +  "type":"dc",
> > +  "interleave_ways":1,
> > +  "interleave_granularity":256,
> > +  "decode_state":"commit",
> > +  "extents":[
> > +    {
> > +      "offset":"0x10000000",
> > +      "length":"64.00 MiB (67.11 MB)",
> > +      "uuid":"00000000-0000-0000-0000-000000000000"
> > +    },
> > +    {
> > +      "offset":"0x8000000",
> > +      "length":"64.00 MiB (67.11 MB)",
> > +      "uuid":"00000000-0000-0000-0000-000000000000"
> > +    }
> > +  ]
> > +}
> > +----
> > +
> > +
> >  -r::
> >  --region::
> >  	Specify CXL region device name(s), or device id(s), to filter the listing.
> > diff --git a/cxl/filter.h b/cxl/filter.h
> > index 70463c4..30e7fe2 100644
> > --- a/cxl/filter.h
> > +++ b/cxl/filter.h
> > @@ -31,6 +31,7 @@ struct cxl_filter_params {
> >  	bool alert_config;
> >  	bool dax;
> >  	bool media_errors;
> > +	bool extents;
> >  	int verbose;
> >  	struct log_ctx ctx;
> >  };
> > @@ -93,6 +94,8 @@ static inline unsigned long cxl_filter_to_flags(struct cxl_filter_params *param)
> >  		flags |= UTIL_JSON_DAX | UTIL_JSON_DAX_DEVS;
> >  	if (param->media_errors)
> >  		flags |= UTIL_JSON_MEDIA_ERRORS;
> > +	if (param->extents)
> > +		flags |= UTIL_JSON_EXTENTS;
> >  	return flags;
> >  }
> >  
> > diff --git a/cxl/json.c b/cxl/json.c
> > index e94c809..7922b32 100644
> > --- a/cxl/json.c
> > +++ b/cxl/json.c
> > @@ -1022,6 +1022,50 @@ void util_cxl_mappings_append_json(struct json_object *jregion,
> >  	json_object_object_add(jregion, "mappings", jmappings);
> >  }
> >  
> > +void util_cxl_extents_append_json(struct json_object *jregion,
> > +				  struct cxl_region *region,
> > +				  unsigned long flags)
> > +{
> > +	struct json_object *jextents;
> > +	struct cxl_region_extent *extent;
> > +
> > +	jextents = json_object_new_array();
> > +	if (!jextents)
> > +		return;
> > +
> > +	cxl_extent_foreach(region, extent) {

Every region rendered with the flag, including non-DC RAM/PMEM regions.
They all get a spurious "extents": [].
I would suggest guarding on cxl_extent_get_first(region) != NULL before
adding the key.

What do you think?

Best regards,
Richard Cheng.

> > +		struct json_object *jextent, *jobj;
> > +		unsigned long long val;
> > +		char uuid_str[40];
> > +		uuid_t uuid;
> > +
> > +		jextent = json_object_new_object();
> > +		if (!jextent)
> > +			continue;
> > +
> > +		val = cxl_extent_get_offset(extent);
> > +		jobj = util_json_object_hex(val, flags);
> > +		if (jobj)
> > +			json_object_object_add(jextent, "offset", jobj);
> > +
> > +		val = cxl_extent_get_length(extent);
> > +		jobj = util_json_object_size(val, flags);
> > +		if (jobj)
> > +			json_object_object_add(jextent, "length", jobj);
> > +
> > +		cxl_extent_get_uuid(extent, uuid);
> > +		uuid_unparse(uuid, uuid_str);
> > +		jobj = json_object_new_string(uuid_str);
> > +		if (jobj)
> > +			json_object_object_add(jextent, "uuid", jobj);
> > +
> > +		json_object_array_add(jextents, jextent);
> > +		json_object_set_userdata(jextent, extent, NULL);
> > +	}
> > +
> > +	json_object_object_add(jregion, "extents", jextents);
> > +}
> > +
> >  struct json_object *util_cxl_region_to_json(struct cxl_region *region,
> >  					     unsigned long flags)
> >  {
> > @@ -1126,6 +1170,9 @@ struct json_object *util_cxl_region_to_json(struct cxl_region *region,
> >  		}
> >  	}
> >  
> > +	if (flags & UTIL_JSON_EXTENTS)
> > +		util_cxl_extents_append_json(jregion, region, flags);
> > +
> >  	if (cxl_region_qos_class_mismatch(region)) {
> >  		jobj = json_object_new_boolean(true);
> >  		if (jobj)
> > diff --git a/cxl/json.h b/cxl/json.h
> > index eb7572b..f9c07ab 100644
> > --- a/cxl/json.h
> > +++ b/cxl/json.h
> > @@ -20,6 +20,9 @@ struct json_object *util_cxl_region_to_json(struct cxl_region *region,
> >  void util_cxl_mappings_append_json(struct json_object *jregion,
> >  				  struct cxl_region *region,
> >  				  unsigned long flags);
> > +void util_cxl_extents_append_json(struct json_object *jregion,
> > +				  struct cxl_region *region,
> > +				  unsigned long flags);
> >  void util_cxl_targets_append_json(struct json_object *jdecoder,
> >  				  struct cxl_decoder *decoder,
> >  				  const char *ident, const char *serial,
> > diff --git a/cxl/list.c b/cxl/list.c
> > index 0b25d78..47d1351 100644
> > --- a/cxl/list.c
> > +++ b/cxl/list.c
> > @@ -59,6 +59,8 @@ static const struct option options[] = {
> >  		    "include alert configuration information"),
> >  	OPT_BOOLEAN('L', "media-errors", &param.media_errors,
> >  		    "include media-error information "),
> > +	OPT_BOOLEAN('N', "extents", &param.extents,
> > +		    "include extent information (Dynamic Capacity regions only)"),
> >  	OPT_INCR('v', "verbose", &param.verbose, "increase output detail"),
> >  #ifdef ENABLE_DEBUG
> >  	OPT_BOOLEAN(0, "debug", &debug, "debug list walk"),
> > @@ -135,6 +137,7 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
> >  		param.decoders = true;
> >  		param.targets = true;
> >  		param.regions = true;
> > +		param.extents = true;
> >  		/*fallthrough*/
> >  	case 0:
> >  		break;
> > diff --git a/util/json.h b/util/json.h
> > index 560f845..79ae324 100644
> > --- a/util/json.h
> > +++ b/util/json.h
> > @@ -21,6 +21,7 @@ enum util_json_flags {
> >  	UTIL_JSON_TARGETS	= (1 << 11),
> >  	UTIL_JSON_PARTITION	= (1 << 12),
> >  	UTIL_JSON_ALERT_CONFIG	= (1 << 13),
> > +	UTIL_JSON_EXTENTS	= (1 << 14),
> >  };
> >  
> >  void util_display_json_array(FILE *f_out, struct json_object *jarray,
> 
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 2/7] libcxl: Add Dynamic RAM A partition mode support
  2026-06-10  3:51     ` Richard Cheng
@ 2026-06-10 16:55       ` Dave Jiang
  2026-06-25  9:08         ` Anisa Su
  0 siblings, 1 reply; 31+ messages in thread
From: Dave Jiang @ 2026-06-10 16:55 UTC (permalink / raw)
  To: Richard Cheng
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Ira Weiny, Alison Schofield,
	John Groves, Gregory Price, Ira Weiny



On 6/9/26 8:51 PM, Richard Cheng wrote:
> On Mon, Jun 08, 2026 at 04:19:47PM +0800, Dave Jiang wrote:
>>
>>
>> On 5/23/26 2:50 AM, Anisa Su wrote:
>>> From: Ira Weiny <ira.weiny@intel.com>

<-- snip -->

>>> diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
>>> index ed4429f..258bdd3 100644
>>> --- a/cxl/lib/libcxl.sym
>>> +++ b/cxl/lib/libcxl.sym
>>> @@ -294,6 +294,10 @@ global:
>>>  	cxl_memdev_get_fwctl;
>>>  	cxl_fwctl_get_major;
>>>  	cxl_fwctl_get_minor;
>>> +	cxl_memdev_get_dynamic_ram_a_size;
>>> +	cxl_memdev_get_dynamic_ram_a_qos_class;
>>> +	cxl_decoder_is_dynamic_ram_a_capable;
>>> +	cxl_decoder_create_dynamic_ram_a_region;
>>>  } LIBECXL_8;
>>>  
>>>  LIBCXL_10 {
> 
> Shouldn't new exported symbols go in a fresh top-level node ?
> Something like LIBCXL_12 ? please note that Patch 4 has the same
> issue.
> 
> Please let me know if I'm wrong or misunderstand anything.
You are correct. These need to be moved to a new block for a new release. 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 5/7] cxl/region: Add extent output to region query
  2026-06-09  0:08   ` Dave Jiang
  2026-06-10  3:55     ` Richard Cheng
@ 2026-06-16 10:45     ` Anisa Su
  1 sibling, 0 replies; 31+ messages in thread
From: Anisa Su @ 2026-06-16 10:45 UTC (permalink / raw)
  To: Dave Jiang
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Ira Weiny, Alison Schofield,
	John Groves, Gregory Price, Ira Weiny

On Mon, Jun 08, 2026 at 05:08:19PM -0700, Dave Jiang wrote:
> 
> 
> On 5/23/26 2:50 AM, Anisa Su wrote:
> > From: Ira Weiny <ira.weiny@intel.com>
> > 
> > DCD regions have 0 or more extents.  The ability to list those and their
> > properties is useful to end users.
> > 
> > Add an option for extent output to region queries.  An example of this
> > is:
> > 
> > 	$ ./build/cxl/cxl list -r 8 -Nu
> > 	{
> > 	  "region":"region8",
> > 	  ...
> > 	  "type":"dc",
> > 	  ...
> > 	  "extents":[
> > 	    {
> > 	      "offset":"0x10000000",
> > 	      "length":"64.00 MiB (67.11 MB)",
> > 	      "tag":"00000000-0000-0000-0000-000000000000"
> 
> I think the code emits "uuid". Update commit log.

oh yeah... updated~

> > 	    },
> > 	    {
> > 	      "offset":"0x8000000",
> > 	      "length":"64.00 MiB (67.11 MB)",
> > 	      "tag":"00000000-0000-0000-0000-000000000000"
> 
> same here
> 
Also updated

> DJ
> 
Thanks,
Anisa
> > 	    }
> > 	  ]
> > 	}
> > 
> > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> > 
> > ---
> > Changes:
> > [iweiny: s/tag/uuid/]
> > ---
> >  Documentation/cxl/cxl-list.txt | 29 +++++++++++++++++++++
> >  cxl/filter.h                   |  3 +++
> >  cxl/json.c                     | 47 ++++++++++++++++++++++++++++++++++
> >  cxl/json.h                     |  3 +++
> >  cxl/list.c                     |  3 +++
> >  util/json.h                    |  1 +
> >  6 files changed, 86 insertions(+)
> > 
> > diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
> > index 193860b..7512687 100644
> > --- a/Documentation/cxl/cxl-list.txt
> > +++ b/Documentation/cxl/cxl-list.txt
> > @@ -426,6 +426,35 @@ OPTIONS
> >  }
> >  ----
> >  
> > +-N::
> > +--extents::
> > +	Append Dynamic Capacity extent information.
> > +----
> > +13:34:28 > ./build/cxl/cxl list -r 8 -Nu
> > +{
> > +  "region":"region8",
> > +  "resource":"0xf030000000",
> > +  "size":"512.00 MiB (536.87 MB)",
> > +  "type":"dc",
> > +  "interleave_ways":1,
> > +  "interleave_granularity":256,
> > +  "decode_state":"commit",
> > +  "extents":[
> > +    {
> > +      "offset":"0x10000000",
> > +      "length":"64.00 MiB (67.11 MB)",
> > +      "uuid":"00000000-0000-0000-0000-000000000000"
> > +    },
> > +    {
> > +      "offset":"0x8000000",
> > +      "length":"64.00 MiB (67.11 MB)",
> > +      "uuid":"00000000-0000-0000-0000-000000000000"
> > +    }
> > +  ]
> > +}
> > +----
> > +
> > +
> >  -r::
> >  --region::
> >  	Specify CXL region device name(s), or device id(s), to filter the listing.
> > diff --git a/cxl/filter.h b/cxl/filter.h
> > index 70463c4..30e7fe2 100644
> > --- a/cxl/filter.h
> > +++ b/cxl/filter.h
> > @@ -31,6 +31,7 @@ struct cxl_filter_params {
> >  	bool alert_config;
> >  	bool dax;
> >  	bool media_errors;
> > +	bool extents;
> >  	int verbose;
> >  	struct log_ctx ctx;
> >  };
> > @@ -93,6 +94,8 @@ static inline unsigned long cxl_filter_to_flags(struct cxl_filter_params *param)
> >  		flags |= UTIL_JSON_DAX | UTIL_JSON_DAX_DEVS;
> >  	if (param->media_errors)
> >  		flags |= UTIL_JSON_MEDIA_ERRORS;
> > +	if (param->extents)
> > +		flags |= UTIL_JSON_EXTENTS;
> >  	return flags;
> >  }
> >  
> > diff --git a/cxl/json.c b/cxl/json.c
> > index e94c809..7922b32 100644
> > --- a/cxl/json.c
> > +++ b/cxl/json.c
> > @@ -1022,6 +1022,50 @@ void util_cxl_mappings_append_json(struct json_object *jregion,
> >  	json_object_object_add(jregion, "mappings", jmappings);
> >  }
> >  
> > +void util_cxl_extents_append_json(struct json_object *jregion,
> > +				  struct cxl_region *region,
> > +				  unsigned long flags)
> > +{
> > +	struct json_object *jextents;
> > +	struct cxl_region_extent *extent;
> > +
> > +	jextents = json_object_new_array();
> > +	if (!jextents)
> > +		return;
> > +
> > +	cxl_extent_foreach(region, extent) {
> > +		struct json_object *jextent, *jobj;
> > +		unsigned long long val;
> > +		char uuid_str[40];
> > +		uuid_t uuid;
> > +
> > +		jextent = json_object_new_object();
> > +		if (!jextent)
> > +			continue;
> > +
> > +		val = cxl_extent_get_offset(extent);
> > +		jobj = util_json_object_hex(val, flags);
> > +		if (jobj)
> > +			json_object_object_add(jextent, "offset", jobj);
> > +
> > +		val = cxl_extent_get_length(extent);
> > +		jobj = util_json_object_size(val, flags);
> > +		if (jobj)
> > +			json_object_object_add(jextent, "length", jobj);
> > +
> > +		cxl_extent_get_uuid(extent, uuid);
> > +		uuid_unparse(uuid, uuid_str);
> > +		jobj = json_object_new_string(uuid_str);
> > +		if (jobj)
> > +			json_object_object_add(jextent, "uuid", jobj);
> > +
> > +		json_object_array_add(jextents, jextent);
> > +		json_object_set_userdata(jextent, extent, NULL);
> > +	}
> > +
> > +	json_object_object_add(jregion, "extents", jextents);
> > +}
> > +
> >  struct json_object *util_cxl_region_to_json(struct cxl_region *region,
> >  					     unsigned long flags)
> >  {
> > @@ -1126,6 +1170,9 @@ struct json_object *util_cxl_region_to_json(struct cxl_region *region,
> >  		}
> >  	}
> >  
> > +	if (flags & UTIL_JSON_EXTENTS)
> > +		util_cxl_extents_append_json(jregion, region, flags);
> > +
> >  	if (cxl_region_qos_class_mismatch(region)) {
> >  		jobj = json_object_new_boolean(true);
> >  		if (jobj)
> > diff --git a/cxl/json.h b/cxl/json.h
> > index eb7572b..f9c07ab 100644
> > --- a/cxl/json.h
> > +++ b/cxl/json.h
> > @@ -20,6 +20,9 @@ struct json_object *util_cxl_region_to_json(struct cxl_region *region,
> >  void util_cxl_mappings_append_json(struct json_object *jregion,
> >  				  struct cxl_region *region,
> >  				  unsigned long flags);
> > +void util_cxl_extents_append_json(struct json_object *jregion,
> > +				  struct cxl_region *region,
> > +				  unsigned long flags);
> >  void util_cxl_targets_append_json(struct json_object *jdecoder,
> >  				  struct cxl_decoder *decoder,
> >  				  const char *ident, const char *serial,
> > diff --git a/cxl/list.c b/cxl/list.c
> > index 0b25d78..47d1351 100644
> > --- a/cxl/list.c
> > +++ b/cxl/list.c
> > @@ -59,6 +59,8 @@ static const struct option options[] = {
> >  		    "include alert configuration information"),
> >  	OPT_BOOLEAN('L', "media-errors", &param.media_errors,
> >  		    "include media-error information "),
> > +	OPT_BOOLEAN('N', "extents", &param.extents,
> > +		    "include extent information (Dynamic Capacity regions only)"),
> >  	OPT_INCR('v', "verbose", &param.verbose, "increase output detail"),
> >  #ifdef ENABLE_DEBUG
> >  	OPT_BOOLEAN(0, "debug", &debug, "debug list walk"),
> > @@ -135,6 +137,7 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
> >  		param.decoders = true;
> >  		param.targets = true;
> >  		param.regions = true;
> > +		param.extents = true;
> >  		/*fallthrough*/
> >  	case 0:
> >  		break;
> > diff --git a/util/json.h b/util/json.h
> > index 560f845..79ae324 100644
> > --- a/util/json.h
> > +++ b/util/json.h
> > @@ -21,6 +21,7 @@ enum util_json_flags {
> >  	UTIL_JSON_TARGETS	= (1 << 11),
> >  	UTIL_JSON_PARTITION	= (1 << 12),
> >  	UTIL_JSON_ALERT_CONFIG	= (1 << 13),
> > +	UTIL_JSON_EXTENTS	= (1 << 14),
> >  };
> >  
> >  void util_display_json_array(FILE *f_out, struct json_object *jarray,
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 5/7] cxl/region: Add extent output to region query
  2026-06-10  3:55     ` Richard Cheng
@ 2026-06-16 10:47       ` Anisa Su
  0 siblings, 0 replies; 31+ messages in thread
From: Anisa Su @ 2026-06-16 10:47 UTC (permalink / raw)
  To: Richard Cheng
  Cc: Dave Jiang, Anisa Su, linux-cxl, linux-kernel, nvdimm,
	Dan Williams, Jonathan Cameron, Davidlohr Bueso, Ira Weiny,
	Alison Schofield, John Groves, Gregory Price, Ira Weiny

On Wed, Jun 10, 2026 at 11:55:12AM +0800, Richard Cheng wrote:
> On Mon, Jun 08, 2026 at 05:08:19PM +0800, Dave Jiang wrote:
> > 
> > 
> > On 5/23/26 2:50 AM, Anisa Su wrote:
> > > From: Ira Weiny <ira.weiny@intel.com>
> > > 
> > > DCD regions have 0 or more extents.  The ability to list those and their
> > > properties is useful to end users.
> > > 
> > > Add an option for extent output to region queries.  An example of this
> > > is:
> > > 
> > > 	$ ./build/cxl/cxl list -r 8 -Nu
> > > 	{
> > > 	  "region":"region8",
> > > 	  ...
> > > 	  "type":"dc",
> > > 	  ...
> > > 	  "extents":[
> > > 	    {
> > > 	      "offset":"0x10000000",
> > > 	      "length":"64.00 MiB (67.11 MB)",
> > > 	      "tag":"00000000-0000-0000-0000-000000000000"
> > 
> > I think the code emits "uuid". Update commit log.
> > > 	    },
> > > 	    {
> > > 	      "offset":"0x8000000",
> > > 	      "length":"64.00 MiB (67.11 MB)",
> > > 	      "tag":"00000000-0000-0000-0000-000000000000"
> > 
> > same here
> > 
> > DJ
> > 
> > > 	    }
> > > 	  ]
> > > 	}
> > > 
> > > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> > > 
> > > ---
> > > Changes:
> > > [iweiny: s/tag/uuid/]
> > > ---
> > >  Documentation/cxl/cxl-list.txt | 29 +++++++++++++++++++++
> > >  cxl/filter.h                   |  3 +++
> > >  cxl/json.c                     | 47 ++++++++++++++++++++++++++++++++++
> > >  cxl/json.h                     |  3 +++
> > >  cxl/list.c                     |  3 +++
> > >  util/json.h                    |  1 +
> > >  6 files changed, 86 insertions(+)
> > > 
> > > diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
> > > index 193860b..7512687 100644
> > > --- a/Documentation/cxl/cxl-list.txt
> > > +++ b/Documentation/cxl/cxl-list.txt
> > > @@ -426,6 +426,35 @@ OPTIONS
> > >  }
> > >  ----
> > >  
> > > +-N::
> > > +--extents::
> > > +	Append Dynamic Capacity extent information.
> > > +----
> > > +13:34:28 > ./build/cxl/cxl list -r 8 -Nu
> > > +{
> > > +  "region":"region8",
> > > +  "resource":"0xf030000000",
> > > +  "size":"512.00 MiB (536.87 MB)",
> > > +  "type":"dc",
> > > +  "interleave_ways":1,
> > > +  "interleave_granularity":256,
> > > +  "decode_state":"commit",
> > > +  "extents":[
> > > +    {
> > > +      "offset":"0x10000000",
> > > +      "length":"64.00 MiB (67.11 MB)",
> > > +      "uuid":"00000000-0000-0000-0000-000000000000"
> > > +    },
> > > +    {
> > > +      "offset":"0x8000000",
> > > +      "length":"64.00 MiB (67.11 MB)",
> > > +      "uuid":"00000000-0000-0000-0000-000000000000"
> > > +    }
> > > +  ]
> > > +}
> > > +----
> > > +
> > > +
> > >  -r::
> > >  --region::
> > >  	Specify CXL region device name(s), or device id(s), to filter the listing.
> > > diff --git a/cxl/filter.h b/cxl/filter.h
> > > index 70463c4..30e7fe2 100644
> > > --- a/cxl/filter.h
> > > +++ b/cxl/filter.h
> > > @@ -31,6 +31,7 @@ struct cxl_filter_params {
> > >  	bool alert_config;
> > >  	bool dax;
> > >  	bool media_errors;
> > > +	bool extents;
> > >  	int verbose;
> > >  	struct log_ctx ctx;
> > >  };
> > > @@ -93,6 +94,8 @@ static inline unsigned long cxl_filter_to_flags(struct cxl_filter_params *param)
> > >  		flags |= UTIL_JSON_DAX | UTIL_JSON_DAX_DEVS;
> > >  	if (param->media_errors)
> > >  		flags |= UTIL_JSON_MEDIA_ERRORS;
> > > +	if (param->extents)
> > > +		flags |= UTIL_JSON_EXTENTS;
> > >  	return flags;
> > >  }
> > >  
> > > diff --git a/cxl/json.c b/cxl/json.c
> > > index e94c809..7922b32 100644
> > > --- a/cxl/json.c
> > > +++ b/cxl/json.c
> > > @@ -1022,6 +1022,50 @@ void util_cxl_mappings_append_json(struct json_object *jregion,
> > >  	json_object_object_add(jregion, "mappings", jmappings);
> > >  }
> > >  
> > > +void util_cxl_extents_append_json(struct json_object *jregion,
> > > +				  struct cxl_region *region,
> > > +				  unsigned long flags)
> > > +{
> > > +	struct json_object *jextents;
> > > +	struct cxl_region_extent *extent;
> > > +
> > > +	jextents = json_object_new_array();
> > > +	if (!jextents)
> > > +		return;
> > > +
> > > +	cxl_extent_foreach(region, extent) {
> 
> Every region rendered with the flag, including non-DC RAM/PMEM regions.
> They all get a spurious "extents": [].
> I would suggest guarding on cxl_extent_get_first(region) != NULL before
> adding the key.
> 
> What do you think?
> 
Good point, thanks for catching that! Now checks
cxl_extent_get_first(region) != NULL at top of function

> Best regards,
> Richard Cheng.
> 
Thanks,
Anisa

> > > +		struct json_object *jextent, *jobj;
> > > +		unsigned long long val;
> > > +		char uuid_str[40];
> > > +		uuid_t uuid;
> > > +
> > > +		jextent = json_object_new_object();
> > > +		if (!jextent)
> > > +			continue;
> > > +
> > > +		val = cxl_extent_get_offset(extent);
> > > +		jobj = util_json_object_hex(val, flags);
> > > +		if (jobj)
> > > +			json_object_object_add(jextent, "offset", jobj);
> > > +
> > > +		val = cxl_extent_get_length(extent);
> > > +		jobj = util_json_object_size(val, flags);
> > > +		if (jobj)
> > > +			json_object_object_add(jextent, "length", jobj);
> > > +
> > > +		cxl_extent_get_uuid(extent, uuid);
> > > +		uuid_unparse(uuid, uuid_str);
> > > +		jobj = json_object_new_string(uuid_str);
> > > +		if (jobj)
> > > +			json_object_object_add(jextent, "uuid", jobj);
> > > +
> > > +		json_object_array_add(jextents, jextent);
> > > +		json_object_set_userdata(jextent, extent, NULL);
> > > +	}
> > > +
> > > +	json_object_object_add(jregion, "extents", jextents);
> > > +}
> > > +
> > >  struct json_object *util_cxl_region_to_json(struct cxl_region *region,
> > >  					     unsigned long flags)
> > >  {
> > > @@ -1126,6 +1170,9 @@ struct json_object *util_cxl_region_to_json(struct cxl_region *region,
> > >  		}
> > >  	}
> > >  
> > > +	if (flags & UTIL_JSON_EXTENTS)
> > > +		util_cxl_extents_append_json(jregion, region, flags);
> > > +
> > >  	if (cxl_region_qos_class_mismatch(region)) {
> > >  		jobj = json_object_new_boolean(true);
> > >  		if (jobj)
> > > diff --git a/cxl/json.h b/cxl/json.h
> > > index eb7572b..f9c07ab 100644
> > > --- a/cxl/json.h
> > > +++ b/cxl/json.h
> > > @@ -20,6 +20,9 @@ struct json_object *util_cxl_region_to_json(struct cxl_region *region,
> > >  void util_cxl_mappings_append_json(struct json_object *jregion,
> > >  				  struct cxl_region *region,
> > >  				  unsigned long flags);
> > > +void util_cxl_extents_append_json(struct json_object *jregion,
> > > +				  struct cxl_region *region,
> > > +				  unsigned long flags);
> > >  void util_cxl_targets_append_json(struct json_object *jdecoder,
> > >  				  struct cxl_decoder *decoder,
> > >  				  const char *ident, const char *serial,
> > > diff --git a/cxl/list.c b/cxl/list.c
> > > index 0b25d78..47d1351 100644
> > > --- a/cxl/list.c
> > > +++ b/cxl/list.c
> > > @@ -59,6 +59,8 @@ static const struct option options[] = {
> > >  		    "include alert configuration information"),
> > >  	OPT_BOOLEAN('L', "media-errors", &param.media_errors,
> > >  		    "include media-error information "),
> > > +	OPT_BOOLEAN('N', "extents", &param.extents,
> > > +		    "include extent information (Dynamic Capacity regions only)"),
> > >  	OPT_INCR('v', "verbose", &param.verbose, "increase output detail"),
> > >  #ifdef ENABLE_DEBUG
> > >  	OPT_BOOLEAN(0, "debug", &debug, "debug list walk"),
> > > @@ -135,6 +137,7 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
> > >  		param.decoders = true;
> > >  		param.targets = true;
> > >  		param.regions = true;
> > > +		param.extents = true;
> > >  		/*fallthrough*/
> > >  	case 0:
> > >  		break;
> > > diff --git a/util/json.h b/util/json.h
> > > index 560f845..79ae324 100644
> > > --- a/util/json.h
> > > +++ b/util/json.h
> > > @@ -21,6 +21,7 @@ enum util_json_flags {
> > >  	UTIL_JSON_TARGETS	= (1 << 11),
> > >  	UTIL_JSON_PARTITION	= (1 << 12),
> > >  	UTIL_JSON_ALERT_CONFIG	= (1 << 13),
> > > +	UTIL_JSON_EXTENTS	= (1 << 14),
> > >  };
> > >  
> > >  void util_display_json_array(FILE *f_out, struct json_object *jarray,
> > 
> > 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli
  2026-06-05  5:43 ` [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Alison Schofield
  2026-06-08  8:11   ` Anisa Su
@ 2026-06-17  7:10   ` Alison Schofield
  2026-06-18  5:52     ` Anisa Su
  1 sibling, 1 reply; 31+ messages in thread
From: Alison Schofield @ 2026-06-17  7:10 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, linux-kernel, nvdimm, Dan Williams, Jonathan Cameron,
	Davidlohr Bueso, Dave Jiang, Ira Weiny, John Groves,
	Gregory Price, Anisa Su

On Thu, Jun 04, 2026 at 10:43:10PM -0700, Alison Schofield wrote:
> On Sat, May 23, 2026 at 02:50:35AM -0700, Anisa Su wrote:
> > CXL Dynamic Capacity Device (DCD) support has continued to evolve in the
> > upstream kernel since Ira's v5 posting [1].  The kernel side has settled
> > on a uuid-driven claim model for sparse DAX devices: dax_resources carry
> > the tag delivered with each extent, and userspace selects which ones to
> > claim by writing a UUID to the dax device's sysfs 'uuid' attribute (or
> > "0" to claim a single untagged resource).  Size on a sparse region is
> > determined by the claim, not requested up-front.
> > 
> > This series brings cxl-cli and daxctl in line with that model and
> > extends cxl_test to exercise the new paths end-to-end.
> 
> Hi Anisa,
> 
> I just now picked this up with the kernel side and took it for a quick
> test drive. Based on what's been touched, first meaningful finding is
> all the DAX unit tests pass, and then for CXL unit tests, all but these
> 2 pass: cxl-security.sh and cxl-dcd.sh
> 
> Please let me know if there are known problems with either of those
> before I explore further.

Hi Anisa,

Good news, DCD exposed a long hidden bug that made cxl-security.sh
fail. It is not an issue w DCD patches.

Found that DCD set changes which mock memdev the test happens to
land on, and that's enough to uncover a latent hex/decimal bug in
CXL nvdimm code. We use to always land on id '1', but now this patch:

tools/testing/cxl: Add DC Regions to mock mem data

reorders the sorted dimm list, so the test selects a dimm with
serial 10 (0xa), and there's the hex/decimal mismatch.

The renumbering is harmless in itself but it just changed the
serial the test exercises and tripped over the old bug.

I'll send a separate fixup patch for the hex/dec cleanup.

(No answer on cxl-dcd.sh yet)

-- Alison

> 
> Question below about dependency....
> 
> > 
> > The corresponding kernel patchset is here:
> > https://lore.kernel.org/linux-cxl/cover.1779528761.git.anisa.su@samsung.com/T/#t
> > 
> > Picked up unchanged from v5 (Ira):
> > 
> >   libcxl: Add Dynamic RAM A partition mode support
> >   cxl/region: Add cxl-cli support for dynamic RAM A
> >   libcxl: Add extent functionality to DC regions
> >   cxl/region: Add extent output to region query
> > 
> > New in v6:
> > 
> >   daxctl: Add --uuid option to create-device for DC DAX regions
> >     - Plumbs writes to the new dax 'uuid' sysfs attribute through a new
> >       daxctl_dev_set_uuid() helper (LIBDAXCTL_11).
> >     - --uuid is mutually exclusive with --size; pass "0" to claim a
> >       single untagged dax_resource.  An unmatched UUID surfaces ENOENT
> >       from the kernel and leaves the device at size 0.
> >     - Documents the option in the man page.
> > 
> >   cxl/test: Add Dynamic Capacity tests (rewritten on top of Ira's
> >   original patch to track the post-redesign kernel)
> >     - Routes untagged claims via --uuid "0" so daxctl exercises the
> >       kernel uuid_store path; tagged claims use real UUID strings.
> >     - Asserts that for DC regions, size-grow returns -EOPNOTSUPP (real grow is
> >       --uuid only) and that tag reuse across More-chains is rejected
> >       by the cross-More uniqueness gate.
> >     - Adds coverage for the new validators: test_uuid_no_match,
> >       test_uuid_no_match_seed_intact, test_uuid_show,
> >       test_cross_more_uniqueness, test_alignment_rejection.
> >     - Sharable-partition coverage (test_shared_extent_inject,
> >       test_seq_integrity_gap) is routed at runtime to a dedicated mock
> >       memdev that tools/testing/cxl stamps with serial 0xDCDC, so a
> >       single cxl_test module load exercises both regimes.
> >     - Localizes positional-arg assignments in every helper so functions
> >       no longer clobber caller globals (the previous behavior leaked
> >       the sharable memdev into later tests).
> >     - test_reject_overlapping arithmetic now lands an actual overlap
> >       inside the DC region (the prior math landed past the end).
> > 
> > Depends on the kernel DCD/sparse-DAX series; without it the new tests
> > will skip and 'cxl list -r N -Nu' will simply report no extents.
> 
> What is this dependency- DCD/sparse-DAX series ?
> 
> > 
> > The branch is also available at:
> > 
> >   https://github.com/anisa-su993/anisa-ndctl/tree/dcd-2026-05-21
> > 
> > Based on pmem/pending commit:
> > 
> >   bbd403a test/cxl-sanitize: avoid sanitize submit/wait race
> > 
> > [1] https://lore.kernel.org/nvdimm/20250413-dcd-region2-v5-0-fbd753a2e0e8@intel.com/
> > 
> > ---
> > Changes in v6:
> > - anisa: New patch — daxctl --uuid option + daxctl_dev_set_uuid() helper
> > - anisa: Rewrite cxl/test DCD tests against the post-redesign kernel
> >          (uuid sysfs claim, tag-group atomic release, cross-More
> >          uniqueness, alignment rejection, DC size-grow refusal)
> > - anisa: Rebase onto bbd403a (pmem/pending)
> > - Link to v5: https://lore.kernel.org/nvdimm/20250413-dcd-region2-v5-0-fbd753a2e0e8@intel.com/
> > 
> > Changes in v5:
> > - iweiny: Adjust all code to view only the dynamic RAM A partition
> > - Alison: s/tag/uuid/ in region query extent output
> > - Link to v4: https://patch.msgid.link/20241214-dcd-region2-v4-0-36550a97f8e2@intel.com
> > 
> > Anisa Su (1):
> >   daxctl: Add --uuid option to create-device for DC regions
> > 
> > Ira Weiny (6):
> >   ndctl: Dynamic Capacity additions for cxl-cli
> >   libcxl: Add Dynamic RAM A partition mode support
> >   cxl/region: Add cxl-cli support for dynamic RAM A
> >   libcxl: Add extent functionality to DC regions
> >   cxl/region: Add extent output to region query
> >   cxl/test: Add Dynamic Capacity tests
> > 
> >  Documentation/cxl/cxl-list.txt                |   29 +
> >  Documentation/cxl/lib/libcxl.txt              |   33 +-
> >  Documentation/daxctl/daxctl-create-device.txt |   12 +
> >  cxl/filter.h                                  |    3 +
> >  cxl/json.c                                    |   67 +
> >  cxl/json.h                                    |    3 +
> >  cxl/lib/libcxl.c                              |  181 +++
> >  cxl/lib/libcxl.sym                            |    9 +
> >  cxl/lib/private.h                             |   14 +
> >  cxl/libcxl.h                                  |   21 +-
> >  cxl/list.c                                    |    3 +
> >  cxl/memdev.c                                  |    4 +-
> >  cxl/region.c                                  |   27 +-
> >  daxctl/device.c                               |   72 +-
> >  daxctl/lib/libdaxctl.c                        |   44 +
> >  daxctl/lib/libdaxctl.sym                      |    5 +
> >  daxctl/libdaxctl.h                            |    1 +
> >  test/cxl-dcd.sh                               | 1267 +++++++++++++++++
> >  test/meson.build                              |    2 +
> >  util/json.h                                   |    1 +
> >  20 files changed, 1771 insertions(+), 27 deletions(-)
> >  create mode 100644 test/cxl-dcd.sh
> > 
> > 
> > base-commit: bbd403a03fa2a1551c1a10bbf78f32027c718758
> > -- 
> > 2.43.0
> > 
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli
  2026-06-17  7:10   ` Alison Schofield
@ 2026-06-18  5:52     ` Anisa Su
  2026-06-19  0:38       ` Alison Schofield
  0 siblings, 1 reply; 31+ messages in thread
From: Anisa Su @ 2026-06-18  5:52 UTC (permalink / raw)
  To: Alison Schofield
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Dave Jiang, Ira Weiny,
	John Groves, Gregory Price

On Wed, Jun 17, 2026 at 12:10:07AM -0700, Alison Schofield wrote:
> On Thu, Jun 04, 2026 at 10:43:10PM -0700, Alison Schofield wrote:
> > On Sat, May 23, 2026 at 02:50:35AM -0700, Anisa Su wrote:
> > > CXL Dynamic Capacity Device (DCD) support has continued to evolve in the
> > > upstream kernel since Ira's v5 posting [1].  The kernel side has settled
> > > on a uuid-driven claim model for sparse DAX devices: dax_resources carry
> > > the tag delivered with each extent, and userspace selects which ones to
> > > claim by writing a UUID to the dax device's sysfs 'uuid' attribute (or
> > > "0" to claim a single untagged resource).  Size on a sparse region is
> > > determined by the claim, not requested up-front.
> > > 
> > > This series brings cxl-cli and daxctl in line with that model and
> > > extends cxl_test to exercise the new paths end-to-end.
> > 
> > Hi Anisa,
> > 
> > I just now picked this up with the kernel side and took it for a quick
> > test drive. Based on what's been touched, first meaningful finding is
> > all the DAX unit tests pass, and then for CXL unit tests, all but these
> > 2 pass: cxl-security.sh and cxl-dcd.sh
> > 
> > Please let me know if there are known problems with either of those
> > before I explore further.
> 
> Hi Anisa,
> 
> Good news, DCD exposed a long hidden bug that made cxl-security.sh
> fail. It is not an issue w DCD patches.
> 
> Found that DCD set changes which mock memdev the test happens to
> land on, and that's enough to uncover a latent hex/decimal bug in
> CXL nvdimm code. We use to always land on id '1', but now this patch:
> 
> tools/testing/cxl: Add DC Regions to mock mem data
> 
> reorders the sorted dimm list, so the test selects a dimm with
> serial 10 (0xa), and there's the hex/decimal mismatch.
> 
> The renumbering is harmless in itself but it just changed the
> serial the test exercises and tripped over the old bug.
> 
> I'll send a separate fixup patch for the hex/dec cleanup.
> 
> (No answer on cxl-dcd.sh yet)
> 
> -- Alison
> 
Thanks for looking into this! I can also look into what might be going
on with cxl-dcd.sh if you let me know the base commit you applied the
dcd patches onto? :)

Thanks,
Anisa

> > 
> > Question below about dependency....
> > 
> > > 
> > > The corresponding kernel patchset is here:
> > > https://lore.kernel.org/linux-cxl/cover.1779528761.git.anisa.su@samsung.com/T/#t
> > > 
> > > Picked up unchanged from v5 (Ira):
> > > 
> > >   libcxl: Add Dynamic RAM A partition mode support
> > >   cxl/region: Add cxl-cli support for dynamic RAM A
> > >   libcxl: Add extent functionality to DC regions
> > >   cxl/region: Add extent output to region query
> > > 
> > > New in v6:
> > > 
> > >   daxctl: Add --uuid option to create-device for DC DAX regions
> > >     - Plumbs writes to the new dax 'uuid' sysfs attribute through a new
> > >       daxctl_dev_set_uuid() helper (LIBDAXCTL_11).
> > >     - --uuid is mutually exclusive with --size; pass "0" to claim a
> > >       single untagged dax_resource.  An unmatched UUID surfaces ENOENT
> > >       from the kernel and leaves the device at size 0.
> > >     - Documents the option in the man page.
> > > 
> > >   cxl/test: Add Dynamic Capacity tests (rewritten on top of Ira's
> > >   original patch to track the post-redesign kernel)
> > >     - Routes untagged claims via --uuid "0" so daxctl exercises the
> > >       kernel uuid_store path; tagged claims use real UUID strings.
> > >     - Asserts that for DC regions, size-grow returns -EOPNOTSUPP (real grow is
> > >       --uuid only) and that tag reuse across More-chains is rejected
> > >       by the cross-More uniqueness gate.
> > >     - Adds coverage for the new validators: test_uuid_no_match,
> > >       test_uuid_no_match_seed_intact, test_uuid_show,
> > >       test_cross_more_uniqueness, test_alignment_rejection.
> > >     - Sharable-partition coverage (test_shared_extent_inject,
> > >       test_seq_integrity_gap) is routed at runtime to a dedicated mock
> > >       memdev that tools/testing/cxl stamps with serial 0xDCDC, so a
> > >       single cxl_test module load exercises both regimes.
> > >     - Localizes positional-arg assignments in every helper so functions
> > >       no longer clobber caller globals (the previous behavior leaked
> > >       the sharable memdev into later tests).
> > >     - test_reject_overlapping arithmetic now lands an actual overlap
> > >       inside the DC region (the prior math landed past the end).
> > > 
> > > Depends on the kernel DCD/sparse-DAX series; without it the new tests
> > > will skip and 'cxl list -r N -Nu' will simply report no extents.
> > 
> > What is this dependency- DCD/sparse-DAX series ?
> > 
> > > 
> > > The branch is also available at:
> > > 
> > >   https://github.com/anisa-su993/anisa-ndctl/tree/dcd-2026-05-21
> > > 
> > > Based on pmem/pending commit:
> > > 
> > >   bbd403a test/cxl-sanitize: avoid sanitize submit/wait race
> > > 
> > > [1] https://lore.kernel.org/nvdimm/20250413-dcd-region2-v5-0-fbd753a2e0e8@intel.com/
> > > 
> > > ---
> > > Changes in v6:
> > > - anisa: New patch — daxctl --uuid option + daxctl_dev_set_uuid() helper
> > > - anisa: Rewrite cxl/test DCD tests against the post-redesign kernel
> > >          (uuid sysfs claim, tag-group atomic release, cross-More
> > >          uniqueness, alignment rejection, DC size-grow refusal)
> > > - anisa: Rebase onto bbd403a (pmem/pending)
> > > - Link to v5: https://lore.kernel.org/nvdimm/20250413-dcd-region2-v5-0-fbd753a2e0e8@intel.com/
> > > 
> > > Changes in v5:
> > > - iweiny: Adjust all code to view only the dynamic RAM A partition
> > > - Alison: s/tag/uuid/ in region query extent output
> > > - Link to v4: https://patch.msgid.link/20241214-dcd-region2-v4-0-36550a97f8e2@intel.com
> > > 
> > > Anisa Su (1):
> > >   daxctl: Add --uuid option to create-device for DC regions
> > > 
> > > Ira Weiny (6):
> > >   ndctl: Dynamic Capacity additions for cxl-cli
> > >   libcxl: Add Dynamic RAM A partition mode support
> > >   cxl/region: Add cxl-cli support for dynamic RAM A
> > >   libcxl: Add extent functionality to DC regions
> > >   cxl/region: Add extent output to region query
> > >   cxl/test: Add Dynamic Capacity tests
> > > 
> > >  Documentation/cxl/cxl-list.txt                |   29 +
> > >  Documentation/cxl/lib/libcxl.txt              |   33 +-
> > >  Documentation/daxctl/daxctl-create-device.txt |   12 +
> > >  cxl/filter.h                                  |    3 +
> > >  cxl/json.c                                    |   67 +
> > >  cxl/json.h                                    |    3 +
> > >  cxl/lib/libcxl.c                              |  181 +++
> > >  cxl/lib/libcxl.sym                            |    9 +
> > >  cxl/lib/private.h                             |   14 +
> > >  cxl/libcxl.h                                  |   21 +-
> > >  cxl/list.c                                    |    3 +
> > >  cxl/memdev.c                                  |    4 +-
> > >  cxl/region.c                                  |   27 +-
> > >  daxctl/device.c                               |   72 +-
> > >  daxctl/lib/libdaxctl.c                        |   44 +
> > >  daxctl/lib/libdaxctl.sym                      |    5 +
> > >  daxctl/libdaxctl.h                            |    1 +
> > >  test/cxl-dcd.sh                               | 1267 +++++++++++++++++
> > >  test/meson.build                              |    2 +
> > >  util/json.h                                   |    1 +
> > >  20 files changed, 1771 insertions(+), 27 deletions(-)
> > >  create mode 100644 test/cxl-dcd.sh
> > > 
> > > 
> > > base-commit: bbd403a03fa2a1551c1a10bbf78f32027c718758
> > > -- 
> > > 2.43.0
> > > 
> > 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli
  2026-06-18  5:52     ` Anisa Su
@ 2026-06-19  0:38       ` Alison Schofield
  2026-06-24  4:51         ` Anisa Su
  0 siblings, 1 reply; 31+ messages in thread
From: Alison Schofield @ 2026-06-19  0:38 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, linux-kernel, nvdimm, Dan Williams, Jonathan Cameron,
	Davidlohr Bueso, Dave Jiang, Ira Weiny, John Groves,
	Gregory Price

On Wed, Jun 17, 2026 at 10:52:04PM -0700, Anisa Su wrote:
> On Wed, Jun 17, 2026 at 12:10:07AM -0700, Alison Schofield wrote:
> > On Thu, Jun 04, 2026 at 10:43:10PM -0700, Alison Schofield wrote:
> > > On Sat, May 23, 2026 at 02:50:35AM -0700, Anisa Su wrote:
> > > > CXL Dynamic Capacity Device (DCD) support has continued to evolve in the
> > > > upstream kernel since Ira's v5 posting [1].  The kernel side has settled
> > > > on a uuid-driven claim model for sparse DAX devices: dax_resources carry
> > > > the tag delivered with each extent, and userspace selects which ones to
> > > > claim by writing a UUID to the dax device's sysfs 'uuid' attribute (or
> > > > "0" to claim a single untagged resource).  Size on a sparse region is
> > > > determined by the claim, not requested up-front.
> > > > 
> > > > This series brings cxl-cli and daxctl in line with that model and
> > > > extends cxl_test to exercise the new paths end-to-end.
> > > 
> > > Hi Anisa,
> > > 
> > > I just now picked this up with the kernel side and took it for a quick
> > > test drive. Based on what's been touched, first meaningful finding is
> > > all the DAX unit tests pass, and then for CXL unit tests, all but these
> > > 2 pass: cxl-security.sh and cxl-dcd.sh
> > > 
> > > Please let me know if there are known problems with either of those
> > > before I explore further.
> > 
> > Hi Anisa,
> > 
> > Good news, DCD exposed a long hidden bug that made cxl-security.sh
> > fail. It is not an issue w DCD patches.
> > 
> > Found that DCD set changes which mock memdev the test happens to
> > land on, and that's enough to uncover a latent hex/decimal bug in
> > CXL nvdimm code. We use to always land on id '1', but now this patch:
> > 
> > tools/testing/cxl: Add DC Regions to mock mem data
> > 
> > reorders the sorted dimm list, so the test selects a dimm with
> > serial 10 (0xa), and there's the hex/decimal mismatch.
> > 
> > The renumbering is harmless in itself but it just changed the
> > serial the test exercises and tripped over the old bug.
> > 
> > I'll send a separate fixup patch for the hex/dec cleanup.
> > 
> > (No answer on cxl-dcd.sh yet)
> > 
> > -- Alison
> > 
> Thanks for looking into this! I can also look into what might be going
> on with cxl-dcd.sh if you let me know the base commit you applied the
> dcd patches onto? :)

The base commit was indeed the key to the cxl-dcd.sh failure.

I'm seeing a probe-ordering race that you may not see unless you're
using v7.1-rc1 or later. The branch linked in the kernel patchset does
not include this commit -

39aa1d4be12b ("dax/cxl, hmem: Initialize hmem early and defer dax_cxl binding")

Dan changed cxl_dax_region to PROBE_PREFER_ASYNCHRONOUS in support the
DAX and HMEM synchronization, so I'm guessing that undoing that, is
not an option. Before that change, cxl_dax_region probed synchronously
and created the zero-sized seed dax device before cxlr_add_existing_extents()
ran, so no race existed.

Move to 7.1 and you *should* see cxl-dcd.sh start failing. Since it's a
timing issue, so you may need to dial down any dynamic debug and do
repeated runs.

The race is on the dax_region device's devres_head between-
(a) the asynchronous cxl_dax_region probe reaching really_probe()
and
(b) cxlr_add_existing_extents() attaching devres to the same device

really_probe() rejects probing devices that already have resources
attached. If (b) wins, probe fails with -EBUSY, cxl_dax_region never
binds, and the seed dax device is never created.

One possible fixup would be to move existing-extent processing into
cxl_dax_region_probe() so that the resource attachment happens
within the probe itself. That looked like more restructuring than I
could quickly test out, so I'm sending it back to you.

Below is a reproducer using cxl_test and cxl-cli. It creates a DC region
and checks immediately if its dax_region driver bound and a seed dax
device exists. An 'unbound' dax_region is the bug.

    #!/bin/bash
    set -u
    CXL=${CXL:-cxl}; NDCTL=${NDCTL:-ndctl}; TRIALS=${1:-10}
    bound=0 unbound=0
    for t in $(seq 1 "$TRIALS"); do
        $NDCTL disable-region -b cxl_test all >/dev/null 2>&1
        modprobe -r cxl_test 2>/dev/null; modprobe cxl_test
        udevadm settle 2>/dev/null; dmesg -C 2>/dev/null
        # first non-sharable memdev with a dynamic_ram_a partition
        # (serial 56540 == 0xDCDC is the mock's sharable fixture)
        mem=$($CXL list -b cxl_test -Mi \
            | jq -r '.[] | select(.dynamic_ram_a_size != null)
                          | select(.serial != 56540) | .memdev' | head -1)
        reg=$($CXL create-region -t dynamic_ram_a -d decoder0.0 -m "$mem" \
            2>/dev/null | jq -r .region)
        rnum=${reg#region}
        # sample immediately, no sleep (what the test does via daxctl)
        daxreg=$(readlink -f /sys/bus/cxl/devices/"$reg"/dax_region"$rnum" 2>/dev/null)
        drv=$([ -e "$daxreg/driver" ] && echo bound || echo UNBOUND)
        seed=$([ -e /sys/bus/dax/devices/dax"$rnum".0/uuid ] && echo yes || echo NO)
        ebusy=$(dmesg 2>/dev/null | grep -c "Resources present before probing")
        printf 'trial %2d: %s drv=%-7s seed=%-3s ebusy_msgs=%s\n' \
            "$t" "$reg" "$drv" "$seed" "$ebusy"
        [ "$drv" = bound ] && bound=$((bound+1)) || unbound=$((unbound+1))
    done
    echo "SUMMARY: bound=$bound unbound(FAIL)=$unbound of $TRIALS"
    [ "$unbound" -eq 0 ] || exit 1

Sample output on a failing kernel-
    trial  1: region9 drv=bound   seed=yes ebusy_msgs=0
    trial  2: region9 drv=UNBOUND seed=NO  ebusy_msgs=1
    trial  3: region9 drv=bound   seed=yes ebusy_msgs=0
    trial  4: region9 drv=UNBOUND seed=NO  ebusy_msgs=1
    ...
    SUMMARY: bound=4 unbound(FAIL)=4 of 8

> 
> Thanks,
> Anisa
> 

snip


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli
  2026-06-19  0:38       ` Alison Schofield
@ 2026-06-24  4:51         ` Anisa Su
  0 siblings, 0 replies; 31+ messages in thread
From: Anisa Su @ 2026-06-24  4:51 UTC (permalink / raw)
  To: Alison Schofield
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Dave Jiang, Ira Weiny,
	John Groves, Gregory Price

On Thu, Jun 18, 2026 at 05:38:33PM -0700, Alison Schofield wrote:
> On Wed, Jun 17, 2026 at 10:52:04PM -0700, Anisa Su wrote:
> > On Wed, Jun 17, 2026 at 12:10:07AM -0700, Alison Schofield wrote:
> > > On Thu, Jun 04, 2026 at 10:43:10PM -0700, Alison Schofield wrote:
> > > > On Sat, May 23, 2026 at 02:50:35AM -0700, Anisa Su wrote:
> > > > > CXL Dynamic Capacity Device (DCD) support has continued to evolve in the
> > > > > upstream kernel since Ira's v5 posting [1].  The kernel side has settled
> > > > > on a uuid-driven claim model for sparse DAX devices: dax_resources carry
> > > > > the tag delivered with each extent, and userspace selects which ones to
> > > > > claim by writing a UUID to the dax device's sysfs 'uuid' attribute (or
> > > > > "0" to claim a single untagged resource).  Size on a sparse region is
> > > > > determined by the claim, not requested up-front.
> > > > > 
> > > > > This series brings cxl-cli and daxctl in line with that model and
> > > > > extends cxl_test to exercise the new paths end-to-end.
> > > > 
> > > > Hi Anisa,
> > > > 
> > > > I just now picked this up with the kernel side and took it for a quick
> > > > test drive. Based on what's been touched, first meaningful finding is
> > > > all the DAX unit tests pass, and then for CXL unit tests, all but these
> > > > 2 pass: cxl-security.sh and cxl-dcd.sh
> > > > 
> > > > Please let me know if there are known problems with either of those
> > > > before I explore further.
> > > 
> > > Hi Anisa,
> > > 
> > > Good news, DCD exposed a long hidden bug that made cxl-security.sh
> > > fail. It is not an issue w DCD patches.
> > > 
> > > Found that DCD set changes which mock memdev the test happens to
> > > land on, and that's enough to uncover a latent hex/decimal bug in
> > > CXL nvdimm code. We use to always land on id '1', but now this patch:
> > > 
> > > tools/testing/cxl: Add DC Regions to mock mem data
> > > 
> > > reorders the sorted dimm list, so the test selects a dimm with
> > > serial 10 (0xa), and there's the hex/decimal mismatch.
> > > 
> > > The renumbering is harmless in itself but it just changed the
> > > serial the test exercises and tripped over the old bug.
> > > 
> > > I'll send a separate fixup patch for the hex/dec cleanup.
> > > 
> > > (No answer on cxl-dcd.sh yet)
> > > 
> > > -- Alison
> > > 
> > Thanks for looking into this! I can also look into what might be going
> > on with cxl-dcd.sh if you let me know the base commit you applied the
> > dcd patches onto? :)
> 
> The base commit was indeed the key to the cxl-dcd.sh failure.
> 
> I'm seeing a probe-ordering race that you may not see unless you're
> using v7.1-rc1 or later. The branch linked in the kernel patchset does
> not include this commit -
> 
> 39aa1d4be12b ("dax/cxl, hmem: Initialize hmem early and defer dax_cxl binding")
> 
> Dan changed cxl_dax_region to PROBE_PREFER_ASYNCHRONOUS in support the
> DAX and HMEM synchronization, so I'm guessing that undoing that, is
> not an option. Before that change, cxl_dax_region probed synchronously
> and created the zero-sized seed dax device before cxlr_add_existing_extents()
> ran, so no race existed.
> 
> Move to 7.1 and you *should* see cxl-dcd.sh start failing. Since it's a
> timing issue, so you may need to dial down any dynamic debug and do
> repeated runs.
> 
> The race is on the dax_region device's devres_head between-
> (a) the asynchronous cxl_dax_region probe reaching really_probe()
> and
> (b) cxlr_add_existing_extents() attaching devres to the same device
> 
> really_probe() rejects probing devices that already have resources
> attached. If (b) wins, probe fails with -EBUSY, cxl_dax_region never
> binds, and the seed dax device is never created.
> 
> One possible fixup would be to move existing-extent processing into
> cxl_dax_region_probe() so that the resource attachment happens
> within the probe itself. That looked like more restructuring than I
> could quickly test out, so I'm sending it back to you.
> 
> Below is a reproducer using cxl_test and cxl-cli. It creates a DC region
> and checks immediately if its dax_region driver bound and a seed dax
> device exists. An 'unbound' dax_region is the bug.
> 
>     #!/bin/bash
>     set -u
>     CXL=${CXL:-cxl}; NDCTL=${NDCTL:-ndctl}; TRIALS=${1:-10}
>     bound=0 unbound=0
>     for t in $(seq 1 "$TRIALS"); do
>         $NDCTL disable-region -b cxl_test all >/dev/null 2>&1
>         modprobe -r cxl_test 2>/dev/null; modprobe cxl_test
>         udevadm settle 2>/dev/null; dmesg -C 2>/dev/null
>         # first non-sharable memdev with a dynamic_ram_a partition
>         # (serial 56540 == 0xDCDC is the mock's sharable fixture)
>         mem=$($CXL list -b cxl_test -Mi \
>             | jq -r '.[] | select(.dynamic_ram_a_size != null)
>                           | select(.serial != 56540) | .memdev' | head -1)
>         reg=$($CXL create-region -t dynamic_ram_a -d decoder0.0 -m "$mem" \
>             2>/dev/null | jq -r .region)
>         rnum=${reg#region}
>         # sample immediately, no sleep (what the test does via daxctl)
>         daxreg=$(readlink -f /sys/bus/cxl/devices/"$reg"/dax_region"$rnum" 2>/dev/null)
>         drv=$([ -e "$daxreg/driver" ] && echo bound || echo UNBOUND)
>         seed=$([ -e /sys/bus/dax/devices/dax"$rnum".0/uuid ] && echo yes || echo NO)
>         ebusy=$(dmesg 2>/dev/null | grep -c "Resources present before probing")
>         printf 'trial %2d: %s drv=%-7s seed=%-3s ebusy_msgs=%s\n' \
>             "$t" "$reg" "$drv" "$seed" "$ebusy"
>         [ "$drv" = bound ] && bound=$((bound+1)) || unbound=$((unbound+1))
>     done
>     echo "SUMMARY: bound=$bound unbound(FAIL)=$unbound of $TRIALS"
>     [ "$unbound" -eq 0 ] || exit 1
> 
> Sample output on a failing kernel-
>     trial  1: region9 drv=bound   seed=yes ebusy_msgs=0
>     trial  2: region9 drv=UNBOUND seed=NO  ebusy_msgs=1
>     trial  3: region9 drv=bound   seed=yes ebusy_msgs=0
>     trial  4: region9 drv=UNBOUND seed=NO  ebusy_msgs=1
>     ...
>     SUMMARY: bound=4 unbound(FAIL)=4 of 8
> 
Thank you so much for investigating. The fix you suggested works: I
moved the processing of existing extents to cxl_dax_region_probe().

This solved the probe-race, but it deadlocked because after processing existing
extents:
cxlr_notify_extent() does guard(device)(dev) on &cxlr->cxlr_dax->dev.
That lock is already held by probe.

To fix:
cxlr_notify_extent() split into core __cxlr_notify_extent() and wrapper
cxlr_notify_extent().

__cxlr_notify_extent() asserts device_lock_assert(dev) instead of
acquiring the lock. Call this directly in the process existing extents
path to skip trying to acquire lock again.

cxlr_notify_extent() wrapper acquires the lock for the other case --
extents added after driver loaded.

Thanks again for the thorough investigation! It was super helpful :)
- Anisa

> > 
> > Thanks,
> > Anisa
> > 
> 
> snip
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 2/7] libcxl: Add Dynamic RAM A partition mode support
  2026-06-08 23:19   ` Dave Jiang
  2026-06-10  3:51     ` Richard Cheng
@ 2026-06-25  9:07     ` Anisa Su
  1 sibling, 0 replies; 31+ messages in thread
From: Anisa Su @ 2026-06-25  9:07 UTC (permalink / raw)
  To: Dave Jiang
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Ira Weiny, Alison Schofield,
	John Groves, Gregory Price, Ira Weiny

On Mon, Jun 08, 2026 at 04:19:47PM -0700, Dave Jiang wrote:
> 
> 
> On 5/23/26 2:50 AM, Anisa Su wrote:
> > From: Ira Weiny <ira.weiny@intel.com>
> > 
> > Dynamic capacity partitions are exposed as a singular dynamic ram
> > partition.
> > 
> > Add CXL library support to read this partition information.
> > 
> > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> 
> Missing Anisa sign off.
> 
> Can probably squash this and the next commit so the usage is shown for the reviewer.
> 
Added my signoff. Also squashed next commit. I added your review tag to the
combined commit since it was on the next commit.

Richard's point about moving new exported symbols to LIBCXL_13 is also
applied.

> DJ

Thanks,
Anisa
> 
> > ---
> >  Documentation/cxl/lib/libcxl.txt |  6 +++--
> >  cxl/lib/libcxl.c                 | 43 ++++++++++++++++++++++++++++++++
> >  cxl/lib/libcxl.sym               |  4 +++
> >  cxl/lib/private.h                |  3 +++
> >  cxl/libcxl.h                     | 10 +++++++-
> >  5 files changed, 63 insertions(+), 3 deletions(-)
> > 
> > diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
> > index 5c3ebd4..9921ac1 100644
> > --- a/Documentation/cxl/lib/libcxl.txt
> > +++ b/Documentation/cxl/lib/libcxl.txt
> > @@ -74,6 +74,7 @@ int cxl_memdev_get_major(struct cxl_memdev *memdev);
> >  int cxl_memdev_get_minor(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_dynamic_ram_a_size(struct cxl_memdev *memdev);
> >  const char *cxl_memdev_get_firmware_version(struct cxl_memdev *memdev);
> >  size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev);
> >  int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev);
> > @@ -93,8 +94,8 @@ The character device node for command submission can be found by default
> >  at /dev/cxl/mem%d, or created with a major / minor returned from
> >  cxl_memdev_get_{major,minor}().
> >  
> > -The 'pmem_size' and 'ram_size' attributes return the current
> > -provisioning of DPA (Device Physical Address / local capacity) in the
> > +The 'pmem_size', 'ram_size', and 'dynamic_ram_a_size' attributes return the
> > +current provisioning of DPA (Device Physical Address / local capacity) in the
> >  device.
> >  
> >  cxl_memdev_get_numa_node() returns the affinitized CPU node number if
> > @@ -453,6 +454,7 @@ enum cxl_decoder_mode {
> >  	CXL_DECODER_MODE_MIXED,
> >  	CXL_DECODER_MODE_PMEM,
> >  	CXL_DECODER_MODE_RAM,
> > +	CXL_DECODER_MODE_DYNAMIC_RAM_A,
> >  };
> >  enum cxl_decoder_mode cxl_decoder_get_mode(struct cxl_decoder *decoder);
> >  int cxl_decoder_set_mode(struct cxl_decoder *decoder, enum cxl_decoder_mode mode);
> > diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
> > index e55a7b4..be0bc03 100644
> > --- a/cxl/lib/libcxl.c
> > +++ b/cxl/lib/libcxl.c
> > @@ -501,6 +501,9 @@ CXL_EXPORT bool cxl_region_qos_class_mismatch(struct cxl_region *region)
> >  		} else if (region->mode == CXL_DECODER_MODE_PMEM) {
> >  			if (root_decoder->qos_class != memdev->pmem_qos_class)
> >  				return true;
> > +		} else if (region->mode == CXL_DECODER_MODE_DYNAMIC_RAM_A) {
> > +			if (root_decoder->qos_class != memdev->dynamic_ram_a_qos_class)
> > +				return true;
> >  		}
> >  	}
> >  
> > @@ -1426,6 +1429,10 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
> >  	if (sysfs_read_attr(ctx, path, buf) == 0)
> >  		memdev->ram_size = strtoull(buf, NULL, 0);
> >  
> > +	sprintf(path, "%s/dynamic_ram_a/size", cxlmem_base);
> > +	if (sysfs_read_attr(ctx, path, buf) == 0)
> > +		memdev->dynamic_ram_a_size = strtoull(buf, NULL, 0);
> > +
> >  	sprintf(path, "%s/pmem/qos_class", cxlmem_base);
> >  	if (sysfs_read_attr(ctx, path, buf) < 0)
> >  		memdev->pmem_qos_class = CXL_QOS_CLASS_NONE;
> > @@ -1438,6 +1445,12 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
> >  	else
> >  		memdev->ram_qos_class = atoi(buf);
> >  
> > +	sprintf(path, "%s/dynamic_ram_a/qos_class", cxlmem_base);
> > +	if (sysfs_read_attr(ctx, path, buf) < 0)
> > +		memdev->dynamic_ram_a_qos_class = CXL_QOS_CLASS_NONE;
> > +	else
> > +		memdev->dynamic_ram_a_qos_class = atoi(buf);
> > +
> >  	sprintf(path, "%s/payload_max", cxlmem_base);
> >  	if (sysfs_read_attr(ctx, path, buf) == 0) {
> >  		memdev->payload_max = strtoull(buf, NULL, 0);
> > @@ -1685,6 +1698,11 @@ 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_dynamic_ram_a_size(struct cxl_memdev *memdev)
> > +{
> > +	return memdev->dynamic_ram_a_size;
> > +}
> > +
> >  CXL_EXPORT int cxl_memdev_get_pmem_qos_class(struct cxl_memdev *memdev)
> >  {
> >  	return memdev->pmem_qos_class;
> > @@ -1695,6 +1713,11 @@ CXL_EXPORT int cxl_memdev_get_ram_qos_class(struct cxl_memdev *memdev)
> >  	return memdev->ram_qos_class;
> >  }
> >  
> > +CXL_EXPORT int cxl_memdev_get_dynamic_ram_a_qos_class(struct cxl_memdev *memdev)
> > +{
> > +	return memdev->dynamic_ram_a_qos_class;
> > +}
> > +
> >  CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev)
> >  {
> >  	return memdev->firmware_version;
> > @@ -2465,6 +2488,8 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
> >  			decoder->mode = CXL_DECODER_MODE_MIXED;
> >  		else if (strcmp(buf, "none") == 0)
> >  			decoder->mode = CXL_DECODER_MODE_NONE;
> > +		else if (strcmp(buf, "dynamic_ram_a") == 0)
> > +			decoder->mode = CXL_DECODER_MODE_DYNAMIC_RAM_A;
> >  		else
> >  			decoder->mode = CXL_DECODER_MODE_MIXED;
> >  	} else
> > @@ -2504,6 +2529,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
> >  	case CXL_PORT_SWITCH:
> >  		decoder->pmem_capable = true;
> >  		decoder->volatile_capable = true;
> > +		decoder->dynamic_ram_a_capable = true;
> >  		decoder->mem_capable = true;
> >  		decoder->accelmem_capable = true;
> >  		sprintf(path, "%s/locked", cxldecoder_base);
> > @@ -2528,6 +2554,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
> >  			{ "cap_type3", &decoder->mem_capable },
> >  			{ "cap_ram", &decoder->volatile_capable },
> >  			{ "cap_pmem", &decoder->pmem_capable },
> > +			{ "cap_dynamic_ram_a", &decoder->dynamic_ram_a_capable },
> >  			{ "locked", &decoder->locked },
> >  		};
> >  
> > @@ -2778,6 +2805,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_DYNAMIC_RAM_A:
> > +		sprintf(buf, "dynamic_ram_a");
> > +		break;
> >  	default:
> >  		err(ctx, "%s: unsupported mode: %d\n",
> >  		    cxl_decoder_get_devname(decoder), mode);
> > @@ -2829,6 +2859,11 @@ CXL_EXPORT bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder)
> >  	return decoder->volatile_capable;
> >  }
> >  
> > +CXL_EXPORT bool cxl_decoder_is_dynamic_ram_a_capable(struct cxl_decoder *decoder)
> > +{
> > +	return decoder->dynamic_ram_a_capable;
> > +}
> > +
> >  CXL_EXPORT bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder)
> >  {
> >  	return decoder->mem_capable;
> > @@ -2903,6 +2938,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_DYNAMIC_RAM_A)
> > +		sprintf(path, "%s/create_dynamic_ram_a_region", decoder->dev_path);
> >  
> >  	rc = sysfs_read_attr(ctx, path, buf);
> >  	if (rc < 0) {
> > @@ -2954,6 +2991,12 @@ cxl_decoder_create_ram_region(struct cxl_decoder *decoder)
> >  	return cxl_decoder_create_region(decoder, CXL_DECODER_MODE_RAM);
> >  }
> >  
> > +CXL_EXPORT struct cxl_region *
> > +cxl_decoder_create_dynamic_ram_a_region(struct cxl_decoder *decoder)
> > +{
> > +	return cxl_decoder_create_region(decoder, CXL_DECODER_MODE_DYNAMIC_RAM_A);
> > +}
> > +
> >  CXL_EXPORT int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder)
> >  {
> >  	return decoder->nr_targets;
> > diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> > index ed4429f..258bdd3 100644
> > --- a/cxl/lib/libcxl.sym
> > +++ b/cxl/lib/libcxl.sym
> > @@ -294,6 +294,10 @@ global:
> >  	cxl_memdev_get_fwctl;
> >  	cxl_fwctl_get_major;
> >  	cxl_fwctl_get_minor;
> > +	cxl_memdev_get_dynamic_ram_a_size;
> > +	cxl_memdev_get_dynamic_ram_a_qos_class;
> > +	cxl_decoder_is_dynamic_ram_a_capable;
> > +	cxl_decoder_create_dynamic_ram_a_region;
> >  } LIBECXL_8;
> >  
> >  LIBCXL_10 {
> > diff --git a/cxl/lib/private.h b/cxl/lib/private.h
> > index d2d71fc..37b7b06 100644
> > --- a/cxl/lib/private.h
> > +++ b/cxl/lib/private.h
> > @@ -52,8 +52,10 @@ struct cxl_memdev {
> >  	struct list_node list;
> >  	unsigned long long pmem_size;
> >  	unsigned long long ram_size;
> > +	unsigned long long dynamic_ram_a_size;
> >  	int ram_qos_class;
> >  	int pmem_qos_class;
> > +	int dynamic_ram_a_qos_class;
> >  	int payload_max;
> >  	size_t lsa_size;
> >  	struct kmod_module *module;
> > @@ -159,6 +161,7 @@ struct cxl_decoder {
> >  	unsigned int interleave_granularity;
> >  	bool pmem_capable;
> >  	bool volatile_capable;
> > +	bool dynamic_ram_a_capable;
> >  	bool mem_capable;
> >  	bool accelmem_capable;
> >  	bool locked;
> > diff --git a/cxl/libcxl.h b/cxl/libcxl.h
> > index e91af90..fd41122 100644
> > --- a/cxl/libcxl.h
> > +++ b/cxl/libcxl.h
> > @@ -75,8 +75,10 @@ struct cxl_fwctl *cxl_memdev_get_fwctl(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_dynamic_ram_a_size(struct cxl_memdev *memdev);
> >  int cxl_memdev_get_pmem_qos_class(struct cxl_memdev *memdev);
> >  int cxl_memdev_get_ram_qos_class(struct cxl_memdev *memdev);
> > +int cxl_memdev_get_dynamic_ram_a_qos_class(struct cxl_memdev *memdev);
> >  const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
> >  bool cxl_memdev_fw_update_in_progress(struct cxl_memdev *memdev);
> >  size_t cxl_memdev_fw_update_get_remaining(struct cxl_memdev *memdev);
> > @@ -210,6 +212,7 @@ enum cxl_decoder_mode {
> >  	CXL_DECODER_MODE_MIXED,
> >  	CXL_DECODER_MODE_PMEM,
> >  	CXL_DECODER_MODE_RAM,
> > +	CXL_DECODER_MODE_DYNAMIC_RAM_A,
> >  };
> >  
> >  static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode)
> > @@ -219,9 +222,10 @@ static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode)
> >  		[CXL_DECODER_MODE_MIXED] = "mixed",
> >  		[CXL_DECODER_MODE_PMEM] = "pmem",
> >  		[CXL_DECODER_MODE_RAM] = "ram",
> > +		[CXL_DECODER_MODE_DYNAMIC_RAM_A] = "dynamic_ram_a",
> >  	};
> >  
> > -	if (mode < CXL_DECODER_MODE_NONE || mode > CXL_DECODER_MODE_RAM)
> > +	if (mode < CXL_DECODER_MODE_NONE || mode > CXL_DECODER_MODE_DYNAMIC_RAM_A)
> >  		mode = CXL_DECODER_MODE_NONE;
> >  	return names[mode];
> >  }
> > @@ -235,6 +239,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, "dynamic_ram_a") == 0)
> > +		return CXL_DECODER_MODE_DYNAMIC_RAM_A;
> >  	return CXL_DECODER_MODE_NONE;
> >  }
> >  
> > @@ -264,6 +270,7 @@ cxl_decoder_get_target_type(struct cxl_decoder *decoder);
> >  bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder);
> >  bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder);
> >  bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder);
> > +bool cxl_decoder_is_dynamic_ram_a_capable(struct cxl_decoder *decoder);
> >  bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder);
> >  bool cxl_decoder_is_locked(struct cxl_decoder *decoder);
> >  unsigned int
> > @@ -272,6 +279,7 @@ 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_pmem_region(struct cxl_decoder *decoder);
> >  struct cxl_region *cxl_decoder_create_ram_region(struct cxl_decoder *decoder);
> > +struct cxl_region *cxl_decoder_create_dynamic_ram_a_region(struct cxl_decoder *decoder);
> >  struct cxl_decoder *cxl_decoder_get_by_name(struct cxl_ctx *ctx,
> >  					    const char *ident);
> >  struct cxl_memdev *cxl_decoder_get_memdev(struct cxl_decoder *decoder);
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 2/7] libcxl: Add Dynamic RAM A partition mode support
  2026-06-10 16:55       ` Dave Jiang
@ 2026-06-25  9:08         ` Anisa Su
  0 siblings, 0 replies; 31+ messages in thread
From: Anisa Su @ 2026-06-25  9:08 UTC (permalink / raw)
  To: Dave Jiang
  Cc: Richard Cheng, Anisa Su, linux-cxl, linux-kernel, nvdimm,
	Dan Williams, Jonathan Cameron, Davidlohr Bueso, Ira Weiny,
	Alison Schofield, John Groves, Gregory Price, Ira Weiny

On Wed, Jun 10, 2026 at 09:55:49AM -0700, Dave Jiang wrote:
> 
> 
> On 6/9/26 8:51 PM, Richard Cheng wrote:
> > On Mon, Jun 08, 2026 at 04:19:47PM +0800, Dave Jiang wrote:
> >>
> >>
> >> On 5/23/26 2:50 AM, Anisa Su wrote:
> >>> From: Ira Weiny <ira.weiny@intel.com>
> 
> <-- snip -->
> 
> >>> diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> >>> index ed4429f..258bdd3 100644
> >>> --- a/cxl/lib/libcxl.sym
> >>> +++ b/cxl/lib/libcxl.sym
> >>> @@ -294,6 +294,10 @@ global:
> >>>  	cxl_memdev_get_fwctl;
> >>>  	cxl_fwctl_get_major;
> >>>  	cxl_fwctl_get_minor;
> >>> +	cxl_memdev_get_dynamic_ram_a_size;
> >>> +	cxl_memdev_get_dynamic_ram_a_qos_class;
> >>> +	cxl_decoder_is_dynamic_ram_a_capable;
> >>> +	cxl_decoder_create_dynamic_ram_a_region;
> >>>  } LIBECXL_8;
> >>>  
> >>>  LIBCXL_10 {
> > 
> > Shouldn't new exported symbols go in a fresh top-level node ?
> > Something like LIBCXL_12 ? please note that Patch 4 has the same
> > issue.
> > 
> > Please let me know if I'm wrong or misunderstand anything.
> You are correct. These need to be moved to a new block for a new release. 

Thank you for finding this! It's gone into LIBCXL_13 after rebasing on
the latest pending branch.

Thanks,
Anisa

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 4/7] libcxl: Add extent functionality to DC regions
  2026-06-09  0:05   ` Dave Jiang
@ 2026-06-25  9:18     ` Anisa Su
  0 siblings, 0 replies; 31+ messages in thread
From: Anisa Su @ 2026-06-25  9:18 UTC (permalink / raw)
  To: Dave Jiang
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Ira Weiny, Alison Schofield,
	John Groves, Gregory Price, Ira Weiny

On Mon, Jun 08, 2026 at 05:05:58PM -0700, Dave Jiang wrote:
> 
> 
> On 5/23/26 2:50 AM, Anisa Su wrote:
> > From: Ira Weiny <ira.weiny@intel.com>
> > 
> > DCD regions have 0 or more extents.  The ability to list those and their
> > properties is useful to end users.
> > 
> > Add extent scanning and reporting functionality to libcxl.
> > 
> > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> 
> Missing Anisa sign-off
> 
Added
> 
> > 
> > ---
> > Changes:
> > [alison: s/tag/uuid/ for extents]
> > ---
> >  Documentation/cxl/lib/libcxl.txt |  27 ++++++
> >  cxl/lib/libcxl.c                 | 138 +++++++++++++++++++++++++++++++
> >  cxl/lib/libcxl.sym               |   5 ++
> >  cxl/lib/private.h                |  11 +++
> >  cxl/libcxl.h                     |  11 +++
> >  5 files changed, 192 insertions(+)
> > 
> > diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
> > index 9921ac1..0ad294c 100644
> > --- a/Documentation/cxl/lib/libcxl.txt
> > +++ b/Documentation/cxl/lib/libcxl.txt
> > @@ -635,6 +635,33 @@ where its properties can be interrogated by daxctl. The helper
> >  cxl_region_get_daxctl_region() returns an 'struct daxctl_region *' that
> >  can be used with other libdaxctl APIs.
> >  
> > +EXTENTS
> > +-------
> > +
> > +=== EXTENT: Enumeration
> > +----
> > +struct cxl_region_extent;
> > +struct cxl_region_extent *cxl_extent_get_first(struct cxl_region *region);
> > +struct cxl_region_extent *cxl_extent_get_next(struct cxl_region_extent *extent);
> > +#define cxl_extent_foreach(region, extent) \
> > +        for (extent = cxl_extent_get_first(region); \
> > +             extent != NULL; \
> > +             extent = cxl_extent_get_next(extent))
> > +
> > +----
> > +
> > +=== EXTENT: Attributes
> > +----
> > +unsigned long long cxl_extent_get_offset(struct cxl_region_extent *extent);
> > +unsigned long long cxl_extent_get_length(struct cxl_region_extent *extent);
> > +void cxl_extent_get_uuid(struct cxl_region_extent *extent, uuid_t uuid);
> > +----
> > +
> > +Extents represent available memory within a dynamic capacity region.  Extent
> > +objects are available for informational purposes to aid in allocation of
> > +memory.
> > +
> > +
> >  include::../../copyright.txt[]
> >  
> >  SEE ALSO
> > diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
> > index be0bc03..c096666 100644
> > --- a/cxl/lib/libcxl.c
> > +++ b/cxl/lib/libcxl.c
> > @@ -635,6 +635,7 @@ static void *add_cxl_region(void *parent, int id, const char *cxlregion_base)
> >  	region->ctx = ctx;
> >  	region->decoder = decoder;
> >  	list_head_init(&region->mappings);
> > +	list_head_init(&region->extents);
> >  
> >  	region->dev_path = strdup(cxlregion_base);
> >  	if (!region->dev_path)
> > @@ -1257,6 +1258,143 @@ cxl_mapping_get_next(struct cxl_memdev_mapping *mapping)
> >  	return list_next(&region->mappings, mapping, list);
> >  }
> >  
> > +static void cxl_extents_init(struct cxl_region *region)
> > +{
> > +	const char *devname = cxl_region_get_devname(region);
> > +	struct cxl_ctx *ctx = cxl_region_get_ctx(region);
> > +	char *extent_path, *dax_region_path;
> > +	struct dirent *de;
> > +	DIR *dir = NULL;
> > +
> > +	if (region->extents_init)
> > +		return;
> > +	region->extents_init = 1;
> > +
> > +	dax_region_path = calloc(1, strlen(region->dev_path) + 64);
> > +	if (!dax_region_path) {
> > +		err(ctx, "%s: allocation failure\n", devname);
> > +		return;
> > +	}
> > +
> > +	extent_path = calloc(1, strlen(region->dev_path) + 100);
> > +	if (!extent_path) {
> > +		err(ctx, "%s: allocation failure\n", devname);
> > +		free(dax_region_path);
> > +		return;
> > +	}
> > +
> > +	sprintf(dax_region_path, "%s/dax_region%d",
> > +		region->dev_path, region->id);
> > +	dir = opendir(dax_region_path);
> > +	if (!dir) {
> > +		err(ctx, "no extents found (%s): %s\n",
> > +			strerror(errno), dax_region_path);
> > +		free(extent_path);
> > +		free(dax_region_path);
> > +		return;
> > +	}
> > +
> > +	while ((de = readdir(dir)) != NULL) {
> > +		struct cxl_region_extent *extent;
> > +		char buf[SYSFS_ATTR_SIZE];
> > +		u64 offset, length;
> > +		int id, region_id;
> > +
> > +		if (sscanf(de->d_name, "extent%d.%d", &region_id, &id) != 2)
> > +			continue;
> > +
> > +		sprintf(extent_path, "%s/extent%d.%d/offset",
> > +			dax_region_path, region_id, id);
> > +		if (sysfs_read_attr(ctx, extent_path, buf) < 0) {
> > +			err(ctx, "%s: failed to read extent%d.%d/offset\n",
> > +				devname, region_id, id);
> > +			continue;
> > +		}
> > +
> > +		offset = strtoull(buf, NULL, 0);
> > +		if (offset == ULLONG_MAX) {
> > +			err(ctx, "%s extent%d.%d: failed to read offset\n",
> > +				devname, region_id, id);
> > +			continue;
> > +		}
> > +
> > +		sprintf(extent_path, "%s/extent%d.%d/length",
> > +			dax_region_path, region_id, id);
> > +		if (sysfs_read_attr(ctx, extent_path, buf) < 0) {
> > +			err(ctx, "%s: failed to read extent%d.%d/length\n",
> > +				devname, region_id, id);
> > +			continue;
> > +		}
> > +
> > +		length = strtoull(buf, NULL, 0);
> > +		if (length == ULLONG_MAX) {
> > +			err(ctx, "%s extent%d.%d: failed to read length\n",
> > +				devname, region_id, id);
> > +			continue;
> > +		}
> > +
> > +		sprintf(extent_path, "%s/extent%d.%d/tag",
> > +			dax_region_path, region_id, id);
> > +		buf[0] = '\0';
> > +		if (sysfs_read_attr(ctx, extent_path, buf) != 0)
> > +			dbg(ctx, "%s extent%d.%d: failed to read uuid\n",
> > +				devname, region_id, id);
> > +
> > +		extent = calloc(1, sizeof(*extent));
> > +		if (!extent) {
> > +			err(ctx, "%s extent%d.%d: allocation failure\n",
> > +				devname, region_id, id);
> > +			continue;
> > +		}
> > +		if (strlen(buf) && uuid_parse(buf, extent->uuid) < 0)
> > +			err(ctx, "%s:%s\n", extent_path, buf);
> > +		extent->region = region;
> > +		extent->offset = offset;
> > +		extent->length = length;
> > +
> > +		list_node_init(&extent->list);
> > +		list_add(&region->extents, &extent->list);
> 
> free_region() never frees any of the extents allocated and added here and thus leak the memory when region is freed.
> 
added loop to free extents in free_region():

list_for_each_safe(&region->extents, extent, _e, list) {
	list_del_from(&region->extents, &extent->list);
	free(extent);
}
> 
> > +		dbg(ctx, "%s added extent%d.%d\n", devname, region_id, id);
> > +	}
> > +	free(dax_region_path);
> > +	free(extent_path);
> > +	closedir(dir);
> > +}
> > +
> > +CXL_EXPORT struct cxl_region_extent *
> > +cxl_extent_get_first(struct cxl_region *region)
> > +{
> > +	cxl_extents_init(region);
> > +
> > +	return list_top(&region->extents, struct cxl_region_extent, list);
> > +}
> > +
> > +CXL_EXPORT struct cxl_region_extent *
> > +cxl_extent_get_next(struct cxl_region_extent *extent)
> > +{
> > +	struct cxl_region *region = extent->region;
> > +
> > +	return list_next(&region->extents, extent, list);
> > +}
> > +
> > +CXL_EXPORT unsigned long long
> > +cxl_extent_get_offset(struct cxl_region_extent *extent)
> > +{
> > +	return extent->offset;
> > +}
> > +
> > +CXL_EXPORT unsigned long long
> > +cxl_extent_get_length(struct cxl_region_extent *extent)
> > +{
> > +	return extent->length;
> > +}
> > +
> > +CXL_EXPORT void
> > +cxl_extent_get_uuid(struct cxl_region_extent *extent, uuid_t uuid)
> > +{
> > +	memcpy(uuid, extent->uuid, sizeof(uuid_t));
> > +}
> > +
> >  CXL_EXPORT struct cxl_decoder *
> >  cxl_mapping_get_decoder(struct cxl_memdev_mapping *mapping)
> >  {
> > diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
> > index 258bdd3..dcfe242 100644
> > --- a/cxl/lib/libcxl.sym
> > +++ b/cxl/lib/libcxl.sym
> > @@ -298,6 +298,11 @@ global:
> >  	cxl_memdev_get_dynamic_ram_a_qos_class;
> >  	cxl_decoder_is_dynamic_ram_a_capable;
> >  	cxl_decoder_create_dynamic_ram_a_region;
> > +	cxl_extent_get_first;
> > +	cxl_extent_get_next;
> > +	cxl_extent_get_offset;
> > +	cxl_extent_get_length;
> > +	cxl_extent_get_uuid;
> >  } LIBECXL_8;

Now in LIBCXL_13

> >  
> >  LIBCXL_10 {
> > diff --git a/cxl/lib/private.h b/cxl/lib/private.h
> > index 37b7b06..c5f3bed 100644
> > --- a/cxl/lib/private.h
> > +++ b/cxl/lib/private.h
> > @@ -183,6 +183,7 @@ struct cxl_region {
> >  	struct cxl_decoder *decoder;
> >  	struct list_node list;
> >  	int mappings_init;
> > +	int extents_init;
> >  	struct cxl_ctx *ctx;
> >  	void *dev_buf;
> >  	size_t buf_len;
> > @@ -200,6 +201,7 @@ struct cxl_region {
> >  	struct daxctl_region *dax_region;
> >  	struct kmod_module *module;
> >  	struct list_head mappings;
> > +	struct list_head extents;
> >  };
> >  
> >  struct cxl_memdev_mapping {
> > @@ -209,6 +211,15 @@ struct cxl_memdev_mapping {
> >  	struct list_node list;
> >  };
> >  
> > +#define CXL_REGION_EXTENT_TAG 0x10
> 
> defined but never used
> 
thanks for the catch! deleted

> DJ
> 
Thanks,
Anisa
> > +struct cxl_region_extent {
> > +	struct cxl_region *region;
> > +	u64 offset;
> > +	u64 length;
> > +	uuid_t uuid;
> > +	struct list_node list;
> > +};
> > +
> >  enum cxl_cmd_query_status {
> >  	CXL_CMD_QUERY_NOT_RUN = 0,
> >  	CXL_CMD_QUERY_OK,
> > diff --git a/cxl/libcxl.h b/cxl/libcxl.h
> > index fd41122..a60509f 100644
> > --- a/cxl/libcxl.h
> > +++ b/cxl/libcxl.h
> > @@ -394,6 +394,17 @@ unsigned int cxl_mapping_get_position(struct cxl_memdev_mapping *mapping);
> >               mapping != NULL; \
> >               mapping = cxl_mapping_get_next(mapping))
> >  
> > +struct cxl_region_extent;
> > +struct cxl_region_extent *cxl_extent_get_first(struct cxl_region *region);
> > +struct cxl_region_extent *cxl_extent_get_next(struct cxl_region_extent *extent);
> > +#define cxl_extent_foreach(region, extent) \
> > +        for (extent = cxl_extent_get_first(region); \
> > +             extent != NULL; \
> > +             extent = cxl_extent_get_next(extent))
> > +unsigned long long cxl_extent_get_offset(struct cxl_region_extent *extent);
> > +unsigned long long cxl_extent_get_length(struct cxl_region_extent *extent);
> > +void cxl_extent_get_uuid(struct cxl_region_extent *extent, uuid_t uuid);
> > +
> >  struct cxl_cmd;
> >  const char *cxl_cmd_get_devname(struct cxl_cmd *cmd);
> >  struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode);
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 6/7] daxctl: Add --uuid option to create-device for sparse regions
  2026-06-09  0:12   ` Dave Jiang
@ 2026-06-25  9:30     ` Anisa Su
  0 siblings, 0 replies; 31+ messages in thread
From: Anisa Su @ 2026-06-25  9:30 UTC (permalink / raw)
  To: Dave Jiang
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Ira Weiny, Alison Schofield,
	John Groves, Gregory Price

On Mon, Jun 08, 2026 at 05:12:59PM -0700, Dave Jiang wrote:
> 
> 
> On 5/23/26 2:50 AM, Anisa Su wrote:
> > Add a --uuid option to 'daxctl create-device' that writes the given
> > uuid to the new dax device's sysfs 'uuid' attribute.  On sparse (DCD)
> > regions this claims dax_resources whose tag matches and populates the
> > seed device with their capacity; size is determined by the claim, so
> > --uuid is mutually exclusive with --size.
> > 
> > Pass "0" to claim a single untagged dax_resource.  A claim that
> > matches no dax_resource leaves the device at size 0; the kernel
> > returns ENOENT.
> > 
> > Plumb the write through a new daxctl_dev_set_uuid() libdaxctl helper
> > (LIBDAXCTL_11) and document the option in the man page.
> > 
> > Signed-off-by: Anisa Su <anisa.su887@gmail.com>
> > ---
> >  Documentation/daxctl/daxctl-create-device.txt | 12 ++++
> >  daxctl/device.c                               | 72 +++++++++++++------
> >  daxctl/lib/libdaxctl.c                        | 44 ++++++++++++
> >  daxctl/lib/libdaxctl.sym                      |  5 ++
> >  daxctl/libdaxctl.h                            |  1 +
> >  5 files changed, 114 insertions(+), 20 deletions(-)
> > 
> > diff --git a/Documentation/daxctl/daxctl-create-device.txt b/Documentation/daxctl/daxctl-create-device.txt
> > index b774b86..27b87d0 100644
> > --- a/Documentation/daxctl/daxctl-create-device.txt
> > +++ b/Documentation/daxctl/daxctl-create-device.txt
> > @@ -82,6 +82,18 @@ include::region-option.txt[]
> >  
> >  	The size must be a multiple of the region alignment.
> >  
> > +	Mutually exclusive with --uuid.
> > +
> > +--uuid=::
> > +	For dax devices on sparse (DCD) regions, claim dax_resource(s) whose
> > +	tag matches the given UUID.  The device's size is determined by the
> > +	claimed capacity, so --uuid cannot be combined with --size.
> > +
> > +	A non-null UUID claims every matching dax_resource in the parent
> > +	region.  The value "0" is shorthand for the null UUID and claims a
> > +	single untagged dax_resource.  A write that matches no dax_resource
> > +	fails with ENOENT and the device is left at size 0.
> > +
> >  -a::
> >  --align::
> >  	Applications that want to establish dax memory mappings with
> > diff --git a/daxctl/device.c b/daxctl/device.c
> > index a4e36b1..21a941e 100644
> > --- a/daxctl/device.c
> > +++ b/daxctl/device.c
> > @@ -30,6 +30,7 @@ static struct {
> >  	const char *size;
> >  	const char *align;
> >  	const char *input;
> > +	const char *uuid;
> >  	bool check_config;
> >  	bool no_online;
> >  	bool no_movable;
> > @@ -85,7 +86,9 @@ OPT_BOOLEAN('C', "check-config", &param.check_config, \
> >  #define CREATE_OPTIONS() \
> >  OPT_STRING('s', "size", &param.size, "size", "size to switch the device to"), \
> >  OPT_STRING('a', "align", &param.align, "align", "alignment to switch the device to"), \
> > -OPT_STRING('\0', "input", &param.input, "input", "input device JSON file")
> > +OPT_STRING('\0', "input", &param.input, "input", "input device JSON file"), \
> > +OPT_STRING('\0', "uuid", &param.uuid, "uuid", \
> > +	"claim sparse dax_resource(s) matching this uuid (\"0\" for untagged)")
> >  
> >  #define DESTROY_OPTIONS() \
> >  OPT_BOOLEAN('f', "force", &param.force, \
> > @@ -808,6 +811,22 @@ static int do_create(struct daxctl_region *region, long long val,
> >  	struct daxctl_dev *dev;
> >  	int i, rc = 0;
> >  	long long alloc = 0;
> > +	uuid_t uuid;
> > +
> > +	if (param.uuid) {
> > +		if (param.size) {
> > +			fprintf(stderr,
> > +				"--uuid and --size are mutually exclusive\n");
> > +			return -EINVAL;
> > +		}
> > +		if (strcmp(param.uuid, "0") == 0) {
> > +			uuid_clear(uuid);
> > +		} else if (uuid_parse(param.uuid, uuid) < 0) {
> > +			fprintf(stderr, "failed to parse uuid '%s'\n",
> > +				param.uuid);
> > +			return -EINVAL;
> > +		}
> > +	}
> >  
> >  	if (daxctl_region_create_dev(region))
> >  		return -ENOSPC;
> > @@ -816,33 +835,46 @@ static int do_create(struct daxctl_region *region, long long val,
> >  	if (!dev)
> >  		return -ENOSPC;
> >  
> > -	if (val == -1)
> > -		val = daxctl_region_get_available_size(region);
> > -
> > -	if (val <= 0)
> > -		return -ENOSPC;
> > -
> >  	if (align > 0) {
> >  		rc = daxctl_dev_set_align(dev, align);
> >  		if (rc < 0)
> >  			return rc;
> >  	}
> >  
> > -	/* @maps is ordered by page_offset */
> > -	for (i = 0; i < nmaps; i++) {
> > -		rc = daxctl_dev_set_mapping(dev, maps[i].start, maps[i].end);
> > -		if (rc < 0)
> > +	if (param.uuid) {
> > +		rc = daxctl_dev_set_uuid(dev, uuid);
> > +		if (rc < 0) {
> > +			fprintf(stderr,
> > +				"%s: failed to claim uuid '%s': %s\n",
> > +				daxctl_dev_get_devname(dev), param.uuid,
> > +				strerror(-rc));
> >  			return rc;
> > -		alloc += (maps[i].end - maps[i].start + 1);
> > -	}
> > -
> > -	if (nmaps > 0 && val > 0 && alloc != val) {
> > -		fprintf(stderr, "%s: allocated %lld but specified size %lld\n",
> > -			daxctl_dev_get_devname(dev), alloc, val);
> > +		}
> >  	} else {
> > -		rc = daxctl_dev_set_size(dev, val);
> > -		if (rc < 0)
> > -			return rc;
> > +		if (val == -1)
> > +			val = daxctl_region_get_available_size(region);
> > +
> > +		if (val <= 0)
> > +			return -ENOSPC;
> > +
> > +		/* @maps is ordered by page_offset */
> > +		for (i = 0; i < nmaps; i++) {
> > +			rc = daxctl_dev_set_mapping(dev, maps[i].start,
> > +						    maps[i].end);
> > +			if (rc < 0)
> > +				return rc;
> > +			alloc += (maps[i].end - maps[i].start + 1);
> > +		}
> > +
> > +		if (nmaps > 0 && val > 0 && alloc != val) {
> > +			fprintf(stderr,
> > +				"%s: allocated %lld but specified size %lld\n",
> > +				daxctl_dev_get_devname(dev), alloc, val);
> > +		} else {
> > +			rc = daxctl_dev_set_size(dev, val);
> > +			if (rc < 0)
> > +				return rc;
> > +		}
> >  	}
> >  
> >  	rc = daxctl_dev_enable_devdax(dev);
> > diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c
> > index 02ae7e5..fe07939 100644
> > --- a/daxctl/lib/libdaxctl.c
> > +++ b/daxctl/lib/libdaxctl.c
> > @@ -1107,6 +1107,50 @@ DAXCTL_EXPORT int daxctl_dev_set_size(struct daxctl_dev *dev, unsigned long long
> >  	return 0;
> >  }
> >  
> > +DAXCTL_EXPORT int daxctl_dev_set_uuid(struct daxctl_dev *dev, uuid_t uuid)
> > +{
> > +	struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev);
> > +	char buf[SYSFS_ATTR_SIZE];
> > +	char *path = dev->dev_buf;
> > +	int len = dev->buf_len;
> > +
> > +	if (snprintf(path, len, "%s/uuid", dev->dev_path) >= len) {
> > +		err(ctx, "%s: buffer too small!\n",
> > +				daxctl_dev_get_devname(dev));
> > +		return -ENXIO;
> 
> snprintf() returns negative errno, propogate
> 
> > +	}
> > +
> > +	if (uuid_is_null(uuid))
> > +		sprintf(buf, "0\n");
> > +	else
> > +		uuid_unparse(uuid, buf);
> > +
> > +	if (sysfs_write_attr(ctx, path, buf) < 0) {
> > +		err(ctx, "%s: failed to set uuid\n",
> > +				daxctl_dev_get_devname(dev));
> > +		return -ENXIO;
> 
> propogate the errno from sysfs_write_attr()
> 
> > +	}
> > +
> > +	/*
> > +	 * On a sparse region the kernel populates the device size as a
> > +	 * side effect of claiming the matching dax_resource(s); refresh
> > +	 * the cached size so callers see the post-claim value.
> > +	 */
> > +	if (snprintf(path, len, "%s/size", dev->dev_path) >= len) {
> > +		err(ctx, "%s: buffer too small!\n",
> > +				daxctl_dev_get_devname(dev));
> > +		return -ENXIO;
> 
> propogate negative return value from snprintf()
> 
> > +	}
> > +	if (sysfs_read_attr(ctx, path, buf) < 0) {
> > +		err(ctx, "%s: failed to read back size\n",
> > +				daxctl_dev_get_devname(dev));
> > +		return -ENXIO;
> 
> propgate negative errno from sysfs_read_attr()
> 
errno propagated. For snprintf, since it returns pos rc if output was
truncated:

rc = snprintf(path, len, "%s/uuid", dev->dev_path);
if (rc < 0)
	return rc;
if (rc >= len) {
	err(ctx, "%s: buffer too small!\n",
			daxctl_dev_get_devname(dev));
	return -ENXIO;
}

> > +	}
> > +	dev->size = strtoull(buf, NULL, 0);
> > +
> > +	return 0;
> > +}
> > +
> >  DAXCTL_EXPORT unsigned long daxctl_dev_get_align(struct daxctl_dev *dev)
> >  {
> >  	return dev->align;
> > diff --git a/daxctl/lib/libdaxctl.sym b/daxctl/lib/libdaxctl.sym
> > index 3098811..16792eb 100644
> > --- a/daxctl/lib/libdaxctl.sym
> > +++ b/daxctl/lib/libdaxctl.sym
> > @@ -104,3 +104,8 @@ LIBDAXCTL_10 {
> >  global:
> >  	daxctl_dev_is_system_ram_capable;
> >  } LIBDAXCTL_9;
> > +
> > +LIBDAXCTL_11 {
> > +global:
> > +	daxctl_dev_set_uuid;
> > +} LIBDAXCTL_10;
> > diff --git a/daxctl/libdaxctl.h b/daxctl/libdaxctl.h
> > index 53c6bbd..cdd5995 100644
> > --- a/daxctl/libdaxctl.h
> > +++ b/daxctl/libdaxctl.h
> > @@ -63,6 +63,7 @@ int daxctl_dev_get_minor(struct daxctl_dev *dev);
> >  unsigned long long daxctl_dev_get_resource(struct daxctl_dev *dev);
> >  unsigned long long daxctl_dev_get_size(struct daxctl_dev *dev);
> >  int daxctl_dev_set_size(struct daxctl_dev *dev, unsigned long long size);
> > +int daxctl_dev_set_uuid(struct daxctl_dev *dev, uuid_t uuid);
> >  unsigned long daxctl_dev_get_align(struct daxctl_dev *dev);
> >  int daxctl_dev_set_align(struct daxctl_dev *dev, unsigned long align);
> >  int daxctl_dev_set_mapping(struct daxctl_dev *dev, unsigned long long start,
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v6 7/7] cxl/test: Add Dynamic Capacity tests
  2026-06-09  0:24   ` Dave Jiang
@ 2026-06-25  9:34     ` Anisa Su
  0 siblings, 0 replies; 31+ messages in thread
From: Anisa Su @ 2026-06-25  9:34 UTC (permalink / raw)
  To: Dave Jiang
  Cc: Anisa Su, linux-cxl, linux-kernel, nvdimm, Dan Williams,
	Jonathan Cameron, Davidlohr Bueso, Ira Weiny, Alison Schofield,
	John Groves, Gregory Price, Ira Weiny

On Mon, Jun 08, 2026 at 05:24:08PM -0700, Dave Jiang wrote:
> 
> 
> On 5/23/26 2:50 AM, Anisa Su wrote:
> > From: Ira Weiny <ira.weiny@intel.com>
> > 
> > cxl_test provides a good way to ensure quick smoke and regression
> > testing.  The complexity of DCD and the new sparse DAX regions
> > required to use them benefits greatly with a series of smoke tests.
> > 
> > The only part of the kernel stack which must be bypassed is the actual
> > irq of DCD events.  However, the event processing itself can be tested
> > via cxl_test calling directly into the event processing.
> > 
> > In this way the rest of the stack; management of sparse regions, the
> > extent device lifetimes, and the dax device operations can be tested.
> > 
> > Add Dynamic Capacity Device tests for kernels which have DCD support.
> > 
> > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> > Signed-off-by: Anisa Su <anisa.su887@gmail.com>
> 
> A nit below. Otherwise
> 
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> 
[snip] 
> > +test_event_reporting()
> > +{
> > +	# Test event reporting
> > +	# results expected
> > +	num_dcd_events_expected=2
> > +
> > +	echo "Test: Prep event trace"
> > +	echo "" > /sys/kernel/tracing/trace
> > +	echo 1 > /sys/kernel/tracing/events/cxl/enable
> > +	echo 1 > /sys/kernel/tracing/tracing_on
> > +
> > +	inject_extent ${device} $base_ext_dpa $base_ext_length ""
> > +	remove_extent ${device} $base_ext_dpa $base_ext_length
> 
> Missing the tag arg
> 
> DJ

Fixed, thanks!

Anisa
> 
> > +
> > +	echo 0 > /sys/kernel/tracing/tracing_on
> > +
> > +	echo "Test: Events seen"
> > +	trace_out=$(cat /sys/kernel/tracing/trace)
> > +
> > +	# Look for DCD events
> > +	num_dcd_events=$(grep -c "cxl_dynamic_capacity" <<< "${trace_out}")
> > +	echo "     LOG     (Expected) : (Found)"
> > +	echo "     DCD events    ($num_dcd_events_expected) : $num_dcd_events"
> > +
> > +	if [ "$num_dcd_events" -ne $num_dcd_events_expected ]; then
> > +		err "$LINENO"
> > +	fi
> > +}
> > +
> > +
> > +# ========================================================================
> > +# main()
> > +# ========================================================================
> > +
> > +modprobe -r cxl_test
> > +modprobe cxl_test
> > +
> > +# The mock stamps a single cxl_mem instance with this serial (0xDCDC).
> > +# That memdev's DC partition is advertised as sharable in CDAT and is
> > +# the only one that can host the shared-extent tests.  All other DCD
> > +# memdevs stay non-sharable and host the rest of the suite.
> > +MOCK_DC_SHARABLE_SERIAL=56540
> > +
> > +readarray -t memdevs < <("$CXL" list -b cxl_test -Mi | jq -r '.[].memdev')
> > +
> > +sharable_mem=""
> > +sharable_decoder=""
> > +sharable_bus=""
> > +sharable_device=""
> > +sharable_dra_size=""
> > +
> > +for cand in ${memdevs[@]}; do
> > +	cand_dra=$($CXL list -m $cand | jq -r '.[].dynamic_ram_a_size')
> > +	if [ "$cand_dra" == "null" ]; then
> > +		continue
> > +	fi
> > +	cand_decoder=$($CXL list -b cxl_test -D -d root -m "$cand" |
> > +		  jq -r ".[] |
> > +		  select(.volatile_capable == true) |
> > +		  select(.nr_targets == 1) |
> > +		  select(.max_available_extent >= ${cand_dra}) |
> > +		  .decoder")
> > +	if [[ -z "$cand_decoder" ]]; then
> > +		continue
> > +	fi
> > +	cand_serial=$($CXL list -m $cand | jq -r '.[].serial')
> > +	cand_bus=`"$CXL" list -b cxl_test -m ${cand} | jq -r '.[].bus'`
> > +	cand_device=$($CXL list -m $cand | jq -r '.[].host')
> > +
> > +	if [ "$cand_serial" == "$MOCK_DC_SHARABLE_SERIAL" ]; then
> > +		if [ -z "$sharable_mem" ]; then
> > +			sharable_mem="$cand"
> > +			sharable_decoder="$cand_decoder"
> > +			sharable_bus="$cand_bus"
> > +			sharable_device="$cand_device"
> > +			sharable_dra_size="$cand_dra"
> > +		fi
> > +	else
> > +		if [ -z "$mem" ]; then
> > +			mem="$cand"
> > +			decoder="$cand_decoder"
> > +			bus="$cand_bus"
> > +			device="$cand_device"
> > +			dra_size="$cand_dra"
> > +		fi
> > +	fi
> > +
> > +	if [ -n "$mem" ] && [ -n "$sharable_mem" ]; then
> > +		break
> > +	fi
> > +done
> > +
> > +echo "TEST: non-sharable bus:${bus} decoder:${decoder} mem:${mem} device:${device} size:${dra_size}"
> > +echo "TEST: sharable     bus:${sharable_bus} decoder:${sharable_decoder} mem:${sharable_mem} device:${sharable_device} size:${sharable_dra_size}"
> > +
> > +if [ "$decoder" == "" ] || [ "$device" == "" ] || [ "$dra_size" == "" ]; then
> > +	echo "No non-sharable mem device/decoder found with DCD support"
> > +	exit 77
> > +fi
> > +
> > +if [ "$sharable_mem" == "" ]; then
> > +	echo "No sharable mem device found (mock did not stamp MOCK_DC_SHARABLE_SERIAL)"
> > +	exit 77
> > +fi
> > +
> > +# testing pre existing extents must be called first as the extents were created
> > +# by cxl-test being loaded
> > +test_pre_existing_extents
> > +test_remove_extent_under_dax_device
> > +test_create_dax_dev_spanning_two_extents
> > +test_inject_tag_support
> > +test_uuid_no_match
> > +test_uuid_no_match_seed_intact
> > +test_uuid_aggregation
> > +test_uuid_show
> > +# These two run on the sharable memdev (serial $MOCK_DC_SHARABLE_SERIAL).
> > +test_shared_extent_inject
> > +test_seq_integrity_gap
> > +test_cross_more_uniqueness
> > +test_alignment_rejection
> > +test_partial_extent_remove
> > +test_multiple_extent_remove
> > +test_destroy_region_without_extent_removal
> > +test_destroy_with_extent_and_dax
> > +test_dax_device_ops
> > +test_reject_overlapping
> > +test_two_regions
> > +test_more_bit
> > +test_driver_reload
> > +test_event_reporting
> > +
> > +modprobe -r cxl_test
> > +
> > +check_dmesg "$LINENO"
> > +
> > +exit 0
> > diff --git a/test/meson.build b/test/meson.build
> > index e0e2193..cd06bf8 100644
> > --- a/test/meson.build
> > +++ b/test/meson.build
> > @@ -171,6 +171,7 @@ cxl_translate = find_program('cxl-translate.sh')
> >  cxl_elc = find_program('cxl-elc.sh')
> >  cxl_dax_hmem = find_program('cxl-dax-hmem.sh')
> >  cxl_region_replay = find_program('cxl-region-replay.sh')
> > +cxl_dcd = find_program('cxl-dcd.sh')
> >  
> >  tests = [
> >    [ 'libndctl',               libndctl,		  'ndctl' ],
> > @@ -207,6 +208,7 @@ tests = [
> >    [ 'cxl-elc.sh',             cxl_elc,            'cxl'   ],
> >    [ 'cxl-dax-hmem.sh',        cxl_dax_hmem,       'cxl'   ],
> >    [ 'cxl-region-replay.sh',   cxl_region_replay,  'cxl'   ],
> > +  [ 'cxl-dcd.sh',             cxl_dcd,            'cxl'   ],
> >  ]
> >  
> >  if get_option('destructive').enabled()
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2026-06-25  9:34 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-23  9:50 [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Anisa Su
2026-05-23  9:50 ` [PATCH v6 1/7] " Anisa Su
2026-06-08 23:18   ` Dave Jiang
2026-05-23  9:50 ` [PATCH v6 2/7] libcxl: Add Dynamic RAM A partition mode support Anisa Su
2026-06-08 23:19   ` Dave Jiang
2026-06-10  3:51     ` Richard Cheng
2026-06-10 16:55       ` Dave Jiang
2026-06-25  9:08         ` Anisa Su
2026-06-25  9:07     ` Anisa Su
2026-05-23  9:50 ` [PATCH v6 3/7] cxl/region: Add cxl-cli support for dynamic RAM A Anisa Su
2026-06-08 23:58   ` Dave Jiang
2026-05-23  9:50 ` [PATCH v6 4/7] libcxl: Add extent functionality to DC regions Anisa Su
2026-06-09  0:05   ` Dave Jiang
2026-06-25  9:18     ` Anisa Su
2026-05-23  9:50 ` [PATCH v6 5/7] cxl/region: Add extent output to region query Anisa Su
2026-06-09  0:08   ` Dave Jiang
2026-06-10  3:55     ` Richard Cheng
2026-06-16 10:47       ` Anisa Su
2026-06-16 10:45     ` Anisa Su
2026-05-23  9:50 ` [PATCH v6 6/7] daxctl: Add --uuid option to create-device for sparse regions Anisa Su
2026-06-09  0:12   ` Dave Jiang
2026-06-25  9:30     ` Anisa Su
2026-05-23  9:50 ` [PATCH v6 7/7] cxl/test: Add Dynamic Capacity tests Anisa Su
2026-06-09  0:24   ` Dave Jiang
2026-06-25  9:34     ` Anisa Su
2026-06-05  5:43 ` [PATCH v6 0/7] ndctl: Dynamic Capacity additions for cxl-cli Alison Schofield
2026-06-08  8:11   ` Anisa Su
2026-06-17  7:10   ` Alison Schofield
2026-06-18  5:52     ` Anisa Su
2026-06-19  0:38       ` Alison Schofield
2026-06-24  4:51         ` Anisa Su

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome