The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v12 0/8] DCD Prep Series
@ 2026-07-31  8:48 Anisa Su
  2026-07-31  8:48 ` [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
                   ` (7 more replies)
  0 siblings, 8 replies; 33+ messages in thread
From: Anisa Su @ 2026-07-31  8:48 UTC (permalink / raw)
  To: linux-cxl
  Cc: nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

These are the first eight patches of the Dynamic Capacity Device (DCD)
series, split from v11 and posted on their own: DCD enumeration, CDAT handling
for DC partitions, the event interrupt plumbing, and two prepatory refactors.
They stand alone, and keep DCD disabled. They are not dependent on the
rest of the series, so I think they are safe enough to pick up ahead
of the rest.

Patch 1 detects the 48h command set and then leaves mds->dcd_supported
false. The flag is only turned on by the event handling patch later
in the full series, so every DCD specific path added here is shut off at
runtime: cxl_configure_dcd() never runs, no DC partition is created,
no DCD interrupt is armed, and the CXL_PARTMODE_DYNAMIC_RAM_1 paths are
unreachable.  The intent is to land the enumeration and plumbing while
the extent to DAX pipeline is still under review.

Based on v7.2-rc5.

GitHub Branch:
https://github.com/anisa-su993/anisa-linux-kernel/tree/dcd-prep-series

v11 -> v12 Changes:
===================
 - Patch 1: cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
        - now leaves DCD disabled (mds->dcd_supported = false)

 - Patch 2: cxl/mem: Read dynamic capacity configuration from the device
        - mbox.c: bound the Get DC Config request by the negotiated
          mailbox payload. Requesting all 8 partitions needs 328 bytes,
          which exceeds a spec-minimum 256-byte mailbox and fails -E2BIG,
          disabling DCD. Cap partition_count per call and read the rest
          via the existing loop (Sashiko)

        - cxlmem.h/mbox.c: bump DC config comment references from CXL 3.2
          to CXL r4.0 (Tables 8-178/179/180), matching the commit log (Dave)

 - Patch 3: cxl/cdat: Gather DSMAS data for DCD partitions\
        - No functional change. Just added the tags below
        + Co-developed-by: Anisa Su (for shareable support)
        + Reviewed-by: Dave Jiang

 - Patch 6:
        - pci.c: size the Set Event Interrupt Policy payload by the
          device's policy layout (Get reply length) instead of assuming
          based on DCD command support; a CXL 3.0+ non-DCD device can have
          the dcd_settings policy bit but not support DCD commands (Benjamin Cheatham).
        - link to report: https://lore.kernel.org/linux-cxl/20260625112638.550691-1-anisa.su@samsung.com/T/#m09b4512dbde6e1f47a27f28c44e23d83a2d628e4

        - pci.c: add cxl_event_drain_mask() helper to set drain mask
          (standard logs when native_cxl, DCD when supported). Shared by
          cxl_event_thread() and the initial drain in cxl_event_config() to
          address the 2 issues reported by Sashiko below:
          1. If cxl_event_req_irq() fails for the DCD log, cxl_irqsetup()
            calls cxl_disable_dcd() — which clears the driver's flag but
            leaves the device armed for DCD interrupts. Then cxl_event_thread(),
            whose drain mask still included the DCD bit, would see
            CXLDEV_EVENT_STATUS_DCD set while cxl_mem_get_event_records()
            refused to drain that log (it checks cxl_dcd_supported(), now false),
            leaving the bit permanently set and spinning the
            do { } while (status) loop forever.
          2. The probe-time drain was gated on native_cxl, but the driver owns the
            DCD log regardless of who owns standard error handling (firmware cannot take DCD).
            So on a BIOS-first platform with a DCD device, the pre-existing DCD events were
            never drained at probe. Because the interrupts are edge-triggered, that already-set
            status bit could strand future notifications
          > link to report: https://lore.kernel.org/linux-cxl/20260625112638.550691-1-anisa.su@samsung.com/T/#m9141704ffeadd08ea86d16121575c4b5c400d107

 - misc. checkpatch and comment cleanups across the set

 - Tested-by: from Wonjae Lee, Junhee Park and Heesoo Kim on all 8 patches

The tags apply to the v11 series but I carried the tags over because
the changes detailed above (256B mailbox size, card with dcd_settings policy
bit but doesn't support 48xx cmds) are not relevant to the test configuration,
which has a sufficiently large mailbox size, and has both the
dcd_settings policy bit + supports the 48xx commands.
But if needed, this can be re-tested on HW. I thought
it might not be very meaningful since DCD is turned off.

Instead, I emulated a 256B mailbox with QEMU and a 3.0 non-DCD device to
check that the fixes work. Detailed below.

v11 (full series):
https://lore.kernel.org/linux-cxl/20260625112638.550691-1-anisa.su@samsung.com/

What changes for devices without DCD
====================================
Code that touches non-DCD devices:

 - ram_size and ram qos_class locate the RAM partition by mode instead of
   assuming index 0; cxl_pmem_size() becomes cxl_part_size().

 - CXL_NR_PARTITIONS_MAX goes from 2 to 3, and each partition records its
   DSMAS handle and shareable flag from CDAT. Both fields are written
   here but not consumed until the rest of the patchset.

Review status
=============
Patches 1, 2 and 6 carry no Reviewed-by: the DCD capability flag, the
Get DC Config read, and the event interrupt configuration, so review
there is especially welcome.  Patches 3 and 8 picked up Dave Jiang's
Reviewed-by since v11; the rest carry their v11 tags unchanged.

Patch organization
==================
 1-3: enumerate DCD command support, read the DC configuration, and pick
      up DSMAS data for DC partitions.
 4-6: split event msgnum configuration from irq setup, factor out the
      interrupt policy check, then configure the DCD event interrupt.
 7-8: prepatory refactors -- return the endpoint decoder from the region
      search, and enforce partition order while collapsing the ram/pmem
      specific partition helpers into one.

Testing
=======
As I mentioned above, the full v11 series was tested on shareable DCD hw
on GNR platform, but since the test configuration doesn't apply to
the changes made in this version, I quickly tweaked QEMU to emulate a
256B mailbox (set CXL_MAILBOX_PAYLOAD_SHIFT = 8) and set the minimum
policy length from 4 to 5 (to include dcd_settings).

The relevant conclusion from the HW testing on v11 is that adding
and releasing extents succeeded, so the event interrupts were working.
Additionally, the device is shareable and an untagged (null uuid)
extent was rejected, so partition data is probably read correctly.

Also used ndctl-test-runner to run cxl_test to test for regressions.
The NDCTL branch tested against is pmem/pending:
https://github.com/anisa-su993/ndctl-test-runner/actions/runs/30613886000

QEMU test results: interrupt policy sizing and Get DC Config bounding
=====================================================================
QEMU:   cxl-type3, two local modifications described per test

Test 1: Set Event Interrupt Policy against a strict device
----------------------------------------------------------
Models the card Benjamin Cheatham reported: CXL 3.0+, exposes the DCD
interrupt-policy field, does NOT advertise the 48h DCD command set.

QEMU modification -- make the device reject the 4 byte policy payload:

    --- a/include/hw/cxl/cxl_events.h
    +++ b/include/hw/cxl/cxl_events.h
    -#define CXL_EVENT_INT_SETTING_MIN_LEN 4
    +#define CXL_EVENT_INT_SETTING_MIN_LEN 5

Topology: plain cxl-type3 (no dc-regions), so no 48h commands appear in
the CEL and the driver leaves DCD unsupported.

With the fix (payload sized from the device's Get reply):

    Event config : OS DCD not supported
    memdev count:  1

The device probes, and the Set succeeds because the driver echoes back
the 5-byte layout the device declared in its Get reply, even though DCD
command support is off.

Without the fix -- same topology
(.size_in = CXL_EVENT_INT_POLICY_BASE_SIZE, i.e. always 4 bytes):

    [   13.635070] cxl_pci:__cxl_pci_mbox_send_cmd:346: cxl_pci 0000:0d:00.0: Mailbox operation had an error: invalid payload length
    [   13.638125] cxl_pci 0000:0d:00.0: Failed to set event interrupt policy : -6
    memdev count:  0

The failure is not cosmetic: cxl_event_config() propagates the error and
cxl_pci_probe() returns it, so the memory device does not attach at all.
This reproduces the reported failure and confirms the fix addresses it.


Test 2: Get DC Config on a spec-minimum mailbox
-----------------------------------------------
CXL r3.1 allows a 256-byte mailbox payload.  Before the v12 change the
driver asked for all CXL_MAX_DC_PARTITIONS (8) partitions in one Get DC
Config -- 8 + 8 * 40 = 328 bytes -- which cxl_internal_send_cmd() rejects
with -E2BIG before the command reaches the device, leaving DCD silently
unavailable.

QEMU modification -- shrink the mailbox to the spec minimum:

    --- a/include/hw/cxl/cxl_device.h
    +++ b/include/hw/cxl/cxl_device.h
    -#define CXL_MAILBOX_PAYLOAD_SHIFT 11        /* 2048 bytes */
    +#define CXL_MAILBOX_PAYLOAD_SHIFT 8         /*  256 bytes */

2a. Single DC region -- PASSES

    [  370.443819] cxl_pci_setup_mailbox: Mailbox payload sized 256
    [  370.574517] cxl_dc_check: DC partition 0 start 0 size 12884901888 blk_size: 2097152
    [  370.579817] cxl_dev_dc_identify: Returning partition 0 0 size 12884901888
    [  370.584529] cxl_configure_dcd: Adding dynamic ram partition 1; 0 size 12884901888
    [  370.588750] add_dpa_res: DPA(dynamic_ram_1): [mem 0x00000000-0x2ffffffff flags 0x200]

    # cat /sys/bus/cxl/devices/mem0/dynamic_ram_1/size
    0x300000000

cxl_dc_check() and cxl_dev_dc_identify() only reach those dev_dbg()s
after cxl_get_dc_config() returns successfully, so the read completed
rather than failing -E2BIG.  Exactly one "Sending command: 0x4800"
appears: the device has one region, so the per-call cap is exercised but
the start_partition continuation loop is not.

2b. Eight DC regions -- PASSES

Re-run with num-dc-regions=8 to force more partitions than fit in one
payload to test that the loop for retrieving all paritions is working.

    Mailbox payload sized 256
    "Sending command: 0x4800" count: 2

    [   13.037634] cxl_pci:__cxl_pci_mbox_send_cmd:263: cxl_pci 0000:0d:00.0: Sending command: 0x4800
    [   13.039762] cxl_pci:cxl_pci_mbox_wait_for_doorbell:74: cxl_pci 0000:0d:00.0: Doorbell wait took 0ms
    [   13.042483] cxl_core:cxl_get_dc_config:2166: cxl_pci 0000:0d:00.0: Read 5/8 DC partitions
    [   13.044020] cxl_core:cxl_dc_check:2111: cxl_pci 0000:0d:00.0: DC partition 0 start 0 size 1610612736 blk_size: 2097152
    [   13.046735] cxl_core:cxl_dc_check:2111: cxl_pci 0000:0d:00.0: DC partition 1 start 1610612736 size 1610612736 blk_size: 2097152
    [   13.049732] cxl_core:cxl_dc_check:2111: cxl_pci 0000:0d:00.0: DC partition 2 start 3221225472 size 1610612736 blk_size: 2097152
    [   13.053862] cxl_core:cxl_dc_check:2111: cxl_pci 0000:0d:00.0: DC partition 3 start 4831838208 size 1610612736 blk_size: 2097152
    [   13.056833] cxl_core:cxl_dc_check:2111: cxl_pci 0000:0d:00.0: DC partition 4 start 6442450944 size 1610612736 blk_size: 2097152
    [   13.059801] cxl_pci:__cxl_pci_mbox_send_cmd:263: cxl_pci 0000:0d:00.0: Sending command: 0x4800
    [   13.062293] cxl_pci:cxl_pci_mbox_wait_for_doorbell:74: cxl_pci 0000:0d:00.0: Doorbell wait took 0ms
    [   13.064680] cxl_core:cxl_get_dc_config:2166: cxl_pci 0000:0d:00.0: Read 3/8 DC partitions
    [   13.066897] cxl_core:cxl_dc_check:2111: cxl_pci 0000:0d:00.0: DC partition 5 start 8053063680 size 1610612736 blk_size: 2097152
    [   13.072694] cxl_core:cxl_dc_check:2111: cxl_pci 0000:0d:00.0: DC partition 6 start 9663676416 size 1610612736 blk_size: 2097152
    [   13.076634] cxl_core:cxl_dc_check:2111: cxl_pci 0000:0d:00.0: DC partition 7 start 11274289152 size 1610612736 blk_size: 2097152
    [   13.080084] cxl_core:cxl_dev_dc_identify:2256: cxl_pci 0000:0d:00.0: Returning partition 0 0 size 1610612736

Two Get DC Config commands rather than one: the device holds eight
partitions and only five fit per call, so start_partition advances and
the remaining three are read in a second call. 

Ira Weiny (8):
  cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
  cxl/mem: Read dynamic capacity configuration from the device
  cxl/cdat: Gather DSMAS data for DCD partitions
  cxl/events: Split event msgnum configuration from irq setup
  cxl/pci: Factor out interrupt policy check
  cxl/mem: Configure dynamic capacity interrupts
  cxl/core: Return endpoint decoder information from region search
  cxl/core: Enforce partition order/simplify partition calls

 drivers/cxl/core/cdat.c   |  13 ++
 drivers/cxl/core/core.h   |   6 +-
 drivers/cxl/core/hdm.c    |  14 +-
 drivers/cxl/core/mbox.c   | 286 +++++++++++++++++++++++++++++++++++++-
 drivers/cxl/core/memdev.c |  36 ++---
 drivers/cxl/core/region.c |   8 +-
 drivers/cxl/cxl.h         |   4 +-
 drivers/cxl/cxlmem.h      |  88 +++++++++++-
 drivers/cxl/mem.c         |   2 +-
 drivers/cxl/pci.c         | 161 ++++++++++++++++-----
 include/cxl/cxl.h         |   7 +-
 11 files changed, 546 insertions(+), 79 deletions(-)


base-commit: f5098b6bae761e346ebcd9da7f95622c04733cff
-- 
2.43.0


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

* [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
  2026-07-31  8:48 [PATCH v12 0/8] DCD Prep Series Anisa Su
@ 2026-07-31  8:48 ` Anisa Su
  2026-08-03 18:03   ` Dave Jiang
                     ` (3 more replies)
  2026-07-31  8:48 ` [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
                   ` (6 subsequent siblings)
  7 siblings, 4 replies; 33+ messages in thread
From: Anisa Su @ 2026-07-31  8:48 UTC (permalink / raw)
  To: linux-cxl
  Cc: nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

From: Ira Weiny <iweiny@kernel.org>

Per the CXL 4.0 specification software must check the Command Effects
Log (CEL) for dynamic capacity command support.

Detect support for the DCD commands while reading the CEL, including:

        Get DC Config
        Get DC Extent List
        Add DC Response
        Release DC

Based on an original patch by Navneet Singh.

Signed-off-by: Ira Weiny <iweiny@kernel.org>
Signed-off-by: Anisa Su <anisa.su@samsung.com>
Tested-by: Wonjae Lee <wj28.lee@samsung.com>
Tested-by: Junhee Park <jh9934.park@samsung.com>
Tested-by: Heesoo Kim <habil.kim@samsung.com>

---
Changes:
1. mbox.c: leave mds->dcd_supported false so the hardware enablement
   patches can be upstreamed ahead of the extent and DAX work; the
   event handling patch re-enables it.
---
 drivers/cxl/core/mbox.c | 44 +++++++++++++++++++++++++++++++++++++++++
 drivers/cxl/cxlmem.h    | 20 +++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index 7c6c5b7450a5..4790524c32a7 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -165,6 +165,38 @@ static void cxl_set_security_cmd_enabled(struct cxl_security_state *security,
 	}
 }
 
+static bool cxl_is_dcd_command(u16 opcode)
+{
+#define CXL_MBOX_OP_DCD_CMDS 0x48
+
+	return (opcode >> 8) == CXL_MBOX_OP_DCD_CMDS;
+}
+
+static void cxl_set_dcd_cmd_enabled(u16 opcode, unsigned long *cmd_mask)
+{
+	switch (opcode) {
+	case CXL_MBOX_OP_GET_DC_CONFIG:
+		set_bit(CXL_DCD_ENABLED_GET_CONFIG, cmd_mask);
+		break;
+	case CXL_MBOX_OP_GET_DC_EXTENT_LIST:
+		set_bit(CXL_DCD_ENABLED_GET_EXTENT_LIST, cmd_mask);
+		break;
+	case CXL_MBOX_OP_ADD_DC_RESPONSE:
+		set_bit(CXL_DCD_ENABLED_ADD_RESPONSE, cmd_mask);
+		break;
+	case CXL_MBOX_OP_RELEASE_DC:
+		set_bit(CXL_DCD_ENABLED_RELEASE, cmd_mask);
+		break;
+	default:
+		break;
+	}
+}
+
+static bool cxl_verify_dcd_cmds(unsigned long *cmds_seen)
+{
+	return bitmap_full(cmds_seen, CXL_DCD_ENABLED_MAX);
+}
+
 static bool cxl_is_poison_command(u16 opcode)
 {
 #define CXL_MBOX_OP_POISON_CMDS 0x43
@@ -757,6 +789,7 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
 	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
 	struct cxl_cel_entry *cel_entry;
 	const int cel_entries = size / sizeof(*cel_entry);
+	DECLARE_BITMAP(dcd_cmds, CXL_DCD_ENABLED_MAX) = {};
 	struct device *dev = mds->cxlds.dev;
 	int i, ro_cmds = 0, wr_cmds = 0;
 
@@ -785,11 +818,22 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
 			enabled++;
 		}
 
+		if (cxl_is_dcd_command(opcode)) {
+			cxl_set_dcd_cmd_enabled(opcode, dcd_cmds);
+			enabled++;
+		}
+
 		dev_dbg(dev, "Opcode 0x%04x %s\n", opcode,
 			enabled ? "enabled" : "unsupported by driver");
 	}
 
 	set_features_cap(cxl_mbox, ro_cmds, wr_cmds);
+	/*
+	 * Disabled until event handling implemented.
+	 */
+	if (cxl_verify_dcd_cmds(dcd_cmds))
+		dev_dbg(dev, "Device supports DCD; capability disabled\n");
+	mds->dcd_supported = false;
 }
 
 static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_memdev_state *mds)
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index ed419d0c59f2..616eeaeee5b9 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -252,6 +252,20 @@ struct cxl_event_state {
 	struct mutex log_lock;
 };
 
+/**
+ * CXL r4.0 Section 8.2.10.9 - Memory Device Command Sets. See Table 8-308.
+ *
+ * The 48h Command Set (Opcodes 4800h - 4803h) defines the device-enabled DCD
+ * commands.
+ */
+enum dcd_cmd_enabled_bits {
+	CXL_DCD_ENABLED_GET_CONFIG,
+	CXL_DCD_ENABLED_GET_EXTENT_LIST,
+	CXL_DCD_ENABLED_ADD_RESPONSE,
+	CXL_DCD_ENABLED_RELEASE,
+	CXL_DCD_ENABLED_MAX
+};
+
 /* Device enabled poison commands */
 enum poison_cmd_enabled_bits {
 	CXL_POISON_ENABLED_LIST,
@@ -427,6 +441,7 @@ static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox)
  * @partition_align_bytes: alignment size for partition-able capacity
  * @active_volatile_bytes: sum of hard + soft volatile
  * @active_persistent_bytes: sum of hard + soft persistent
+ * @dcd_supported: all DCD commands are supported
  * @event: event log driver state
  * @poison: poison driver state info
  * @security: security driver state info
@@ -446,6 +461,7 @@ struct cxl_memdev_state {
 	u64 partition_align_bytes;
 	u64 active_volatile_bytes;
 	u64 active_persistent_bytes;
+	bool dcd_supported;
 
 	struct cxl_event_state event;
 	struct cxl_poison_state poison;
@@ -507,6 +523,10 @@ enum cxl_opcode {
 	CXL_MBOX_OP_UNLOCK		= 0x4503,
 	CXL_MBOX_OP_FREEZE_SECURITY	= 0x4504,
 	CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE	= 0x4505,
+	CXL_MBOX_OP_GET_DC_CONFIG	= 0x4800,
+	CXL_MBOX_OP_GET_DC_EXTENT_LIST	= 0x4801,
+	CXL_MBOX_OP_ADD_DC_RESPONSE	= 0x4802,
+	CXL_MBOX_OP_RELEASE_DC		= 0x4803,
 	CXL_MBOX_OP_MAX			= 0x10000
 };
 
-- 
2.43.0


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

* [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device
  2026-07-31  8:48 [PATCH v12 0/8] DCD Prep Series Anisa Su
  2026-07-31  8:48 ` [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
@ 2026-07-31  8:48 ` Anisa Su
  2026-08-03 14:33   ` Richard Cheng
                     ` (2 more replies)
  2026-07-31  8:48 ` [PATCH v12 3/8] cxl/cdat: Gather DSMAS data for DCD partitions Anisa Su
                   ` (5 subsequent siblings)
  7 siblings, 3 replies; 33+ messages in thread
From: Anisa Su @ 2026-07-31  8:48 UTC (permalink / raw)
  To: linux-cxl
  Cc: nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

From: Ira Weiny <iweiny@kernel.org>

Devices which optionally support Dynamic Capacity (DC) are configured
via mailbox commands.  CXL r4.0 section 9.13.3 requires the host to issue
the Get DC Configuration command in order to properly configure DCDs.
Without the Get DC Configuration command DCD can't be supported.

Implement the DC mailbox commands as specified in CXL 4.0 section
8.2.10.9.9 (opcodes 48XXh) to read and store the DCD configuration
information.  Disable DCD if an invalid configuration is found.

Linux has no support for more than one dynamic capacity partition.  Read
and validate all the partitions but configure only the first partition
as 'dynamic ram 1'.  Additional partitions can be added in the future if
such a device ever materializes.  Additionally it is anticipated that no
skips will be present from the end of the pmem partition.  Check for and
disallow this configuration as well.

Based on an original patch by Navneet Singh.

Signed-off-by: Ira Weiny <iweiny@kernel.org>
Signed-off-by: Anisa Su <anisa.su@samsung.com>
Tested-by: Wonjae Lee <wj28.lee@samsung.com>
Tested-by: Junhee Park <jh9934.park@samsung.com>
Tested-by: Heesoo Kim <habil.kim@samsung.com>

---
Changes:
1. mbox.c: bound the Get DC Config request by the negotiated mailbox
   payload. Requesting all CXL_MAX_DC_PARTITIONS (8) needs a 328-byte
   response, which exceeds a spec-minimum 256-byte mailbox and fails
   -E2BIG, disabling DCD. Cap partition_count per call to what the
   payload holds and read the rest via the existing start_partition
   loop.
---
 drivers/cxl/core/hdm.c  |   2 +
 drivers/cxl/core/mbox.c | 220 ++++++++++++++++++++++++++++++++++++++++
 drivers/cxl/cxlmem.h    |  55 ++++++++++
 drivers/cxl/pci.c       |   3 +
 include/cxl/cxl.h       |   3 +-
 5 files changed, 282 insertions(+), 1 deletion(-)

diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 0c80b76a5f9b..0ef076c08ed2 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -446,6 +446,8 @@ static const char *cxl_mode_name(enum cxl_partition_mode mode)
 		return "ram";
 	case CXL_PARTMODE_PMEM:
 		return "pmem";
+	case CXL_PARTMODE_DYNAMIC_RAM_1:
+		return "dynamic_ram_1";
 	default:
 		return "";
 	};
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index 4790524c32a7..d79019fbd790 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -1352,6 +1352,197 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
 	return -EBUSY;
 }
 
+static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_array,
+			u8 index, struct cxl_dc_partition *dev_part)
+{
+	u64 blk_size = le64_to_cpu(dev_part->block_size);
+	u64 len = le64_to_cpu(dev_part->length);
+
+	part_array[index].start = le64_to_cpu(dev_part->base);
+	part_array[index].size = le64_to_cpu(dev_part->decode_length);
+	part_array[index].size *= CXL_CAPACITY_MULTIPLIER;
+
+	/* Check partitions are in increasing DPA order */
+	if (index > 0) {
+		struct cxl_dc_partition_info *prev_part = &part_array[index - 1];
+
+		if ((prev_part->start + prev_part->size) >
+		     part_array[index].start) {
+			dev_err(dev,
+				"DPA ordering violation for DC partition %d and %d\n",
+				index - 1, index);
+			return -EINVAL;
+		}
+	}
+
+	if (part_array[index].size == 0 || len == 0 ||
+	    part_array[index].size < len || !IS_ALIGNED(len, blk_size)) {
+		dev_err(dev, "DC partition %d invalid length; size %llu len %llu blk size %llu\n",
+			index, part_array[index].size, len, blk_size);
+		return -EINVAL;
+	}
+
+	if (blk_size == 0 || blk_size % CXL_DCD_BLOCK_LINE_SIZE ||
+	    !is_power_of_2(blk_size)) {
+		dev_err(dev, "DC partition %d invalid block size %llu\n",
+			index, blk_size);
+		return -EINVAL;
+	}
+
+	if (!IS_ALIGNED(part_array[index].start, SZ_256M) ||
+	    !IS_ALIGNED(part_array[index].start, blk_size)) {
+		dev_err(dev, "DC partition %d invalid start %llu blk size %llu\n",
+			index, part_array[index].start, blk_size);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "DC partition %d start %llu size %llu blk_size: %llu\n",
+		index, part_array[index].start, part_array[index].size,
+		blk_size);
+
+	return 0;
+}
+
+/* Returns the number of partitions in dc_resp or -ERRNO */
+static int cxl_get_dc_config(struct cxl_mailbox *mbox, u8 start_partition,
+			     u8 partition_count,
+			     struct cxl_mbox_get_dc_config_out *dc_resp,
+			     size_t dc_resp_size)
+{
+	struct cxl_mbox_get_dc_config_in get_dc = (struct cxl_mbox_get_dc_config_in) {
+		.partition_count = partition_count,
+		.start_partition_index = start_partition,
+	};
+	struct cxl_mbox_cmd mbox_cmd = (struct cxl_mbox_cmd) {
+		.opcode = CXL_MBOX_OP_GET_DC_CONFIG,
+		.payload_in = &get_dc,
+		.size_in = sizeof(get_dc),
+		.size_out = dc_resp_size,
+		.payload_out = dc_resp,
+		.min_out = 8,
+	};
+	size_t expected_sz;
+	int rc;
+
+	rc = cxl_internal_send_cmd(mbox, &mbox_cmd);
+	if (rc < 0)
+		return rc;
+
+	if (dc_resp->partitions_returned > partition_count) {
+		dev_err(mbox->host, "Device returned %u partitions, requested %u\n",
+			dc_resp->partitions_returned, partition_count);
+		return -EIO;
+	}
+
+	/*
+	 * The payload carries trailing extent/tag count fields after the
+	 * partition array (CXL r4.0 Table 8-179) which the driver ignores, so
+	 * the response is at least, not exactly, expected_sz.
+	 */
+	expected_sz = struct_size(dc_resp, partition,
+				  dc_resp->partitions_returned);
+
+	if (mbox_cmd.size_out < expected_sz) {
+		dev_err(mbox->host,
+			"Payload size %zu less than expected %zu for %u partitions\n",
+			mbox_cmd.size_out,
+			expected_sz,
+			dc_resp->partitions_returned);
+		return -EIO;
+	}
+
+	dev_dbg(mbox->host, "Read %d/%d DC partitions\n",
+		dc_resp->partitions_returned, dc_resp->avail_partition_count);
+	return dc_resp->partitions_returned;
+}
+
+/**
+ * cxl_dev_dc_identify() - Reads the dynamic capacity information from the
+ *                         device.
+ * @mbox: Mailbox to query
+ * @dc_info: The dynamic partition information to return
+ *
+ * Read Dynamic Capacity information from the device and return the partition
+ * information.
+ *
+ * Return: 0 if identify was executed successfully, -ERRNO on error.
+ *         on error only dc_info is left unchanged.
+ */
+int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
+			struct cxl_dc_partition_info *dc_info)
+{
+	struct cxl_dc_partition_info partitions[CXL_MAX_DC_PARTITIONS];
+	struct cxl_mbox_get_dc_config_out *dc_resp __free(kfree) = NULL;
+	struct device *dev = mbox->host;
+	u8 start_partition;
+	u8 num_partitions;
+	u8 partition_count;
+	size_t dc_resp_size;
+
+	/* Bound requested number of partitions by mailbox payload size */
+	partition_count = min_t(size_t, CXL_MAX_DC_PARTITIONS,
+				(mbox->payload_size - sizeof(*dc_resp) -
+				 sizeof(struct cxl_mbox_get_dc_config_tail)) /
+				sizeof(struct cxl_dc_partition));
+	dc_resp_size = struct_size(dc_resp, partition, partition_count) +
+		       sizeof(struct cxl_mbox_get_dc_config_tail);
+
+	dc_resp = kmalloc(dc_resp_size, GFP_KERNEL);
+	if (!dc_resp)
+		return -ENOMEM;
+
+	/*
+	 * Read and check all partition information for validity and potential
+	 * debugging; see debug output in cxl_dc_check()
+	 */
+	start_partition = 0;
+	num_partitions = 0;
+	do {
+		int rc, i, j;
+
+		rc = cxl_get_dc_config(mbox, start_partition, partition_count,
+				       dc_resp, dc_resp_size);
+		if (rc < 0) {
+			dev_err(dev, "Failed to get DC config: %d\n", rc);
+			return rc;
+		}
+
+		if (rc == 0) {
+			dev_err(dev,
+				"Device reported %u partitions available but returned none at index %u\n",
+				dc_resp->avail_partition_count, start_partition);
+			return -EIO;
+		}
+
+		num_partitions += rc;
+
+		if (num_partitions < 1 || num_partitions > CXL_MAX_DC_PARTITIONS) {
+			dev_err(dev, "Invalid num of dynamic capacity partitions %d\n",
+				num_partitions);
+			return -EINVAL;
+		}
+
+		for (i = start_partition, j = 0; i < num_partitions; i++, j++) {
+			rc = cxl_dc_check(dev, partitions, i,
+					  &dc_resp->partition[j]);
+			if (rc)
+				return rc;
+		}
+
+		start_partition = num_partitions;
+
+	} while (num_partitions < dc_resp->avail_partition_count);
+
+	/* Return 1st partition */
+	dc_info->start = partitions[0].start;
+	dc_info->size = partitions[0].size;
+	dev_dbg(dev, "Returning partition 0 %llu size %llu\n",
+		dc_info->start, dc_info->size);
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
+
 static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode)
 {
 	int i = info->nr_partitions;
@@ -1422,6 +1613,35 @@ int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
 }
 EXPORT_SYMBOL_NS_GPL(cxl_get_dirty_count, "CXL");
 
+void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
+{
+	struct cxl_dc_partition_info dc_info = { 0 };
+	struct device *dev = mds->cxlds.dev;
+	int rc;
+
+	rc = cxl_dev_dc_identify(&mds->cxlds.cxl_mbox, &dc_info);
+	if (rc) {
+		dev_warn(dev,
+			 "Failed to read Dynamic Capacity config: %d\n", rc);
+		cxl_disable_dcd(mds);
+		return;
+	}
+
+	/* Skips between pmem and the dynamic partition are not supported */
+	if (dc_info.start != info->size) {
+		dev_warn(dev,
+			 "Dynamic Capacity skip from pmem not supported\n");
+		cxl_disable_dcd(mds);
+		return;
+	}
+
+	info->size += dc_info.size;
+	dev_dbg(dev, "Adding dynamic ram partition 1; %llu size %llu\n",
+		dc_info.start, dc_info.size);
+	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
+
 int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds)
 {
 	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 616eeaeee5b9..a9782939d82b 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -407,6 +407,8 @@ struct cxl_security_state {
 	struct kernfs_node *sanitize_node;
 };
 
+#define CXL_MAX_DC_PARTITIONS 8
+
 static inline resource_size_t cxl_pmem_size(struct cxl_dev_state *cxlds)
 {
 	/*
@@ -691,6 +693,39 @@ struct cxl_mbox_set_shutdown_state_in {
 	u8 state;
 } __packed;
 
+/* See CXL r4.0 Table 8-178 get dynamic capacity config Input Payload */
+struct cxl_mbox_get_dc_config_in {
+	u8 partition_count;
+	u8 start_partition_index;
+} __packed;
+
+/* See CXL r4.0 Table 8-179 get dynamic capacity config Output Payload */
+struct cxl_mbox_get_dc_config_out {
+	u8 avail_partition_count;
+	u8 partitions_returned;
+	u8 rsvd[6];
+	/* See CXL r4.0 Table 8-180 */
+	struct cxl_dc_partition {
+		__le64 base;
+		__le64 decode_length;
+		__le64 length;
+		__le64 block_size;
+		__le32 dsmad_handle;
+		u8 flags;
+		u8 rsvd[3];
+	} __packed partition[] __counted_by(partitions_returned);
+	/* Trailing extent/tag count fields unused */
+} __packed;
+
+/* Trailing counts; cannot be a member after the flex array above */
+struct cxl_mbox_get_dc_config_tail {
+	__le32 num_extents_supported;
+	__le32 num_extents_available;
+	__le32 num_tags_supported;
+	__le32 num_tags_available;
+} __packed;
+#define CXL_DCD_BLOCK_LINE_SIZE 0x40
+
 /* Set Timestamp CXL 3.0 Spec 8.2.9.4.2 */
 struct cxl_mbox_set_timestamp_in {
 	__le64 timestamp;
@@ -814,9 +849,18 @@ enum {
 int cxl_internal_send_cmd(struct cxl_mailbox *cxl_mbox,
 			  struct cxl_mbox_cmd *cmd);
 int cxl_dev_state_identify(struct cxl_memdev_state *mds);
+
+struct cxl_dc_partition_info {
+	u64 start;
+	u64 size;
+};
+
+int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
+			struct cxl_dc_partition_info *dc_info);
 int cxl_await_media_ready(struct cxl_dev_state *cxlds);
 int cxl_enumerate_cmds(struct cxl_memdev_state *mds);
 int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
+void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
 struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial,
 						 u16 dvsec);
 void set_exclusive_cxl_commands(struct cxl_memdev_state *mds,
@@ -830,6 +874,17 @@ void cxl_event_trace_record(struct cxl_memdev *cxlmd,
 			    const uuid_t *uuid, union cxl_event *evt);
 int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count);
 int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds);
+
+static inline bool cxl_dcd_supported(struct cxl_memdev_state *mds)
+{
+	return mds->dcd_supported;
+}
+
+static inline void cxl_disable_dcd(struct cxl_memdev_state *mds)
+{
+	mds->dcd_supported = false;
+}
+
 int cxl_set_timestamp(struct cxl_memdev_state *mds);
 int cxl_poison_state_init(struct cxl_memdev_state *mds);
 int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 267c679b0b3c..9b320a2b6fe0 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -870,6 +870,9 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (rc)
 		return rc;
 
+	if (cxl_dcd_supported(mds))
+		cxl_configure_dcd(mds, &range_info);
+
 	rc = cxl_dpa_setup(cxlds, &range_info);
 	if (rc)
 		return rc;
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 016c74fb747c..87c2bd73df21 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -133,6 +133,7 @@ struct cxl_dpa_perf {
 enum cxl_partition_mode {
 	CXL_PARTMODE_RAM,
 	CXL_PARTMODE_PMEM,
+	CXL_PARTMODE_DYNAMIC_RAM_1,
 };
 
 /**
@@ -147,7 +148,7 @@ struct cxl_dpa_partition {
 	enum cxl_partition_mode mode;
 };
 
-#define CXL_NR_PARTITIONS_MAX 2
+#define CXL_NR_PARTITIONS_MAX 3
 
 /**
  * struct cxl_dev_state - The driver device state
-- 
2.43.0


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

* [PATCH v12 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
  2026-07-31  8:48 [PATCH v12 0/8] DCD Prep Series Anisa Su
  2026-07-31  8:48 ` [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
  2026-07-31  8:48 ` [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
@ 2026-07-31  8:48 ` Anisa Su
  2026-08-03 22:56   ` Alison Schofield
  2026-07-31  8:48 ` [PATCH v12 4/8] cxl/events: Split event msgnum configuration from irq setup Anisa Su
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: Anisa Su @ 2026-07-31  8:48 UTC (permalink / raw)
  To: linux-cxl
  Cc: nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

From: Ira Weiny <iweiny@kernel.org>

Additional DCD partition (AKA region) information is contained in the
DSMAS CDAT tables, including performance, read only, and shareable
attributes.

Match DCD partitions with DSMAS tables and store the meta data.

Signed-off-by: Ira Weiny <iweiny@kernel.org>
Co-developed-by: Anisa Su <anisa.su@samsung.com>
Signed-off-by: Anisa Su <anisa.su@samsung.com>
Tested-by: Wonjae Lee <wj28.lee@samsung.com>
Tested-by: Junhee Park <jh9934.park@samsung.com>
Tested-by: Heesoo Kim <habil.kim@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
---
 drivers/cxl/core/cdat.c | 13 +++++++++++++
 drivers/cxl/core/hdm.c  |  1 +
 drivers/cxl/core/mbox.c | 22 ++++++++++++++++------
 drivers/cxl/cxlmem.h    |  2 ++
 include/cxl/cxl.h       |  4 ++++
 5 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
index 5c9f07262513..37136b2cf7e4 100644
--- a/drivers/cxl/core/cdat.c
+++ b/drivers/cxl/core/cdat.c
@@ -17,6 +17,7 @@ struct dsmas_entry {
 	struct access_coordinate cdat_coord[ACCESS_COORDINATE_MAX];
 	int entries;
 	int qos_class;
+	bool shareable;
 };
 
 static u32 cdat_normalize(u16 entry, u64 base, u8 type)
@@ -74,6 +75,7 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
 		return -ENOMEM;
 
 	dent->handle = dsmas->dsmad_handle;
+	dent->shareable = dsmas->flags & ACPI_CDAT_DSMAS_SHAREABLE;
 	dent->dpa_range.start = le64_to_cpu((__force __le64)dsmas->dpa_base_address);
 	dent->dpa_range.end = le64_to_cpu((__force __le64)dsmas->dpa_base_address) +
 			      le64_to_cpu((__force __le64)dsmas->dpa_length) - 1;
@@ -266,15 +268,26 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
 		bool found = false;
 
 		for (int i = 0; i < cxlds->nr_partitions; i++) {
+			enum cxl_partition_mode mode = cxlds->part[i].mode;
 			struct resource *res = &cxlds->part[i].res;
+			u8 handle = cxlds->part[i].handle;
 			struct range range = {
 				.start = res->start,
 				.end = res->end,
 			};
 
 			if (range_contains(&range, &dent->dpa_range)) {
+				if (mode == CXL_PARTMODE_DYNAMIC_RAM_1 &&
+				    dent->handle != handle) {
+					dev_warn(dev,
+						 "Dynamic RAM perf mismatch; %pra (%u) vs %pra (%u)\n",
+						 &range, handle, &dent->dpa_range,
+						 dent->handle);
+					continue;
+				}
 				update_perf_entry(dev, dent,
 						  &cxlds->part[i].perf);
+				cxlds->part[i].shareable = dent->shareable;
 				found = true;
 				break;
 			}
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 0ef076c08ed2..7f63b86887f4 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -477,6 +477,7 @@ int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
 
 		cxlds->part[i].perf.qos_class = CXL_QOS_CLASS_INVALID;
 		cxlds->part[i].mode = part->mode;
+		cxlds->part[i].handle = part->handle;
 
 		/* Require ordered + contiguous partitions */
 		if (i) {
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index d79019fbd790..a6cdea9f4080 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -1357,10 +1357,16 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_a
 {
 	u64 blk_size = le64_to_cpu(dev_part->block_size);
 	u64 len = le64_to_cpu(dev_part->length);
+	u32 handle = le32_to_cpu(dev_part->dsmad_handle);
 
 	part_array[index].start = le64_to_cpu(dev_part->base);
 	part_array[index].size = le64_to_cpu(dev_part->decode_length);
 	part_array[index].size *= CXL_CAPACITY_MULTIPLIER;
+	if (handle & ~0xFF) {
+		dev_warn(dev, "DSMAD handle 0x%x has non-zero reserved bits\n", handle);
+		return -EINVAL;
+	}
+	part_array[index].handle = handle;
 
 	/* Check partitions are in increasing DPA order */
 	if (index > 0) {
@@ -1536,6 +1542,7 @@ int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
 	/* Return 1st partition */
 	dc_info->start = partitions[0].start;
 	dc_info->size = partitions[0].size;
+	dc_info->handle = partitions[0].handle;
 	dev_dbg(dev, "Returning partition 0 %llu size %llu\n",
 		dc_info->start, dc_info->size);
 
@@ -1543,7 +1550,8 @@ int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
 }
 EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
 
-static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode)
+static void add_part(struct cxl_dpa_info *info, u64 start, u64 size,
+		     enum cxl_partition_mode mode, u8 handle)
 {
 	int i = info->nr_partitions;
 
@@ -1555,6 +1563,7 @@ static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_pa
 		.end = start + size - 1,
 	};
 	info->part[i].mode = mode;
+	info->part[i].handle = handle;
 	info->nr_partitions++;
 }
 
@@ -1572,9 +1581,9 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
 	info->size = mds->total_bytes;
 
 	if (mds->partition_align_bytes == 0) {
-		add_part(info, 0, mds->volatile_only_bytes, CXL_PARTMODE_RAM);
+		add_part(info, 0, mds->volatile_only_bytes, CXL_PARTMODE_RAM, 0);
 		add_part(info, mds->volatile_only_bytes,
-			 mds->persistent_only_bytes, CXL_PARTMODE_PMEM);
+			 mds->persistent_only_bytes, CXL_PARTMODE_PMEM, 0);
 		return 0;
 	}
 
@@ -1584,9 +1593,9 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
 		return rc;
 	}
 
-	add_part(info, 0, mds->active_volatile_bytes, CXL_PARTMODE_RAM);
+	add_part(info, 0, mds->active_volatile_bytes, CXL_PARTMODE_RAM, 0);
 	add_part(info, mds->active_volatile_bytes, mds->active_persistent_bytes,
-		 CXL_PARTMODE_PMEM);
+		 CXL_PARTMODE_PMEM, 0);
 
 	return 0;
 }
@@ -1638,7 +1647,8 @@ void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
 	info->size += dc_info.size;
 	dev_dbg(dev, "Adding dynamic ram partition 1; %llu size %llu\n",
 		dc_info.start, dc_info.size);
-	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
+	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1,
+		 dc_info.handle);
 }
 EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
 
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index a9782939d82b..eb1e6f7a2038 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -140,6 +140,7 @@ struct cxl_dpa_info {
 	struct cxl_dpa_part_info {
 		struct range range;
 		enum cxl_partition_mode mode;
+		u8 handle;
 	} part[CXL_NR_PARTITIONS_MAX];
 	int nr_partitions;
 };
@@ -853,6 +854,7 @@ int cxl_dev_state_identify(struct cxl_memdev_state *mds);
 struct cxl_dc_partition_info {
 	u64 start;
 	u64 size;
+	u8 handle;
 };
 
 int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 87c2bd73df21..baf0132c70a3 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -141,11 +141,15 @@ enum cxl_partition_mode {
  * @res: shortcut to the partition in the DPA resource tree (cxlds->dpa_res)
  * @perf: performance attributes of the partition from CDAT
  * @mode: operation mode for the DPA capacity, e.g. ram, pmem, dynamic...
+ * @handle: DSMAS handle intended to represent this partition
+ * @shareable: Is the partition sharable (from its CDAT DSMAS entry)
  */
 struct cxl_dpa_partition {
 	struct resource res;
 	struct cxl_dpa_perf perf;
 	enum cxl_partition_mode mode;
+	u8 handle;
+	bool shareable;
 };
 
 #define CXL_NR_PARTITIONS_MAX 3
-- 
2.43.0


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

* [PATCH v12 4/8] cxl/events: Split event msgnum configuration from irq setup
  2026-07-31  8:48 [PATCH v12 0/8] DCD Prep Series Anisa Su
                   ` (2 preceding siblings ...)
  2026-07-31  8:48 ` [PATCH v12 3/8] cxl/cdat: Gather DSMAS data for DCD partitions Anisa Su
@ 2026-07-31  8:48 ` Anisa Su
  2026-08-03 23:01   ` Alison Schofield
  2026-07-31  8:48 ` [PATCH v12 5/8] cxl/pci: Factor out interrupt policy check Anisa Su
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: Anisa Su @ 2026-07-31  8:48 UTC (permalink / raw)
  To: linux-cxl
  Cc: nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

From: Ira Weiny <iweiny@kernel.org>

Dynamic Capacity Devices (DCD) require event interrupts to process
memory addition or removal.  BIOS may have control over non-DCD event
processing.  DCD interrupt configuration needs to be separate from
memory event interrupt configuration.

Split cxl_event_config_msgnums() from irq setup in preparation for
separate DCD interrupts configuration.

Signed-off-by: Ira Weiny <iweiny@kernel.org>
Signed-off-by: Anisa Su <anisa.su@samsung.com>
Tested-by: Wonjae Lee <wj28.lee@samsung.com>
Tested-by: Junhee Park <jh9934.park@samsung.com>
Tested-by: Heesoo Kim <habil.kim@samsung.com>
Reviewed-by: Jonathan Cameron <jic23@kernel.org>
Reviewed-by: Fan Ni <nifan.cxl@gmail.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
---
 drivers/cxl/pci.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 9b320a2b6fe0..8e122de1876b 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -599,35 +599,31 @@ static int cxl_event_config_msgnums(struct cxl_memdev_state *mds,
 	return cxl_event_get_int_policy(mds, policy);
 }
 
-static int cxl_event_irqsetup(struct cxl_memdev_state *mds)
+static int cxl_event_irqsetup(struct cxl_memdev_state *mds,
+			      struct cxl_event_interrupt_policy *policy)
 {
 	struct cxl_dev_state *cxlds = &mds->cxlds;
-	struct cxl_event_interrupt_policy policy;
 	int rc;
 
-	rc = cxl_event_config_msgnums(mds, &policy);
-	if (rc)
-		return rc;
-
-	rc = cxl_event_req_irq(cxlds, policy.info_settings);
+	rc = cxl_event_req_irq(cxlds, policy->info_settings);
 	if (rc) {
 		dev_err(cxlds->dev, "Failed to get interrupt for event Info log\n");
 		return rc;
 	}
 
-	rc = cxl_event_req_irq(cxlds, policy.warn_settings);
+	rc = cxl_event_req_irq(cxlds, policy->warn_settings);
 	if (rc) {
 		dev_err(cxlds->dev, "Failed to get interrupt for event Warn log\n");
 		return rc;
 	}
 
-	rc = cxl_event_req_irq(cxlds, policy.failure_settings);
+	rc = cxl_event_req_irq(cxlds, policy->failure_settings);
 	if (rc) {
 		dev_err(cxlds->dev, "Failed to get interrupt for event Failure log\n");
 		return rc;
 	}
 
-	rc = cxl_event_req_irq(cxlds, policy.fatal_settings);
+	rc = cxl_event_req_irq(cxlds, policy->fatal_settings);
 	if (rc) {
 		dev_err(cxlds->dev, "Failed to get interrupt for event Fatal log\n");
 		return rc;
@@ -674,11 +670,15 @@ static int cxl_event_config(struct pci_host_bridge *host_bridge,
 		return -EBUSY;
 	}
 
+	rc = cxl_event_config_msgnums(mds, &policy);
+	if (rc)
+		return rc;
+
 	rc = cxl_mem_alloc_event_buf(mds);
 	if (rc)
 		return rc;
 
-	rc = cxl_event_irqsetup(mds);
+	rc = cxl_event_irqsetup(mds, &policy);
 	if (rc)
 		return rc;
 
-- 
2.43.0


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

* [PATCH v12 5/8] cxl/pci: Factor out interrupt policy check
  2026-07-31  8:48 [PATCH v12 0/8] DCD Prep Series Anisa Su
                   ` (3 preceding siblings ...)
  2026-07-31  8:48 ` [PATCH v12 4/8] cxl/events: Split event msgnum configuration from irq setup Anisa Su
@ 2026-07-31  8:48 ` Anisa Su
  2026-08-03 23:55   ` Alison Schofield
  2026-07-31  8:48 ` [PATCH v12 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: Anisa Su @ 2026-07-31  8:48 UTC (permalink / raw)
  To: linux-cxl
  Cc: nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Dan Williams, Anisa Su

From: Ira Weiny <iweiny@kernel.org>

Dynamic Capacity Devices (DCD) require event interrupts to process
memory addition or removal.  BIOS may have control over non-DCD event
processing.  DCD interrupt configuration needs to be separate from
memory event interrupt configuration.

Factor out event interrupt setting validation.

Link: https://lore.kernel.org/all/663922b475e50_d54d72945b@dwillia2-xfh.jf.intel.com.notmuch/ [1]
Suggested-by: Dan Williams <djbw@kernel.org>
Signed-off-by: Ira Weiny <iweiny@kernel.org>
Signed-off-by: Anisa Su <anisa.su@samsung.com>
Tested-by: Wonjae Lee <wj28.lee@samsung.com>
Tested-by: Junhee Park <jh9934.park@samsung.com>
Tested-by: Heesoo Kim <habil.kim@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jic23@kernel.org>
Reviewed-by: Fan Ni <nifan.cxl@gmail.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
---
 drivers/cxl/pci.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 8e122de1876b..df13fb8802c3 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -639,6 +639,21 @@ static bool cxl_event_int_is_fw(u8 setting)
 	return mode == CXL_INT_FW;
 }
 
+static bool cxl_event_validate_mem_policy(struct cxl_memdev_state *mds,
+					  struct cxl_event_interrupt_policy *policy)
+{
+	if (cxl_event_int_is_fw(policy->info_settings) ||
+	    cxl_event_int_is_fw(policy->warn_settings) ||
+	    cxl_event_int_is_fw(policy->failure_settings) ||
+	    cxl_event_int_is_fw(policy->fatal_settings)) {
+		dev_err(mds->cxlds.dev,
+			"FW still in control of Event Logs despite _OSC settings\n");
+		return false;
+	}
+
+	return true;
+}
+
 static int cxl_event_config(struct pci_host_bridge *host_bridge,
 			    struct cxl_memdev_state *mds, bool irq_avail)
 {
@@ -661,14 +676,8 @@ static int cxl_event_config(struct pci_host_bridge *host_bridge,
 	if (rc)
 		return rc;
 
-	if (cxl_event_int_is_fw(policy.info_settings) ||
-	    cxl_event_int_is_fw(policy.warn_settings) ||
-	    cxl_event_int_is_fw(policy.failure_settings) ||
-	    cxl_event_int_is_fw(policy.fatal_settings)) {
-		dev_err(mds->cxlds.dev,
-			"FW still in control of Event Logs despite _OSC settings\n");
+	if (!cxl_event_validate_mem_policy(mds, &policy))
 		return -EBUSY;
-	}
 
 	rc = cxl_event_config_msgnums(mds, &policy);
 	if (rc)
-- 
2.43.0


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

* [PATCH v12 6/8] cxl/mem: Configure dynamic capacity interrupts
  2026-07-31  8:48 [PATCH v12 0/8] DCD Prep Series Anisa Su
                   ` (4 preceding siblings ...)
  2026-07-31  8:48 ` [PATCH v12 5/8] cxl/pci: Factor out interrupt policy check Anisa Su
@ 2026-07-31  8:48 ` Anisa Su
  2026-08-04  0:13   ` Jonathan Cameron
  2026-08-04  0:34   ` Alison Schofield
  2026-07-31  8:48 ` [PATCH v12 7/8] cxl/core: Return endpoint decoder information from region search Anisa Su
  2026-07-31  8:48 ` [PATCH v12 8/8] cxl/core: Enforce partition order/simplify partition calls Anisa Su
  7 siblings, 2 replies; 33+ messages in thread
From: Anisa Su @ 2026-07-31  8:48 UTC (permalink / raw)
  To: linux-cxl
  Cc: nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

From: Ira Weiny <iweiny@kernel.org>

Dynamic Capacity Devices (DCD) support extent change notifications
through the event log mechanism.  The interrupt mailbox commands were
extended in CXL 3.1 to support these notifications.  Firmware can't
configure DCD events to be FW controlled but can retain control of
memory events.

Configure DCD event log interrupts on devices supporting dynamic
capacity.  Disable DCD if interrupts are not supported.

Care is taken to preserve the interrupt policy set by the FW if FW first
has been selected by the BIOS.

Based on an original patch by Navneet Singh.

Signed-off-by: Ira Weiny <iweiny@kernel.org>
Signed-off-by: Anisa Su <anisa.su@samsung.com>
Tested-by: Wonjae Lee <wj28.lee@samsung.com>
Tested-by: Junhee Park <jh9934.park@samsung.com>
Tested-by: Heesoo Kim <habil.kim@samsung.com>

---
Changes:
1. pci.c: size the Set Event Interrupt Policy payload by the device's
   policy layout (from the Get reply length) rather than by DCD command
   support. A CXL 3.0+ device carries the dcd_settings field for spec
   compliance even without DCD commands, so keying the size on
   cxl_dcd_supported() sent a short payload and failed the set. Reported
   by Benjamin Cheatham.
2. pci.c: add cxl_event_drain_mask() (standard logs when native_cxl, DCD
   when supported) and use it for both cxl_event_thread()'s drain mask and
   the initial drain in cxl_event_config(). Gating DCD on support stops a
   disabled-but-armed DCD from spinning the thread; draining the DCD log
   initially even when !native_cxl stops pre-existing DCD events (and
   future edge-triggered interrupts) from being stranded.
---
 drivers/cxl/cxl.h    |   4 +-
 drivers/cxl/cxlmem.h |   2 +
 drivers/cxl/pci.c    | 119 +++++++++++++++++++++++++++++++++++--------
 3 files changed, 104 insertions(+), 21 deletions(-)

diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index c0e5308e4d1b..51396eb993e7 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -192,11 +192,13 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw)
 #define CXLDEV_EVENT_STATUS_WARN		BIT(1)
 #define CXLDEV_EVENT_STATUS_FAIL		BIT(2)
 #define CXLDEV_EVENT_STATUS_FATAL		BIT(3)
+#define CXLDEV_EVENT_STATUS_DCD			BIT(4)
 
 #define CXLDEV_EVENT_STATUS_ALL (CXLDEV_EVENT_STATUS_INFO |	\
 				 CXLDEV_EVENT_STATUS_WARN |	\
 				 CXLDEV_EVENT_STATUS_FAIL |	\
-				 CXLDEV_EVENT_STATUS_FATAL)
+				 CXLDEV_EVENT_STATUS_FATAL |	\
+				 CXLDEV_EVENT_STATUS_DCD)
 
 /* CXL rev 3.0 section 8.2.9.2.4; Table 8-52 */
 #define CXLDEV_EVENT_INT_MODE_MASK	GENMASK(1, 0)
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index eb1e6f7a2038..666e01326dae 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -240,7 +240,9 @@ struct cxl_event_interrupt_policy {
 	u8 warn_settings;
 	u8 failure_settings;
 	u8 fatal_settings;
+	u8 dcd_settings;
 } __packed;
+#define CXL_EVENT_INT_POLICY_BASE_SIZE 4 /* info, warn, failure, fatal */
 
 /**
  * struct cxl_event_state - Event log driver state
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index df13fb8802c3..6cb344ec4f3a 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -509,11 +509,30 @@ static bool cxl_alloc_irq_vectors(struct pci_dev *pdev)
 	return true;
 }
 
+/*
+ * Event logs the driver drains: standard logs when native_cxl, DCD when
+ * supported.
+ */
+static u32 cxl_event_drain_mask(struct pci_host_bridge *host_bridge,
+				struct cxl_memdev_state *mds)
+{
+	u32 mask = 0;
+
+	if (host_bridge->native_cxl_error)
+		mask |= CXLDEV_EVENT_STATUS_ALL & ~CXLDEV_EVENT_STATUS_DCD;
+	if (cxl_dcd_supported(mds))
+		mask |= CXLDEV_EVENT_STATUS_DCD;
+	return mask;
+}
+
 static irqreturn_t cxl_event_thread(int irq, void *id)
 {
 	struct cxl_dev_id *dev_id = id;
 	struct cxl_dev_state *cxlds = dev_id->cxlds;
 	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
+	struct pci_host_bridge *host_bridge =
+		pci_find_host_bridge(to_pci_dev(cxlds->dev)->bus);
+	u32 mask = cxl_event_drain_mask(host_bridge, mds);
 	u32 status;
 
 	do {
@@ -522,8 +541,8 @@ static irqreturn_t cxl_event_thread(int irq, void *id)
 		 * ignore the reserved upper 32 bits
 		 */
 		status = readl(cxlds->regs.status + CXLDEV_DEV_EVENT_STATUS_OFFSET);
-		/* Ignore logs unknown to the driver */
-		status &= CXLDEV_EVENT_STATUS_ALL;
+		/* Ignore logs unknown to the driver or owned by BIOS */
+		status &= mask;
 		if (!status)
 			break;
 		cxl_mem_get_event_records(mds, status);
@@ -550,42 +569,62 @@ static int cxl_event_req_irq(struct cxl_dev_state *cxlds, u8 setting)
 }
 
 static int cxl_event_get_int_policy(struct cxl_memdev_state *mds,
-				    struct cxl_event_interrupt_policy *policy)
+				    struct cxl_event_interrupt_policy *policy,
+				    size_t *policy_size)
 {
 	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
 	struct cxl_mbox_cmd mbox_cmd = {
 		.opcode = CXL_MBOX_OP_GET_EVT_INT_POLICY,
 		.payload_out = policy,
 		.size_out = sizeof(*policy),
+		/* CXL 2.0 firmware omits dcd_settings; accept the shorter reply */
+		.min_out = CXL_EVENT_INT_POLICY_BASE_SIZE,
 	};
 	int rc;
 
 	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
-	if (rc < 0)
+	if (rc < 0) {
 		dev_err(mds->cxlds.dev,
 			"Failed to get event interrupt policy : %d", rc);
+		return rc;
+	}
 
+	if (policy_size)
+		*policy_size = mbox_cmd.size_out;
 	return rc;
 }
 
 static int cxl_event_config_msgnums(struct cxl_memdev_state *mds,
-				    struct cxl_event_interrupt_policy *policy)
+				    struct cxl_event_interrupt_policy *policy,
+				    bool native_cxl, size_t policy_size)
 {
 	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
 	struct cxl_mbox_cmd mbox_cmd;
 	int rc;
 
-	*policy = (struct cxl_event_interrupt_policy) {
-		.info_settings = CXL_INT_MSI_MSIX,
-		.warn_settings = CXL_INT_MSI_MSIX,
-		.failure_settings = CXL_INT_MSI_MSIX,
-		.fatal_settings = CXL_INT_MSI_MSIX,
-	};
+	/* memory event policy is left if FW has control */
+	if (native_cxl) {
+		*policy = (struct cxl_event_interrupt_policy) {
+			.info_settings = CXL_INT_MSI_MSIX,
+			.warn_settings = CXL_INT_MSI_MSIX,
+			.failure_settings = CXL_INT_MSI_MSIX,
+			.fatal_settings = CXL_INT_MSI_MSIX,
+			.dcd_settings = 0,
+		};
+	}
+
+	/*
+	 * A CXL 3.0+ device can carry dcd_settings field without DCD command
+	 * support, so size the request by the device's policy_size and only
+	 * enable the DCD interrupt when DCD commands are supported.
+	 */
+	if (cxl_dcd_supported(mds))
+		policy->dcd_settings = CXL_INT_MSI_MSIX;
 
 	mbox_cmd = (struct cxl_mbox_cmd) {
 		.opcode = CXL_MBOX_OP_SET_EVT_INT_POLICY,
 		.payload_in = policy,
-		.size_in = sizeof(*policy),
+		.size_in = policy_size,
 	};
 
 	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
@@ -596,7 +635,7 @@ static int cxl_event_config_msgnums(struct cxl_memdev_state *mds,
 	}
 
 	/* Retrieve final interrupt settings */
-	return cxl_event_get_int_policy(mds, policy);
+	return cxl_event_get_int_policy(mds, policy, NULL);
 }
 
 static int cxl_event_irqsetup(struct cxl_memdev_state *mds,
@@ -632,6 +671,30 @@ static int cxl_event_irqsetup(struct cxl_memdev_state *mds,
 	return 0;
 }
 
+static int cxl_irqsetup(struct cxl_memdev_state *mds,
+			struct cxl_event_interrupt_policy *policy,
+			bool native_cxl)
+{
+	struct cxl_dev_state *cxlds = &mds->cxlds;
+	int rc;
+
+	if (native_cxl) {
+		rc = cxl_event_irqsetup(mds, policy);
+		if (rc)
+			return rc;
+	}
+
+	if (cxl_dcd_supported(mds)) {
+		rc = cxl_event_req_irq(cxlds, policy->dcd_settings);
+		if (rc) {
+			dev_err(cxlds->dev, "Failed to get interrupt for DCD event log\n");
+			cxl_disable_dcd(mds);
+		}
+	}
+
+	return 0;
+}
+
 static bool cxl_event_int_is_fw(u8 setting)
 {
 	u8 mode = FIELD_GET(CXLDEV_EVENT_INT_MODE_MASK, setting);
@@ -657,29 +720,39 @@ static bool cxl_event_validate_mem_policy(struct cxl_memdev_state *mds,
 static int cxl_event_config(struct pci_host_bridge *host_bridge,
 			    struct cxl_memdev_state *mds, bool irq_avail)
 {
-	struct cxl_event_interrupt_policy policy;
+	struct cxl_event_interrupt_policy policy = { 0 };
+	bool native_cxl = host_bridge->native_cxl_error;
+	size_t policy_size;
+	u32 status;
 	int rc;
 
 	/*
 	 * When BIOS maintains CXL error reporting control, it will process
 	 * event records.  Only one agent can do so.
+	 *
+	 * If BIOS has control of events and DCD is not supported skip event
+	 * configuration.
 	 */
-	if (!host_bridge->native_cxl_error)
+	if (!native_cxl && !cxl_dcd_supported(mds))
 		return 0;
 
 	if (!irq_avail) {
 		dev_info(mds->cxlds.dev, "No interrupt support, disable event processing.\n");
+		if (cxl_dcd_supported(mds)) {
+			dev_info(mds->cxlds.dev, "DCD requires interrupts, disable DCD\n");
+			cxl_disable_dcd(mds);
+		}
 		return 0;
 	}
 
-	rc = cxl_event_get_int_policy(mds, &policy);
+	rc = cxl_event_get_int_policy(mds, &policy, &policy_size);
 	if (rc)
 		return rc;
 
-	if (!cxl_event_validate_mem_policy(mds, &policy))
+	if (native_cxl && !cxl_event_validate_mem_policy(mds, &policy))
 		return -EBUSY;
 
-	rc = cxl_event_config_msgnums(mds, &policy);
+	rc = cxl_event_config_msgnums(mds, &policy, native_cxl, policy_size);
 	if (rc)
 		return rc;
 
@@ -687,11 +760,17 @@ static int cxl_event_config(struct pci_host_bridge *host_bridge,
 	if (rc)
 		return rc;
 
-	rc = cxl_event_irqsetup(mds, &policy);
+	rc = cxl_irqsetup(mds, &policy, native_cxl);
 	if (rc)
 		return rc;
 
-	cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
+	status = cxl_event_drain_mask(host_bridge, mds);
+	if (status)
+		cxl_mem_get_event_records(mds, status);
+
+	dev_dbg(mds->cxlds.dev, "Event config : %s DCD %s\n",
+		native_cxl ? "OS" : "BIOS",
+		cxl_dcd_supported(mds) ? "supported" : "not supported");
 
 	return 0;
 }
-- 
2.43.0


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

* [PATCH v12 7/8] cxl/core: Return endpoint decoder information from region search
  2026-07-31  8:48 [PATCH v12 0/8] DCD Prep Series Anisa Su
                   ` (5 preceding siblings ...)
  2026-07-31  8:48 ` [PATCH v12 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
@ 2026-07-31  8:48 ` Anisa Su
  2026-08-04  1:02   ` Alison Schofield
  2026-07-31  8:48 ` [PATCH v12 8/8] cxl/core: Enforce partition order/simplify partition calls Anisa Su
  7 siblings, 1 reply; 33+ messages in thread
From: Anisa Su @ 2026-07-31  8:48 UTC (permalink / raw)
  To: linux-cxl
  Cc: nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

From: Ira Weiny <iweiny@kernel.org>

cxl_dpa_to_region() finds the region from a <DPA, device> tuple.
The search involves finding the device endpoint decoder as well.

Dynamic capacity extent processing uses the endpoint decoder HPA
information to calculate the HPA offset.  In addition, well behaved
extents should be contained within an endpoint decoder.

Return the endpoint decoder found to be used in subsequent DCD code.

Signed-off-by: Ira Weiny <iweiny@kernel.org>
Signed-off-by: Anisa Su <anisa.su@samsung.com>
Tested-by: Wonjae Lee <wj28.lee@samsung.com>
Tested-by: Junhee Park <jh9934.park@samsung.com>
Tested-by: Heesoo Kim <habil.kim@samsung.com>
Reviewed-by: Jonathan Cameron <jic23@kernel.org>
Reviewed-by: Fan Ni <nifan.cxl@gmail.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
---
 drivers/cxl/core/core.h   | 6 ++++--
 drivers/cxl/core/mbox.c   | 2 +-
 drivers/cxl/core/memdev.c | 4 ++--
 drivers/cxl/core/region.c | 8 +++++++-
 4 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 07555ae63859..e4bd220faa92 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -47,7 +47,8 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
 int cxl_region_init(void);
 void cxl_region_exit(void);
 int cxl_get_poison_by_endpoint(struct cxl_port *port);
-struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
+struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa,
+				     struct cxl_endpoint_decoder **cxled);
 u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
 		   u64 dpa);
 int devm_cxl_add_dax_region(struct cxl_region *cxlr);
@@ -61,7 +62,8 @@ static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
 	return ULLONG_MAX;
 }
 static inline
-struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa)
+struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa,
+				     struct cxl_endpoint_decoder **cxled)
 {
 	return NULL;
 }
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index a6cdea9f4080..b18ea02ed2e6 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -969,7 +969,7 @@ void cxl_event_trace_record(struct cxl_memdev *cxlmd,
 		guard(rwsem_read)(&cxl_rwsem.dpa);
 
 		dpa = le64_to_cpu(evt->media_hdr.phys_addr) & CXL_DPA_MASK;
-		cxlr = cxl_dpa_to_region(cxlmd, dpa);
+		cxlr = cxl_dpa_to_region(cxlmd, dpa, NULL);
 		if (cxlr) {
 			u64 cache_size = cxlr->params.cache_size;
 
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index 33a3d2e7b13a..1565a5cf0f32 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -317,7 +317,7 @@ int cxl_inject_poison_locked(struct cxl_memdev *cxlmd, u64 dpa)
 	if (rc)
 		return rc;
 
-	cxlr = cxl_dpa_to_region(cxlmd, dpa);
+	cxlr = cxl_dpa_to_region(cxlmd, dpa, NULL);
 	if (cxlr)
 		dev_warn_once(cxl_mbox->host,
 			      "poison inject dpa:%#llx region: %s\n", dpa,
@@ -386,7 +386,7 @@ int cxl_clear_poison_locked(struct cxl_memdev *cxlmd, u64 dpa)
 	if (rc)
 		return rc;
 
-	cxlr = cxl_dpa_to_region(cxlmd, dpa);
+	cxlr = cxl_dpa_to_region(cxlmd, dpa, NULL);
 	if (cxlr)
 		dev_warn_once(cxl_mbox->host,
 			      "poison clear dpa:%#llx region: %s\n", dpa,
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 1e211542b6b6..ec5e5b7090cf 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3012,6 +3012,7 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port)
 struct cxl_dpa_to_region_context {
 	struct cxl_region *cxlr;
 	u64 dpa;
+	struct cxl_endpoint_decoder *cxled;
 };
 
 static int __cxl_dpa_to_region(struct device *dev, void *arg)
@@ -3045,11 +3046,13 @@ static int __cxl_dpa_to_region(struct device *dev, void *arg)
 			dev_name(dev));
 
 	ctx->cxlr = cxlr;
+	ctx->cxled = cxled;
 
 	return 1;
 }
 
-struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa)
+struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa,
+				     struct cxl_endpoint_decoder **cxled)
 {
 	struct cxl_dpa_to_region_context ctx;
 	struct cxl_port *port = cxlmd->endpoint;
@@ -3063,6 +3066,9 @@ struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa)
 	if (cxl_num_decoders_committed(port))
 		device_for_each_child(&port->dev, &ctx, __cxl_dpa_to_region);
 
+	if (cxled)
+		*cxled = ctx.cxled;
+
 	return ctx.cxlr;
 }
 
-- 
2.43.0


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

* [PATCH v12 8/8] cxl/core: Enforce partition order/simplify partition calls
  2026-07-31  8:48 [PATCH v12 0/8] DCD Prep Series Anisa Su
                   ` (6 preceding siblings ...)
  2026-07-31  8:48 ` [PATCH v12 7/8] cxl/core: Return endpoint decoder information from region search Anisa Su
@ 2026-07-31  8:48 ` Anisa Su
  2026-08-04  0:16   ` Jonathan Cameron
  2026-08-04  1:37   ` Alison Schofield
  7 siblings, 2 replies; 33+ messages in thread
From: Anisa Su @ 2026-07-31  8:48 UTC (permalink / raw)
  To: linux-cxl
  Cc: nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

From: Ira Weiny <iweiny@kernel.org>

Device partitions have an implied order which is made more complex by
the addition of a dynamic partition

Remove the ram special case information calls in favor of generic calls
with a check ahead of time to ensure the preservation of the implied
partition order.

Signed-off-by: Ira Weiny <iweiny@kernel.org>
Signed-off-by: Anisa Su <anisa.su@samsung.com>
Tested-by: Wonjae Lee <wj28.lee@samsung.com>
Tested-by: Junhee Park <jh9934.park@samsung.com>
Tested-by: Heesoo Kim <habil.kim@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
---
 drivers/cxl/core/hdm.c    | 11 ++++++++++-
 drivers/cxl/core/memdev.c | 32 +++++++++-----------------------
 drivers/cxl/cxlmem.h      |  9 +++------
 drivers/cxl/mem.c         |  2 +-
 4 files changed, 23 insertions(+), 31 deletions(-)

diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 7f63b86887f4..d87dfbcac6a5 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -457,6 +457,7 @@ static const char *cxl_mode_name(enum cxl_partition_mode mode)
 int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
 {
 	struct device *dev = cxlds->dev;
+	int i;
 
 	guard(rwsem_write)(&cxl_rwsem.dpa);
 
@@ -469,9 +470,17 @@ int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
 		return 0;
 	}
 
+	/* Verify partitions are in expected order. */
+	for (i = 1; i < info->nr_partitions; i++) {
+		if (info->part[i].mode < info->part[i - 1].mode) {
+			dev_err(dev, "Partition order mismatch\n");
+			return -EINVAL;
+		}
+	}
+
 	cxlds->dpa_res = DEFINE_RES_MEM(0, info->size);
 
-	for (int i = 0; i < info->nr_partitions; i++) {
+	for (i = 0; i < info->nr_partitions; i++) {
 		const struct cxl_dpa_part_info *part = &info->part[i];
 		int rc;
 
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index 1565a5cf0f32..136443bffcb8 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -77,20 +77,12 @@ static ssize_t label_storage_size_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(label_storage_size);
 
-static resource_size_t cxl_ram_size(struct cxl_dev_state *cxlds)
-{
-	/* Static RAM is only expected at partition 0. */
-	if (cxlds->part[0].mode != CXL_PARTMODE_RAM)
-		return 0;
-	return resource_size(&cxlds->part[0].res);
-}
-
 static ssize_t ram_size_show(struct device *dev, struct device_attribute *attr,
 			     char *buf)
 {
 	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
-	unsigned long long len = cxl_ram_size(cxlds);
+	unsigned long long len = cxl_part_size(cxlds, CXL_PARTMODE_RAM);
 
 	return sysfs_emit(buf, "%#llx\n", len);
 }
@@ -103,7 +95,7 @@ static ssize_t pmem_size_show(struct device *dev, struct device_attribute *attr,
 {
 	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
-	unsigned long long len = cxl_pmem_size(cxlds);
+	unsigned long long len = cxl_part_size(cxlds, CXL_PARTMODE_PMEM);
 
 	return sysfs_emit(buf, "%#llx\n", len);
 }
@@ -426,10 +418,11 @@ static struct attribute *cxl_memdev_attributes[] = {
 	NULL,
 };
 
-static struct cxl_dpa_perf *to_pmem_perf(struct cxl_dev_state *cxlds)
+static struct cxl_dpa_perf *part_perf(struct cxl_dev_state *cxlds,
+				      enum cxl_partition_mode mode)
 {
 	for (int i = 0; i < cxlds->nr_partitions; i++)
-		if (cxlds->part[i].mode == CXL_PARTMODE_PMEM)
+		if (cxlds->part[i].mode == mode)
 			return &cxlds->part[i].perf;
 	return NULL;
 }
@@ -440,7 +433,7 @@ static ssize_t pmem_qos_class_show(struct device *dev,
 	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
 
-	return sysfs_emit(buf, "%d\n", to_pmem_perf(cxlds)->qos_class);
+	return sysfs_emit(buf, "%d\n", part_perf(cxlds, CXL_PARTMODE_PMEM)->qos_class);
 }
 
 static struct device_attribute dev_attr_pmem_qos_class =
@@ -452,20 +445,13 @@ static struct attribute *cxl_memdev_pmem_attributes[] = {
 	NULL,
 };
 
-static struct cxl_dpa_perf *to_ram_perf(struct cxl_dev_state *cxlds)
-{
-	if (cxlds->part[0].mode != CXL_PARTMODE_RAM)
-		return NULL;
-	return &cxlds->part[0].perf;
-}
-
 static ssize_t ram_qos_class_show(struct device *dev,
 				  struct device_attribute *attr, char *buf)
 {
 	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
 
-	return sysfs_emit(buf, "%d\n", to_ram_perf(cxlds)->qos_class);
+	return sysfs_emit(buf, "%d\n", part_perf(cxlds, CXL_PARTMODE_RAM)->qos_class);
 }
 
 static struct device_attribute dev_attr_ram_qos_class =
@@ -501,7 +487,7 @@ static umode_t cxl_ram_visible(struct kobject *kobj, struct attribute *a, int n)
 {
 	struct device *dev = kobj_to_dev(kobj);
 	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
-	struct cxl_dpa_perf *perf = to_ram_perf(cxlmd->cxlds);
+	struct cxl_dpa_perf *perf = part_perf(cxlmd->cxlds, CXL_PARTMODE_RAM);
 
 	if (a == &dev_attr_ram_qos_class.attr &&
 	    (!perf || perf->qos_class == CXL_QOS_CLASS_INVALID))
@@ -520,7 +506,7 @@ static umode_t cxl_pmem_visible(struct kobject *kobj, struct attribute *a, int n
 {
 	struct device *dev = kobj_to_dev(kobj);
 	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
-	struct cxl_dpa_perf *perf = to_pmem_perf(cxlmd->cxlds);
+	struct cxl_dpa_perf *perf = part_perf(cxlmd->cxlds, CXL_PARTMODE_PMEM);
 
 	if (a == &dev_attr_pmem_qos_class.attr &&
 	    (!perf || perf->qos_class == CXL_QOS_CLASS_INVALID))
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 666e01326dae..77f6417a1de7 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -412,14 +412,11 @@ struct cxl_security_state {
 
 #define CXL_MAX_DC_PARTITIONS 8
 
-static inline resource_size_t cxl_pmem_size(struct cxl_dev_state *cxlds)
+static inline resource_size_t cxl_part_size(struct cxl_dev_state *cxlds,
+					    enum cxl_partition_mode mode)
 {
-	/*
-	 * Static PMEM may be at partition index 0 when there is no static RAM
-	 * capacity.
-	 */
 	for (int i = 0; i < cxlds->nr_partitions; i++)
-		if (cxlds->part[i].mode == CXL_PARTMODE_PMEM)
+		if (cxlds->part[i].mode == mode)
 			return resource_size(&cxlds->part[i].res);
 	return 0;
 }
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 798e5c369cfc..9c4ef401cbad 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -139,7 +139,7 @@ static int cxl_mem_probe(struct device *dev)
 		return -ENXIO;
 	}
 
-	if (cxl_pmem_size(cxlds) && IS_ENABLED(CONFIG_CXL_PMEM)) {
+	if (cxl_part_size(cxlds, CXL_PARTMODE_PMEM) && IS_ENABLED(CONFIG_CXL_PMEM)) {
 		rc = devm_cxl_add_nvdimm(dev, parent_port, cxlmd);
 		if (rc) {
 			if (rc == -ENODEV)
-- 
2.43.0


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

* Re: [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device
  2026-07-31  8:48 ` [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
@ 2026-08-03 14:33   ` Richard Cheng
  2026-08-03 22:11     ` Anisa Su
  2026-08-03 21:52   ` Alison Schofield
  2026-08-03 23:55   ` Jonathan Cameron
  2 siblings, 1 reply; 33+ messages in thread
From: Richard Cheng @ 2026-08-03 14:33 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

On Fri, Jul 31, 2026 at 01:48:07AM +0800, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Devices which optionally support Dynamic Capacity (DC) are configured
> via mailbox commands.  CXL r4.0 section 9.13.3 requires the host to issue
> the Get DC Configuration command in order to properly configure DCDs.
> Without the Get DC Configuration command DCD can't be supported.
> 
> Implement the DC mailbox commands as specified in CXL 4.0 section
> 8.2.10.9.9 (opcodes 48XXh) to read and store the DCD configuration
> information.  Disable DCD if an invalid configuration is found.
> 
> Linux has no support for more than one dynamic capacity partition.  Read
> and validate all the partitions but configure only the first partition
> as 'dynamic ram 1'.  Additional partitions can be added in the future if
> such a device ever materializes.  Additionally it is anticipated that no
> skips will be present from the end of the pmem partition.  Check for and
> disallow this configuration as well.
> 
> Based on an original patch by Navneet Singh.
> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>
> 
> ---
> Changes:
> 1. mbox.c: bound the Get DC Config request by the negotiated mailbox
>    payload. Requesting all CXL_MAX_DC_PARTITIONS (8) needs a 328-byte
>    response, which exceeds a spec-minimum 256-byte mailbox and fails
>    -E2BIG, disabling DCD. Cap partition_count per call to what the
>    payload holds and read the rest via the existing start_partition
>    loop.
> ---
>  drivers/cxl/core/hdm.c  |   2 +
>  drivers/cxl/core/mbox.c | 220 ++++++++++++++++++++++++++++++++++++++++
>  drivers/cxl/cxlmem.h    |  55 ++++++++++
>  drivers/cxl/pci.c       |   3 +
>  include/cxl/cxl.h       |   3 +-
>  5 files changed, 282 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 0c80b76a5f9b..0ef076c08ed2 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -446,6 +446,8 @@ static const char *cxl_mode_name(enum cxl_partition_mode mode)
>  		return "ram";
>  	case CXL_PARTMODE_PMEM:
>  		return "pmem";
> +	case CXL_PARTMODE_DYNAMIC_RAM_1:
> +		return "dynamic_ram_1";
>  	default:
>  		return "";
>  	};
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 4790524c32a7..d79019fbd790 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -1352,6 +1352,197 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
>  	return -EBUSY;
>  }
>  
> +static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_array,
> +			u8 index, struct cxl_dc_partition *dev_part)
> +{
> +	u64 blk_size = le64_to_cpu(dev_part->block_size);
> +	u64 len = le64_to_cpu(dev_part->length);
> +
> +	part_array[index].start = le64_to_cpu(dev_part->base);
> +	part_array[index].size = le64_to_cpu(dev_part->decode_length);
> +	part_array[index].size *= CXL_CAPACITY_MULTIPLIER;
> +
> +	/* Check partitions are in increasing DPA order */
> +	if (index > 0) {
> +		struct cxl_dc_partition_info *prev_part = &part_array[index - 1];
> +
> +		if ((prev_part->start + prev_part->size) >
> +		     part_array[index].start) {
> +			dev_err(dev,
> +				"DPA ordering violation for DC partition %d and %d\n",
> +				index - 1, index);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (part_array[index].size == 0 || len == 0 ||
> +	    part_array[index].size < len || !IS_ALIGNED(len, blk_size)) {
> +		dev_err(dev, "DC partition %d invalid length; size %llu len %llu blk size %llu\n",
> +			index, part_array[index].size, len, blk_size);
> +		return -EINVAL;
> +	}
> +

Hi Anisa,

I think here , 0s are legal, according to spec CXL 4.0 8.2.10.9.9.1 "Get Dynamic Capacity Configuration",
Table 8-347 "DC Region Configuration". It says "Report 0 if the region is unavailable for Dynamici Capacity"

Imagine if we have a device ports 8 regions and only region 0 is configured, regions 1~7 come back with 0s.
cxl_dc_check() will check them all, so the first zero region fails and cxl_configure_dcd() ends up disabling
DCD on a device that is perfectly fine.

Maybe we should skip the all-zero regions instead of rejecting them ?

Best regards,
Richard Cheng.

> +	if (blk_size == 0 || blk_size % CXL_DCD_BLOCK_LINE_SIZE ||
> +	    !is_power_of_2(blk_size)) {
> +		dev_err(dev, "DC partition %d invalid block size %llu\n",
> +			index, blk_size);
> +		return -EINVAL;
> +	}
> +
> +	if (!IS_ALIGNED(part_array[index].start, SZ_256M) ||
> +	    !IS_ALIGNED(part_array[index].start, blk_size)) {
> +		dev_err(dev, "DC partition %d invalid start %llu blk size %llu\n",
> +			index, part_array[index].start, blk_size);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(dev, "DC partition %d start %llu size %llu blk_size: %llu\n",
> +		index, part_array[index].start, part_array[index].size,
> +		blk_size);
> +
> +	return 0;
> +}
> +
> +/* Returns the number of partitions in dc_resp or -ERRNO */
> +static int cxl_get_dc_config(struct cxl_mailbox *mbox, u8 start_partition,
> +			     u8 partition_count,
> +			     struct cxl_mbox_get_dc_config_out *dc_resp,
> +			     size_t dc_resp_size)
> +{
> +	struct cxl_mbox_get_dc_config_in get_dc = (struct cxl_mbox_get_dc_config_in) {
> +		.partition_count = partition_count,
> +		.start_partition_index = start_partition,
> +	};
> +	struct cxl_mbox_cmd mbox_cmd = (struct cxl_mbox_cmd) {
> +		.opcode = CXL_MBOX_OP_GET_DC_CONFIG,
> +		.payload_in = &get_dc,
> +		.size_in = sizeof(get_dc),
> +		.size_out = dc_resp_size,
> +		.payload_out = dc_resp,
> +		.min_out = 8,
> +	};
> +	size_t expected_sz;
> +	int rc;
> +
> +	rc = cxl_internal_send_cmd(mbox, &mbox_cmd);
> +	if (rc < 0)
> +		return rc;
> +
> +	if (dc_resp->partitions_returned > partition_count) {
> +		dev_err(mbox->host, "Device returned %u partitions, requested %u\n",
> +			dc_resp->partitions_returned, partition_count);
> +		return -EIO;
> +	}
> +
> +	/*
> +	 * The payload carries trailing extent/tag count fields after the
> +	 * partition array (CXL r4.0 Table 8-179) which the driver ignores, so
> +	 * the response is at least, not exactly, expected_sz.
> +	 */
> +	expected_sz = struct_size(dc_resp, partition,
> +				  dc_resp->partitions_returned);
> +
> +	if (mbox_cmd.size_out < expected_sz) {
> +		dev_err(mbox->host,
> +			"Payload size %zu less than expected %zu for %u partitions\n",
> +			mbox_cmd.size_out,
> +			expected_sz,
> +			dc_resp->partitions_returned);
> +		return -EIO;
> +	}
> +
> +	dev_dbg(mbox->host, "Read %d/%d DC partitions\n",
> +		dc_resp->partitions_returned, dc_resp->avail_partition_count);
> +	return dc_resp->partitions_returned;
> +}
> +
> +/**
> + * cxl_dev_dc_identify() - Reads the dynamic capacity information from the
> + *                         device.
> + * @mbox: Mailbox to query
> + * @dc_info: The dynamic partition information to return
> + *
> + * Read Dynamic Capacity information from the device and return the partition
> + * information.
> + *
> + * Return: 0 if identify was executed successfully, -ERRNO on error.
> + *         on error only dc_info is left unchanged.
> + */
> +int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> +			struct cxl_dc_partition_info *dc_info)
> +{
> +	struct cxl_dc_partition_info partitions[CXL_MAX_DC_PARTITIONS];
> +	struct cxl_mbox_get_dc_config_out *dc_resp __free(kfree) = NULL;
> +	struct device *dev = mbox->host;
> +	u8 start_partition;
> +	u8 num_partitions;
> +	u8 partition_count;
> +	size_t dc_resp_size;
> +
> +	/* Bound requested number of partitions by mailbox payload size */
> +	partition_count = min_t(size_t, CXL_MAX_DC_PARTITIONS,
> +				(mbox->payload_size - sizeof(*dc_resp) -
> +				 sizeof(struct cxl_mbox_get_dc_config_tail)) /
> +				sizeof(struct cxl_dc_partition));
> +	dc_resp_size = struct_size(dc_resp, partition, partition_count) +
> +		       sizeof(struct cxl_mbox_get_dc_config_tail);
> +
> +	dc_resp = kmalloc(dc_resp_size, GFP_KERNEL);
> +	if (!dc_resp)
> +		return -ENOMEM;
> +
> +	/*
> +	 * Read and check all partition information for validity and potential
> +	 * debugging; see debug output in cxl_dc_check()
> +	 */
> +	start_partition = 0;
> +	num_partitions = 0;
> +	do {
> +		int rc, i, j;
> +
> +		rc = cxl_get_dc_config(mbox, start_partition, partition_count,
> +				       dc_resp, dc_resp_size);
> +		if (rc < 0) {
> +			dev_err(dev, "Failed to get DC config: %d\n", rc);
> +			return rc;
> +		}
> +
> +		if (rc == 0) {
> +			dev_err(dev,
> +				"Device reported %u partitions available but returned none at index %u\n",
> +				dc_resp->avail_partition_count, start_partition);
> +			return -EIO;
> +		}
> +
> +		num_partitions += rc;
> +
> +		if (num_partitions < 1 || num_partitions > CXL_MAX_DC_PARTITIONS) {
> +			dev_err(dev, "Invalid num of dynamic capacity partitions %d\n",
> +				num_partitions);
> +			return -EINVAL;
> +		}
> +
> +		for (i = start_partition, j = 0; i < num_partitions; i++, j++) {
> +			rc = cxl_dc_check(dev, partitions, i,
> +					  &dc_resp->partition[j]);
> +			if (rc)
> +				return rc;
> +		}
> +
> +		start_partition = num_partitions;
> +
> +	} while (num_partitions < dc_resp->avail_partition_count);
> +
> +	/* Return 1st partition */
> +	dc_info->start = partitions[0].start;
> +	dc_info->size = partitions[0].size;
> +	dev_dbg(dev, "Returning partition 0 %llu size %llu\n",
> +		dc_info->start, dc_info->size);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
> +
>  static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode)
>  {
>  	int i = info->nr_partitions;
> @@ -1422,6 +1613,35 @@ int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_get_dirty_count, "CXL");
>  
> +void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> +{
> +	struct cxl_dc_partition_info dc_info = { 0 };
> +	struct device *dev = mds->cxlds.dev;
> +	int rc;
> +
> +	rc = cxl_dev_dc_identify(&mds->cxlds.cxl_mbox, &dc_info);
> +	if (rc) {
> +		dev_warn(dev,
> +			 "Failed to read Dynamic Capacity config: %d\n", rc);
> +		cxl_disable_dcd(mds);
> +		return;
> +	}
> +
> +	/* Skips between pmem and the dynamic partition are not supported */
> +	if (dc_info.start != info->size) {
> +		dev_warn(dev,
> +			 "Dynamic Capacity skip from pmem not supported\n");
> +		cxl_disable_dcd(mds);
> +		return;
> +	}
> +
> +	info->size += dc_info.size;
> +	dev_dbg(dev, "Adding dynamic ram partition 1; %llu size %llu\n",
> +		dc_info.start, dc_info.size);
> +	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
> +
>  int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds)
>  {
>  	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 616eeaeee5b9..a9782939d82b 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -407,6 +407,8 @@ struct cxl_security_state {
>  	struct kernfs_node *sanitize_node;
>  };
>  
> +#define CXL_MAX_DC_PARTITIONS 8
> +
>  static inline resource_size_t cxl_pmem_size(struct cxl_dev_state *cxlds)
>  {
>  	/*
> @@ -691,6 +693,39 @@ struct cxl_mbox_set_shutdown_state_in {
>  	u8 state;
>  } __packed;
>  
> +/* See CXL r4.0 Table 8-178 get dynamic capacity config Input Payload */
> +struct cxl_mbox_get_dc_config_in {
> +	u8 partition_count;
> +	u8 start_partition_index;
> +} __packed;
> +
> +/* See CXL r4.0 Table 8-179 get dynamic capacity config Output Payload */
> +struct cxl_mbox_get_dc_config_out {
> +	u8 avail_partition_count;
> +	u8 partitions_returned;
> +	u8 rsvd[6];
> +	/* See CXL r4.0 Table 8-180 */
> +	struct cxl_dc_partition {
> +		__le64 base;
> +		__le64 decode_length;
> +		__le64 length;
> +		__le64 block_size;
> +		__le32 dsmad_handle;
> +		u8 flags;
> +		u8 rsvd[3];
> +	} __packed partition[] __counted_by(partitions_returned);
> +	/* Trailing extent/tag count fields unused */
> +} __packed;
> +
> +/* Trailing counts; cannot be a member after the flex array above */
> +struct cxl_mbox_get_dc_config_tail {
> +	__le32 num_extents_supported;
> +	__le32 num_extents_available;
> +	__le32 num_tags_supported;
> +	__le32 num_tags_available;
> +} __packed;
> +#define CXL_DCD_BLOCK_LINE_SIZE 0x40
> +
>  /* Set Timestamp CXL 3.0 Spec 8.2.9.4.2 */
>  struct cxl_mbox_set_timestamp_in {
>  	__le64 timestamp;
> @@ -814,9 +849,18 @@ enum {
>  int cxl_internal_send_cmd(struct cxl_mailbox *cxl_mbox,
>  			  struct cxl_mbox_cmd *cmd);
>  int cxl_dev_state_identify(struct cxl_memdev_state *mds);
> +
> +struct cxl_dc_partition_info {
> +	u64 start;
> +	u64 size;
> +};
> +
> +int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> +			struct cxl_dc_partition_info *dc_info);
>  int cxl_await_media_ready(struct cxl_dev_state *cxlds);
>  int cxl_enumerate_cmds(struct cxl_memdev_state *mds);
>  int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
> +void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
>  struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial,
>  						 u16 dvsec);
>  void set_exclusive_cxl_commands(struct cxl_memdev_state *mds,
> @@ -830,6 +874,17 @@ void cxl_event_trace_record(struct cxl_memdev *cxlmd,
>  			    const uuid_t *uuid, union cxl_event *evt);
>  int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count);
>  int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds);
> +
> +static inline bool cxl_dcd_supported(struct cxl_memdev_state *mds)
> +{
> +	return mds->dcd_supported;
> +}
> +
> +static inline void cxl_disable_dcd(struct cxl_memdev_state *mds)
> +{
> +	mds->dcd_supported = false;
> +}
> +
>  int cxl_set_timestamp(struct cxl_memdev_state *mds);
>  int cxl_poison_state_init(struct cxl_memdev_state *mds);
>  int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 267c679b0b3c..9b320a2b6fe0 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -870,6 +870,9 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  	if (rc)
>  		return rc;
>  
> +	if (cxl_dcd_supported(mds))
> +		cxl_configure_dcd(mds, &range_info);
> +
>  	rc = cxl_dpa_setup(cxlds, &range_info);
>  	if (rc)
>  		return rc;
> diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
> index 016c74fb747c..87c2bd73df21 100644
> --- a/include/cxl/cxl.h
> +++ b/include/cxl/cxl.h
> @@ -133,6 +133,7 @@ struct cxl_dpa_perf {
>  enum cxl_partition_mode {
>  	CXL_PARTMODE_RAM,
>  	CXL_PARTMODE_PMEM,
> +	CXL_PARTMODE_DYNAMIC_RAM_1,
>  };
>  
>  /**
> @@ -147,7 +148,7 @@ struct cxl_dpa_partition {
>  	enum cxl_partition_mode mode;
>  };
>  
> -#define CXL_NR_PARTITIONS_MAX 2
> +#define CXL_NR_PARTITIONS_MAX 3
>  
>  /**
>   * struct cxl_dev_state - The driver device state
> -- 
> 2.43.0
> 
> 

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

* Re: [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
  2026-07-31  8:48 ` [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
@ 2026-08-03 18:03   ` Dave Jiang
  2026-08-03 21:30   ` Alison Schofield
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 33+ messages in thread
From: Dave Jiang @ 2026-08-03 18:03 UTC (permalink / raw)
  To: Anisa Su, linux-cxl
  Cc: nvdimm, linux-kernel, Alison Schofield, Jonathan Cameron, Fan Ni,
	Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su



On 7/31/26 1:48 AM, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Per the CXL 4.0 specification software must check the Command Effects
> Log (CEL) for dynamic capacity command support.
> 
> Detect support for the DCD commands while reading the CEL, including:
> 
>         Get DC Config
>         Get DC Extent List
>         Add DC Response
>         Release DC
> 
> Based on an original patch by Navneet Singh.
> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>
> 
> ---
> Changes:
> 1. mbox.c: leave mds->dcd_supported false so the hardware enablement
>    patches can be upstreamed ahead of the extent and DAX work; the
>    event handling patch re-enables it.
> ---
>  drivers/cxl/core/mbox.c | 44 +++++++++++++++++++++++++++++++++++++++++
>  drivers/cxl/cxlmem.h    | 20 +++++++++++++++++++
>  2 files changed, 64 insertions(+)
> 
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 7c6c5b7450a5..4790524c32a7 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -165,6 +165,38 @@ static void cxl_set_security_cmd_enabled(struct cxl_security_state *security,
>  	}
>  }
>  
> +static bool cxl_is_dcd_command(u16 opcode)
> +{
> +#define CXL_MBOX_OP_DCD_CMDS 0x48
> +
> +	return (opcode >> 8) == CXL_MBOX_OP_DCD_CMDS;
> +}
> +
> +static void cxl_set_dcd_cmd_enabled(u16 opcode, unsigned long *cmd_mask)
> +{
> +	switch (opcode) {
> +	case CXL_MBOX_OP_GET_DC_CONFIG:
> +		set_bit(CXL_DCD_ENABLED_GET_CONFIG, cmd_mask);
> +		break;
> +	case CXL_MBOX_OP_GET_DC_EXTENT_LIST:
> +		set_bit(CXL_DCD_ENABLED_GET_EXTENT_LIST, cmd_mask);
> +		break;
> +	case CXL_MBOX_OP_ADD_DC_RESPONSE:
> +		set_bit(CXL_DCD_ENABLED_ADD_RESPONSE, cmd_mask);
> +		break;
> +	case CXL_MBOX_OP_RELEASE_DC:
> +		set_bit(CXL_DCD_ENABLED_RELEASE, cmd_mask);
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static bool cxl_verify_dcd_cmds(unsigned long *cmds_seen)
> +{
> +	return bitmap_full(cmds_seen, CXL_DCD_ENABLED_MAX);
> +}
> +
>  static bool cxl_is_poison_command(u16 opcode)
>  {
>  #define CXL_MBOX_OP_POISON_CMDS 0x43
> @@ -757,6 +789,7 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
>  	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
>  	struct cxl_cel_entry *cel_entry;
>  	const int cel_entries = size / sizeof(*cel_entry);
> +	DECLARE_BITMAP(dcd_cmds, CXL_DCD_ENABLED_MAX) = {};
>  	struct device *dev = mds->cxlds.dev;
>  	int i, ro_cmds = 0, wr_cmds = 0;
>  
> @@ -785,11 +818,22 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
>  			enabled++;
>  		}
>  
> +		if (cxl_is_dcd_command(opcode)) {
> +			cxl_set_dcd_cmd_enabled(opcode, dcd_cmds);
> +			enabled++;
> +		}
> +
>  		dev_dbg(dev, "Opcode 0x%04x %s\n", opcode,
>  			enabled ? "enabled" : "unsupported by driver");
>  	}
>  
>  	set_features_cap(cxl_mbox, ro_cmds, wr_cmds);
> +	/*
> +	 * Disabled until event handling implemented.
> +	 */
> +	if (cxl_verify_dcd_cmds(dcd_cmds))
> +		dev_dbg(dev, "Device supports DCD; capability disabled\n");
> +	mds->dcd_supported = false;

Assume this is intentional to disable DCD for the prep series. Should mention that in the commit log as something to come back to and change later. Also prevent sashiko from tripping on that. With the note:

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

DJ


>  }
>  
>  static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_memdev_state *mds)
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index ed419d0c59f2..616eeaeee5b9 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -252,6 +252,20 @@ struct cxl_event_state {
>  	struct mutex log_lock;
>  };
>  
> +/**
> + * CXL r4.0 Section 8.2.10.9 - Memory Device Command Sets. See Table 8-308.
> + *
> + * The 48h Command Set (Opcodes 4800h - 4803h) defines the device-enabled DCD
> + * commands.
> + */
> +enum dcd_cmd_enabled_bits {
> +	CXL_DCD_ENABLED_GET_CONFIG,
> +	CXL_DCD_ENABLED_GET_EXTENT_LIST,
> +	CXL_DCD_ENABLED_ADD_RESPONSE,
> +	CXL_DCD_ENABLED_RELEASE,
> +	CXL_DCD_ENABLED_MAX
> +};
> +
>  /* Device enabled poison commands */
>  enum poison_cmd_enabled_bits {
>  	CXL_POISON_ENABLED_LIST,
> @@ -427,6 +441,7 @@ static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox)
>   * @partition_align_bytes: alignment size for partition-able capacity
>   * @active_volatile_bytes: sum of hard + soft volatile
>   * @active_persistent_bytes: sum of hard + soft persistent
> + * @dcd_supported: all DCD commands are supported
>   * @event: event log driver state
>   * @poison: poison driver state info
>   * @security: security driver state info
> @@ -446,6 +461,7 @@ struct cxl_memdev_state {
>  	u64 partition_align_bytes;
>  	u64 active_volatile_bytes;
>  	u64 active_persistent_bytes;
> +	bool dcd_supported;
>  
>  	struct cxl_event_state event;
>  	struct cxl_poison_state poison;
> @@ -507,6 +523,10 @@ enum cxl_opcode {
>  	CXL_MBOX_OP_UNLOCK		= 0x4503,
>  	CXL_MBOX_OP_FREEZE_SECURITY	= 0x4504,
>  	CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE	= 0x4505,
> +	CXL_MBOX_OP_GET_DC_CONFIG	= 0x4800,
> +	CXL_MBOX_OP_GET_DC_EXTENT_LIST	= 0x4801,
> +	CXL_MBOX_OP_ADD_DC_RESPONSE	= 0x4802,
> +	CXL_MBOX_OP_RELEASE_DC		= 0x4803,
>  	CXL_MBOX_OP_MAX			= 0x10000
>  };
>  


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

* Re: [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
  2026-07-31  8:48 ` [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
  2026-08-03 18:03   ` Dave Jiang
@ 2026-08-03 21:30   ` Alison Schofield
  2026-08-03 23:12     ` Jonathan Cameron
  2026-08-04 15:45   ` Gregory Price
  2026-08-04 16:55   ` Alison Schofield
  3 siblings, 1 reply; 33+ messages in thread
From: Alison Schofield @ 2026-08-03 21:30 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Jonathan Cameron,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, Jul 31, 2026 at 01:48:06AM -0700, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Per the CXL 4.0 specification software must check the Command Effects
> Log (CEL) for dynamic capacity command support.
> 
> Detect support for the DCD commands while reading the CEL, including:
> 
>         Get DC Config
>         Get DC Extent List
>         Add DC Response
>         Release DC
> 
> Based on an original patch by Navneet Singh.
> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>
> 
> ---
> Changes:
> 1. mbox.c: leave mds->dcd_supported false so the hardware enablement
>    patches can be upstreamed ahead of the extent and DAX work; the
>    event handling patch re-enables it.
> ---
>  drivers/cxl/core/mbox.c | 44 +++++++++++++++++++++++++++++++++++++++++
>  drivers/cxl/cxlmem.h    | 20 +++++++++++++++++++
>  2 files changed, 64 insertions(+)
> 
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 7c6c5b7450a5..4790524c32a7 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -165,6 +165,38 @@ static void cxl_set_security_cmd_enabled(struct cxl_security_state *security,
>  	}
>  }
>  
> +static bool cxl_is_dcd_command(u16 opcode)
> +{
> +#define CXL_MBOX_OP_DCD_CMDS 0x48
> +
> +	return (opcode >> 8) == CXL_MBOX_OP_DCD_CMDS;
> +}
> +
> +static void cxl_set_dcd_cmd_enabled(u16 opcode, unsigned long *cmd_mask)
> +{
> +	switch (opcode) {
> +	case CXL_MBOX_OP_GET_DC_CONFIG:
> +		set_bit(CXL_DCD_ENABLED_GET_CONFIG, cmd_mask);
> +		break;
> +	case CXL_MBOX_OP_GET_DC_EXTENT_LIST:
> +		set_bit(CXL_DCD_ENABLED_GET_EXTENT_LIST, cmd_mask);
> +		break;
> +	case CXL_MBOX_OP_ADD_DC_RESPONSE:
> +		set_bit(CXL_DCD_ENABLED_ADD_RESPONSE, cmd_mask);
> +		break;
> +	case CXL_MBOX_OP_RELEASE_DC:
> +		set_bit(CXL_DCD_ENABLED_RELEASE, cmd_mask);
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static bool cxl_verify_dcd_cmds(unsigned long *cmds_seen)
> +{
> +	return bitmap_full(cmds_seen, CXL_DCD_ENABLED_MAX);
> +}
> +
>  static bool cxl_is_poison_command(u16 opcode)
>  {
>  #define CXL_MBOX_OP_POISON_CMDS 0x43
> @@ -757,6 +789,7 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
>  	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
>  	struct cxl_cel_entry *cel_entry;
>  	const int cel_entries = size / sizeof(*cel_entry);
> +	DECLARE_BITMAP(dcd_cmds, CXL_DCD_ENABLED_MAX) = {};
>  	struct device *dev = mds->cxlds.dev;
>  	int i, ro_cmds = 0, wr_cmds = 0;
>  
> @@ -785,11 +818,22 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
>  			enabled++;
>  		}
>  
> +		if (cxl_is_dcd_command(opcode)) {
> +			cxl_set_dcd_cmd_enabled(opcode, dcd_cmds);
> +			enabled++;
> +		}
> +
>  		dev_dbg(dev, "Opcode 0x%04x %s\n", opcode,
>  			enabled ? "enabled" : "unsupported by driver");
>  	}
>  
>  	set_features_cap(cxl_mbox, ro_cmds, wr_cmds);
> +	/*
> +	 * Disabled until event handling implemented.
> +	 */
> +	if (cxl_verify_dcd_cmds(dcd_cmds))
> +		dev_dbg(dev, "Device supports DCD; capability disabled\n");
> +	mds->dcd_supported = false;
>  }

When I view how this lands in the entirety of cxl_walk_cel() it seems
to needlessly be different from the pattern set by poison and security
handling in the same function and structures. Could this follow that
more closely?  Doing so would remove this 'tail' work here of checking
and setting the boolean.

This appended diff touches patch 2 where it has the first caller, but
pasting all here for simplicity - you'll apply per patch if you take it.

The bitmap lives in mds structure and it is already where the bool is now.
The only piece of the poison and security pattern not worth copying
yet is the wrapper struct cxl_poison_state and struct cxl_security_state
exist because each grew a mutex and other per-class state, and DCD has
none of that in this series (yet).

diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index b18ea02ed2e6..27cfe0ee4752 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -172,7 +172,7 @@ static bool cxl_is_dcd_command(u16 opcode)
        return (opcode >> 8) == CXL_MBOX_OP_DCD_CMDS;
 }

-static void cxl_set_dcd_cmd_enabled(u16 opcode, unsigned long *cmd_mask)
+static void cxl_set_dcd_cmd_enabled(unsigned long *cmd_mask, u16 opcode)
 {
        switch (opcode) {
        case CXL_MBOX_OP_GET_DC_CONFIG:
@@ -192,11 +192,6 @@ static void cxl_set_dcd_cmd_enabled(u16 opcode, unsigned long *cmd_mask)
        }
 }

-static bool cxl_verify_dcd_cmds(unsigned long *cmds_seen)
-{
-       return bitmap_full(cmds_seen, CXL_DCD_ENABLED_MAX);
-}
-
 static bool cxl_is_poison_command(u16 opcode)
 {
 #define CXL_MBOX_OP_POISON_CMDS 0x43
@@ -789,7 +784,6 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
        struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
        struct cxl_cel_entry *cel_entry;
        const int cel_entries = size / sizeof(*cel_entry);
-       DECLARE_BITMAP(dcd_cmds, CXL_DCD_ENABLED_MAX) = {};
        struct device *dev = mds->cxlds.dev;
        int i, ro_cmds = 0, wr_cmds = 0;

@@ -819,7 +813,7 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
                }

                if (cxl_is_dcd_command(opcode)) {
-                       cxl_set_dcd_cmd_enabled(opcode, dcd_cmds);
+                       cxl_set_dcd_cmd_enabled(mds->dcd_enabled_cmds, opcode);
                        enabled++;
                }

@@ -828,12 +822,6 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
        }

        set_features_cap(cxl_mbox, ro_cmds, wr_cmds);
-       /*
-        * Disabled until event handling implemented.
-        */
-       if (cxl_verify_dcd_cmds(dcd_cmds))
-               dev_dbg(dev, "Device supports DCD; capability disabled\n");
-       mds->dcd_supported = false;
 }

 static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_memdev_state *mds)
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 77f6417a1de7..bfa746c5a7c8 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -443,7 +443,7 @@ static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox)
  * @partition_align_bytes: alignment size for partition-able capacity
  * @active_volatile_bytes: sum of hard + soft volatile
  * @active_persistent_bytes: sum of hard + soft persistent
- * @dcd_supported: all DCD commands are supported
+ * @dcd_enabled_cmds: DCD commands the device enabled in the CEL
  * @event: event log driver state
  * @poison: poison driver state info
  * @security: security driver state info
@@ -463,7 +463,7 @@ struct cxl_memdev_state {
        u64 partition_align_bytes;
        u64 active_volatile_bytes;
        u64 active_persistent_bytes;
-       bool dcd_supported;
+       DECLARE_BITMAP(dcd_enabled_cmds, CXL_DCD_ENABLED_MAX);

        struct cxl_event_state event;
        struct cxl_poison_state poison;
@@ -878,12 +878,16 @@ int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds);

 static inline bool cxl_dcd_supported(struct cxl_memdev_state *mds)
 {
-       return mds->dcd_supported;
+       /*
+        * Disabled until extent event handling is implemented.  Enable by
+        * returning bitmap_full(mds->dcd_enabled_cmds, CXL_DCD_ENABLED_MAX).
+        */
+       return false;
 }

 static inline void cxl_disable_dcd(struct cxl_memdev_state *mds)
 {
-       mds->dcd_supported = false;
+       bitmap_zero(mds->dcd_enabled_cmds, CXL_DCD_ENABLED_MAX);
 }

 int cxl_set_timestamp(struct cxl_memdev_state *mds);

>
snip

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

* Re: [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device
  2026-07-31  8:48 ` [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
  2026-08-03 14:33   ` Richard Cheng
@ 2026-08-03 21:52   ` Alison Schofield
  2026-08-04  9:12     ` Anisa Su
  2026-08-03 23:55   ` Jonathan Cameron
  2 siblings, 1 reply; 33+ messages in thread
From: Alison Schofield @ 2026-08-03 21:52 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Jonathan Cameron,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, Jul 31, 2026 at 01:48:07AM -0700, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Devices which optionally support Dynamic Capacity (DC) are configured
> via mailbox commands.  CXL r4.0 section 9.13.3 requires the host to issue
> the Get DC Configuration command in order to properly configure DCDs.
> Without the Get DC Configuration command DCD can't be supported.
> 
> Implement the DC mailbox commands as specified in CXL 4.0 section
> 8.2.10.9.9 (opcodes 48XXh) to read and store the DCD configuration
> information.  Disable DCD if an invalid configuration is found.
> 
> Linux has no support for more than one dynamic capacity partition.  Read
> and validate all the partitions but configure only the first partition
> as 'dynamic ram 1'.  Additional partitions can be added in the future if
> such a device ever materializes.  Additionally it is anticipated that no
> skips will be present from the end of the pmem partition.  Check for and
> disallow this configuration as well.

Hi Anisa,

"Additionally, it is anticipated that no skips will be present.." sounds
woogedy. The paragraph starts with a Linux limitation of only one partition.
and next mentions skips. Is that a Linux implementation rule, a spec
rule, or something else?

See below wrt validation checks.


> 
> Based on an original patch by Navneet Singh.
> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>
> 
> ---
> Changes:
> 1. mbox.c: bound the Get DC Config request by the negotiated mailbox
>    payload. Requesting all CXL_MAX_DC_PARTITIONS (8) needs a 328-byte
>    response, which exceeds a spec-minimum 256-byte mailbox and fails
>    -E2BIG, disabling DCD. Cap partition_count per call to what the
>    payload holds and read the rest via the existing start_partition
>    loop.
> ---
>  drivers/cxl/core/hdm.c  |   2 +
>  drivers/cxl/core/mbox.c | 220 ++++++++++++++++++++++++++++++++++++++++
>  drivers/cxl/cxlmem.h    |  55 ++++++++++
>  drivers/cxl/pci.c       |   3 +
>  include/cxl/cxl.h       |   3 +-
>  5 files changed, 282 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 0c80b76a5f9b..0ef076c08ed2 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -446,6 +446,8 @@ static const char *cxl_mode_name(enum cxl_partition_mode mode)
>  		return "ram";
>  	case CXL_PARTMODE_PMEM:
>  		return "pmem";
> +	case CXL_PARTMODE_DYNAMIC_RAM_1:
> +		return "dynamic_ram_1";
>  	default:
>  		return "";
>  	};
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 4790524c32a7..d79019fbd790 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -1352,6 +1352,197 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
>  	return -EBUSY;
>  }
>  
> +static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_array,
> +			u8 index, struct cxl_dc_partition *dev_part)
> +{
> +	u64 blk_size = le64_to_cpu(dev_part->block_size);
> +	u64 len = le64_to_cpu(dev_part->length);
> +
> +	part_array[index].start = le64_to_cpu(dev_part->base);
> +	part_array[index].size = le64_to_cpu(dev_part->decode_length);
> +	part_array[index].size *= CXL_CAPACITY_MULTIPLIER;
> +
> +	/* Check partitions are in increasing DPA order */
> +	if (index > 0) {
> +		struct cxl_dc_partition_info *prev_part = &part_array[index - 1];
> +
> +		if ((prev_part->start + prev_part->size) >
> +		     part_array[index].start) {
> +			dev_err(dev,
> +				"DPA ordering violation for DC partition %d and %d\n",
> +				index - 1, index);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (part_array[index].size == 0 || len == 0 ||
> +	    part_array[index].size < len || !IS_ALIGNED(len, blk_size)) {
> +		dev_err(dev, "DC partition %d invalid length; size %llu len %llu blk size %llu\n",
> +			index, part_array[index].size, len, blk_size);
> +		return -EINVAL;
> +	}
> +
> +	if (blk_size == 0 || blk_size % CXL_DCD_BLOCK_LINE_SIZE ||
> +	    !is_power_of_2(blk_size)) {
> +		dev_err(dev, "DC partition %d invalid block size %llu\n",
> +			index, blk_size);
> +		return -EINVAL;
> +	}
> +
> +	if (!IS_ALIGNED(part_array[index].start, SZ_256M) ||
> +	    !IS_ALIGNED(part_array[index].start, blk_size)) {
> +		dev_err(dev, "DC partition %d invalid start %llu blk size %llu\n",
> +			index, part_array[index].start, blk_size);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(dev, "DC partition %d start %llu size %llu blk_size: %llu\n",
> +		index, part_array[index].start, part_array[index].size,
> +		blk_size);
> +
> +	return 0;
> +}


cxl_dc_check() above needs rework.
Please go back through it against CXL r4.0 Tables 8-346 and 8-347 and confirm
that each constraint the code enforces is the constraint the spec actually
states. The check need to run in dependency order, and then each failure
needs to report the specific field or relationship that is invalid.

A terse list of what looks wrong from here. (I have not chased each one)

- blk_size is used as the alignment in IS_ALIGNED(len, blk_size) and
  IS_ALIGNED(start, blk_size) before it is validated.
- Four unrelated conditions share one "invalid length" message.
- The start check combines the 256 MiB and block-size requirements, so the
  message cannot say which alignment failed.
- "DPA ordering violation" is really an overlap check, and gaps appear to be
  allowed.
- avail_partition_count is never checked against the 1 to 8 range Table 8-346
  requires.
- cxl_configure_dcd()'s "skip from pmem not supported" also fires on overlap,
  and prints neither address.
- min_out = 8 is a bare constant for the header ahead of the flex array
- DPA addresses and sizes print in decimal, rest of CXL uses hex 
- The Table 8-178, 8-179 and 8-180 citations are not r4.0 numbering

And RichardC called out this one in his prior review -
- Zero geometry fields are legal per Table 8-347, as already raised on the
  list. Agreed, and the fallout of skipping rather than rejecting needs
  working through.

-- Alison


> +
> +/* Returns the number of partitions in dc_resp or -ERRNO */
> +static int cxl_get_dc_config(struct cxl_mailbox *mbox, u8 start_partition,
> +			     u8 partition_count,
> +			     struct cxl_mbox_get_dc_config_out *dc_resp,
> +			     size_t dc_resp_size)
> +{
> +	struct cxl_mbox_get_dc_config_in get_dc = (struct cxl_mbox_get_dc_config_in) {
> +		.partition_count = partition_count,
> +		.start_partition_index = start_partition,
> +	};
> +	struct cxl_mbox_cmd mbox_cmd = (struct cxl_mbox_cmd) {
> +		.opcode = CXL_MBOX_OP_GET_DC_CONFIG,
> +		.payload_in = &get_dc,
> +		.size_in = sizeof(get_dc),
> +		.size_out = dc_resp_size,
> +		.payload_out = dc_resp,
> +		.min_out = 8,
> +	};
> +	size_t expected_sz;
> +	int rc;
> +
> +	rc = cxl_internal_send_cmd(mbox, &mbox_cmd);
> +	if (rc < 0)
> +		return rc;
> +
> +	if (dc_resp->partitions_returned > partition_count) {
> +		dev_err(mbox->host, "Device returned %u partitions, requested %u\n",
> +			dc_resp->partitions_returned, partition_count);
> +		return -EIO;
> +	}
> +
> +	/*
> +	 * The payload carries trailing extent/tag count fields after the
> +	 * partition array (CXL r4.0 Table 8-179) which the driver ignores, so
> +	 * the response is at least, not exactly, expected_sz.
> +	 */
> +	expected_sz = struct_size(dc_resp, partition,
> +				  dc_resp->partitions_returned);
> +
> +	if (mbox_cmd.size_out < expected_sz) {
> +		dev_err(mbox->host,
> +			"Payload size %zu less than expected %zu for %u partitions\n",
> +			mbox_cmd.size_out,
> +			expected_sz,
> +			dc_resp->partitions_returned);
> +		return -EIO;
> +	}
> +
> +	dev_dbg(mbox->host, "Read %d/%d DC partitions\n",
> +		dc_resp->partitions_returned, dc_resp->avail_partition_count);
> +	return dc_resp->partitions_returned;
> +}
> +
> +/**
> + * cxl_dev_dc_identify() - Reads the dynamic capacity information from the
> + *                         device.
> + * @mbox: Mailbox to query
> + * @dc_info: The dynamic partition information to return
> + *
> + * Read Dynamic Capacity information from the device and return the partition
> + * information.
> + *
> + * Return: 0 if identify was executed successfully, -ERRNO on error.
> + *         on error only dc_info is left unchanged.
> + */
> +int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> +			struct cxl_dc_partition_info *dc_info)
> +{
> +	struct cxl_dc_partition_info partitions[CXL_MAX_DC_PARTITIONS];
> +	struct cxl_mbox_get_dc_config_out *dc_resp __free(kfree) = NULL;
> +	struct device *dev = mbox->host;
> +	u8 start_partition;
> +	u8 num_partitions;
> +	u8 partition_count;
> +	size_t dc_resp_size;
> +
> +	/* Bound requested number of partitions by mailbox payload size */
> +	partition_count = min_t(size_t, CXL_MAX_DC_PARTITIONS,
> +				(mbox->payload_size - sizeof(*dc_resp) -
> +				 sizeof(struct cxl_mbox_get_dc_config_tail)) /
> +				sizeof(struct cxl_dc_partition));
> +	dc_resp_size = struct_size(dc_resp, partition, partition_count) +
> +		       sizeof(struct cxl_mbox_get_dc_config_tail);
> +
> +	dc_resp = kmalloc(dc_resp_size, GFP_KERNEL);
> +	if (!dc_resp)
> +		return -ENOMEM;
> +
> +	/*
> +	 * Read and check all partition information for validity and potential
> +	 * debugging; see debug output in cxl_dc_check()
> +	 */
needless comment


> +	start_partition = 0;
> +	num_partitions = 0;
> +	do {
> +		int rc, i, j;
> +
> +		rc = cxl_get_dc_config(mbox, start_partition, partition_count,
> +				       dc_resp, dc_resp_size);
> +		if (rc < 0) {
> +			dev_err(dev, "Failed to get DC config: %d\n", rc);
> +			return rc;
> +		}
> +
> +		if (rc == 0) {
> +			dev_err(dev,
> +				"Device reported %u partitions available but returned none at index %u\n",
> +				dc_resp->avail_partition_count, start_partition);
> +			return -EIO;
> +		}
> +
> +		num_partitions += rc;
> +
> +		if (num_partitions < 1 || num_partitions > CXL_MAX_DC_PARTITIONS) {
> +			dev_err(dev, "Invalid num of dynamic capacity partitions %d\n",
> +				num_partitions);
> +			return -EINVAL;
> +		}
> +
> +		for (i = start_partition, j = 0; i < num_partitions; i++, j++) {
> +			rc = cxl_dc_check(dev, partitions, i,
> +					  &dc_resp->partition[j]);
> +			if (rc)
> +				return rc;
> +		}
> +
> +		start_partition = num_partitions;
> +
> +	} while (num_partitions < dc_resp->avail_partition_count);
> +
> +	/* Return 1st partition */
> +	dc_info->start = partitions[0].start;
> +	dc_info->size = partitions[0].size;
> +	dev_dbg(dev, "Returning partition 0 %llu size %llu\n",
> +		dc_info->start, dc_info->size);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
> +
>  static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode)
>  {
>  	int i = info->nr_partitions;
> @@ -1422,6 +1613,35 @@ int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_get_dirty_count, "CXL");
>  
> +void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> +{
> +	struct cxl_dc_partition_info dc_info = { 0 };
> +	struct device *dev = mds->cxlds.dev;
> +	int rc;
> +
> +	rc = cxl_dev_dc_identify(&mds->cxlds.cxl_mbox, &dc_info);
> +	if (rc) {
> +		dev_warn(dev,
> +			 "Failed to read Dynamic Capacity config: %d\n", rc);
> +		cxl_disable_dcd(mds);
> +		return;
> +	}
> +
> +	/* Skips between pmem and the dynamic partition are not supported */
> +	if (dc_info.start != info->size) {
> +		dev_warn(dev,
> +			 "Dynamic Capacity skip from pmem not supported\n");
> +		cxl_disable_dcd(mds);
> +		return;
> +	}
> +
> +	info->size += dc_info.size;
> +	dev_dbg(dev, "Adding dynamic ram partition 1; %llu size %llu\n",
> +		dc_info.start, dc_info.size);
> +	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
> +
>  int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds)
>  {
>  	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 616eeaeee5b9..a9782939d82b 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -407,6 +407,8 @@ struct cxl_security_state {
>  	struct kernfs_node *sanitize_node;
>  };
>  
> +#define CXL_MAX_DC_PARTITIONS 8
> +
>  static inline resource_size_t cxl_pmem_size(struct cxl_dev_state *cxlds)
>  {
>  	/*
> @@ -691,6 +693,39 @@ struct cxl_mbox_set_shutdown_state_in {
>  	u8 state;
>  } __packed;
>  
> +/* See CXL r4.0 Table 8-178 get dynamic capacity config Input Payload */
> +struct cxl_mbox_get_dc_config_in {
> +	u8 partition_count;
> +	u8 start_partition_index;
> +} __packed;
> +
> +/* See CXL r4.0 Table 8-179 get dynamic capacity config Output Payload */
> +struct cxl_mbox_get_dc_config_out {
> +	u8 avail_partition_count;
> +	u8 partitions_returned;
> +	u8 rsvd[6];
> +	/* See CXL r4.0 Table 8-180 */
> +	struct cxl_dc_partition {
> +		__le64 base;
> +		__le64 decode_length;
> +		__le64 length;
> +		__le64 block_size;
> +		__le32 dsmad_handle;
> +		u8 flags;
> +		u8 rsvd[3];
> +	} __packed partition[] __counted_by(partitions_returned);
> +	/* Trailing extent/tag count fields unused */
> +} __packed;
> +
> +/* Trailing counts; cannot be a member after the flex array above */
> +struct cxl_mbox_get_dc_config_tail {
> +	__le32 num_extents_supported;
> +	__le32 num_extents_available;
> +	__le32 num_tags_supported;
> +	__le32 num_tags_available;
> +} __packed;
> +#define CXL_DCD_BLOCK_LINE_SIZE 0x40
> +
>  /* Set Timestamp CXL 3.0 Spec 8.2.9.4.2 */
>  struct cxl_mbox_set_timestamp_in {
>  	__le64 timestamp;
> @@ -814,9 +849,18 @@ enum {
>  int cxl_internal_send_cmd(struct cxl_mailbox *cxl_mbox,
>  			  struct cxl_mbox_cmd *cmd);
>  int cxl_dev_state_identify(struct cxl_memdev_state *mds);
> +
> +struct cxl_dc_partition_info {
> +	u64 start;
> +	u64 size;
> +};
> +
> +int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> +			struct cxl_dc_partition_info *dc_info);
>  int cxl_await_media_ready(struct cxl_dev_state *cxlds);
>  int cxl_enumerate_cmds(struct cxl_memdev_state *mds);
>  int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
> +void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
>  struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial,
>  						 u16 dvsec);
>  void set_exclusive_cxl_commands(struct cxl_memdev_state *mds,
> @@ -830,6 +874,17 @@ void cxl_event_trace_record(struct cxl_memdev *cxlmd,
>  			    const uuid_t *uuid, union cxl_event *evt);
>  int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count);
>  int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds);
> +
> +static inline bool cxl_dcd_supported(struct cxl_memdev_state *mds)
> +{
> +	return mds->dcd_supported;
> +}
> +
> +static inline void cxl_disable_dcd(struct cxl_memdev_state *mds)
> +{
> +	mds->dcd_supported = false;
> +}
> +
>  int cxl_set_timestamp(struct cxl_memdev_state *mds);
>  int cxl_poison_state_init(struct cxl_memdev_state *mds);
>  int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 267c679b0b3c..9b320a2b6fe0 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -870,6 +870,9 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  	if (rc)
>  		return rc;
>  
> +	if (cxl_dcd_supported(mds))
> +		cxl_configure_dcd(mds, &range_info);
> +
>  	rc = cxl_dpa_setup(cxlds, &range_info);
>  	if (rc)
>  		return rc;
> diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
> index 016c74fb747c..87c2bd73df21 100644
> --- a/include/cxl/cxl.h
> +++ b/include/cxl/cxl.h
> @@ -133,6 +133,7 @@ struct cxl_dpa_perf {
>  enum cxl_partition_mode {
>  	CXL_PARTMODE_RAM,
>  	CXL_PARTMODE_PMEM,
> +	CXL_PARTMODE_DYNAMIC_RAM_1,
>  };
>  
>  /**
> @@ -147,7 +148,7 @@ struct cxl_dpa_partition {
>  	enum cxl_partition_mode mode;
>  };
>  
> -#define CXL_NR_PARTITIONS_MAX 2
> +#define CXL_NR_PARTITIONS_MAX 3
>  
>  /**
>   * struct cxl_dev_state - The driver device state
> -- 
> 2.43.0
> 

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

* Re: [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device
  2026-08-03 14:33   ` Richard Cheng
@ 2026-08-03 22:11     ` Anisa Su
  0 siblings, 0 replies; 33+ messages in thread
From: Anisa Su @ 2026-08-03 22:11 UTC (permalink / raw)
  To: Richard Cheng
  Cc: Anisa Su, linux-cxl, nvdimm, linux-kernel, Dave Jiang,
	Alison Schofield, Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma,
	Davidlohr Bueso, Ira Weiny, Benjamin Cheatham, Wonjae Lee,
	Junhee Park, Heesoo Kim

On Mon, Aug 03, 2026 at 10:33:27PM +0800, Richard Cheng wrote:
> On Fri, Jul 31, 2026 at 01:48:07AM +0800, Anisa Su wrote:
> > From: Ira Weiny <iweiny@kernel.org>
> > 
> > Devices which optionally support Dynamic Capacity (DC) are configured
> > via mailbox commands.  CXL r4.0 section 9.13.3 requires the host to issue
> > the Get DC Configuration command in order to properly configure DCDs.
> > Without the Get DC Configuration command DCD can't be supported.
> > 
> > Implement the DC mailbox commands as specified in CXL 4.0 section
> > 8.2.10.9.9 (opcodes 48XXh) to read and store the DCD configuration
> > information.  Disable DCD if an invalid configuration is found.
> > 
> > Linux has no support for more than one dynamic capacity partition.  Read
> > and validate all the partitions but configure only the first partition
> > as 'dynamic ram 1'.  Additional partitions can be added in the future if
> > such a device ever materializes.  Additionally it is anticipated that no
> > skips will be present from the end of the pmem partition.  Check for and
> > disallow this configuration as well.
> > 
> > Based on an original patch by Navneet Singh.
> > 
> > Signed-off-by: Ira Weiny <iweiny@kernel.org>
> > Signed-off-by: Anisa Su <anisa.su@samsung.com>
> > Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> > Tested-by: Junhee Park <jh9934.park@samsung.com>
> > Tested-by: Heesoo Kim <habil.kim@samsung.com>
> > 
> > ---
> > Changes:
> > 1. mbox.c: bound the Get DC Config request by the negotiated mailbox
> >    payload. Requesting all CXL_MAX_DC_PARTITIONS (8) needs a 328-byte
> >    response, which exceeds a spec-minimum 256-byte mailbox and fails
> >    -E2BIG, disabling DCD. Cap partition_count per call to what the
> >    payload holds and read the rest via the existing start_partition
> >    loop.
> > ---
> >  drivers/cxl/core/hdm.c  |   2 +
> >  drivers/cxl/core/mbox.c | 220 ++++++++++++++++++++++++++++++++++++++++
> >  drivers/cxl/cxlmem.h    |  55 ++++++++++
> >  drivers/cxl/pci.c       |   3 +
> >  include/cxl/cxl.h       |   3 +-
> >  5 files changed, 282 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> > index 0c80b76a5f9b..0ef076c08ed2 100644
> > --- a/drivers/cxl/core/hdm.c
> > +++ b/drivers/cxl/core/hdm.c
> > @@ -446,6 +446,8 @@ static const char *cxl_mode_name(enum cxl_partition_mode mode)
> >  		return "ram";
> >  	case CXL_PARTMODE_PMEM:
> >  		return "pmem";
> > +	case CXL_PARTMODE_DYNAMIC_RAM_1:
> > +		return "dynamic_ram_1";
> >  	default:
> >  		return "";
> >  	};
> > diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> > index 4790524c32a7..d79019fbd790 100644
> > --- a/drivers/cxl/core/mbox.c
> > +++ b/drivers/cxl/core/mbox.c
> > @@ -1352,6 +1352,197 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
> >  	return -EBUSY;
> >  }
> >  
> > +static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_array,
> > +			u8 index, struct cxl_dc_partition *dev_part)
> > +{
> > +	u64 blk_size = le64_to_cpu(dev_part->block_size);
> > +	u64 len = le64_to_cpu(dev_part->length);
> > +
> > +	part_array[index].start = le64_to_cpu(dev_part->base);
> > +	part_array[index].size = le64_to_cpu(dev_part->decode_length);
> > +	part_array[index].size *= CXL_CAPACITY_MULTIPLIER;
> > +
> > +	/* Check partitions are in increasing DPA order */
> > +	if (index > 0) {
> > +		struct cxl_dc_partition_info *prev_part = &part_array[index - 1];
> > +
> > +		if ((prev_part->start + prev_part->size) >
> > +		     part_array[index].start) {
> > +			dev_err(dev,
> > +				"DPA ordering violation for DC partition %d and %d\n",
> > +				index - 1, index);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (part_array[index].size == 0 || len == 0 ||
> > +	    part_array[index].size < len || !IS_ALIGNED(len, blk_size)) {
> > +		dev_err(dev, "DC partition %d invalid length; size %llu len %llu blk size %llu\n",
> > +			index, part_array[index].size, len, blk_size);
> > +		return -EINVAL;
> > +	}
> > +
> 
> Hi Anisa,
> 
> I think here , 0s are legal, according to spec CXL 4.0 8.2.10.9.9.1 "Get Dynamic Capacity Configuration",
> Table 8-347 "DC Region Configuration". It says "Report 0 if the region is unavailable for Dynamici Capacity"
> 
> Imagine if we have a device ports 8 regions and only region 0 is configured, regions 1~7 come back with 0s.
> cxl_dc_check() will check them all, so the first zero region fails and cxl_configure_dcd() ends up disabling
> DCD on a device that is perfectly fine.
> 
> Maybe we should skip the all-zero regions instead of rejecting them ?
> 
> Best regards,
> Richard Cheng.
> 
Thank you for taking a look! Here is what the fix looks like:

Helper function above cxl_dc_check to check for all-zeros and return early
in cxl_dc_check() if unavailable:

/*
 * A partition unavailable for Dynamic Capacity reports zeros for decode_length,
 * length, and block_size.
 */
static bool cxl_dc_partition_unavailable(struct cxl_dc_partition *dev_part)
{
	return !le64_to_cpu(dev_part->decode_length) &&
	       !le64_to_cpu(dev_part->length) &&
	       !le64_to_cpu(dev_part->block_size);
}

static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_array,
			u8 index, struct cxl_dc_partition *dev_part)
{
	u64 blk_size = le64_to_cpu(dev_part->block_size);
	u64 len = le64_to_cpu(dev_part->length);
	u32 handle = le32_to_cpu(dev_part->dsmad_handle);

	/*
	 * Not an error; leave the entry empty. A partially zeroed partition
	 * is rejected by the checks below. CXL r4.0 Table 8-347.
	 */
	if (cxl_dc_partition_unavailable(dev_part)) {
		part_array[index] = (struct cxl_dc_partition_info) { };
		dev_dbg(dev, "Partition %d unavailable for DC\n", index);
		return 0;
	}
	
	... Rest of the function unchanged ...
	
}

I only skip if all 3 fields (decode length, length, and block size) are 0, so
these checks in cxl_dc_check() are still here to catch if only a subset of
the 3 fields are 0:

	if (part_array[index].size == 0 || len == 0 ||
	    part_array[index].size < len || !IS_ALIGNED(len, blk_size)) {
		dev_err(dev, "DC partition %d invalid length; size %llu len %llu blk size %llu\n",
			index, part_array[index].size, len, blk_size);
		return -EINVAL;
	}

	if (blk_size == 0 || blk_size % CXL_DCD_BLOCK_LINE_SIZE ||
	    !is_power_of_2(blk_size)) {
		dev_err(dev, "DC partition %d invalid block size %llu\n",
			index, blk_size);
		return -EINVAL;
	}

Let me know if you think that makes sense.

Thanks,
Anisa

> > +	if (blk_size == 0 || blk_size % CXL_DCD_BLOCK_LINE_SIZE ||
> > +	    !is_power_of_2(blk_size)) {
> > +		dev_err(dev, "DC partition %d invalid block size %llu\n",
> > +			index, blk_size);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (!IS_ALIGNED(part_array[index].start, SZ_256M) ||
> > +	    !IS_ALIGNED(part_array[index].start, blk_size)) {
> > +		dev_err(dev, "DC partition %d invalid start %llu blk size %llu\n",
> > +			index, part_array[index].start, blk_size);
> > +		return -EINVAL;
> > +	}
> > +
> > +	dev_dbg(dev, "DC partition %d start %llu size %llu blk_size: %llu\n",
> > +		index, part_array[index].start, part_array[index].size,
> > +		blk_size);
> > +
> > +	return 0;
> > +}
> > +
> > +/* Returns the number of partitions in dc_resp or -ERRNO */
> > +static int cxl_get_dc_config(struct cxl_mailbox *mbox, u8 start_partition,
> > +			     u8 partition_count,
> > +			     struct cxl_mbox_get_dc_config_out *dc_resp,
> > +			     size_t dc_resp_size)
> > +{
> > +	struct cxl_mbox_get_dc_config_in get_dc = (struct cxl_mbox_get_dc_config_in) {
> > +		.partition_count = partition_count,
> > +		.start_partition_index = start_partition,
> > +	};
> > +	struct cxl_mbox_cmd mbox_cmd = (struct cxl_mbox_cmd) {
> > +		.opcode = CXL_MBOX_OP_GET_DC_CONFIG,
> > +		.payload_in = &get_dc,
> > +		.size_in = sizeof(get_dc),
> > +		.size_out = dc_resp_size,
> > +		.payload_out = dc_resp,
> > +		.min_out = 8,
> > +	};
> > +	size_t expected_sz;
> > +	int rc;
> > +
> > +	rc = cxl_internal_send_cmd(mbox, &mbox_cmd);
> > +	if (rc < 0)
> > +		return rc;
> > +
> > +	if (dc_resp->partitions_returned > partition_count) {
> > +		dev_err(mbox->host, "Device returned %u partitions, requested %u\n",
> > +			dc_resp->partitions_returned, partition_count);
> > +		return -EIO;
> > +	}
> > +
> > +	/*
> > +	 * The payload carries trailing extent/tag count fields after the
> > +	 * partition array (CXL r4.0 Table 8-179) which the driver ignores, so
> > +	 * the response is at least, not exactly, expected_sz.
> > +	 */
> > +	expected_sz = struct_size(dc_resp, partition,
> > +				  dc_resp->partitions_returned);
> > +
> > +	if (mbox_cmd.size_out < expected_sz) {
> > +		dev_err(mbox->host,
> > +			"Payload size %zu less than expected %zu for %u partitions\n",
> > +			mbox_cmd.size_out,
> > +			expected_sz,
> > +			dc_resp->partitions_returned);
> > +		return -EIO;
> > +	}
> > +
> > +	dev_dbg(mbox->host, "Read %d/%d DC partitions\n",
> > +		dc_resp->partitions_returned, dc_resp->avail_partition_count);
> > +	return dc_resp->partitions_returned;
> > +}
> > +
> > +/**
> > + * cxl_dev_dc_identify() - Reads the dynamic capacity information from the
> > + *                         device.
> > + * @mbox: Mailbox to query
> > + * @dc_info: The dynamic partition information to return
> > + *
> > + * Read Dynamic Capacity information from the device and return the partition
> > + * information.
> > + *
> > + * Return: 0 if identify was executed successfully, -ERRNO on error.
> > + *         on error only dc_info is left unchanged.
> > + */
> > +int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> > +			struct cxl_dc_partition_info *dc_info)
> > +{
> > +	struct cxl_dc_partition_info partitions[CXL_MAX_DC_PARTITIONS];
> > +	struct cxl_mbox_get_dc_config_out *dc_resp __free(kfree) = NULL;
> > +	struct device *dev = mbox->host;
> > +	u8 start_partition;
> > +	u8 num_partitions;
> > +	u8 partition_count;
> > +	size_t dc_resp_size;
> > +
> > +	/* Bound requested number of partitions by mailbox payload size */
> > +	partition_count = min_t(size_t, CXL_MAX_DC_PARTITIONS,
> > +				(mbox->payload_size - sizeof(*dc_resp) -
> > +				 sizeof(struct cxl_mbox_get_dc_config_tail)) /
> > +				sizeof(struct cxl_dc_partition));
> > +	dc_resp_size = struct_size(dc_resp, partition, partition_count) +
> > +		       sizeof(struct cxl_mbox_get_dc_config_tail);
> > +
> > +	dc_resp = kmalloc(dc_resp_size, GFP_KERNEL);
> > +	if (!dc_resp)
> > +		return -ENOMEM;
> > +
> > +	/*
> > +	 * Read and check all partition information for validity and potential
> > +	 * debugging; see debug output in cxl_dc_check()
> > +	 */
> > +	start_partition = 0;
> > +	num_partitions = 0;
> > +	do {
> > +		int rc, i, j;
> > +
> > +		rc = cxl_get_dc_config(mbox, start_partition, partition_count,
> > +				       dc_resp, dc_resp_size);
> > +		if (rc < 0) {
> > +			dev_err(dev, "Failed to get DC config: %d\n", rc);
> > +			return rc;
> > +		}
> > +
> > +		if (rc == 0) {
> > +			dev_err(dev,
> > +				"Device reported %u partitions available but returned none at index %u\n",
> > +				dc_resp->avail_partition_count, start_partition);
> > +			return -EIO;
> > +		}
> > +
> > +		num_partitions += rc;
> > +
> > +		if (num_partitions < 1 || num_partitions > CXL_MAX_DC_PARTITIONS) {
> > +			dev_err(dev, "Invalid num of dynamic capacity partitions %d\n",
> > +				num_partitions);
> > +			return -EINVAL;
> > +		}
> > +
> > +		for (i = start_partition, j = 0; i < num_partitions; i++, j++) {
> > +			rc = cxl_dc_check(dev, partitions, i,
> > +					  &dc_resp->partition[j]);
> > +			if (rc)
> > +				return rc;
> > +		}
> > +
> > +		start_partition = num_partitions;
> > +
> > +	} while (num_partitions < dc_resp->avail_partition_count);
> > +
> > +	/* Return 1st partition */
> > +	dc_info->start = partitions[0].start;
> > +	dc_info->size = partitions[0].size;
> > +	dev_dbg(dev, "Returning partition 0 %llu size %llu\n",
> > +		dc_info->start, dc_info->size);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
> > +
> >  static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode)
> >  {
> >  	int i = info->nr_partitions;
> > @@ -1422,6 +1613,35 @@ int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
> >  }
> >  EXPORT_SYMBOL_NS_GPL(cxl_get_dirty_count, "CXL");
> >  
> > +void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> > +{
> > +	struct cxl_dc_partition_info dc_info = { 0 };
> > +	struct device *dev = mds->cxlds.dev;
> > +	int rc;
> > +
> > +	rc = cxl_dev_dc_identify(&mds->cxlds.cxl_mbox, &dc_info);
> > +	if (rc) {
> > +		dev_warn(dev,
> > +			 "Failed to read Dynamic Capacity config: %d\n", rc);
> > +		cxl_disable_dcd(mds);
> > +		return;
> > +	}
> > +
> > +	/* Skips between pmem and the dynamic partition are not supported */
> > +	if (dc_info.start != info->size) {
> > +		dev_warn(dev,
> > +			 "Dynamic Capacity skip from pmem not supported\n");
> > +		cxl_disable_dcd(mds);
> > +		return;
> > +	}
> > +
> > +	info->size += dc_info.size;
> > +	dev_dbg(dev, "Adding dynamic ram partition 1; %llu size %llu\n",
> > +		dc_info.start, dc_info.size);
> > +	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> > +}
> > +EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
> > +
> >  int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds)
> >  {
> >  	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
> > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > index 616eeaeee5b9..a9782939d82b 100644
> > --- a/drivers/cxl/cxlmem.h
> > +++ b/drivers/cxl/cxlmem.h
> > @@ -407,6 +407,8 @@ struct cxl_security_state {
> >  	struct kernfs_node *sanitize_node;
> >  };
> >  
> > +#define CXL_MAX_DC_PARTITIONS 8
> > +
> >  static inline resource_size_t cxl_pmem_size(struct cxl_dev_state *cxlds)
> >  {
> >  	/*
> > @@ -691,6 +693,39 @@ struct cxl_mbox_set_shutdown_state_in {
> >  	u8 state;
> >  } __packed;
> >  
> > +/* See CXL r4.0 Table 8-178 get dynamic capacity config Input Payload */
> > +struct cxl_mbox_get_dc_config_in {
> > +	u8 partition_count;
> > +	u8 start_partition_index;
> > +} __packed;
> > +
> > +/* See CXL r4.0 Table 8-179 get dynamic capacity config Output Payload */
> > +struct cxl_mbox_get_dc_config_out {
> > +	u8 avail_partition_count;
> > +	u8 partitions_returned;
> > +	u8 rsvd[6];
> > +	/* See CXL r4.0 Table 8-180 */
> > +	struct cxl_dc_partition {
> > +		__le64 base;
> > +		__le64 decode_length;
> > +		__le64 length;
> > +		__le64 block_size;
> > +		__le32 dsmad_handle;
> > +		u8 flags;
> > +		u8 rsvd[3];
> > +	} __packed partition[] __counted_by(partitions_returned);
> > +	/* Trailing extent/tag count fields unused */
> > +} __packed;
> > +
> > +/* Trailing counts; cannot be a member after the flex array above */
> > +struct cxl_mbox_get_dc_config_tail {
> > +	__le32 num_extents_supported;
> > +	__le32 num_extents_available;
> > +	__le32 num_tags_supported;
> > +	__le32 num_tags_available;
> > +} __packed;
> > +#define CXL_DCD_BLOCK_LINE_SIZE 0x40
> > +
> >  /* Set Timestamp CXL 3.0 Spec 8.2.9.4.2 */
> >  struct cxl_mbox_set_timestamp_in {
> >  	__le64 timestamp;
> > @@ -814,9 +849,18 @@ enum {
> >  int cxl_internal_send_cmd(struct cxl_mailbox *cxl_mbox,
> >  			  struct cxl_mbox_cmd *cmd);
> >  int cxl_dev_state_identify(struct cxl_memdev_state *mds);
> > +
> > +struct cxl_dc_partition_info {
> > +	u64 start;
> > +	u64 size;
> > +};
> > +
> > +int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> > +			struct cxl_dc_partition_info *dc_info);
> >  int cxl_await_media_ready(struct cxl_dev_state *cxlds);
> >  int cxl_enumerate_cmds(struct cxl_memdev_state *mds);
> >  int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
> > +void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
> >  struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial,
> >  						 u16 dvsec);
> >  void set_exclusive_cxl_commands(struct cxl_memdev_state *mds,
> > @@ -830,6 +874,17 @@ void cxl_event_trace_record(struct cxl_memdev *cxlmd,
> >  			    const uuid_t *uuid, union cxl_event *evt);
> >  int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count);
> >  int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds);
> > +
> > +static inline bool cxl_dcd_supported(struct cxl_memdev_state *mds)
> > +{
> > +	return mds->dcd_supported;
> > +}
> > +
> > +static inline void cxl_disable_dcd(struct cxl_memdev_state *mds)
> > +{
> > +	mds->dcd_supported = false;
> > +}
> > +
> >  int cxl_set_timestamp(struct cxl_memdev_state *mds);
> >  int cxl_poison_state_init(struct cxl_memdev_state *mds);
> >  int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
> > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> > index 267c679b0b3c..9b320a2b6fe0 100644
> > --- a/drivers/cxl/pci.c
> > +++ b/drivers/cxl/pci.c
> > @@ -870,6 +870,9 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> >  	if (rc)
> >  		return rc;
> >  
> > +	if (cxl_dcd_supported(mds))
> > +		cxl_configure_dcd(mds, &range_info);
> > +
> >  	rc = cxl_dpa_setup(cxlds, &range_info);
> >  	if (rc)
> >  		return rc;
> > diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
> > index 016c74fb747c..87c2bd73df21 100644
> > --- a/include/cxl/cxl.h
> > +++ b/include/cxl/cxl.h
> > @@ -133,6 +133,7 @@ struct cxl_dpa_perf {
> >  enum cxl_partition_mode {
> >  	CXL_PARTMODE_RAM,
> >  	CXL_PARTMODE_PMEM,
> > +	CXL_PARTMODE_DYNAMIC_RAM_1,
> >  };
> >  
> >  /**
> > @@ -147,7 +148,7 @@ struct cxl_dpa_partition {
> >  	enum cxl_partition_mode mode;
> >  };
> >  
> > -#define CXL_NR_PARTITIONS_MAX 2
> > +#define CXL_NR_PARTITIONS_MAX 3
> >  
> >  /**
> >   * struct cxl_dev_state - The driver device state
> > -- 
> > 2.43.0
> > 
> > 

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

* Re: [PATCH v12 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
  2026-07-31  8:48 ` [PATCH v12 3/8] cxl/cdat: Gather DSMAS data for DCD partitions Anisa Su
@ 2026-08-03 22:56   ` Alison Schofield
  2026-08-05  6:58     ` Anisa Su
  0 siblings, 1 reply; 33+ messages in thread
From: Alison Schofield @ 2026-08-03 22:56 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Jonathan Cameron,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, Jul 31, 2026 at 01:48:08AM -0700, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Additional DCD partition (AKA region) information is contained in the
> DSMAS CDAT tables, including performance, read only, and shareable
> attributes.
> 
> Match DCD partitions with DSMAS tables and store the meta data.
> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Co-developed-by: Anisa Su <anisa.su@samsung.com>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
>  drivers/cxl/core/cdat.c | 13 +++++++++++++
>  drivers/cxl/core/hdm.c  |  1 +
>  drivers/cxl/core/mbox.c | 22 ++++++++++++++++------
>  drivers/cxl/cxlmem.h    |  2 ++
>  include/cxl/cxl.h       |  4 ++++
>  5 files changed, 36 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> index 5c9f07262513..37136b2cf7e4 100644
> --- a/drivers/cxl/core/cdat.c
> +++ b/drivers/cxl/core/cdat.c
> @@ -17,6 +17,7 @@ struct dsmas_entry {
>  	struct access_coordinate cdat_coord[ACCESS_COORDINATE_MAX];
>  	int entries;
>  	int qos_class;
> +	bool shareable;
>  };
>  
>  static u32 cdat_normalize(u16 entry, u64 base, u8 type)
> @@ -74,6 +75,7 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
>  		return -ENOMEM;
>  
>  	dent->handle = dsmas->dsmad_handle;
> +	dent->shareable = dsmas->flags & ACPI_CDAT_DSMAS_SHAREABLE;
>  	dent->dpa_range.start = le64_to_cpu((__force __le64)dsmas->dpa_base_address);
>  	dent->dpa_range.end = le64_to_cpu((__force __le64)dsmas->dpa_base_address) +
>  			      le64_to_cpu((__force __le64)dsmas->dpa_length) - 1;
> @@ -266,15 +268,26 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
>  		bool found = false;
>  
>  		for (int i = 0; i < cxlds->nr_partitions; i++) {
> +			enum cxl_partition_mode mode = cxlds->part[i].mode;
>  			struct resource *res = &cxlds->part[i].res;
> +			u8 handle = cxlds->part[i].handle;
>  			struct range range = {
>  				.start = res->start,
>  				.end = res->end,
>  			};
>  
>  			if (range_contains(&range, &dent->dpa_range)) {
> +				if (mode == CXL_PARTMODE_DYNAMIC_RAM_1 &&
> +				    dent->handle != handle) {
> +					dev_warn(dev,
> +						 "Dynamic RAM perf mismatch; %pra (%u) vs %pra (%u)\n",
> +						 &range, handle, &dent->dpa_range,
> +						 dent->handle);
> +					continue;

Seems like the above message needs to say that the DSMAD handle from
mbox disagrees w CDAT DSMAS handle. Those (%u) that are handles have
no description.
With the above mismatch and continue, the loop exits w found still
false. Is that really true?


> +				}
>  				update_perf_entry(dev, dent,
>  						  &cxlds->part[i].perf);
> +				cxlds->part[i].shareable = dent->shareable;


why is shareable assigned for every node, and not only DYNAMIC_RAM_1

>  				found = true;
>  				break;
>  			}

Above nesting can be improved. Inverting range_contains() to unindent
the main body. A 'part' local pointer would reduce repeated indexing.


> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 0ef076c08ed2..7f63b86887f4 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -477,6 +477,7 @@ int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
>  
>  		cxlds->part[i].perf.qos_class = CXL_QOS_CLASS_INVALID;
>  		cxlds->part[i].mode = part->mode;
> +		cxlds->part[i].handle = part->handle;
>  
>  		/* Require ordered + contiguous partitions */
>  		if (i) {
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index d79019fbd790..a6cdea9f4080 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -1357,10 +1357,16 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_a
>  {
>  	u64 blk_size = le64_to_cpu(dev_part->block_size);
>  	u64 len = le64_to_cpu(dev_part->length);
> +	u32 handle = le32_to_cpu(dev_part->dsmad_handle);
>  
>  	part_array[index].start = le64_to_cpu(dev_part->base);
>  	part_array[index].size = le64_to_cpu(dev_part->decode_length);
>  	part_array[index].size *= CXL_CAPACITY_MULTIPLIER;
> +	if (handle & ~0xFF) {
> +		dev_warn(dev, "DSMAD handle 0x%x has non-zero reserved bits\n", handle);

I don't see reserve bits for a DSMAD handle. I think this check is valid
but the message has the wrong constraint. Expect it is a CDAT constraint.


> +		return -EINVAL;
> +	}
> +	part_array[index].handle = handle;
>  
>  	/* Check partitions are in increasing DPA order */
>  	if (index > 0) {
> @@ -1536,6 +1542,7 @@ int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
>  	/* Return 1st partition */
>  	dc_info->start = partitions[0].start;
>  	dc_info->size = partitions[0].size;
> +	dc_info->handle = partitions[0].handle;
>  	dev_dbg(dev, "Returning partition 0 %llu size %llu\n",
>  		dc_info->start, dc_info->size);
>  
> @@ -1543,7 +1550,8 @@ int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
>  
> -static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode)
> +static void add_part(struct cxl_dpa_info *info, u64 start, u64 size,
> +		     enum cxl_partition_mode mode, u8 handle)
>  {
>  	int i = info->nr_partitions;
>  
> @@ -1555,6 +1563,7 @@ static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_pa
>  		.end = start + size - 1,
>  	};
>  	info->part[i].mode = mode;
> +	info->part[i].handle = handle;
>  	info->nr_partitions++;
>  }
>  
> @@ -1572,9 +1581,9 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
>  	info->size = mds->total_bytes;
>  
>  	if (mds->partition_align_bytes == 0) {
> -		add_part(info, 0, mds->volatile_only_bytes, CXL_PARTMODE_RAM);
> +		add_part(info, 0, mds->volatile_only_bytes, CXL_PARTMODE_RAM, 0);
>  		add_part(info, mds->volatile_only_bytes,
> -			 mds->persistent_only_bytes, CXL_PARTMODE_PMEM);
> +			 mds->persistent_only_bytes, CXL_PARTMODE_PMEM, 0);
>  		return 0;
>  	}
>  
> @@ -1584,9 +1593,9 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
>  		return rc;
>  	}
>  
> -	add_part(info, 0, mds->active_volatile_bytes, CXL_PARTMODE_RAM);
> +	add_part(info, 0, mds->active_volatile_bytes, CXL_PARTMODE_RAM, 0);
>  	add_part(info, mds->active_volatile_bytes, mds->active_persistent_bytes,
> -		 CXL_PARTMODE_PMEM);
> +		 CXL_PARTMODE_PMEM, 0);
>  
>  	return 0;
>  }
> @@ -1638,7 +1647,8 @@ void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
>  	info->size += dc_info.size;
>  	dev_dbg(dev, "Adding dynamic ram partition 1; %llu size %llu\n",
>  		dc_info.start, dc_info.size);
> -	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> +	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1,
> +		 dc_info.handle);
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
>  
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index a9782939d82b..eb1e6f7a2038 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -140,6 +140,7 @@ struct cxl_dpa_info {
>  	struct cxl_dpa_part_info {
>  		struct range range;
>  		enum cxl_partition_mode mode;
> +		u8 handle;
>  	} part[CXL_NR_PARTITIONS_MAX];
>  	int nr_partitions;
>  };
> @@ -853,6 +854,7 @@ int cxl_dev_state_identify(struct cxl_memdev_state *mds);
>  struct cxl_dc_partition_info {
>  	u64 start;
>  	u64 size;
> +	u8 handle;
>  };
>  
>  int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
> index 87c2bd73df21..baf0132c70a3 100644
> --- a/include/cxl/cxl.h
> +++ b/include/cxl/cxl.h
> @@ -141,11 +141,15 @@ enum cxl_partition_mode {
>   * @res: shortcut to the partition in the DPA resource tree (cxlds->dpa_res)
>   * @perf: performance attributes of the partition from CDAT
>   * @mode: operation mode for the DPA capacity, e.g. ram, pmem, dynamic...
> + * @handle: DSMAS handle intended to represent this partition

What is with the 'intended'? Either it represents the partition
or the match failed.


> + * @shareable: Is the partition sharable (from its CDAT DSMAS entry)
                                   ^
				   shareable


>   */
>  struct cxl_dpa_partition {
>  	struct resource res;
>  	struct cxl_dpa_perf perf;
>  	enum cxl_partition_mode mode;
> +	u8 handle;
> +	bool shareable;
>  };
>  
>  #define CXL_NR_PARTITIONS_MAX 3
> -- 
> 2.43.0
> 

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

* Re: [PATCH v12 4/8] cxl/events: Split event msgnum configuration from irq setup
  2026-07-31  8:48 ` [PATCH v12 4/8] cxl/events: Split event msgnum configuration from irq setup Anisa Su
@ 2026-08-03 23:01   ` Alison Schofield
  0 siblings, 0 replies; 33+ messages in thread
From: Alison Schofield @ 2026-08-03 23:01 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Jonathan Cameron,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, Jul 31, 2026 at 01:48:09AM -0700, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Dynamic Capacity Devices (DCD) require event interrupts to process
> memory addition or removal.  BIOS may have control over non-DCD event
> processing.  DCD interrupt configuration needs to be separate from
> memory event interrupt configuration.
> 
> Split cxl_event_config_msgnums() from irq setup in preparation for
> separate DCD interrupts configuration.

Reviewed-by: Alison Schofield <alison.schofield@intel.com>

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

* Re: [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
  2026-08-03 21:30   ` Alison Schofield
@ 2026-08-03 23:12     ` Jonathan Cameron
  2026-08-04 15:45       ` Gregory Price
  0 siblings, 1 reply; 33+ messages in thread
From: Jonathan Cameron @ 2026-08-03 23:12 UTC (permalink / raw)
  To: Alison Schofield
  Cc: Anisa Su, linux-cxl, nvdimm, linux-kernel, Dave Jiang, Fan Ni,
	Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Mon, 3 Aug 2026 14:30:12 -0700
Alison Schofield <alison.schofield@intel.com> wrote:

> On Fri, Jul 31, 2026 at 01:48:06AM -0700, Anisa Su wrote:
> > From: Ira Weiny <iweiny@kernel.org>
> > 
> > Per the CXL 4.0 specification software must check the Command Effects
> > Log (CEL) for dynamic capacity command support.
> > 
> > Detect support for the DCD commands while reading the CEL, including:
> > 
> >         Get DC Config
> >         Get DC Extent List
> >         Add DC Response
> >         Release DC
> > 
> > Based on an original patch by Navneet Singh.
> > 
> > Signed-off-by: Ira Weiny <iweiny@kernel.org>
> > Signed-off-by: Anisa Su <anisa.su@samsung.com>
> > Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> > Tested-by: Junhee Park <jh9934.park@samsung.com>
> > Tested-by: Heesoo Kim <habil.kim@samsung.com>
> > 
> > ---
> > Changes:
> > 1. mbox.c: leave mds->dcd_supported false so the hardware enablement
> >    patches can be upstreamed ahead of the extent and DAX work; the
> >    event handling patch re-enables it.
> > ---
> >  drivers/cxl/core/mbox.c | 44 +++++++++++++++++++++++++++++++++++++++++
> >  drivers/cxl/cxlmem.h    | 20 +++++++++++++++++++
> >  2 files changed, 64 insertions(+)
> > 
> > diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> > index 7c6c5b7450a5..4790524c32a7 100644
> > --- a/drivers/cxl/core/mbox.c
> > +++ b/drivers/cxl/core/mbox.c
> > @@ -165,6 +165,38 @@ static void cxl_set_security_cmd_enabled(struct cxl_security_state *security,
> >  	}
> >  }
> >  
> > +static bool cxl_is_dcd_command(u16 opcode)
> > +{
> > +#define CXL_MBOX_OP_DCD_CMDS 0x48
> > +
> > +	return (opcode >> 8) == CXL_MBOX_OP_DCD_CMDS;
> > +}
> > +
> > +static void cxl_set_dcd_cmd_enabled(u16 opcode, unsigned long *cmd_mask)
> > +{
> > +	switch (opcode) {
> > +	case CXL_MBOX_OP_GET_DC_CONFIG:
> > +		set_bit(CXL_DCD_ENABLED_GET_CONFIG, cmd_mask);
> > +		break;
> > +	case CXL_MBOX_OP_GET_DC_EXTENT_LIST:
> > +		set_bit(CXL_DCD_ENABLED_GET_EXTENT_LIST, cmd_mask);
> > +		break;
> > +	case CXL_MBOX_OP_ADD_DC_RESPONSE:
> > +		set_bit(CXL_DCD_ENABLED_ADD_RESPONSE, cmd_mask);
> > +		break;
> > +	case CXL_MBOX_OP_RELEASE_DC:
> > +		set_bit(CXL_DCD_ENABLED_RELEASE, cmd_mask);
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +}
> > +
> > +static bool cxl_verify_dcd_cmds(unsigned long *cmds_seen)
> > +{
> > +	return bitmap_full(cmds_seen, CXL_DCD_ENABLED_MAX);
> > +}
> > +
> >  static bool cxl_is_poison_command(u16 opcode)
> >  {
> >  #define CXL_MBOX_OP_POISON_CMDS 0x43
> > @@ -757,6 +789,7 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
> >  	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
> >  	struct cxl_cel_entry *cel_entry;
> >  	const int cel_entries = size / sizeof(*cel_entry);
> > +	DECLARE_BITMAP(dcd_cmds, CXL_DCD_ENABLED_MAX) = {};
> >  	struct device *dev = mds->cxlds.dev;
> >  	int i, ro_cmds = 0, wr_cmds = 0;
> >  
> > @@ -785,11 +818,22 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
> >  			enabled++;
> >  		}
> >  
> > +		if (cxl_is_dcd_command(opcode)) {
> > +			cxl_set_dcd_cmd_enabled(opcode, dcd_cmds);
> > +			enabled++;
> > +		}
> > +
> >  		dev_dbg(dev, "Opcode 0x%04x %s\n", opcode,
> >  			enabled ? "enabled" : "unsupported by driver");
> >  	}
> >  
> >  	set_features_cap(cxl_mbox, ro_cmds, wr_cmds);
> > +	/*
> > +	 * Disabled until event handling implemented.
> > +	 */
> > +	if (cxl_verify_dcd_cmds(dcd_cmds))
> > +		dev_dbg(dev, "Device supports DCD; capability disabled\n");
> > +	mds->dcd_supported = false;
> >  }  
> 
> When I view how this lands in the entirety of cxl_walk_cel() it seems
> to needlessly be different from the pattern set by poison and security
> handling in the same function and structures. Could this follow that
> more closely?  Doing so would remove this 'tail' work here of checking
> and setting the boolean.
> 
> This appended diff touches patch 2 where it has the first caller, but
> pasting all here for simplicity - you'll apply per patch if you take it.
> 
> The bitmap lives in mds structure and it is already where the bool is now.
> The only piece of the poison and security pattern not worth copying
> yet is the wrapper struct cxl_poison_state and struct cxl_security_state
> exist because each grew a mutex and other per-class state, and DCD has
> none of that in this series (yet).
Hi Alison,

This rang a bell as I remembered Ira did it this way originally -
so I did some archaeology.

[PATCH v8 01/21] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
https://lore.kernel.org/all/67786b76a0c5_f58f294b1@dwillia2-xfh.jf.intel.com.notmuch/

Dan's point was that we need that infrastructure for poison and security
because a subset is a realistic possibility. For DCD today it's all or
nothing so why keep a bitmap around?

I agree there is merit in having all the command types handled the same
but perhaps it is clearer to just have a bool.  Of course when someone
adds another DCD command on the device side (which by definition will have
to be optional) then we will need to revisit.

Jonathan

> 
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index b18ea02ed2e6..27cfe0ee4752 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -172,7 +172,7 @@ static bool cxl_is_dcd_command(u16 opcode)
>         return (opcode >> 8) == CXL_MBOX_OP_DCD_CMDS;
>  }
> 
> -static void cxl_set_dcd_cmd_enabled(u16 opcode, unsigned long *cmd_mask)
> +static void cxl_set_dcd_cmd_enabled(unsigned long *cmd_mask, u16 opcode)
>  {
>         switch (opcode) {
>         case CXL_MBOX_OP_GET_DC_CONFIG:
> @@ -192,11 +192,6 @@ static void cxl_set_dcd_cmd_enabled(u16 opcode, unsigned long *cmd_mask)
>         }
>  }
> 
> -static bool cxl_verify_dcd_cmds(unsigned long *cmds_seen)
> -{
> -       return bitmap_full(cmds_seen, CXL_DCD_ENABLED_MAX);
> -}
> -
>  static bool cxl_is_poison_command(u16 opcode)
>  {
>  #define CXL_MBOX_OP_POISON_CMDS 0x43
> @@ -789,7 +784,6 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
>         struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
>         struct cxl_cel_entry *cel_entry;
>         const int cel_entries = size / sizeof(*cel_entry);
> -       DECLARE_BITMAP(dcd_cmds, CXL_DCD_ENABLED_MAX) = {};
>         struct device *dev = mds->cxlds.dev;
>         int i, ro_cmds = 0, wr_cmds = 0;
> 
> @@ -819,7 +813,7 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
>                 }
> 
>                 if (cxl_is_dcd_command(opcode)) {
> -                       cxl_set_dcd_cmd_enabled(opcode, dcd_cmds);
> +                       cxl_set_dcd_cmd_enabled(mds->dcd_enabled_cmds, opcode);
>                         enabled++;
>                 }
> 
> @@ -828,12 +822,6 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
>         }
> 
>         set_features_cap(cxl_mbox, ro_cmds, wr_cmds);
> -       /*
> -        * Disabled until event handling implemented.
> -        */
> -       if (cxl_verify_dcd_cmds(dcd_cmds))
> -               dev_dbg(dev, "Device supports DCD; capability disabled\n");
> -       mds->dcd_supported = false;
>  }
> 
>  static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_memdev_state *mds)
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 77f6417a1de7..bfa746c5a7c8 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -443,7 +443,7 @@ static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox)
>   * @partition_align_bytes: alignment size for partition-able capacity
>   * @active_volatile_bytes: sum of hard + soft volatile
>   * @active_persistent_bytes: sum of hard + soft persistent
> - * @dcd_supported: all DCD commands are supported
> + * @dcd_enabled_cmds: DCD commands the device enabled in the CEL
>   * @event: event log driver state
>   * @poison: poison driver state info
>   * @security: security driver state info
> @@ -463,7 +463,7 @@ struct cxl_memdev_state {
>         u64 partition_align_bytes;
>         u64 active_volatile_bytes;
>         u64 active_persistent_bytes;
> -       bool dcd_supported;
> +       DECLARE_BITMAP(dcd_enabled_cmds, CXL_DCD_ENABLED_MAX);
> 
>         struct cxl_event_state event;
>         struct cxl_poison_state poison;
> @@ -878,12 +878,16 @@ int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds);
> 
>  static inline bool cxl_dcd_supported(struct cxl_memdev_state *mds)
>  {
> -       return mds->dcd_supported;
> +       /*
> +        * Disabled until extent event handling is implemented.  Enable by
> +        * returning bitmap_full(mds->dcd_enabled_cmds, CXL_DCD_ENABLED_MAX).
> +        */
> +       return false;
>  }
> 
>  static inline void cxl_disable_dcd(struct cxl_memdev_state *mds)
>  {
> -       mds->dcd_supported = false;
> +       bitmap_zero(mds->dcd_enabled_cmds, CXL_DCD_ENABLED_MAX);
>  }
> 
>  int cxl_set_timestamp(struct cxl_memdev_state *mds);
> 
> >  
> snip


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

* Re: [PATCH v12 5/8] cxl/pci: Factor out interrupt policy check
  2026-07-31  8:48 ` [PATCH v12 5/8] cxl/pci: Factor out interrupt policy check Anisa Su
@ 2026-08-03 23:55   ` Alison Schofield
  0 siblings, 0 replies; 33+ messages in thread
From: Alison Schofield @ 2026-08-03 23:55 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Jonathan Cameron,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Dan Williams, Anisa Su

On Fri, Jul 31, 2026 at 01:48:10AM -0700, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Dynamic Capacity Devices (DCD) require event interrupts to process
> memory addition or removal.  BIOS may have control over non-DCD event
> processing.  DCD interrupt configuration needs to be separate from
> memory event interrupt configuration.
> 
> Factor out event interrupt setting validation.

Hi Anisa,

"Validation" seems like the wrong term here. The helper checks for
firmware ownership of the memory event logs. It is not validating
the event interrupt settings.

Which leads to my next comment -

snip
> 
>  
> +static bool cxl_event_validate_mem_policy(struct cxl_memdev_state *mds,
> +					  struct cxl_event_interrupt_policy *policy)
> +{
> +	if (cxl_event_int_is_fw(policy->info_settings) ||
> +	    cxl_event_int_is_fw(policy->warn_settings) ||
> +	    cxl_event_int_is_fw(policy->failure_settings) ||
> +	    cxl_event_int_is_fw(policy->fatal_settings)) {
> +		dev_err(mds->cxlds.dev,
> +			"FW still in control of Event Logs despite _OSC settings\n");
> +		return false;
> +	}
> +
> +	return true;
> +}

Why such a general name here? This helper seems to have one very specific purpose.

cxl_event_validate_mem_policy() doesn't validate the policy. It checks whether
firmware still owns the standard memory event logs despite _OSC granting OS
control. I'd prefer naming it after that condition rather than "validate".
Something like cxl_event_fw_owns_mem_logs() reads much closer to what the
code actually does.

--Alison


> +
>  static int cxl_event_config(struct pci_host_bridge *host_bridge,
>  			    struct cxl_memdev_state *mds, bool irq_avail)
>  {
> @@ -661,14 +676,8 @@ static int cxl_event_config(struct pci_host_bridge *host_bridge,
>  	if (rc)
>  		return rc;
>  
> -	if (cxl_event_int_is_fw(policy.info_settings) ||
> -	    cxl_event_int_is_fw(policy.warn_settings) ||
> -	    cxl_event_int_is_fw(policy.failure_settings) ||
> -	    cxl_event_int_is_fw(policy.fatal_settings)) {
> -		dev_err(mds->cxlds.dev,
> -			"FW still in control of Event Logs despite _OSC settings\n");
> +	if (!cxl_event_validate_mem_policy(mds, &policy))
>  		return -EBUSY;
> -	}
>  
>  	rc = cxl_event_config_msgnums(mds, &policy);
>  	if (rc)
> -- 
> 2.43.0
> 

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

* Re: [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device
  2026-07-31  8:48 ` [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
  2026-08-03 14:33   ` Richard Cheng
  2026-08-03 21:52   ` Alison Schofield
@ 2026-08-03 23:55   ` Jonathan Cameron
  2026-08-04  9:54     ` Anisa Su
  2 siblings, 1 reply; 33+ messages in thread
From: Jonathan Cameron @ 2026-08-03 23:55 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, 31 Jul 2026 01:48:07 -0700
Anisa Su <anisa.su887@gmail.com> wrote:

> From: Ira Weiny <iweiny@kernel.org>
> 
> Devices which optionally support Dynamic Capacity (DC) are configured
> via mailbox commands.  CXL r4.0 section 9.13.3 requires the host to issue
> the Get DC Configuration command in order to properly configure DCDs.
> Without the Get DC Configuration command DCD can't be supported.
> 
> Implement the DC mailbox commands as specified in CXL 4.0 section
> 8.2.10.9.9 (opcodes 48XXh) to read and store the DCD configuration
> information.  Disable DCD if an invalid configuration is found.
> 
> Linux has no support for more than one dynamic capacity partition.  Read
> and validate all the partitions but configure only the first partition
> as 'dynamic ram 1'.  Additional partitions can be added in the future if
> such a device ever materializes.  Additionally it is anticipated that no
> skips will be present from the end of the pmem partition.  Check for and
> disallow this configuration as well.
> 
> Based on an original patch by Navneet Singh.
> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>

Hi Anisa,

A few things to add to the reviews of others.

Jonathan
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 4790524c32a7..d79019fbd790 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -1352,6 +1352,197 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
>  	return -EBUSY;
>  }
>  
> +static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_array,
> +			u8 index, struct cxl_dc_partition *dev_part)
> +{
> +	u64 blk_size = le64_to_cpu(dev_part->block_size);
> +	u64 len = le64_to_cpu(dev_part->length);
> +
> +	part_array[index].start = le64_to_cpu(dev_part->base);
> +	part_array[index].size = le64_to_cpu(dev_part->decode_length);
> +	part_array[index].size *= CXL_CAPACITY_MULTIPLIER;

I'd be tempted to make it clear you are setting whole structure with

	part_array[index] = (struct cxl_dc_partition_info) {
		.start = le64_to_cpu(dev_part->base),
		.size = le64_to_cpu(dev_part->decode_length) * CXL_CAPACITY_MULTIPLIER,
//This is a case where I think readability justifies the long line.

	};
> +
> +	/* Check partitions are in increasing DPA order */
> +	if (index > 0) {
> +		struct cxl_dc_partition_info *prev_part = &part_array[index - 1];
> +
> +		if ((prev_part->start + prev_part->size) >
> +		     part_array[index].start) {

I'd be tempted to put the above on one line.

> +			dev_err(dev,
> +				"DPA ordering violation for DC partition %d and %d\n",
> +				index - 1, index);
> +			return -EINVAL;
> +		}
> +	}

Alison's comments seem valid to me and apply to rest of this function at least so I'll
not add anything else on that.

> +
> +}

...

> +/**
> + * cxl_dev_dc_identify() - Reads the dynamic capacity information from the
> + *                         device.
> + * @mbox: Mailbox to query
> + * @dc_info: The dynamic partition information to return
> + *
> + * Read Dynamic Capacity information from the device and return the partition
> + * information.
> + *
> + * Return: 0 if identify was executed successfully, -ERRNO on error.
> + *         on error only dc_info is left unchanged.
> + */
> +int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> +			struct cxl_dc_partition_info *dc_info)
> +{
> +	struct cxl_dc_partition_info partitions[CXL_MAX_DC_PARTITIONS];
> +	struct cxl_mbox_get_dc_config_out *dc_resp __free(kfree) = NULL;
> +	struct device *dev = mbox->host;
> +	u8 start_partition;
> +	u8 num_partitions;
> +	u8 partition_count;
> +	size_t dc_resp_size;
> +
> +	/* Bound requested number of partitions by mailbox payload size */
> +	partition_count = min_t(size_t, CXL_MAX_DC_PARTITIONS,
> +				(mbox->payload_size - sizeof(*dc_resp) -
> +				 sizeof(struct cxl_mbox_get_dc_config_tail)) /
> +				sizeof(struct cxl_dc_partition));
> +	dc_resp_size = struct_size(dc_resp, partition, partition_count) +
> +		       sizeof(struct cxl_mbox_get_dc_config_tail);
> +
> +	dc_resp = kmalloc(dc_resp_size, GFP_KERNEL);
> +	if (!dc_resp)
> +		return -ENOMEM;
> +
> +	/*
> +	 * Read and check all partition information for validity and potential
> +	 * debugging; see debug output in cxl_dc_check()
> +	 */
> +	start_partition = 0;
> +	num_partitions = 0;
> +	do {
> +		int rc, i, j;
> +
> +		rc = cxl_get_dc_config(mbox, start_partition, partition_count,
> +				       dc_resp, dc_resp_size);
> +		if (rc < 0) {
> +			dev_err(dev, "Failed to get DC config: %d\n", rc);
> +			return rc;
> +		}
> +
> +		if (rc == 0) {
> +			dev_err(dev,
> +				"Device reported %u partitions available but returned none at index %u\n",
> +				dc_resp->avail_partition_count, start_partition);
> +			return -EIO;
> +		}
> +
> +		num_partitions += rc;
> +
> +		if (num_partitions < 1 || num_partitions > CXL_MAX_DC_PARTITIONS) {

How could num_partitions end up either 0 or negative given we added at least 1 and
I don't see a path by which it will get decremented. Roll over isn't a thing given
we would have failed the other check on a previous loop.


> +			dev_err(dev, "Invalid num of dynamic capacity partitions %d\n",
> +				num_partitions);
> +			return -EINVAL;
> +		}
> +
> +		for (i = start_partition, j = 0; i < num_partitions; i++, j++) {
> +			rc = cxl_dc_check(dev, partitions, i,
> +					  &dc_resp->partition[j]);
> +			if (rc)
> +				return rc;
> +		}
> +
> +		start_partition = num_partitions;
> +
> +	} while (num_partitions < dc_resp->avail_partition_count);
> +
> +	/* Return 1st partition */
> +	dc_info->start = partitions[0].start;
> +	dc_info->size = partitions[0].size;
> +	dev_dbg(dev, "Returning partition 0 %llu size %llu\n",
> +		dc_info->start, dc_info->size);
> +
> +	return 0;
> +}



> @@ -1422,6 +1613,35 @@ int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_get_dirty_count, "CXL");
>  
> +void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> +{
> +	struct cxl_dc_partition_info dc_info = { 0 };

I think the preferred way of doing this is heading for the C23 option of

  = { };

but CXL has plenty of { 0 }; so fair enough if you prefer to keep it this
way.  For anyone concerned about older compilers there is a specific selftest
for { } doing what we expect even if the structure has holes.


> +	struct device *dev = mds->cxlds.dev;
> +	int rc;
> +
> +	rc = cxl_dev_dc_identify(&mds->cxlds.cxl_mbox, &dc_info);
> +	if (rc) {
> +		dev_warn(dev,
> +			 "Failed to read Dynamic Capacity config: %d\n", rc);

Fun question on whether we consider a DCD capable device failing such
a basic thing as a warning level thing or a hard failure thing.

To me DCD is a important core device feature (unlikely maybe fwctl, features etc)
so we should be failing. 

We should definitely be returning an error from here, even if ultimate
we decide not to act on that error in cxl_pci_probe().

> +		cxl_disable_dcd(mds);
> +		return;
> +	}
> +
> +	/* Skips between pmem and the dynamic partition are not supported */
> +	if (dc_info.start != info->size) {
> +		dev_warn(dev,
> +			 "Dynamic Capacity skip from pmem not supported\n");
> +		cxl_disable_dcd(mds);
> +		return;

In this case not failing hard is probably fair if that is a specification
allowed feature we are just not choosing to support for now.

> +	}
> +
> +	info->size += dc_info.size;
> +	dev_dbg(dev, "Adding dynamic ram partition 1; %llu size %llu\n",
> +		dc_info.start, dc_info.size);
> +	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");



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

* Re: [PATCH v12 6/8] cxl/mem: Configure dynamic capacity interrupts
  2026-07-31  8:48 ` [PATCH v12 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
@ 2026-08-04  0:13   ` Jonathan Cameron
  2026-08-04  0:34   ` Alison Schofield
  1 sibling, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2026-08-04  0:13 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, 31 Jul 2026 01:48:11 -0700
Anisa Su <anisa.su887@gmail.com> wrote:

> From: Ira Weiny <iweiny@kernel.org>
> 
> Dynamic Capacity Devices (DCD) support extent change notifications
> through the event log mechanism.  The interrupt mailbox commands were
> extended in CXL 3.1 to support these notifications.  Firmware can't
> configure DCD events to be FW controlled but can retain control of
> memory events.
> 
> Configure DCD event log interrupts on devices supporting dynamic
> capacity.  Disable DCD if interrupts are not supported.
> 
> Care is taken to preserve the interrupt policy set by the FW if FW first
> has been selected by the BIOS.
> 
> Based on an original patch by Navneet Singh.
> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>

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

* Re: [PATCH v12 8/8] cxl/core: Enforce partition order/simplify partition calls
  2026-07-31  8:48 ` [PATCH v12 8/8] cxl/core: Enforce partition order/simplify partition calls Anisa Su
@ 2026-08-04  0:16   ` Jonathan Cameron
  2026-08-04  1:37   ` Alison Schofield
  1 sibling, 0 replies; 33+ messages in thread
From: Jonathan Cameron @ 2026-08-04  0:16 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, 31 Jul 2026 01:48:13 -0700
Anisa Su <anisa.su887@gmail.com> wrote:

> From: Ira Weiny <iweiny@kernel.org>
> 
> Device partitions have an implied order which is made more complex by
> the addition of a dynamic partition
> 
> Remove the ram special case information calls in favor of generic calls
> with a check ahead of time to ensure the preservation of the implied
> partition order.
> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Seems fine
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>

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

* Re: [PATCH v12 6/8] cxl/mem: Configure dynamic capacity interrupts
  2026-07-31  8:48 ` [PATCH v12 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
  2026-08-04  0:13   ` Jonathan Cameron
@ 2026-08-04  0:34   ` Alison Schofield
  1 sibling, 0 replies; 33+ messages in thread
From: Alison Schofield @ 2026-08-04  0:34 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Jonathan Cameron,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, Jul 31, 2026 at 01:48:11AM -0700, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Dynamic Capacity Devices (DCD) support extent change notifications
> through the event log mechanism.  The interrupt mailbox commands were
> extended in CXL 3.1 to support these notifications.  Firmware can't
> configure DCD events to be FW controlled but can retain control of
> memory events.
> 
> Configure DCD event log interrupts on devices supporting dynamic
> capacity.  Disable DCD if interrupts are not supported.
> 
> Care is taken to preserve the interrupt policy set by the FW if FW first
> has been selected by the BIOS.

Hi Anisa,

I gave one comment in response to a Sashiko comment, wrt 'drain'.
Another below-


> +
> +	/*
> +	 * A CXL 3.0+ device can carry dcd_settings field without DCD command
> +	 * support, so size the request by the device's policy_size and only
> +	 * enable the DCD interrupt when DCD commands are supported.
> +	 */
> +	if (cxl_dcd_supported(mds))
> +		policy->dcd_settings = CXL_INT_MSI_MSIX;
>  
>  	mbox_cmd = (struct cxl_mbox_cmd) {
>  		.opcode = CXL_MBOX_OP_SET_EVT_INT_POLICY,
>  		.payload_in = policy,
> -		.size_in = sizeof(*policy),
> +		.size_in = policy_size,
>  	};

Can you help me understand what happens if policy_size is only 4 bytes.
dcd_settings is never sent in the Set command. How do we know cxl_irqsetup()
isn't using a value the device never accepted?


>  	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
> @@ -596,7 +635,7 @@ static int cxl_event_config_msgnums(struct cxl_memdev_state *mds,
>  	}
>  
>  	/* Retrieve final interrupt settings */
> -	return cxl_event_get_int_policy(mds, policy);
> +	return cxl_event_get_int_policy(mds, policy, NULL);
>  }

snip


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

* Re: [PATCH v12 7/8] cxl/core: Return endpoint decoder information from region search
  2026-07-31  8:48 ` [PATCH v12 7/8] cxl/core: Return endpoint decoder information from region search Anisa Su
@ 2026-08-04  1:02   ` Alison Schofield
  0 siblings, 0 replies; 33+ messages in thread
From: Alison Schofield @ 2026-08-04  1:02 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Jonathan Cameron,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, Jul 31, 2026 at 01:48:12AM -0700, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> cxl_dpa_to_region() finds the region from a <DPA, device> tuple.
> The search involves finding the device endpoint decoder as well.
> 
> Dynamic capacity extent processing uses the endpoint decoder HPA
> information to calculate the HPA offset.  In addition, well behaved
> extents should be contained within an endpoint decoder.
> 
> Return the endpoint decoder found to be used in subsequent DCD code.

Hi Anisa,

I realize this series is intentionally preparatory work for the next set,
and that's often a reasonable way to stage things. However, this patch
is an example of where splitting the work makes review harder because
the new interface is no longer paired with its first user. I also
realize this patch carries review tags from the earlier posting, but
those reviewers (me included ;)) were able to evaluate the API together
with its caller.

I went back and looked at the a v10 series where the caller is present,
and the callsite looks fine. But I don't think reviewers should need
to consult an earlier series to understand a new API, nor assume the
caller will remain unchanged by the time the next series is posted.

Can we either -

1. Drop this change from this  prep series and introduce it together
with its first caller in the next series, or
2. Keep it here and but clearly document the API contract, that *cxled
is only valid when the returned cxlr is not NULL.

As presented, no caller requests cxled, so the intended API contract
cannot really be verified from this patch alone.

-- Alison

/BTW - I think other patches in this prep set carry similar issues
but this one popped for me because of the subject. I headed into
it thinking a quick ACK, but no luck.

> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>
> Reviewed-by: Jonathan Cameron <jic23@kernel.org>
> Reviewed-by: Fan Ni <nifan.cxl@gmail.com>
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> Reviewed-by: Li Ming <ming.li@zohomail.com>
> Reviewed-by: Alison Schofield <alison.schofield@intel.com>
> ---
>  drivers/cxl/core/core.h   | 6 ++++--
>  drivers/cxl/core/mbox.c   | 2 +-
>  drivers/cxl/core/memdev.c | 4 ++--
>  drivers/cxl/core/region.c | 8 +++++++-
>  4 files changed, 14 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 07555ae63859..e4bd220faa92 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -47,7 +47,8 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
>  int cxl_region_init(void);
>  void cxl_region_exit(void);
>  int cxl_get_poison_by_endpoint(struct cxl_port *port);
> -struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
> +struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa,
> +				     struct cxl_endpoint_decoder **cxled);
>  u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
>  		   u64 dpa);
>  int devm_cxl_add_dax_region(struct cxl_region *cxlr);
> @@ -61,7 +62,8 @@ static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
>  	return ULLONG_MAX;
>  }
>  static inline
> -struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa)
> +struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa,
> +				     struct cxl_endpoint_decoder **cxled)
>  {
>  	return NULL;
>  }
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index a6cdea9f4080..b18ea02ed2e6 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -969,7 +969,7 @@ void cxl_event_trace_record(struct cxl_memdev *cxlmd,
>  		guard(rwsem_read)(&cxl_rwsem.dpa);
>  
>  		dpa = le64_to_cpu(evt->media_hdr.phys_addr) & CXL_DPA_MASK;
> -		cxlr = cxl_dpa_to_region(cxlmd, dpa);
> +		cxlr = cxl_dpa_to_region(cxlmd, dpa, NULL);
>  		if (cxlr) {
>  			u64 cache_size = cxlr->params.cache_size;
>  
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index 33a3d2e7b13a..1565a5cf0f32 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -317,7 +317,7 @@ int cxl_inject_poison_locked(struct cxl_memdev *cxlmd, u64 dpa)
>  	if (rc)
>  		return rc;
>  
> -	cxlr = cxl_dpa_to_region(cxlmd, dpa);
> +	cxlr = cxl_dpa_to_region(cxlmd, dpa, NULL);
>  	if (cxlr)
>  		dev_warn_once(cxl_mbox->host,
>  			      "poison inject dpa:%#llx region: %s\n", dpa,
> @@ -386,7 +386,7 @@ int cxl_clear_poison_locked(struct cxl_memdev *cxlmd, u64 dpa)
>  	if (rc)
>  		return rc;
>  
> -	cxlr = cxl_dpa_to_region(cxlmd, dpa);
> +	cxlr = cxl_dpa_to_region(cxlmd, dpa, NULL);
>  	if (cxlr)
>  		dev_warn_once(cxl_mbox->host,
>  			      "poison clear dpa:%#llx region: %s\n", dpa,
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 1e211542b6b6..ec5e5b7090cf 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3012,6 +3012,7 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port)
>  struct cxl_dpa_to_region_context {
>  	struct cxl_region *cxlr;
>  	u64 dpa;
> +	struct cxl_endpoint_decoder *cxled;
>  };
>  
>  static int __cxl_dpa_to_region(struct device *dev, void *arg)
> @@ -3045,11 +3046,13 @@ static int __cxl_dpa_to_region(struct device *dev, void *arg)
>  			dev_name(dev));
>  
>  	ctx->cxlr = cxlr;
> +	ctx->cxled = cxled;
>  
>  	return 1;
>  }
>  
> -struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa)
> +struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa,
> +				     struct cxl_endpoint_decoder **cxled)
>  {
>  	struct cxl_dpa_to_region_context ctx;
>  	struct cxl_port *port = cxlmd->endpoint;
> @@ -3063,6 +3066,9 @@ struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa)
>  	if (cxl_num_decoders_committed(port))
>  		device_for_each_child(&port->dev, &ctx, __cxl_dpa_to_region);
>  
> +	if (cxled)
> +		*cxled = ctx.cxled;
> +
>  	return ctx.cxlr;
>  }
>  
> -- 
> 2.43.0
> 

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

* Re: [PATCH v12 8/8] cxl/core: Enforce partition order/simplify partition calls
  2026-07-31  8:48 ` [PATCH v12 8/8] cxl/core: Enforce partition order/simplify partition calls Anisa Su
  2026-08-04  0:16   ` Jonathan Cameron
@ 2026-08-04  1:37   ` Alison Schofield
  1 sibling, 0 replies; 33+ messages in thread
From: Alison Schofield @ 2026-08-04  1:37 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Jonathan Cameron,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, Jul 31, 2026 at 01:48:13AM -0700, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Device partitions have an implied order which is made more complex by
> the addition of a dynamic partition
> 
> Remove the ram special case information calls in favor of generic calls
> with a check ahead of time to ensure the preservation of the implied
> partition order.

Hi Anisa,

I think the changelog needs more explanation here. It says there is an
"implied partition order", that is made "more complex by the addition
of a dynamic partition" but never explains what that order is, where it
comes from, or that this patch starts enforcing it.

I jumped to the diff thinking it must be there, but nope. If I 
am remembering correctly we've had ascii art for this layouts
so I'm surprised that's not here.

After this patch, the code relies on the numeric ordering of
enum cxl_partition_mode. That seems worth documenting, perhaps alongside
the enum itself.

A bit more below-

skip
> @@ -457,6 +457,7 @@ static const char *cxl_mode_name(enum cxl_partition_mode mode)
>  int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
>  {
>  	struct device *dev = cxlds->dev;
> +	int i;
>  
>  	guard(rwsem_write)(&cxl_rwsem.dpa);
>  
> @@ -469,9 +470,17 @@ int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
>  		return 0;
>  	}
>  
> +	/* Verify partitions are in expected order. */
> +	for (i = 1; i < info->nr_partitions; i++) {
Just 'int i' above. Sharing that index is not worth the extra diff.

> +		if (info->part[i].mode < info->part[i - 1].mode) {
> +			dev_err(dev, "Partition order mismatch\n");

A bit minimalist err message. Do we have more useful info to add?


> +			return -EINVAL;
> +		}
> +	}
> +
>  	cxlds->dpa_res = DEFINE_RES_MEM(0, info->size);
>  
> -	for (int i = 0; i < info->nr_partitions; i++) {
> +	for (i = 0; i < info->nr_partitions; i++) {
>  		const struct cxl_dpa_part_info *part = &info->part[i];
>  		int rc;

snip


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

* Re: [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device
  2026-08-03 21:52   ` Alison Schofield
@ 2026-08-04  9:12     ` Anisa Su
  2026-08-04 16:00       ` Gregory Price
  0 siblings, 1 reply; 33+ messages in thread
From: Anisa Su @ 2026-08-04  9:12 UTC (permalink / raw)
  To: Alison Schofield
  Cc: Anisa Su, linux-cxl, nvdimm, linux-kernel, Dave Jiang,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim

On Mon, Aug 03, 2026 at 02:52:17PM -0700, Alison Schofield wrote:
> On Fri, Jul 31, 2026 at 01:48:07AM -0700, Anisa Su wrote:
> > From: Ira Weiny <iweiny@kernel.org>
> > 
> > Devices which optionally support Dynamic Capacity (DC) are configured
> > via mailbox commands.  CXL r4.0 section 9.13.3 requires the host to issue
> > the Get DC Configuration command in order to properly configure DCDs.
> > Without the Get DC Configuration command DCD can't be supported.
> > 
> > Implement the DC mailbox commands as specified in CXL 4.0 section
> > 8.2.10.9.9 (opcodes 48XXh) to read and store the DCD configuration
> > information.  Disable DCD if an invalid configuration is found.
> > 
> > Linux has no support for more than one dynamic capacity partition.  Read
> > and validate all the partitions but configure only the first partition
> > as 'dynamic ram 1'.  Additional partitions can be added in the future if
> > such a device ever materializes.  Additionally it is anticipated that no
> > skips will be present from the end of the pmem partition.  Check for and
> > disallow this configuration as well.
> 
> Hi Anisa,
> 
> "Additionally, it is anticipated that no skips will be present.." sounds
> woogedy. The paragraph starts with a Linux limitation of only one partition.
> and next mentions skips. Is that a Linux implementation rule, a spec
> rule, or something else?
> 

Sorry for the lack of clarity. The commit message has been revised to
make it clear that DPA gaps between partitions are a Linux rule. The
spec permits a gap: the DC region base is device assigned and only has to be
256MB aligned (r4.0 Table 8-347). It now reads:

    Linux has no support for more than one dynamic capacity partition. Read
    and validate all the partitions but configure only the first partition
    as 'dynamic ram 1'. Additional partitions can be added in the future if
    such a device ever materializes. Linux additionally requires partitions
    to be contiguous. Therefore, check for and disallow any gap between the
    end of the pmem partition and the first DC partition, as well as gaps
    between the remaining DC partitions.

> See below wrt validation checks.
> 
> 
> > 
> > Based on an original patch by Navneet Singh.
> > 
> > Signed-off-by: Ira Weiny <iweiny@kernel.org>
> > Signed-off-by: Anisa Su <anisa.su@samsung.com>
> > Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> > Tested-by: Junhee Park <jh9934.park@samsung.com>
> > Tested-by: Heesoo Kim <habil.kim@samsung.com>
> > 
> > ---
> > Changes:
> > 1. mbox.c: bound the Get DC Config request by the negotiated mailbox
> >    payload. Requesting all CXL_MAX_DC_PARTITIONS (8) needs a 328-byte
> >    response, which exceeds a spec-minimum 256-byte mailbox and fails
> >    -E2BIG, disabling DCD. Cap partition_count per call to what the
> >    payload holds and read the rest via the existing start_partition
> >    loop.
> > ---
> >  drivers/cxl/core/hdm.c  |   2 +
> >  drivers/cxl/core/mbox.c | 220 ++++++++++++++++++++++++++++++++++++++++
> >  drivers/cxl/cxlmem.h    |  55 ++++++++++
> >  drivers/cxl/pci.c       |   3 +
> >  include/cxl/cxl.h       |   3 +-
> >  5 files changed, 282 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> > index 0c80b76a5f9b..0ef076c08ed2 100644
> > --- a/drivers/cxl/core/hdm.c
> > +++ b/drivers/cxl/core/hdm.c
> > @@ -446,6 +446,8 @@ static const char *cxl_mode_name(enum cxl_partition_mode mode)
> >  		return "ram";
> >  	case CXL_PARTMODE_PMEM:
> >  		return "pmem";
> > +	case CXL_PARTMODE_DYNAMIC_RAM_1:
> > +		return "dynamic_ram_1";
> >  	default:
> >  		return "";
> >  	};
> > diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> > index 4790524c32a7..d79019fbd790 100644
> > --- a/drivers/cxl/core/mbox.c
> > +++ b/drivers/cxl/core/mbox.c
> > @@ -1352,6 +1352,197 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
> >  	return -EBUSY;
> >  }
> >  
> > +static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_array,
> > +			u8 index, struct cxl_dc_partition *dev_part)
> > +{
> > +	u64 blk_size = le64_to_cpu(dev_part->block_size);
> > +	u64 len = le64_to_cpu(dev_part->length);
> > +
> > +	part_array[index].start = le64_to_cpu(dev_part->base);
> > +	part_array[index].size = le64_to_cpu(dev_part->decode_length);
> > +	part_array[index].size *= CXL_CAPACITY_MULTIPLIER;
> > +
> > +	/* Check partitions are in increasing DPA order */
> > +	if (index > 0) {
> > +		struct cxl_dc_partition_info *prev_part = &part_array[index - 1];
> > +
> > +		if ((prev_part->start + prev_part->size) >
> > +		     part_array[index].start) {
> > +			dev_err(dev,
> > +				"DPA ordering violation for DC partition %d and %d\n",
> > +				index - 1, index);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (part_array[index].size == 0 || len == 0 ||
> > +	    part_array[index].size < len || !IS_ALIGNED(len, blk_size)) {
> > +		dev_err(dev, "DC partition %d invalid length; size %llu len %llu blk size %llu\n",
> > +			index, part_array[index].size, len, blk_size);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (blk_size == 0 || blk_size % CXL_DCD_BLOCK_LINE_SIZE ||
> > +	    !is_power_of_2(blk_size)) {
> > +		dev_err(dev, "DC partition %d invalid block size %llu\n",
> > +			index, blk_size);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (!IS_ALIGNED(part_array[index].start, SZ_256M) ||
> > +	    !IS_ALIGNED(part_array[index].start, blk_size)) {
> > +		dev_err(dev, "DC partition %d invalid start %llu blk size %llu\n",
> > +			index, part_array[index].start, blk_size);
> > +		return -EINVAL;
> > +	}
> > +
> > +	dev_dbg(dev, "DC partition %d start %llu size %llu blk_size: %llu\n",
> > +		index, part_array[index].start, part_array[index].size,
> > +		blk_size);
> > +
> > +	return 0;
> > +}
> 
> 
> cxl_dc_check() above needs rework.
> Please go back through it against CXL r4.0 Tables 8-346 and 8-347 and confirm
> that each constraint the code enforces is the constraint the spec actually
> states. The check need to run in dependency order, and then each failure
> needs to report the specific field or relationship that is invalid.
> 
Done, double checked both tables. The function now validates in
dependency order with one message per field:

    /* Block size is a power of 2 and a multiple of 40h */
    if (blk_size == 0 || !is_power_of_2(blk_size) ||
        blk_size % CXL_DCD_BLOCK_LINE_SIZE) {
            dev_err(dev, "DC partition %d invalid block size %#llx\n", ...);

*** note sashiko pointed out is_power_of_2(unsigned long n) truncates
on 32-bit builds, so is_power_of_2 is replaced with (blk_size & (blk_size - 1))

    if (part_array[index].size == 0)   -> "zero decode length"
    if (len == 0)                      -> "zero length"
    if (len > part_array[index].size)  -> "length %#llx exceeds decode length %#llx"
    if (!IS_ALIGNED(len, blk_size))    -> "length %#llx not a multiple of block size %#llx"
    if (!IS_ALIGNED(start, SZ_256M))   -> "base %#llx not aligned to 256M"

    /* Partitions must be contiguous and in increasing DPA order */

Point by point details below.

> A terse list of what looks wrong from here. (I have not chased each one)
> 
> - blk_size is used as the alignment in IS_ALIGNED(len, blk_size) and
>   IS_ALIGNED(start, blk_size) before it is validated.

Fixed, block size is validated first now.

To clarify the exact fix and reason:

In v12, a block size of 0 would still be rejected, but print "invalid length"
instead of "invalid block size:

	if (part_array[index].size == 0 || len == 0 ||
          part_array[index].size < len || !IS_ALIGNED(len, blk_size)) {
              dev_err(dev, "DC partition %d invalid length; size %llu len %llu blk size %
              return -EINVAL;
      }

!IS_ALIGNED(len, blk_size) = !(len & ~0ULL)

len & ~0ULL == 0 iff. len == 0. Since len is checked before the
alignement check, then !(len &0ULL) evaluates to true and prints
"invalid length". By moving the blk_size check above, the correct error
message "invalid block size" is printed for blk_size == 0.

> - Four unrelated conditions share one "invalid length" message.

Fixed, four separate checks, each naming its own field.

 if (part_array[index].size == 0)   -> "zero decode length"
 if (len == 0)                      -> "zero length"
 if (len > part_array[index].size)  -> "length %#llx exceeds decode length %#llx"
 if (!IS_ALIGNED(len, blk_size))    -> "length %#llx not a multiple of block size %#llx

> - The start check combines the 256 MiB and block-size requirements, so the
>   message cannot say which alignment failed.

Fixed: Table 8-347 requires the base to be 256MB aligned, and states the block
size alignment for the starting DPAs of extents, **not** for the region
base. So the start check now only requires 256 MiB alignment.

> - "DPA ordering violation" is really an overlap check, and gaps appear to be
>   allowed.

Fixed to tighten the check. One thing to note: only DC partition 0 is configured,
so this check would mean DCD is disabled for a device with a usable
partition 0, but which has gaps between later partitions. If
multi-partition support is landed later, gaps would be caught by
cxl_dpa_check when the partition is configured. Should
cxl_dev_dc_identify() only check for spec compliance and defer the
Linux rule of no gaps between partitions up to cxl_dpa_check()?

	/* Partitions must be contiguous and in increasing DPA order */
	for (int prev = index - 1; prev >= 0; prev--) {
		struct cxl_dc_partition_info *prev_part = &part_array[prev];

		if (prev_part->size == 0)
			continue;

		if (prev_part->start + prev_part->size != part_array[index].start) {
			dev_err(dev,
				"DC partition %d and %d are not contiguous\n",
				prev, index);
			return -EINVAL;
		}
		break;
	}
> - avail_partition_count is never checked against the 1 to 8 range Table 8-346
>   requires.

Added in cxl_get_dc_config(), before the count is used as the bound for
the read loop:

    /* A DCD reports between 1 and 8 partitions */
    if (dc_resp->avail_partition_count == 0 ||
        dc_resp->avail_partition_count > CXL_MAX_DC_PARTITIONS) {
            dev_err(mbox->host,
                    "Device reported %u available DC partitions, expected 1 to %u\n",
                    dc_resp->avail_partition_count, CXL_MAX_DC_PARTITIONS);
            return -EIO;
    }

This also answers the Sashiko report on this patch, which asked whether
a device with 0 partitions is failed incorrectly. Table 8-346 says "A
DCD shall report between 1 and 8 regions. All other values are
reserved.", so 0 is not a legal value and failing is the correct
behavior.

> - cxl_configure_dcd()'s "skip from pmem not supported" also fires on overlap,
>   and prints neither address.

Split into two checks that print both addresses:

    if (dc_info.start < info->size)
            dev_err(dev, "DC partition 0 base %#llx overlaps static capacity ending at %#llx\n", ...);

    /* A gap between static capacity and the DC partition is not supported */
    if (dc_info.start > info->size)
            dev_warn(dev, "DC partition 0 base %#llx leaves a gap from static capacity ending at %#llx\n", ...);

An overlap is the device contradicting itself so it is an error, a gap
is legal but unsupported so it stays a warning. Both still disable DCD.

The message also said "pmem capacity", which I think is less accurate
than "static capacity", since we could have a volatile only device with
no pmem partition.

> - min_out = 8 is a bare constant for the header ahead of the flex array

Fixed. Uses sizeof(*dc_resp), which for a flex array struct is exactly the fixed
header.

> - DPA addresses and sizes print in decimal, rest of CXL uses hex 

Converted to hex -- Partition indices stay %d.

> - The Table 8-178, 8-179 and 8-180 citations are not r4.0 numbering
> 
Sorry, my bad!

Corrected to Table 8-345 (input payload), 8-346 (output payload) and 8-347
(region configuration). I also re-checked the Table 8-308 citation in
patch 1 and that one is correct.

> And RichardC called out this one in his prior review -
> - Zero geometry fields are legal per Table 8-347, as already raised on the
>   list. Agreed, and the fallout of skipping rather than rejecting needs
>   working through.
> 

Fixed: New helper, and cxl_dc_check() returns early leaving the array
entry empty:

    static bool cxl_dc_partition_unavailable(struct cxl_dc_partition *dev_part)
    {
            return !le64_to_cpu(dev_part->decode_length) &&
                   !le64_to_cpu(dev_part->length) &&
                   !le64_to_cpu(dev_part->block_size);
    }

Regarding the fallout of skipping:
- Only those three fields are tested. Table 8-347 states the report-0
  behaviour for decode length, length and block size only, so a stale
  base or DSMAD handle on an unavailable region is not an error.
- If only some of the three are zero, falls through to the individual
  blk_size, len, and decode_len checks above and rejects the partition.
- The contiguity check checks against the last non-empty entry rather
  than index - 1, so a skipped partition in the middle cannot mask an
  overlap between the partitions either side of it.
- cxl_dev_dc_identify() returns -ENODEV if partition 0 itself is
  unavailable, since Linux only wires up partition 0. No message there,
  cxl_dc_check() has already logged the skip.

> -- Alison
> 
Thanks,
Anisa
> 
> > +
> > +/* Returns the number of partitions in dc_resp or -ERRNO */
> > +static int cxl_get_dc_config(struct cxl_mailbox *mbox, u8 start_partition,
> > +			     u8 partition_count,
> > +			     struct cxl_mbox_get_dc_config_out *dc_resp,
> > +			     size_t dc_resp_size)
> > +{
> > +	struct cxl_mbox_get_dc_config_in get_dc = (struct cxl_mbox_get_dc_config_in) {
> > +		.partition_count = partition_count,
> > +		.start_partition_index = start_partition,
> > +	};
> > +	struct cxl_mbox_cmd mbox_cmd = (struct cxl_mbox_cmd) {
> > +		.opcode = CXL_MBOX_OP_GET_DC_CONFIG,
> > +		.payload_in = &get_dc,
> > +		.size_in = sizeof(get_dc),
> > +		.size_out = dc_resp_size,
> > +		.payload_out = dc_resp,
> > +		.min_out = 8,
> > +	};
> > +	size_t expected_sz;
> > +	int rc;
> > +
> > +	rc = cxl_internal_send_cmd(mbox, &mbox_cmd);
> > +	if (rc < 0)
> > +		return rc;
> > +
> > +	if (dc_resp->partitions_returned > partition_count) {
> > +		dev_err(mbox->host, "Device returned %u partitions, requested %u\n",
> > +			dc_resp->partitions_returned, partition_count);
> > +		return -EIO;
> > +	}
> > +
> > +	/*
> > +	 * The payload carries trailing extent/tag count fields after the
> > +	 * partition array (CXL r4.0 Table 8-179) which the driver ignores, so
> > +	 * the response is at least, not exactly, expected_sz.
> > +	 */
> > +	expected_sz = struct_size(dc_resp, partition,
> > +				  dc_resp->partitions_returned);
> > +
> > +	if (mbox_cmd.size_out < expected_sz) {
> > +		dev_err(mbox->host,
> > +			"Payload size %zu less than expected %zu for %u partitions\n",
> > +			mbox_cmd.size_out,
> > +			expected_sz,
> > +			dc_resp->partitions_returned);
> > +		return -EIO;
> > +	}
> > +
> > +	dev_dbg(mbox->host, "Read %d/%d DC partitions\n",
> > +		dc_resp->partitions_returned, dc_resp->avail_partition_count);
> > +	return dc_resp->partitions_returned;
> > +}
> > +
> > +/**
> > + * cxl_dev_dc_identify() - Reads the dynamic capacity information from the
> > + *                         device.
> > + * @mbox: Mailbox to query
> > + * @dc_info: The dynamic partition information to return
> > + *
> > + * Read Dynamic Capacity information from the device and return the partition
> > + * information.
> > + *
> > + * Return: 0 if identify was executed successfully, -ERRNO on error.
> > + *         on error only dc_info is left unchanged.
> > + */
> > +int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> > +			struct cxl_dc_partition_info *dc_info)
> > +{
> > +	struct cxl_dc_partition_info partitions[CXL_MAX_DC_PARTITIONS];
> > +	struct cxl_mbox_get_dc_config_out *dc_resp __free(kfree) = NULL;
> > +	struct device *dev = mbox->host;
> > +	u8 start_partition;
> > +	u8 num_partitions;
> > +	u8 partition_count;
> > +	size_t dc_resp_size;
> > +
> > +	/* Bound requested number of partitions by mailbox payload size */
> > +	partition_count = min_t(size_t, CXL_MAX_DC_PARTITIONS,
> > +				(mbox->payload_size - sizeof(*dc_resp) -
> > +				 sizeof(struct cxl_mbox_get_dc_config_tail)) /
> > +				sizeof(struct cxl_dc_partition));
> > +	dc_resp_size = struct_size(dc_resp, partition, partition_count) +
> > +		       sizeof(struct cxl_mbox_get_dc_config_tail);
> > +
> > +	dc_resp = kmalloc(dc_resp_size, GFP_KERNEL);
> > +	if (!dc_resp)
> > +		return -ENOMEM;
> > +
> > +	/*
> > +	 * Read and check all partition information for validity and potential
> > +	 * debugging; see debug output in cxl_dc_check()
> > +	 */
> needless comment
> 
> 
> > +	start_partition = 0;
> > +	num_partitions = 0;
> > +	do {
> > +		int rc, i, j;
> > +
> > +		rc = cxl_get_dc_config(mbox, start_partition, partition_count,
> > +				       dc_resp, dc_resp_size);
> > +		if (rc < 0) {
> > +			dev_err(dev, "Failed to get DC config: %d\n", rc);
> > +			return rc;
> > +		}
> > +
> > +		if (rc == 0) {
> > +			dev_err(dev,
> > +				"Device reported %u partitions available but returned none at index %u\n",
> > +				dc_resp->avail_partition_count, start_partition);
> > +			return -EIO;
> > +		}
> > +
> > +		num_partitions += rc;
> > +
> > +		if (num_partitions < 1 || num_partitions > CXL_MAX_DC_PARTITIONS) {
> > +			dev_err(dev, "Invalid num of dynamic capacity partitions %d\n",
> > +				num_partitions);
> > +			return -EINVAL;
> > +		}
> > +
> > +		for (i = start_partition, j = 0; i < num_partitions; i++, j++) {
> > +			rc = cxl_dc_check(dev, partitions, i,
> > +					  &dc_resp->partition[j]);
> > +			if (rc)
> > +				return rc;
> > +		}
> > +
> > +		start_partition = num_partitions;
> > +
> > +	} while (num_partitions < dc_resp->avail_partition_count);
> > +
> > +	/* Return 1st partition */
> > +	dc_info->start = partitions[0].start;
> > +	dc_info->size = partitions[0].size;
> > +	dev_dbg(dev, "Returning partition 0 %llu size %llu\n",
> > +		dc_info->start, dc_info->size);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
> > +
> >  static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode)
> >  {
> >  	int i = info->nr_partitions;
> > @@ -1422,6 +1613,35 @@ int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
> >  }
> >  EXPORT_SYMBOL_NS_GPL(cxl_get_dirty_count, "CXL");
> >  
> > +void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> > +{
> > +	struct cxl_dc_partition_info dc_info = { 0 };
> > +	struct device *dev = mds->cxlds.dev;
> > +	int rc;
> > +
> > +	rc = cxl_dev_dc_identify(&mds->cxlds.cxl_mbox, &dc_info);
> > +	if (rc) {
> > +		dev_warn(dev,
> > +			 "Failed to read Dynamic Capacity config: %d\n", rc);
> > +		cxl_disable_dcd(mds);
> > +		return;
> > +	}
> > +
> > +	/* Skips between pmem and the dynamic partition are not supported */
> > +	if (dc_info.start != info->size) {
> > +		dev_warn(dev,
> > +			 "Dynamic Capacity skip from pmem not supported\n");
> > +		cxl_disable_dcd(mds);
> > +		return;
> > +	}
> > +
> > +	info->size += dc_info.size;
> > +	dev_dbg(dev, "Adding dynamic ram partition 1; %llu size %llu\n",
> > +		dc_info.start, dc_info.size);
> > +	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> > +}
> > +EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
> > +
> >  int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds)
> >  {
> >  	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
> > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > index 616eeaeee5b9..a9782939d82b 100644
> > --- a/drivers/cxl/cxlmem.h
> > +++ b/drivers/cxl/cxlmem.h
> > @@ -407,6 +407,8 @@ struct cxl_security_state {
> >  	struct kernfs_node *sanitize_node;
> >  };
> >  
> > +#define CXL_MAX_DC_PARTITIONS 8
> > +
> >  static inline resource_size_t cxl_pmem_size(struct cxl_dev_state *cxlds)
> >  {
> >  	/*
> > @@ -691,6 +693,39 @@ struct cxl_mbox_set_shutdown_state_in {
> >  	u8 state;
> >  } __packed;
> >  
> > +/* See CXL r4.0 Table 8-178 get dynamic capacity config Input Payload */
> > +struct cxl_mbox_get_dc_config_in {
> > +	u8 partition_count;
> > +	u8 start_partition_index;
> > +} __packed;
> > +
> > +/* See CXL r4.0 Table 8-179 get dynamic capacity config Output Payload */
> > +struct cxl_mbox_get_dc_config_out {
> > +	u8 avail_partition_count;
> > +	u8 partitions_returned;
> > +	u8 rsvd[6];
> > +	/* See CXL r4.0 Table 8-180 */
> > +	struct cxl_dc_partition {
> > +		__le64 base;
> > +		__le64 decode_length;
> > +		__le64 length;
> > +		__le64 block_size;
> > +		__le32 dsmad_handle;
> > +		u8 flags;
> > +		u8 rsvd[3];
> > +	} __packed partition[] __counted_by(partitions_returned);
> > +	/* Trailing extent/tag count fields unused */
> > +} __packed;
> > +
> > +/* Trailing counts; cannot be a member after the flex array above */
> > +struct cxl_mbox_get_dc_config_tail {
> > +	__le32 num_extents_supported;
> > +	__le32 num_extents_available;
> > +	__le32 num_tags_supported;
> > +	__le32 num_tags_available;
> > +} __packed;
> > +#define CXL_DCD_BLOCK_LINE_SIZE 0x40
> > +
> >  /* Set Timestamp CXL 3.0 Spec 8.2.9.4.2 */
> >  struct cxl_mbox_set_timestamp_in {
> >  	__le64 timestamp;
> > @@ -814,9 +849,18 @@ enum {
> >  int cxl_internal_send_cmd(struct cxl_mailbox *cxl_mbox,
> >  			  struct cxl_mbox_cmd *cmd);
> >  int cxl_dev_state_identify(struct cxl_memdev_state *mds);
> > +
> > +struct cxl_dc_partition_info {
> > +	u64 start;
> > +	u64 size;
> > +};
> > +
> > +int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> > +			struct cxl_dc_partition_info *dc_info);
> >  int cxl_await_media_ready(struct cxl_dev_state *cxlds);
> >  int cxl_enumerate_cmds(struct cxl_memdev_state *mds);
> >  int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
> > +void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
> >  struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial,
> >  						 u16 dvsec);
> >  void set_exclusive_cxl_commands(struct cxl_memdev_state *mds,
> > @@ -830,6 +874,17 @@ void cxl_event_trace_record(struct cxl_memdev *cxlmd,
> >  			    const uuid_t *uuid, union cxl_event *evt);
> >  int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count);
> >  int cxl_arm_dirty_shutdown(struct cxl_memdev_state *mds);
> > +
> > +static inline bool cxl_dcd_supported(struct cxl_memdev_state *mds)
> > +{
> > +	return mds->dcd_supported;
> > +}
> > +
> > +static inline void cxl_disable_dcd(struct cxl_memdev_state *mds)
> > +{
> > +	mds->dcd_supported = false;
> > +}
> > +
> >  int cxl_set_timestamp(struct cxl_memdev_state *mds);
> >  int cxl_poison_state_init(struct cxl_memdev_state *mds);
> >  int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
> > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> > index 267c679b0b3c..9b320a2b6fe0 100644
> > --- a/drivers/cxl/pci.c
> > +++ b/drivers/cxl/pci.c
> > @@ -870,6 +870,9 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> >  	if (rc)
> >  		return rc;
> >  
> > +	if (cxl_dcd_supported(mds))
> > +		cxl_configure_dcd(mds, &range_info);
> > +
> >  	rc = cxl_dpa_setup(cxlds, &range_info);
> >  	if (rc)
> >  		return rc;
> > diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
> > index 016c74fb747c..87c2bd73df21 100644
> > --- a/include/cxl/cxl.h
> > +++ b/include/cxl/cxl.h
> > @@ -133,6 +133,7 @@ struct cxl_dpa_perf {
> >  enum cxl_partition_mode {
> >  	CXL_PARTMODE_RAM,
> >  	CXL_PARTMODE_PMEM,
> > +	CXL_PARTMODE_DYNAMIC_RAM_1,
> >  };
> >  
> >  /**
> > @@ -147,7 +148,7 @@ struct cxl_dpa_partition {
> >  	enum cxl_partition_mode mode;
> >  };
> >  
> > -#define CXL_NR_PARTITIONS_MAX 2
> > +#define CXL_NR_PARTITIONS_MAX 3
> >  
> >  /**
> >   * struct cxl_dev_state - The driver device state
> > -- 
> > 2.43.0
> > 

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

* Re: [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device
  2026-08-03 23:55   ` Jonathan Cameron
@ 2026-08-04  9:54     ` Anisa Su
  0 siblings, 0 replies; 33+ messages in thread
From: Anisa Su @ 2026-08-04  9:54 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Anisa Su, linux-cxl, nvdimm, linux-kernel, Dave Jiang,
	Alison Schofield, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim

On Tue, Aug 04, 2026 at 12:55:27AM +0100, Jonathan Cameron wrote:
> On Fri, 31 Jul 2026 01:48:07 -0700
> Anisa Su <anisa.su887@gmail.com> wrote:
> 
> > From: Ira Weiny <iweiny@kernel.org>
> > 
> > Devices which optionally support Dynamic Capacity (DC) are configured
> > via mailbox commands.  CXL r4.0 section 9.13.3 requires the host to issue
> > the Get DC Configuration command in order to properly configure DCDs.
> > Without the Get DC Configuration command DCD can't be supported.
> > 
> > Implement the DC mailbox commands as specified in CXL 4.0 section
> > 8.2.10.9.9 (opcodes 48XXh) to read and store the DCD configuration
> > information.  Disable DCD if an invalid configuration is found.
> > 
> > Linux has no support for more than one dynamic capacity partition.  Read
> > and validate all the partitions but configure only the first partition
> > as 'dynamic ram 1'.  Additional partitions can be added in the future if
> > such a device ever materializes.  Additionally it is anticipated that no
> > skips will be present from the end of the pmem partition.  Check for and
> > disallow this configuration as well.
> > 
> > Based on an original patch by Navneet Singh.
> > 
> > Signed-off-by: Ira Weiny <iweiny@kernel.org>
> > Signed-off-by: Anisa Su <anisa.su@samsung.com>
> > Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> > Tested-by: Junhee Park <jh9934.park@samsung.com>
> > Tested-by: Heesoo Kim <habil.kim@samsung.com>
> 
> Hi Anisa,
> 
> A few things to add to the reviews of others.
> 
Thanks Jonathan! Details below.

> Jonathan
> > diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> > index 4790524c32a7..d79019fbd790 100644
> > --- a/drivers/cxl/core/mbox.c
> > +++ b/drivers/cxl/core/mbox.c
> > @@ -1352,6 +1352,197 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
> >  	return -EBUSY;
> >  }
> >  
> > +static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_array,
> > +			u8 index, struct cxl_dc_partition *dev_part)
> > +{
> > +	u64 blk_size = le64_to_cpu(dev_part->block_size);
> > +	u64 len = le64_to_cpu(dev_part->length);
> > +
> > +	part_array[index].start = le64_to_cpu(dev_part->base);
> > +	part_array[index].size = le64_to_cpu(dev_part->decode_length);
> > +	part_array[index].size *= CXL_CAPACITY_MULTIPLIER;
> 
> I'd be tempted to make it clear you are setting whole structure with
> 
> 	part_array[index] = (struct cxl_dc_partition_info) {
> 		.start = le64_to_cpu(dev_part->base),
> 		.size = le64_to_cpu(dev_part->decode_length) * CXL_CAPACITY_MULTIPLIER,
> //This is a case where I think readability justifies the long line.
> 
Agreed, and it also improves readability of patch 3, which adds the DSMAD handle.
Originally handle verification came after setting start and size, and
before setting handle. Now, the handle verification goes once in front,
and all 3 fields set together. So the end result reads:

	if (handle & ~0xFF) {
		dev_warn(dev, "DSMAD handle 0x%x has non-zero reserved bits\n", handle);
		return -EINVAL;
	}

	part_array[index] = (struct cxl_dc_partition_info) {
		.start = le64_to_cpu(dev_part->base),
		.size = le64_to_cpu(dev_part->decode_length) * CXL_CAPACITY_MULTIPLIER,
		.handle = handle,
	};

> 	};
> > +
> > +	/* Check partitions are in increasing DPA order */
> > +	if (index > 0) {
> > +		struct cxl_dc_partition_info *prev_part = &part_array[index - 1];
> > +
> > +		if ((prev_part->start + prev_part->size) >
> > +		     part_array[index].start) {
> 
> I'd be tempted to put the above on one line.
> 
Done.
	if (prev_part->start + prev_part->size != part_array[index].start) {

The comparison changed from > to != because Alison pointed out this was
only an overlap check and gaps were passing. Since Linux requires
contiguous partitions I made it require exact contiguity.

One thing I mentioned in my reply to Alison: since only partiton 0 is
configured, a device with a valid partition 0 has DCD disabled if
a gap exists in subsequent partitions, even though the spec permits the
DPA gaps and we never use other partitions.

cxl_dpa_setup() is where partitions are configured and enforces the contiguity
rule. Is it more correct for cxl_dev_dc_identify() to check for spec compliance
only (no overlap) and defer the Linux-specific rule of no gaps between
partitions up to cxl_dpa_check()?

> > +			dev_err(dev,
> > +				"DPA ordering violation for DC partition %d and %d\n",
> > +				index - 1, index);
> > +			return -EINVAL;
> > +		}
> > +	}
> 
> Alison's comments seem valid to me and apply to rest of this function at least so I'll
> not add anything else on that.
> 

Agreed and worked through.

> > +
> > +}
> 
> ...
> 
> > +/**
> > + * cxl_dev_dc_identify() - Reads the dynamic capacity information from the
> > + *                         device.
> > + * @mbox: Mailbox to query
> > + * @dc_info: The dynamic partition information to return
> > + *
> > + * Read Dynamic Capacity information from the device and return the partition
> > + * information.
> > + *
> > + * Return: 0 if identify was executed successfully, -ERRNO on error.
> > + *         on error only dc_info is left unchanged.
> > + */
> > +int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> > +			struct cxl_dc_partition_info *dc_info)
> > +{
> > +	struct cxl_dc_partition_info partitions[CXL_MAX_DC_PARTITIONS];
> > +	struct cxl_mbox_get_dc_config_out *dc_resp __free(kfree) = NULL;
> > +	struct device *dev = mbox->host;
> > +	u8 start_partition;
> > +	u8 num_partitions;
> > +	u8 partition_count;
> > +	size_t dc_resp_size;
> > +
> > +	/* Bound requested number of partitions by mailbox payload size */
> > +	partition_count = min_t(size_t, CXL_MAX_DC_PARTITIONS,
> > +				(mbox->payload_size - sizeof(*dc_resp) -
> > +				 sizeof(struct cxl_mbox_get_dc_config_tail)) /
> > +				sizeof(struct cxl_dc_partition));
> > +	dc_resp_size = struct_size(dc_resp, partition, partition_count) +
> > +		       sizeof(struct cxl_mbox_get_dc_config_tail);
> > +
> > +	dc_resp = kmalloc(dc_resp_size, GFP_KERNEL);
> > +	if (!dc_resp)
> > +		return -ENOMEM;
> > +
> > +	/*
> > +	 * Read and check all partition information for validity and potential
> > +	 * debugging; see debug output in cxl_dc_check()
> > +	 */
> > +	start_partition = 0;
> > +	num_partitions = 0;
> > +	do {
> > +		int rc, i, j;
> > +
> > +		rc = cxl_get_dc_config(mbox, start_partition, partition_count,
> > +				       dc_resp, dc_resp_size);
> > +		if (rc < 0) {
> > +			dev_err(dev, "Failed to get DC config: %d\n", rc);
> > +			return rc;
> > +		}
> > +
> > +		if (rc == 0) {
> > +			dev_err(dev,
> > +				"Device reported %u partitions available but returned none at index %u\n",
> > +				dc_resp->avail_partition_count, start_partition);
> > +			return -EIO;
> > +		}
> > +
> > +		num_partitions += rc;
> > +
> > +		if (num_partitions < 1 || num_partitions > CXL_MAX_DC_PARTITIONS) {
> 
> How could num_partitions end up either 0 or negative given we added at least 1 and
> I don't see a path by which it will get decremented. Roll over isn't a thing given
> we would have failed the other check on a previous loop.
> 
Good point, dropped. This leaves:
	
	if (num_partitions > CXL_MAX_DC_PARTITIONS) {

Alison separately asked for avail_partition_count to be range checked
against the 1 to 8 Table 8-346 requires, which is now done in
cxl_get_dc_config() before the count is used as the loop bound. So this
check is left as a defense against a faulty device that somehow returns
more partitions than advertised.

> 
> > +			dev_err(dev, "Invalid num of dynamic capacity partitions %d\n",
> > +				num_partitions);
> > +			return -EINVAL;
> > +		}
> > +
> > +		for (i = start_partition, j = 0; i < num_partitions; i++, j++) {
> > +			rc = cxl_dc_check(dev, partitions, i,
> > +					  &dc_resp->partition[j]);
> > +			if (rc)
> > +				return rc;
> > +		}
> > +
> > +		start_partition = num_partitions;
> > +
> > +	} while (num_partitions < dc_resp->avail_partition_count);
> > +
> > +	/* Return 1st partition */
> > +	dc_info->start = partitions[0].start;
> > +	dc_info->size = partitions[0].size;
> > +	dev_dbg(dev, "Returning partition 0 %llu size %llu\n",
> > +		dc_info->start, dc_info->size);
> > +
> > +	return 0;
> > +}
> 
> 
> 
> > @@ -1422,6 +1613,35 @@ int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
> >  }
> >  EXPORT_SYMBOL_NS_GPL(cxl_get_dirty_count, "CXL");
> >  
> > +void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> > +{
> > +	struct cxl_dc_partition_info dc_info = { 0 };
> 
> I think the preferred way of doing this is heading for the C23 option of
> 
>   = { };
> 
> but CXL has plenty of { 0 }; so fair enough if you prefer to keep it this
> way.  For anyone concerned about older compilers there is a specific selftest
> for { } doing what we expect even if the structure has holes.
> 
Switched to { }.

> 
> > +	struct device *dev = mds->cxlds.dev;
> > +	int rc;
> > +
> > +	rc = cxl_dev_dc_identify(&mds->cxlds.cxl_mbox, &dc_info);
> > +	if (rc) {
> > +		dev_warn(dev,
> > +			 "Failed to read Dynamic Capacity config: %d\n", rc);
> 
> Fun question on whether we consider a DCD capable device failing such
> a basic thing as a warning level thing or a hard failure thing.
> 
> To me DCD is a important core device feature (unlikely maybe fwctl, features etc)
> so we should be failing. 
> 
> We should definitely be returning an error from here, even if ultimate
> we decide not to act on that error in cxl_pci_probe().
> 
Agreed, cxl_configure_dcd() returns int now. cxl_pci_probe() now
disables dcd if cxl_configure_dcd() fails:

	if (cxl_dcd_supported(mds)) {
		rc = cxl_configure_dcd(mds, &range_info);
		if (rc)
			cxl_disable_dcd(mds);
	}

The three paths return distinct errors:
1. the cxl_dev_dc_identify() err propagates,
2. -EINVAL for a DC partition overlapping static capacity
3. -EOPNOTSUPP for the gap below.

Re: should "we consider a DCD capable device failing such a basic thing as
a warning level thing or a hard failure thing":

I went with continuing the probe without DCD. Failing cxl_pci_probe()
costs the device its static capacity too, which seems a worse outcome than
running without dynamic capacity.
But I could be persuaded the other way -- if a DCD device that cannot report
its DC Config then it should not come up at all.

> > +		cxl_disable_dcd(mds);
> > +		return;
> > +	}
> > +
> > +	/* Skips between pmem and the dynamic partition are not supported */
> > +	if (dc_info.start != info->size) {
> > +		dev_warn(dev,
> > +			 "Dynamic Capacity skip from pmem not supported\n");
> > +		cxl_disable_dcd(mds);
> > +		return;
> 
> In this case not failing hard is probably fair if that is a specification
> allowed feature we are just not choosing to support for now.
> 
Agreed, so it stays a warning and returns -EOPNOTSUPP. From Alison's
review, I split it up to differentiate skip from overlap. Overlap
returns -EINVAL. So now we have:

	if (dc_info.start < info->size) {
		dev_err(dev, "DC partition 0 base %#llx overlaps static capacity ending at %#llx\n",
			dc_info.start, info->size);
		return -EINVAL;
	}

	/* A gap between static capacity and the DC partition is not supported */
	if (dc_info.start > info->size) {
		dev_warn(dev, "DC partition 0 base %#llx leaves a gap from static capacity ending at %#llx\n",
			 dc_info.start, info->size);
		return -EOPNOTSUPP;
	}

The old message also said "pmem" when the value it compares against is
info->size, ie mds->total_bytes, the whole static capacity from Identify
rather than the end of the pmem partition, which is not quite accurate
for a volatile-only device. Reworded.

Thanks,
Anisa

> > +	}
> > +
> > +	info->size += dc_info.size;
> > +	dev_dbg(dev, "Adding dynamic ram partition 1; %llu size %llu\n",
> > +		dc_info.start, dc_info.size);
> > +	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> > +}
> > +EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
> 
> 

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

* Re: [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
  2026-08-03 23:12     ` Jonathan Cameron
@ 2026-08-04 15:45       ` Gregory Price
  0 siblings, 0 replies; 33+ messages in thread
From: Gregory Price @ 2026-08-04 15:45 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alison Schofield, Anisa Su, linux-cxl, nvdimm, linux-kernel,
	Dave Jiang, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

On Tue, Aug 04, 2026 at 12:12:45AM +0100, Jonathan Cameron wrote:
> > 
> > When I view how this lands in the entirety of cxl_walk_cel() it seems
> > to needlessly be different from the pattern set by poison and security
> > handling in the same function and structures. Could this follow that
> > more closely?  Doing so would remove this 'tail' work here of checking
> > and setting the boolean.
> > 
> > This appended diff touches patch 2 where it has the first caller, but
> > pasting all here for simplicity - you'll apply per patch if you take it.
> > 
> > The bitmap lives in mds structure and it is already where the bool is now.
> > The only piece of the poison and security pattern not worth copying
> > yet is the wrapper struct cxl_poison_state and struct cxl_security_state
> > exist because each grew a mutex and other per-class state, and DCD has
> > none of that in this series (yet).
> Hi Alison,
> 
> This rang a bell as I remembered Ira did it this way originally -
> so I did some archaeology.
> 
> [PATCH v8 01/21] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
> https://lore.kernel.org/all/67786b76a0c5_f58f294b1@dwillia2-xfh.jf.intel.com.notmuch/
> 
> Dan's point was that we need that infrastructure for poison and security
> because a subset is a realistic possibility. For DCD today it's all or
> nothing so why keep a bitmap around?
> 
> I agree there is merit in having all the command types handled the same
> but perhaps it is clearer to just have a bool.  Of course when someone
> adds another DCD command on the device side (which by definition will have
> to be optional) then we will need to revisit.
> 

Yeah, and it's not really binding ABI so no need for complexity until we
get there.  Honestly the feature bit check is essentially the same as
the bitmap check at this point, so seems find to push that
generalization off until we actually need it.

~Gregory

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

* Re: [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
  2026-07-31  8:48 ` [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
  2026-08-03 18:03   ` Dave Jiang
  2026-08-03 21:30   ` Alison Schofield
@ 2026-08-04 15:45   ` Gregory Price
  2026-08-04 18:10     ` Anisa Su
  2026-08-04 16:55   ` Alison Schofield
  3 siblings, 1 reply; 33+ messages in thread
From: Gregory Price @ 2026-08-04 15:45 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Alison Schofield,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim,
	Anisa Su

On Fri, Jul 31, 2026 at 01:48:06AM -0700, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Per the CXL 4.0 specification software must check the Command Effects
> Log (CEL) for dynamic capacity command support.
> 
> Detect support for the DCD commands while reading the CEL, including:
> 
>         Get DC Config
>         Get DC Extent List
>         Add DC Response
>         Release DC
> 
> Based on an original patch by Navneet Singh.
> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>
> 

Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>


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

* Re: [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device
  2026-08-04  9:12     ` Anisa Su
@ 2026-08-04 16:00       ` Gregory Price
  0 siblings, 0 replies; 33+ messages in thread
From: Gregory Price @ 2026-08-04 16:00 UTC (permalink / raw)
  To: Anisa Su
  Cc: Alison Schofield, linux-cxl, nvdimm, linux-kernel, Dave Jiang,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim

On Tue, Aug 04, 2026 at 02:12:44AM -0700, Anisa Su wrote:
> On Mon, Aug 03, 2026 at 02:52:17PM -0700, Alison Schofield wrote:
> > On Fri, Jul 31, 2026 at 01:48:07AM -0700, Anisa Su wrote:
> > > From: Ira Weiny <iweiny@kernel.org>
> > > 
> > > Devices which optionally support Dynamic Capacity (DC) are configured
> > > via mailbox commands.  CXL r4.0 section 9.13.3 requires the host to issue
> > > the Get DC Configuration command in order to properly configure DCDs.
> > > Without the Get DC Configuration command DCD can't be supported.
> > > 
> > > Implement the DC mailbox commands as specified in CXL 4.0 section
> > > 8.2.10.9.9 (opcodes 48XXh) to read and store the DCD configuration
> > > information.  Disable DCD if an invalid configuration is found.
> > > 
> > > Linux has no support for more than one dynamic capacity partition.  Read
> > > and validate all the partitions but configure only the first partition
> > > as 'dynamic ram 1'.  Additional partitions can be added in the future if
> > > such a device ever materializes.  Additionally it is anticipated that no
> > > skips will be present from the end of the pmem partition.  Check for and
> > > disallow this configuration as well.
> > 
> > Hi Anisa,
> > 
> > "Additionally, it is anticipated that no skips will be present.." sounds
> > woogedy. The paragraph starts with a Linux limitation of only one partition.
> > and next mentions skips. Is that a Linux implementation rule, a spec
> > rule, or something else?
> > 
> 
> Sorry for the lack of clarity. The commit message has been revised to
> make it clear that DPA gaps between partitions are a Linux rule. The
> spec permits a gap: the DC region base is device assigned and only has to be
> 256MB aligned (r4.0 Table 8-347). It now reads:
> 
>     Linux has no support for more than one dynamic capacity partition. Read
>     and validate all the partitions but configure only the first partition
>     as 'dynamic ram 1'. Additional partitions can be added in the future if
>     such a device ever materializes. Linux additionally requires partitions
>     to be contiguous. Therefore, check for and disallow any gap between the
>     end of the pmem partition and the first DC partition, as well as gaps
>     between the remaining DC partitions.
> 

Is this limitation something we expect to ever change, or is it
something like "The spec doesn't disallow it, but basically there's no
good way to reason about this otherwise"?

Where reasonable, we should try to work the "Linux opinion of how this
should work" into the CXL docs in-tree so it's clear where we put
constraints in where the spec does not.

~Gregory

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

* Re: [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
  2026-07-31  8:48 ` [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
                     ` (2 preceding siblings ...)
  2026-08-04 15:45   ` Gregory Price
@ 2026-08-04 16:55   ` Alison Schofield
  2026-08-04 18:13     ` Anisa Su
  3 siblings, 1 reply; 33+ messages in thread
From: Alison Schofield @ 2026-08-04 16:55 UTC (permalink / raw)
  To: Anisa Su
  Cc: linux-cxl, nvdimm, linux-kernel, Dave Jiang, Jonathan Cameron,
	Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso, Ira Weiny,
	Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim, Anisa Su

On Fri, Jul 31, 2026 at 01:48:06AM -0700, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
> 
> Per the CXL 4.0 specification software must check the Command Effects
> Log (CEL) for dynamic capacity command support.
> 
> Detect support for the DCD commands while reading the CEL, including:
> 
>         Get DC Config
>         Get DC Extent List
>         Add DC Response
>         Release DC
> 
> Based on an original patch by Navneet Singh.
> 
> Signed-off-by: Ira Weiny <iweiny@kernel.org>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> Tested-by: Junhee Park <jh9934.park@samsung.com>
> Tested-by: Heesoo Kim <habil.kim@samsung.com>
> 
> ---
> Changes:
> 1. mbox.c: leave mds->dcd_supported false so the hardware enablement
>    patches can be upstreamed ahead of the extent and DAX work; the
>    event handling patch re-enables it.
> ---
>  drivers/cxl/core/mbox.c | 44 +++++++++++++++++++++++++++++++++++++++++
>  drivers/cxl/cxlmem.h    | 20 +++++++++++++++++++
>  2 files changed, 64 insertions(+)
> 
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 7c6c5b7450a5..4790524c32a7 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -165,6 +165,38 @@ static void cxl_set_security_cmd_enabled(struct cxl_security_state *security,
>  	}
>  }
>  
> +static bool cxl_is_dcd_command(u16 opcode)
> +{
> +#define CXL_MBOX_OP_DCD_CMDS 0x48
> +
> +	return (opcode >> 8) == CXL_MBOX_OP_DCD_CMDS;
> +}
> +
> +static void cxl_set_dcd_cmd_enabled(u16 opcode, unsigned long *cmd_mask)
> +{
> +	switch (opcode) {
> +	case CXL_MBOX_OP_GET_DC_CONFIG:
> +		set_bit(CXL_DCD_ENABLED_GET_CONFIG, cmd_mask);
> +		break;
> +	case CXL_MBOX_OP_GET_DC_EXTENT_LIST:
> +		set_bit(CXL_DCD_ENABLED_GET_EXTENT_LIST, cmd_mask);
> +		break;
> +	case CXL_MBOX_OP_ADD_DC_RESPONSE:
> +		set_bit(CXL_DCD_ENABLED_ADD_RESPONSE, cmd_mask);
> +		break;
> +	case CXL_MBOX_OP_RELEASE_DC:
> +		set_bit(CXL_DCD_ENABLED_RELEASE, cmd_mask);
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static bool cxl_verify_dcd_cmds(unsigned long *cmds_seen)
> +{
> +	return bitmap_full(cmds_seen, CXL_DCD_ENABLED_MAX);
> +}
> +
>  static bool cxl_is_poison_command(u16 opcode)
>  {
>  #define CXL_MBOX_OP_POISON_CMDS 0x43
> @@ -757,6 +789,7 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
>  	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
>  	struct cxl_cel_entry *cel_entry;
>  	const int cel_entries = size / sizeof(*cel_entry);
> +	DECLARE_BITMAP(dcd_cmds, CXL_DCD_ENABLED_MAX) = {};
>  	struct device *dev = mds->cxlds.dev;
>  	int i, ro_cmds = 0, wr_cmds = 0;
>  
> @@ -785,11 +818,22 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
>  			enabled++;
>  		}
>  
> +		if (cxl_is_dcd_command(opcode)) {
> +			cxl_set_dcd_cmd_enabled(opcode, dcd_cmds);
> +			enabled++;
> +		}
> +
>  		dev_dbg(dev, "Opcode 0x%04x %s\n", opcode,
>  			enabled ? "enabled" : "unsupported by driver");
>  	}
>  
>  	set_features_cap(cxl_mbox, ro_cmds, wr_cmds);
> +	/*
> +	 * Disabled until event handling implemented.
> +	 */
> +	if (cxl_verify_dcd_cmds(dcd_cmds))
> +		dev_dbg(dev, "Device supports DCD; capability disabled\n");
> +	mds->dcd_supported = false;
>  }

Hi Anisa,

I read and agree with the other reviewers that this does not need to
mirror the poison and security command handling.

The one-use cxl_verify_dcd_cmds() helper still seems excessive. The
bitmap can be checked directly here:

    if (bitmap_full(dcd_cmds, CXL_DCD_ENABLED_MAX))
            dev_dbg(dev, "Device supports DCD; capability disabled\n");

And then once DCD is ready to be enabled, the assignment can become:

    mds->dcd_supported =
            bitmap_full(dcd_cmds, CXL_DCD_ENABLED_MAX);

-- Alison

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

* Re: [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
  2026-08-04 15:45   ` Gregory Price
@ 2026-08-04 18:10     ` Anisa Su
  0 siblings, 0 replies; 33+ messages in thread
From: Anisa Su @ 2026-08-04 18:10 UTC (permalink / raw)
  To: Gregory Price
  Cc: Anisa Su, linux-cxl, nvdimm, linux-kernel, Dave Jiang,
	Alison Schofield, Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma,
	Davidlohr Bueso, Ira Weiny, Benjamin Cheatham, Wonjae Lee,
	Junhee Park, Heesoo Kim

On Tue, Aug 04, 2026 at 11:45:34AM -0400, Gregory Price wrote:
> On Fri, Jul 31, 2026 at 01:48:06AM -0700, Anisa Su wrote:
> > From: Ira Weiny <iweiny@kernel.org>
> > 
> > Per the CXL 4.0 specification software must check the Command Effects
> > Log (CEL) for dynamic capacity command support.
> > 
> > Detect support for the DCD commands while reading the CEL, including:
> > 
> >         Get DC Config
> >         Get DC Extent List
> >         Add DC Response
> >         Release DC
> > 
> > Based on an original patch by Navneet Singh.
> > 
> > Signed-off-by: Ira Weiny <iweiny@kernel.org>
> > Signed-off-by: Anisa Su <anisa.su@samsung.com>
> > Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> > Tested-by: Junhee Park <jh9934.park@samsung.com>
> > Tested-by: Heesoo Kim <habil.kim@samsung.com>
> > 
> 
> Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
> 
Thanks for taking a look : )
- Anisa

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

* Re: [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
  2026-08-04 16:55   ` Alison Schofield
@ 2026-08-04 18:13     ` Anisa Su
  0 siblings, 0 replies; 33+ messages in thread
From: Anisa Su @ 2026-08-04 18:13 UTC (permalink / raw)
  To: Alison Schofield
  Cc: Anisa Su, linux-cxl, nvdimm, linux-kernel, Dave Jiang,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim

On Tue, Aug 04, 2026 at 09:55:36AM -0700, Alison Schofield wrote:
> On Fri, Jul 31, 2026 at 01:48:06AM -0700, Anisa Su wrote:
> > From: Ira Weiny <iweiny@kernel.org>
> > 
> > Per the CXL 4.0 specification software must check the Command Effects
> > Log (CEL) for dynamic capacity command support.
> > 
> > Detect support for the DCD commands while reading the CEL, including:
> > 
> >         Get DC Config
> >         Get DC Extent List
> >         Add DC Response
> >         Release DC
> > 
> > Based on an original patch by Navneet Singh.
> > 
> > Signed-off-by: Ira Weiny <iweiny@kernel.org>
> > Signed-off-by: Anisa Su <anisa.su@samsung.com>
> > Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> > Tested-by: Junhee Park <jh9934.park@samsung.com>
> > Tested-by: Heesoo Kim <habil.kim@samsung.com>
> > 
> > ---
> > Changes:
> > 1. mbox.c: leave mds->dcd_supported false so the hardware enablement
> >    patches can be upstreamed ahead of the extent and DAX work; the
> >    event handling patch re-enables it.
> > ---
> >  drivers/cxl/core/mbox.c | 44 +++++++++++++++++++++++++++++++++++++++++
> >  drivers/cxl/cxlmem.h    | 20 +++++++++++++++++++
> >  2 files changed, 64 insertions(+)
> > 
> > diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> > index 7c6c5b7450a5..4790524c32a7 100644
> > --- a/drivers/cxl/core/mbox.c
> > +++ b/drivers/cxl/core/mbox.c
> > @@ -165,6 +165,38 @@ static void cxl_set_security_cmd_enabled(struct cxl_security_state *security,
> >  	}
> >  }
> >  
> > +static bool cxl_is_dcd_command(u16 opcode)
> > +{
> > +#define CXL_MBOX_OP_DCD_CMDS 0x48
> > +
> > +	return (opcode >> 8) == CXL_MBOX_OP_DCD_CMDS;
> > +}
> > +
> > +static void cxl_set_dcd_cmd_enabled(u16 opcode, unsigned long *cmd_mask)
> > +{
> > +	switch (opcode) {
> > +	case CXL_MBOX_OP_GET_DC_CONFIG:
> > +		set_bit(CXL_DCD_ENABLED_GET_CONFIG, cmd_mask);
> > +		break;
> > +	case CXL_MBOX_OP_GET_DC_EXTENT_LIST:
> > +		set_bit(CXL_DCD_ENABLED_GET_EXTENT_LIST, cmd_mask);
> > +		break;
> > +	case CXL_MBOX_OP_ADD_DC_RESPONSE:
> > +		set_bit(CXL_DCD_ENABLED_ADD_RESPONSE, cmd_mask);
> > +		break;
> > +	case CXL_MBOX_OP_RELEASE_DC:
> > +		set_bit(CXL_DCD_ENABLED_RELEASE, cmd_mask);
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +}
> > +
> > +static bool cxl_verify_dcd_cmds(unsigned long *cmds_seen)
> > +{
> > +	return bitmap_full(cmds_seen, CXL_DCD_ENABLED_MAX);
> > +}
> > +
> >  static bool cxl_is_poison_command(u16 opcode)
> >  {
> >  #define CXL_MBOX_OP_POISON_CMDS 0x43
> > @@ -757,6 +789,7 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
> >  	struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
> >  	struct cxl_cel_entry *cel_entry;
> >  	const int cel_entries = size / sizeof(*cel_entry);
> > +	DECLARE_BITMAP(dcd_cmds, CXL_DCD_ENABLED_MAX) = {};
> >  	struct device *dev = mds->cxlds.dev;
> >  	int i, ro_cmds = 0, wr_cmds = 0;
> >  
> > @@ -785,11 +818,22 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
> >  			enabled++;
> >  		}
> >  
> > +		if (cxl_is_dcd_command(opcode)) {
> > +			cxl_set_dcd_cmd_enabled(opcode, dcd_cmds);
> > +			enabled++;
> > +		}
> > +
> >  		dev_dbg(dev, "Opcode 0x%04x %s\n", opcode,
> >  			enabled ? "enabled" : "unsupported by driver");
> >  	}
> >  
> >  	set_features_cap(cxl_mbox, ro_cmds, wr_cmds);
> > +	/*
> > +	 * Disabled until event handling implemented.
> > +	 */
> > +	if (cxl_verify_dcd_cmds(dcd_cmds))
> > +		dev_dbg(dev, "Device supports DCD; capability disabled\n");
> > +	mds->dcd_supported = false;
> >  }
> 
> Hi Anisa,
> 
> I read and agree with the other reviewers that this does not need to
> mirror the poison and security command handling.
> 
> The one-use cxl_verify_dcd_cmds() helper still seems excessive. The
> bitmap can be checked directly here:
> 
>     if (bitmap_full(dcd_cmds, CXL_DCD_ENABLED_MAX))
>             dev_dbg(dev, "Device supports DCD; capability disabled\n");
> 
> And then once DCD is ready to be enabled, the assignment can become:
> 
>     mds->dcd_supported =
>             bitmap_full(dcd_cmds, CXL_DCD_ENABLED_MAX);
> 
> -- Alison

Applied :) Thanks Alison!

- Anisa

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

* Re: [PATCH v12 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
  2026-08-03 22:56   ` Alison Schofield
@ 2026-08-05  6:58     ` Anisa Su
  0 siblings, 0 replies; 33+ messages in thread
From: Anisa Su @ 2026-08-05  6:58 UTC (permalink / raw)
  To: Alison Schofield
  Cc: Anisa Su, linux-cxl, nvdimm, linux-kernel, Dave Jiang,
	Jonathan Cameron, Fan Ni, Li Ming, Vishal Verma, Davidlohr Bueso,
	Ira Weiny, Benjamin Cheatham, Wonjae Lee, Junhee Park, Heesoo Kim

On Mon, Aug 03, 2026 at 03:56:36PM -0700, Alison Schofield wrote:
> On Fri, Jul 31, 2026 at 01:48:08AM -0700, Anisa Su wrote:
> > From: Ira Weiny <iweiny@kernel.org>
> > 
> > Additional DCD partition (AKA region) information is contained in the
> > DSMAS CDAT tables, including performance, read only, and shareable
> > attributes.
> > 
> > Match DCD partitions with DSMAS tables and store the meta data.
> > 
> > Signed-off-by: Ira Weiny <iweiny@kernel.org>
> > Co-developed-by: Anisa Su <anisa.su@samsung.com>
> > Signed-off-by: Anisa Su <anisa.su@samsung.com>
> > Tested-by: Wonjae Lee <wj28.lee@samsung.com>
> > Tested-by: Junhee Park <jh9934.park@samsung.com>
> > Tested-by: Heesoo Kim <habil.kim@samsung.com>
> > Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> > ---
> >  drivers/cxl/core/cdat.c | 13 +++++++++++++
> >  drivers/cxl/core/hdm.c  |  1 +
> >  drivers/cxl/core/mbox.c | 22 ++++++++++++++++------
> >  drivers/cxl/cxlmem.h    |  2 ++
> >  include/cxl/cxl.h       |  4 ++++
> >  5 files changed, 36 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> > index 5c9f07262513..37136b2cf7e4 100644
> > --- a/drivers/cxl/core/cdat.c
> > +++ b/drivers/cxl/core/cdat.c
> > @@ -17,6 +17,7 @@ struct dsmas_entry {
> >  	struct access_coordinate cdat_coord[ACCESS_COORDINATE_MAX];
> >  	int entries;
> >  	int qos_class;
> > +	bool shareable;
> >  };
> >  
> >  static u32 cdat_normalize(u16 entry, u64 base, u8 type)
> > @@ -74,6 +75,7 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
> >  		return -ENOMEM;
> >  
> >  	dent->handle = dsmas->dsmad_handle;
> > +	dent->shareable = dsmas->flags & ACPI_CDAT_DSMAS_SHAREABLE;
> >  	dent->dpa_range.start = le64_to_cpu((__force __le64)dsmas->dpa_base_address);
> >  	dent->dpa_range.end = le64_to_cpu((__force __le64)dsmas->dpa_base_address) +
> >  			      le64_to_cpu((__force __le64)dsmas->dpa_length) - 1;
> > @@ -266,15 +268,26 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> >  		bool found = false;
> >  
> >  		for (int i = 0; i < cxlds->nr_partitions; i++) {
> > +			enum cxl_partition_mode mode = cxlds->part[i].mode;
> >  			struct resource *res = &cxlds->part[i].res;
> > +			u8 handle = cxlds->part[i].handle;
> >  			struct range range = {
> >  				.start = res->start,
> >  				.end = res->end,
> >  			};
> >  
> >  			if (range_contains(&range, &dent->dpa_range)) {
> > +				if (mode == CXL_PARTMODE_DYNAMIC_RAM_1 &&
> > +				    dent->handle != handle) {
> > +					dev_warn(dev,
> > +						 "Dynamic RAM perf mismatch; %pra (%u) vs %pra (%u)\n",
> > +						 &range, handle, &dent->dpa_range,
> > +						 dent->handle);
> > +					continue;
> 
> Seems like the above message needs to say that the DSMAD handle from
> mbox disagrees w CDAT DSMAS handle. Those (%u) that are handles have
> no description.

Reworded to:

	dev_warn(dev,
		 "DSMAD handle mismatch: mailbox %u %pra, CDAT %u %pra\n",
		 part->handle, &range,
		 dent->handle, &dent->dpa_range);

which for example would read as:

	DSMAD handle mismatch: [range 0x100000000-0x1ffffffff] has 3, DSMAS [range 0x100000000-0x17fffffff] has 7

I applied it with your suggestion below to invert the range_contains()
and use a 'part' local pointer to reduce nesting and repeated indexing,
but the line is still quite long (99 chars), since it's still within the
xa_for_each(dsmas_xa...){} and the for loop. The message could
be clearer, but I wasn't sure how to do so without going over
100 chars, so I added a comment:

		/*
		 * part->handle is from Get DC Config, dent->handle
		 * from the CDAT DSMAS entry.
		 */
		if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1 &&
		    dent->handle != part->handle) {
			dev_warn(dev,
				 "DSMAD handle mismatch: %pra has %u, DSMAS %pra has %u\n",
				 &range, part->handle,
				 &dent->dpa_range, dent->handle);
			break;
		}


> With the above mismatch and continue, the loop exits w found still
> false. Is that really true?
> 
Good point. found is now set to true before the above check. So with the
inverted range_contains() suggestion:

	if (!range_contains(&range, &dent->dpa_range))
		continue;

	found = true;
	if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1 &&
	    dent->handle != part->handle) {
		dev_warn(...);
		break;
	}


> 
> > +				}
> >  				update_perf_entry(dev, dent,
> >  						  &cxlds->part[i].perf);
> > +				cxlds->part[i].shareable = dent->shareable;
> 
> 
> why is shareable assigned for every node, and not only DYNAMIC_RAM_1
> 
Fixed to only be assigned if the mode is DYNAMIC_RAM_1 now:

	update_perf_entry(dev, dent, &part->perf);
	if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
		part->shareable = dent->shareable;

Although we now have 2 places that gate on if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1),
I chose to keep this as a separate check. If we consolidate this with
the above part->handle != dent->handle check, it would add another level
of nesting and make the dev_warn message from above to over 100 cols.

> >  				found = true;
> >  				break;
> >  			}
> 
> Above nesting can be improved. Inverting range_contains() to unindent
> the main body. A 'part' local pointer would reduce repeated indexing.
> 
Done.

> 
> > diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> > index 0ef076c08ed2..7f63b86887f4 100644
> > --- a/drivers/cxl/core/hdm.c
> > +++ b/drivers/cxl/core/hdm.c
> > @@ -477,6 +477,7 @@ int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
> >  
> >  		cxlds->part[i].perf.qos_class = CXL_QOS_CLASS_INVALID;
> >  		cxlds->part[i].mode = part->mode;
> > +		cxlds->part[i].handle = part->handle;
> >  
> >  		/* Require ordered + contiguous partitions */
> >  		if (i) {
> > diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> > index d79019fbd790..a6cdea9f4080 100644
> > --- a/drivers/cxl/core/mbox.c
> > +++ b/drivers/cxl/core/mbox.c
> > @@ -1357,10 +1357,16 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part_a
> >  {
> >  	u64 blk_size = le64_to_cpu(dev_part->block_size);
> >  	u64 len = le64_to_cpu(dev_part->length);
> > +	u32 handle = le32_to_cpu(dev_part->dsmad_handle);
> >  
> >  	part_array[index].start = le64_to_cpu(dev_part->base);
> >  	part_array[index].size = le64_to_cpu(dev_part->decode_length);
> >  	part_array[index].size *= CXL_CAPACITY_MULTIPLIER;
> > +	if (handle & ~0xFF) {
> > +		dev_warn(dev, "DSMAD handle 0x%x has non-zero reserved bits\n", handle);
> 
> I don't see reserve bits for a DSMAD handle. I think this check is valid
> but the message has the wrong constraint. Expect it is a CDAT constraint.
> 
Yes, thank you for point it out. CXL r4.0 Table 8-347 gives DSMADHandle a
full 4 bytes and declares no reserved bits; the table's Reserved field
is the 3 bytes at 0x25, after Flags. The 8 bit limit comes from CDAT,
where struct acpi_cdat_dsmas has a u8 dsmad_handle, so a value above
0xFF cannot name a DSMAS entry that exists.

The driver stores the handle as u8 in cxl_dpa_partition, cxl_dc_partition_info and cdat.c's
dsmas_entry, so 0x100 would narrow to 0x00 and silently match whichever
DSMAS entry has handle 0. The mssage now reads:

	/* The CDAT DSMAD handle this refers to is 8 bits */
	if (handle & ~0xFF) {
		dev_warn(dev, "DSMAD handle 0x%x exceeds the 8 bit CDAT DSMAD handle\n",
			 handle);

> 
> > +		return -EINVAL;
> > +	}
> > +	part_array[index].handle = handle;
> >  
> >  	/* Check partitions are in increasing DPA order */
> >  	if (index > 0) {
> > @@ -1536,6 +1542,7 @@ int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> >  	/* Return 1st partition */
> >  	dc_info->start = partitions[0].start;
> >  	dc_info->size = partitions[0].size;
> > +	dc_info->handle = partitions[0].handle;
> >  	dev_dbg(dev, "Returning partition 0 %llu size %llu\n",
> >  		dc_info->start, dc_info->size);
> >  
> > @@ -1543,7 +1550,8 @@ int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> >  }
> >  EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
> >  
> > -static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_partition_mode mode)
> > +static void add_part(struct cxl_dpa_info *info, u64 start, u64 size,
> > +		     enum cxl_partition_mode mode, u8 handle)
> >  {
> >  	int i = info->nr_partitions;
> >  
> > @@ -1555,6 +1563,7 @@ static void add_part(struct cxl_dpa_info *info, u64 start, u64 size, enum cxl_pa
> >  		.end = start + size - 1,
> >  	};
> >  	info->part[i].mode = mode;
> > +	info->part[i].handle = handle;
> >  	info->nr_partitions++;
> >  }
> >  
> > @@ -1572,9 +1581,9 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> >  	info->size = mds->total_bytes;
> >  
> >  	if (mds->partition_align_bytes == 0) {
> > -		add_part(info, 0, mds->volatile_only_bytes, CXL_PARTMODE_RAM);
> > +		add_part(info, 0, mds->volatile_only_bytes, CXL_PARTMODE_RAM, 0);
> >  		add_part(info, mds->volatile_only_bytes,
> > -			 mds->persistent_only_bytes, CXL_PARTMODE_PMEM);
> > +			 mds->persistent_only_bytes, CXL_PARTMODE_PMEM, 0);
> >  		return 0;
> >  	}
> >  
> > @@ -1584,9 +1593,9 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> >  		return rc;
> >  	}
> >  
> > -	add_part(info, 0, mds->active_volatile_bytes, CXL_PARTMODE_RAM);
> > +	add_part(info, 0, mds->active_volatile_bytes, CXL_PARTMODE_RAM, 0);
> >  	add_part(info, mds->active_volatile_bytes, mds->active_persistent_bytes,
> > -		 CXL_PARTMODE_PMEM);
> > +		 CXL_PARTMODE_PMEM, 0);
> >  
> >  	return 0;
> >  }
> > @@ -1638,7 +1647,8 @@ void cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> >  	info->size += dc_info.size;
> >  	dev_dbg(dev, "Adding dynamic ram partition 1; %llu size %llu\n",
> >  		dc_info.start, dc_info.size);
> > -	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> > +	add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1,
> > +		 dc_info.handle);
> >  }
> >  EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
> >  
> > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > index a9782939d82b..eb1e6f7a2038 100644
> > --- a/drivers/cxl/cxlmem.h
> > +++ b/drivers/cxl/cxlmem.h
> > @@ -140,6 +140,7 @@ struct cxl_dpa_info {
> >  	struct cxl_dpa_part_info {
> >  		struct range range;
> >  		enum cxl_partition_mode mode;
> > +		u8 handle;
> >  	} part[CXL_NR_PARTITIONS_MAX];
> >  	int nr_partitions;
> >  };
> > @@ -853,6 +854,7 @@ int cxl_dev_state_identify(struct cxl_memdev_state *mds);
> >  struct cxl_dc_partition_info {
> >  	u64 start;
> >  	u64 size;
> > +	u8 handle;
> >  };
> >  
> >  int cxl_dev_dc_identify(struct cxl_mailbox *mbox,
> > diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
> > index 87c2bd73df21..baf0132c70a3 100644
> > --- a/include/cxl/cxl.h
> > +++ b/include/cxl/cxl.h
> > @@ -141,11 +141,15 @@ enum cxl_partition_mode {
> >   * @res: shortcut to the partition in the DPA resource tree (cxlds->dpa_res)
> >   * @perf: performance attributes of the partition from CDAT
> >   * @mode: operation mode for the DPA capacity, e.g. ram, pmem, dynamic...
> > + * @handle: DSMAS handle intended to represent this partition
> 
> What is with the 'intended'? Either it represents the partition
> or the match failed.
> 
Dropped, now says "DSMAS handle that represents this partition".

> 
> > + * @shareable: Is the partition sharable (from its CDAT DSMAS entry)
>                                    ^
> 				   shareable
> 
Fixed.

Thanks,
Anisa
> 
> >   */
> >  struct cxl_dpa_partition {
> >  	struct resource res;
> >  	struct cxl_dpa_perf perf;
> >  	enum cxl_partition_mode mode;
> > +	u8 handle;
> > +	bool shareable;
> >  };
> >  
> >  #define CXL_NR_PARTITIONS_MAX 3
> > -- 
> > 2.43.0
> > 

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

end of thread, other threads:[~2026-08-05  6:58 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-31  8:48 [PATCH v12 0/8] DCD Prep Series Anisa Su
2026-07-31  8:48 ` [PATCH v12 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
2026-08-03 18:03   ` Dave Jiang
2026-08-03 21:30   ` Alison Schofield
2026-08-03 23:12     ` Jonathan Cameron
2026-08-04 15:45       ` Gregory Price
2026-08-04 15:45   ` Gregory Price
2026-08-04 18:10     ` Anisa Su
2026-08-04 16:55   ` Alison Schofield
2026-08-04 18:13     ` Anisa Su
2026-07-31  8:48 ` [PATCH v12 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
2026-08-03 14:33   ` Richard Cheng
2026-08-03 22:11     ` Anisa Su
2026-08-03 21:52   ` Alison Schofield
2026-08-04  9:12     ` Anisa Su
2026-08-04 16:00       ` Gregory Price
2026-08-03 23:55   ` Jonathan Cameron
2026-08-04  9:54     ` Anisa Su
2026-07-31  8:48 ` [PATCH v12 3/8] cxl/cdat: Gather DSMAS data for DCD partitions Anisa Su
2026-08-03 22:56   ` Alison Schofield
2026-08-05  6:58     ` Anisa Su
2026-07-31  8:48 ` [PATCH v12 4/8] cxl/events: Split event msgnum configuration from irq setup Anisa Su
2026-08-03 23:01   ` Alison Schofield
2026-07-31  8:48 ` [PATCH v12 5/8] cxl/pci: Factor out interrupt policy check Anisa Su
2026-08-03 23:55   ` Alison Schofield
2026-07-31  8:48 ` [PATCH v12 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
2026-08-04  0:13   ` Jonathan Cameron
2026-08-04  0:34   ` Alison Schofield
2026-07-31  8:48 ` [PATCH v12 7/8] cxl/core: Return endpoint decoder information from region search Anisa Su
2026-08-04  1:02   ` Alison Schofield
2026-07-31  8:48 ` [PATCH v12 8/8] cxl/core: Enforce partition order/simplify partition calls Anisa Su
2026-08-04  0:16   ` Jonathan Cameron
2026-08-04  1:37   ` Alison Schofield

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