* [PATCH v14 0/8] DCD Prep Series
@ 2026-09-18 20:30 Anisa Su
2026-09-18 20:30 ` [PATCH v14 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
` (7 more replies)
0 siblings, 8 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-18 20:30 UTC (permalink / raw)
To: linux-cxl
Cc: Dave Jiang, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Richard Cheng, Ben Cheatham, Anisa Su
These are the first eight patches of the Dynamic Capacity Device (DCD)
series. They set up: DCD enumeration, CDAT handling for DC partitions,
the event interrupt plumbing, a preparatory refactor, and
documentation of the DPA partition layout. They stand alone and keep DCD
disabled, and are not dependent on the rest of the series.
Patch 1 detects the 48h command set and then leaves mds->dcd_supported
false. The flag is only turned on by the series that adds DCD event
handling, 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.3-rc3
GitHub Branch:
https://github.com/anisa-su993/anisa-linux-kernel/tree/dcd-prep-v14
v13: https://lore.kernel.org/linux-cxl/20260908102124.2231730-2-anisa.su@samsung.com/T/#m5b414fa4a1ae0bf26421e2a38a668c43e1aec1fb
v12: https://lore.kernel.org/linux-cxl/20260731084901.1512819-1-anisa.su@samsung.com/
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: preparatory refactor -- enforce partition order while collapsing
the ram/pmem specific partition helpers into one.
8: document the DPA partition layout the previous patches enforce,
and where Linux is stricter than the specification.
v13 -> v14 Changes:
===================
- Patch 1: cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
- No code change. Tags only.
- Patch 2: cxl/mem: Read dynamic capacity configuration from the device
- Don't disable DCD when the device reports more than
CXL_MAX_DC_PARTITIONS. Today's spec allows 1 to 8, but a later
revision may allow more. Read only the first 8 and only configure
partition 0. (Jonathan)
- Reject a decode length that overflows when scaled by
CXL_CAPACITY_MULTIPLIER. Decode length is a count of 256M
units, so a device reporting 2^36 or more wraps and result in
a plausible looking partition size instead of an error
(Richard).
- Reject a DC partition size that overflows the DPA total in
cxl_configure_dcd(). The wrapped total read as zero capacity,
which cxl_dpa_setup() accepts silently, so the device
enumerated with no DPA and no error (Richard).
- Commit message: CXL r4.0 9.13.3 describes Get DC
Configuration as the way to obtain partition characteristics
rather than "require" it for set up; the single partition limit is
stated as initial enablement (Jonathan)
- Use min() instead of min_t() for calculating partition_count (Jonathan).
- Check media_ready at the start of cxl_configure_dcd();
don't configure DC partition 0 if the media is not ready (Claude /kreview)
- Patch 3: cxl/cdat: Gather DSMAS data for DCD partitions
- Record the DSMAS shareable flag independently of QOS
discovery. cxl_port_perf_data_calculate() returns -ENOENT when
firmware recommends no QTG for any entry, so the early return
on that skipped assigning the shareable flag even though the
the CDAT was successfully parsed. (Richard).
- Patch 4: cxl/events: Split event msgnum configuration from irq setup
- No code change. Tags only.
- Patch 5: cxl/pci: Factor out interrupt policy check
- No code change. Tags only.
- Patch 6: cxl/mem: Configure dynamic capacity interrupts
- Drop CXLDEV_EVENT_STATUS_ALL and instead opt into
each event log explicitly, especially because more log types
may be added in the future (Jonathan).
- Return early from cxl_event_config() if DCD is disabled and
BIOS owns the other logs. Nothing is left to configure, so
return early (Claude /kreview)
- Patch 7: cxl/core: Enforce partition order/simplify partition calls
- No code change. Tags only.
- Patch 8: Documentation/cxl: Document DPA partition layout and
ordering rules
- Drop the paragraph saying support for additional DC partitions
may be added later; documentation should not predict future
work. (Jonathan / Davidlohr).
- Break the DCD entry in maturity-map.rst into sub-items so
multi-step upstreaming shows what is there and sets
expectations: partition enumeration / configuration is [1],
extent add / release handling and DC-backed DAX regions stay
[0], and the parent stays [0] because nothing is usable until
dcd_supported is turned on (Davidlohr).
Review status
=============
- Dropped Alison's Tested-by tags, since the NDCTL regression testing
was ran against v13
Patch 2:
+ Alison's Reviewed-by tag on v13, but I added a check for media_ready
at the top of cxl_configure_dcd() before configuring DC partition 0
Please let me know if you would rather re-review!
Patch 6: + Reviewed-by Jonathan, Alison.
Besides the change requested by Jonathan (drop CXL_EVENT_STATUS_ALL),
I also added an early return in cxl_event_config() on BIOS-first
platforms once DCD has been disabled. I kept the tags, but please
let me know if both of you would rather re-review.
Testing
=======
On HW:
This series leaves mds->dcd_supported false on purpose, so
cxl_configure_dcd(), cxl_dev_dc_identify() and cxl_dc_check() are
unreachable at runtime as posted. So the test below is done with the rest
of the series on top, otherwise the results would not be very meaningful.
Tested by Wonjae Lee/Junhee Park/Heesoo Kim whose tested-by tags are
carried over to this revision.
Since this is the prep patch, we just tested adding and removing 1 extent
(2GB, tag=5be13bce-ae34-4a77-b6c3-16df975fcf1a) to verify that the
partition and interrupts are configured correctly.
--- Add ---
[14181.811999] cxl_pci 0000:b8:00.0: Reading event logs: 10
[14181.812040] cxl_pci 0000:b8:00.0: Sending command: 0x0100
[14181.812077] cxl_pci 0000:b8:00.0: Doorbell wait took 0ms
[14181.812121] cxl_pci 0000:b8:00.0: DCD event add : DPA:0x0 LEN:0x80000000
[14181.812133] cxl_pci 0000:b8:00.0: DC extent DPA [range 0x0000000000000000-0x000000007fffffff] (DCR:[range 0x0000000000000000-0x0000001fffffffff])(5be13bce-ae34-4a77-b6c3-16df975fcf1a)
[14181.812146] cxl_pci 0000:b8:00.0: DC extent DPA [range 0x0000000000000000-0x000000007fffffff] (DCR:[range 0x0000000000000000-0x0000001fffffffff])(5be13bce-ae34-4a77-b6c3-16df975fcf1a)
[14181.812152] cxl_pci 0000:b8:00.0: DC extent DPA [range 0x0000000000000000-0x000000007fffffff] (DCR:[range 0x0000000000000000-0x0000001fffffffff])(5be13bce-ae34-4a77-b6c3-16df975fcf1a)
[14181.812162] cxl decoder9.0: dpa:0x0 mapped in region:region9
[14181.812201] cxl decoder9.0: Add extent [range 0x0000000000000000-0x000000007fffffff] (5be13bce-ae34-4a77-b6c3-16df975fcf1a)
[14181.812208] cxl_dax_region dax_region9: Alloc new tag group
[14181.812257] extent9.1: dc_extent HPA [range 0x0000000000000000-0x000000007fffffff] (5be13bce-ae34-4a77-b6c3-16df975fcf1a)
[14181.812265] cxl_dax_region dax_region9: Trying notify: type 0 tag 5be13bce-ae34-4a77-b6c3-16df975fcf1a
[14181.812270] cxl_dax_region dax_region9: Notify: type 0 tag 5be13bce-ae34-4a77-b6c3-16df975fcf1a
[14181.812299] cxl_pci 0000:b8:00.0: Sending command: 0x4802
[14181.812473] cxl_pci 0000:b8:00.0: Doorbell wait took 0ms
[14181.812482] cxl_pci 0000:b8:00.0: Event log '4': Clearing 1
[14181.812488] cxl_pci 0000:b8:00.0: Sending command: 0x0101
[14181.812514] cxl_pci 0000:b8:00.0: Doorbell wait took 0ms
[14181.812520] cxl_pci 0000:b8:00.0: Sending command: 0x0100
[14181.812550] cxl_pci 0000:b8:00.0: Doorbell wait took 0ms
--- Release --
[14291.325683] cxl_pci 0000:b8:00.0: Reading event logs: 10
[14291.325701] cxl_pci 0000:b8:00.0: Sending command: 0x0100
[14291.325737] cxl_pci 0000:b8:00.0: Doorbell wait took 0ms
[14291.325820] cxl_pci 0000:b8:00.0: DCD event release : DPA:0x0 LEN:0x80000000
[14291.325833] cxl decoder9.0: dpa:0x0 mapped in region:region9
[14291.444166] cxl_dax_region dax_region9: Trying notify: type 1 tag 5be13bce-ae34-4a77-b6c3-16df975fcf1a
[14291.444175] cxl_dax_region dax_region9: Notify: type 1 tag 5be13bce-ae34-4a77-b6c3-16df975fcf1a
[14291.444203] extent9.1: DAX region rm extent HPA [range 0x0000000000000000-0x000000007fffffff]
[14291.444238] cxl decoder9.0: Remove extent [range 0x0000000000000000-0x000000007fffffff] (5be13bce-ae34-4a77-b6c3-16df975fcf1a)
[14291.444242] cxl_pci 0000:b8:00.0: Release response dpa [range 0x0000000000000000-0x000000007fffffff]
[14291.444258] cxl_pci 0000:b8:00.0: Sending command: 0x4803
[14291.444448] cxl_pci 0000:b8:00.0: Doorbell wait took 0ms
[14291.444471] cxl_pci 0000:b8:00.0: Event log '4': Clearing 3
[14291.444474] cxl_pci 0000:b8:00.0: Sending command: 0x0101
[14291.444497] cxl_pci 0000:b8:00.0: Doorbell wait took 0ms
[14291.444500] cxl_pci 0000:b8:00.0: Sending command: 0x0100
[14291.444523] cxl_pci 0000:b8:00.0: Doorbell wait took 0ms
cxl_test:
Ran cxl_test ndctl/pending for regression testing:
https://github.com/anisa-su993/ndctl-test-runner/actions/runs/35388628475/job/105741489718
18/18 Ok
QEMU:
Additional testing is done on QEMU for catching various cases that would not
occur on the hardware because of configuration differences.
The tool used automatically starts various topos using a QEMU branch
I modified to allow certain bad inputs for testing:
https://github.com/anisa-su993/cxl-tests/blob/main/dcd/run-all.sh
1. Eight DC partitions, only the first is used
-----------------------------------------------
2G ram + 512M pmem static, so static capacity ends at 0xa0000000, and
eight 512M DC partitions above it. Five partition records fit in one
reply on the 256 byte mailbox, so the read continues from partition 5
in a second command. This also tests that the loop to Get DC Region Info
works.
cxl_pci 0000:0d:00.0: Read 5/8 DC partitions
cxl_pci 0000:0d:00.0: DC partition 0 start 0xa0000000 size 0x20000000 blk_size: 0x200000
cxl_pci 0000:0d:00.0: Read 3/8 DC partitions
cxl_pci 0000:0d:00.0: Adding dynamic ram partition 1; 0xa0000000 size 0x20000000
cxl_pci 0000:0d:00.0: DPA(ram): [mem 0x00000000-0x7fffffff flags 0x200]
cxl_pci 0000:0d:00.0: DPA(pmem): [mem 0x80000000-0x9fffffff flags 0x200]
cxl_pci 0000:0d:00.0: DPA(dynamic_ram_1): [mem 0xa0000000-0xbfffffff flags 0x200]
All eight partitions are read, but only the first is validated and set up for use.
2. Block size/decode len/DSMAD handle validation
-------------------------------------------------
1 DC Partition.
Fuzz QEMU to respond to Get DC Configuration with a bad block size /
decode length / partition base/ DSMAD handle / # of partitions and
look for the corresponding dev_err message:
block size 0x100000040 DC partition 0 invalid block size 0x100000040
decode length 0 DC partition 0 zero decode length
decode length 2^36 DC partition 0 decode length 0x1000000000 overflows
base 0xa0001000 DC partition 0 base 0xa0001000 not aligned to 256M
base 0x90000000 DC partition 0 base 0x90000000 overlaps static capacity ending at 0xa0000000
base 0xb0000000 DC partition 0 base 0xb0000000 leaves a gap from static capacity ending at 0xa0000000
DSMAD handle 0x100 DSMAD handle 0x100 exceeds the 8 bit CDAT DSMAD handle
0 partitions available Device reported no DC partitions
Anisa Su (1):
Documentation/cxl: Document DPA partition layout and ordering rules
Ira Weiny (7):
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: Enforce partition order/simplify partition calls
.../driver-api/cxl/linux/cxl-driver.rst | 35 +++
Documentation/driver-api/cxl/maturity-map.rst | 4 +
drivers/cxl/core/cdat.c | 49 ++-
drivers/cxl/core/hdm.c | 13 +
drivers/cxl/core/mbox.c | 285 ++++++++++++++++++
drivers/cxl/core/memdev.c | 95 ++++--
drivers/cxl/cxl.h | 6 +-
drivers/cxl/cxlmem.h | 88 +++++-
drivers/cxl/mem.c | 2 +-
drivers/cxl/pci.c | 162 ++++++++--
include/cxl/cxl.h | 27 +-
tools/testing/cxl/test/mem.c | 5 +-
12 files changed, 684 insertions(+), 87 deletions(-)
base-commit: fd73f4a6659897191fa0d40695fe370925dd3780
--
2.43.0
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v14 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD)
2026-09-18 20:30 [PATCH v14 0/8] DCD Prep Series Anisa Su
@ 2026-09-18 20:30 ` Anisa Su
2026-09-18 20:30 ` [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
` (6 subsequent siblings)
7 siblings, 0 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-18 20:30 UTC (permalink / raw)
To: linux-cxl
Cc: Dave Jiang, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim
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
Leave mds->dcd_supported false. Dynamic capacity is only usable once
the driver can handle Add and Release Capacity events. Detect and log
the capability here and let the commit which adds DCD event handling set
the flag.
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: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Richard Cheng <icheng@nvidia.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
---
drivers/cxl/core/mbox.c | 39 +++++++++++++++++++++++++++++++++++++++
drivers/cxl/cxlmem.h | 20 ++++++++++++++++++++
2 files changed, 59 insertions(+)
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index 55828a836c01..199bb986d674 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -171,6 +171,33 @@ 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_is_poison_command(u16 opcode)
{
#define CXL_MBOX_OP_POISON_CMDS 0x43
@@ -759,6 +786,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;
@@ -787,11 +815,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 (bitmap_full(dcd_cmds, CXL_DCD_ENABLED_MAX))
+ 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 c401e3a1af06..0c8e23276dc6 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
@@ -445,6 +460,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;
@@ -505,6 +521,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] 41+ messages in thread
* [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device
2026-09-18 20:30 [PATCH v14 0/8] DCD Prep Series Anisa Su
2026-09-18 20:30 ` [PATCH v14 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
@ 2026-09-18 20:30 ` Anisa Su
2026-09-18 20:44 ` sashiko-bot
` (3 more replies)
2026-09-18 20:30 ` [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions Anisa Su
` (5 subsequent siblings)
7 siblings, 4 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-18 20:30 UTC (permalink / raw)
To: linux-cxl
Cc: Dave Jiang, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim
From: Ira Weiny <iweiny@kernel.org>
Devices which support Dynamic Capacity (DC) are configured
via mailbox commands. CXL r4.0 section 9.13.3 describes the use of
the Get DC Configuration command in order to obtain DCD partition
characteristics.
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.
Initial enablement for Linux only supports one dynamic capacity
partition. Read all the partitions the device reports but validate
only the first, and configure it as 'dynamic ram 1'.
The specification requires that volatile capacity starts at DPA 0 and pmem
starts at the DPA immediately following it, but DC partitions only need
to be 256MB aligned according to CXL r4.0 section 8.2.10.9.9.1 Table 8-347.
So a device could leave a gap between ram/pmem (static) capacity and its first
DC partition, or between one DC partition and the next.
However, Linux chooses not to support gaps between static and dynamic
capacity, and requires the first DC partition to begin at the DPA
immediately following static capacity.
Based on an original patch by Navneet Singh.
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: Alison Schofield <alison.schofield@intel.com>
---
Changes:
1. mbox.c: do not refuse DCD when the device reports more than
CXL_MAX_DC_PARTITIONS. A future spec revision may allow more and
Linux only maps partition 0, so read only the first 8. A count of
zero is still refused.
Jonathan
2. mbox.c: reject a decode length that overflows when scaled by
CXL_CAPACITY_MULTIPLIER. A device reporting 2^36 or more wraps and
yields a plausible looking partition size rather than an error.
Richard
3. memdev.c: reject a DC partition size that overflows the DPA total.
The wrapped total reads as zero capacity, which cxl_dpa_setup()
accepts silently, so the device enumerates with no DPA and no error.
Richard
4. memdev.c: skip DC configuration when the media is not ready, as
cxl_mem_dpa_fetch() does for static capacity. The DC base was compared
against a static size forced to 0, disabling DCD with a misleading
gap message, or configuring dynamic capacity on a DCD-only device
whose static partitions were withheld.
---
drivers/cxl/core/hdm.c | 2 +
drivers/cxl/core/mbox.c | 237 ++++++++++++++++++++++++++++++++++++++
drivers/cxl/core/memdev.c | 48 ++++++++
drivers/cxl/cxlmem.h | 55 +++++++++
drivers/cxl/pci.c | 6 +
include/cxl/cxl.h | 3 +-
6 files changed, 350 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 199bb986d674..c96c16cc08e1 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -1349,6 +1349,243 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
return -EBUSY;
}
+/*
+ * 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,
+ struct cxl_dc_partition *dev_part)
+{
+ u64 decode_len = le64_to_cpu(dev_part->decode_length);
+ u64 blk_size = le64_to_cpu(dev_part->block_size);
+ u64 len = le64_to_cpu(dev_part->length);
+ u64 size;
+
+ /*
+ * 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 = (struct cxl_dc_partition_info) { };
+ dev_dbg(dev, "Partition 0 unavailable for DC\n");
+ return 0;
+ }
+
+ /*
+ * Decode length is a count of 256M units, so a device reporting 2^36
+ * or more wraps.
+ */
+ if (check_mul_overflow(decode_len, (u64)CXL_CAPACITY_MULTIPLIER, &size)) {
+ dev_err(dev, "DC partition 0 decode length %#llx overflows\n",
+ decode_len);
+ return -EINVAL;
+ }
+
+ *part = (struct cxl_dc_partition_info) {
+ .start = le64_to_cpu(dev_part->base),
+ .size = size,
+ };
+
+ /*
+ * Block size is a power of 2 and a multiple of 40h. is_power_of_2()
+ * takes an unsigned long, which truncates blk_size on 32 bit.
+ */
+ if (blk_size == 0 || (blk_size & (blk_size - 1)) ||
+ blk_size % CXL_DCD_BLOCK_LINE_SIZE) {
+ dev_err(dev, "DC partition 0 invalid block size %#llx\n", blk_size);
+ return -EINVAL;
+ }
+
+ if (part->size == 0) {
+ dev_err(dev, "DC partition 0 zero decode length\n");
+ return -EINVAL;
+ }
+
+ if (len == 0) {
+ dev_err(dev, "DC partition 0 zero length\n");
+ return -EINVAL;
+ }
+
+ if (len > part->size) {
+ dev_err(dev, "DC partition 0 length %#llx exceeds decode length %#llx\n",
+ len, part->size);
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(len, blk_size)) {
+ dev_err(dev, "DC partition 0 length %#llx not a multiple of block size %#llx\n",
+ len, blk_size);
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(part->start, SZ_256M)) {
+ dev_err(dev, "DC partition 0 base %#llx not aligned to 256M\n",
+ part->start);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "DC partition 0 start %#llx size %#llx blk_size: %#llx\n",
+ part->start, part->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,
+ /* The device must return at least the fixed header */
+ .min_out = sizeof(*dc_resp),
+ };
+ 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 trailing extent/tag counts (CXL r4.0 Table 8-346) are not read */
+ 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 every partition the device reports, but validate only the first:
+ * Linux maps partition 0 and nothing else, so a defect in capacity the
+ * driver never touches is not a reason to refuse the device dynamic
+ * capacity. The remaining entries of @partitions are left unset.
+ *
+ * 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 avail_partitions = 0;
+ u8 start_partition;
+ u8 num_partitions;
+ u8 partition_count;
+ size_t dc_resp_size;
+
+ /*
+ * Bound requested number of partitions by mailbox payload size. The
+ * 256 byte spec minimum, verified in cxl_pci_setup_mailbox(), keeps
+ * the subtraction below from underflowing.
+ */
+ partition_count = min(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;
+
+ start_partition = 0;
+ num_partitions = 0;
+ do {
+ int returned, rc;
+
+ returned = cxl_get_dc_config(mbox, start_partition,
+ partition_count, dc_resp,
+ dc_resp_size);
+ if (returned < 0) {
+ dev_err(dev, "Failed to get DC config: %d\n", returned);
+ return returned;
+ }
+
+ if (returned == 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;
+ }
+
+ /* A DCD reports at least one partition (CXL r4.0 Table 8-346) */
+ if (dc_resp->avail_partition_count == 0) {
+ dev_err(dev, "Device reported no DC partitions\n");
+ return -EIO;
+ }
+
+ avail_partitions = min(dc_resp->avail_partition_count,
+ CXL_MAX_DC_PARTITIONS);
+
+ /*
+ * Only partition 0 is mapped, so it is the only one checked.
+ * The rest are read to complete the enumeration and dropped.
+ */
+ if (start_partition == 0) {
+ rc = cxl_dc_check(dev, &partitions[0],
+ &dc_resp->partition[0]);
+ if (rc)
+ return rc;
+ }
+
+ num_partitions += returned;
+ start_partition = num_partitions;
+
+ } while (num_partitions < avail_partitions);
+
+ /* Linux only supports the 1st partition; nothing to do if it is unavailable */
+ if (partitions[0].size == 0)
+ return -ENODEV;
+
+ *dc_info = partitions[0];
+ dev_dbg(dev, "Returning partition 0 %#llx size %#llx\n",
+ dc_info->start, dc_info->size);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
+
int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
{
struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index b3419df586b9..15df8db314d9 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -643,6 +643,54 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
}
EXPORT_SYMBOL_NS_GPL(cxl_mem_dpa_fetch, "CXL");
+int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
+{
+ struct cxl_dc_partition_info dc_info = { };
+ struct device *dev = mds->cxlds.dev;
+ u64 total;
+ int rc;
+
+ if (!mds->cxlds.media_ready)
+ return 0;
+
+ 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);
+ return rc;
+ }
+
+ 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;
+ }
+
+ if (check_add_overflow(info->size, dc_info.size, &total)) {
+ dev_err(dev,
+ "DC partition 0 size %#llx overflows DPA space ending at %#llx\n",
+ dc_info.size, info->size);
+ return -EINVAL;
+ }
+
+ info->size = total;
+ dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\n",
+ dc_info.start, dc_info.size);
+ add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
+
/**
* cxl_set_capacity: initialize dpa by a driver without a mailbox.
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 0c8e23276dc6..4fac65dbb808 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)
{
/*
@@ -689,6 +691,39 @@ struct cxl_mbox_set_shutdown_state_in {
u8 state;
} __packed;
+/* See CXL r4.0 Table 8-345 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-346 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-347 */
+ 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;
@@ -812,9 +847,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);
+int 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,
@@ -828,6 +872,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 c7c91e8dc51d..2fe9db4b3d86 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -868,6 +868,12 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (rc)
return rc;
+ if (cxl_dcd_supported(mds)) {
+ rc = cxl_configure_dcd(mds, &range_info);
+ if (rc)
+ cxl_disable_dcd(mds);
+ }
+
rc = cxl_dpa_setup(cxlds, &range_info);
if (rc)
return rc;
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 802b143de83d..4ef9511fbbdd 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] 41+ messages in thread
* [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-18 20:30 [PATCH v14 0/8] DCD Prep Series Anisa Su
2026-09-18 20:30 ` [PATCH v14 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
2026-09-18 20:30 ` [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
@ 2026-09-18 20:30 ` Anisa Su
2026-09-21 21:55 ` Dave Jiang
` (2 more replies)
2026-09-18 20:30 ` [PATCH v14 4/8] cxl/events: Split event msgnum configuration from irq setup Anisa Su
` (4 subsequent siblings)
7 siblings, 3 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-18 20:30 UTC (permalink / raw)
To: linux-cxl
Cc: Dave Jiang, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim
From: Ira Weiny <iweiny@kernel.org>
Additional DCD partition (AKA region) information is contained in the
DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
---
Changes:
1. cdat.c: record the DSMAS shareable flag independently of QTG
discovery. cxl_port_perf_data_calculate() returns -ENOENT when FW
recommends no QTG for any entry, which skipped the assignment even
though the CDAT parsed successfully and the DSMAS flag was valid.
Richard
---
drivers/cxl/core/cdat.c | 49 +++++++++++++++++++++++++++++----------
drivers/cxl/core/hdm.c | 1 +
drivers/cxl/core/mbox.c | 9 +++++++
drivers/cxl/core/memdev.c | 17 ++++++++------
drivers/cxl/cxlmem.h | 2 ++
include/cxl/cxl.h | 4 ++++
6 files changed, 63 insertions(+), 19 deletions(-)
diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
index 5c9f07262513..6e7950f12134 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,8 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
return -ENOMEM;
dent->handle = dsmas->dsmad_handle;
+ /* Shareable is CDAT 1.03 and later, DSMAS Flags bit 3 */
+ 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;
@@ -255,8 +258,13 @@ static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
dent->coord[ACCESS_COORDINATE_CPU].write_latency);
}
-static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
- struct xarray *dsmas_xa)
+/*
+ * The DSMAS shareable flag is a property of the CDAT entry and is recorded
+ * even if FW returns no QTG recommendations for any entry; only update perf
+ * if those recommendations exist, indicated by @set_perf
+ */
+static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
+ struct xarray *dsmas_xa, bool set_perf)
{
struct device *dev = cxlds->dev;
struct dsmas_entry *dent;
@@ -266,18 +274,34 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
bool found = false;
for (int i = 0; i < cxlds->nr_partitions; i++) {
- struct resource *res = &cxlds->part[i].res;
+ struct cxl_dpa_partition *part = &cxlds->part[i];
struct range range = {
- .start = res->start,
- .end = res->end,
+ .start = part->res.start,
+ .end = part->res.end,
};
- if (range_contains(&range, &dent->dpa_range)) {
- update_perf_entry(dev, dent,
- &cxlds->part[i].perf);
- found = true;
+ if (!range_contains(&range, &dent->dpa_range))
+ continue;
+
+ found = true;
+ /*
+ * 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;
}
+
+ if (set_perf)
+ update_perf_entry(dev, dent, &part->perf);
+ if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
+ part->shareable = dent->shareable;
+ break;
}
if (!found)
@@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
}
rc = cxl_port_perf_data_calculate(port, dsmas_xa);
- if (rc) {
+ if (rc)
dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
+
+ cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
+ if (rc)
return;
- }
- cxl_memdev_set_qos_class(cxlds, dsmas_xa);
cxl_qos_class_verify(cxlmd);
cxl_memdev_update_perf(cxlmd);
}
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 c96c16cc08e1..d84a90320081 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -1366,6 +1366,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
u64 decode_len = le64_to_cpu(dev_part->decode_length);
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);
u64 size;
/*
@@ -1378,6 +1379,13 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
return 0;
}
+ /* 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;
+ }
+
/*
* Decode length is a count of 256M units, so a device reporting 2^36
* or more wraps.
@@ -1391,6 +1399,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
*part = (struct cxl_dc_partition_info) {
.start = le64_to_cpu(dev_part->base),
.size = size,
+ .handle = handle,
};
/*
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index 15df8db314d9..8883c8850759 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -594,7 +594,8 @@ bool is_cxl_memdev(const struct device *dev)
}
EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "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;
@@ -606,6 +607,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++;
}
@@ -623,9 +625,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;
}
@@ -635,9 +637,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;
}
@@ -685,7 +687,8 @@ int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
info->size = total;
dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\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);
return 0;
}
@@ -704,7 +707,7 @@ int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
.size = capacity,
};
- add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
+ add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM, 0);
return cxl_dpa_setup(cxlds, &range_info);
}
EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 4fac65dbb808..9fd5af2e41d4 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;
};
@@ -851,6 +852,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 4ef9511fbbdd..43edcde5bce3 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 that represents this partition
+ * @shareable: Is the partition shareable (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] 41+ messages in thread
* [PATCH v14 4/8] cxl/events: Split event msgnum configuration from irq setup
2026-09-18 20:30 [PATCH v14 0/8] DCD Prep Series Anisa Su
` (2 preceding siblings ...)
2026-09-18 20:30 ` [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions Anisa Su
@ 2026-09-18 20:30 ` Anisa Su
2026-09-22 5:35 ` Richard Cheng
2026-09-18 20:30 ` [PATCH v14 5/8] cxl/pci: Factor out interrupt policy check Anisa Su
` (3 subsequent siblings)
7 siblings, 1 reply; 41+ messages in thread
From: Anisa Su @ 2026-09-18 20:30 UTC (permalink / raw)
To: linux-cxl
Cc: Dave Jiang, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim, Jonathan Cameron,
Fan Ni
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>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
---
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 2fe9db4b3d86..4867657f5526 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -600,35 +600,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;
@@ -675,11 +671,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] 41+ messages in thread
* [PATCH v14 5/8] cxl/pci: Factor out interrupt policy check
2026-09-18 20:30 [PATCH v14 0/8] DCD Prep Series Anisa Su
` (3 preceding siblings ...)
2026-09-18 20:30 ` [PATCH v14 4/8] cxl/events: Split event msgnum configuration from irq setup Anisa Su
@ 2026-09-18 20:30 ` Anisa Su
2026-09-22 5:37 ` Richard Cheng
2026-09-18 20:30 ` [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
` (2 subsequent siblings)
7 siblings, 1 reply; 41+ messages in thread
From: Anisa Su @ 2026-09-18 20:30 UTC (permalink / raw)
To: linux-cxl
Cc: Dave Jiang, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny,
Dan Williams, Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim,
Jonathan Cameron, Fan Ni
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 check for firmware ownership of memory event logs.
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>
Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
---
drivers/cxl/pci.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 4867657f5526..7b754af2168d 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -640,6 +640,14 @@ static bool cxl_event_int_is_fw(u8 setting)
return mode == CXL_INT_FW;
}
+static bool cxl_event_fw_owns_mem_logs(struct cxl_event_interrupt_policy *policy)
+{
+ return 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);
+}
+
static int cxl_event_config(struct pci_host_bridge *host_bridge,
struct cxl_memdev_state *mds, bool irq_avail)
{
@@ -662,10 +670,7 @@ 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)) {
+ if (cxl_event_fw_owns_mem_logs(&policy)) {
dev_err(mds->cxlds.dev,
"FW still in control of Event Logs despite _OSC settings\n");
return -EBUSY;
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts
2026-09-18 20:30 [PATCH v14 0/8] DCD Prep Series Anisa Su
` (4 preceding siblings ...)
2026-09-18 20:30 ` [PATCH v14 5/8] cxl/pci: Factor out interrupt policy check Anisa Su
@ 2026-09-18 20:30 ` Anisa Su
2026-09-18 20:43 ` sashiko-bot
` (2 more replies)
2026-09-18 20:30 ` [PATCH v14 7/8] cxl/core: Enforce partition order/simplify partition calls Anisa Su
2026-09-18 20:30 ` [PATCH v14 8/8] Documentation/cxl: Document DPA partition layout and ordering rules Anisa Su
7 siblings, 3 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-18 20:30 UTC (permalink / raw)
To: linux-cxl
Cc: Dave Jiang, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim
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.
The DCD event log is not drained here. cxl_event_drain_mask() reports
only the logs the driver can service, and the DCD bit is added by
the patch introducing DCD event handling. Until then a DCD interrupt
wakes the event thread, which finds no log it owns and returns.
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>
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: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
---
Changes:
1. [jonathan]: get rid of CXL_EVENT_STATUS_ALL and instead opt into
event logs we want to read individually in cxl_event_drain_mask. This
makes which logs the driver drains explicit if new logs are added in
the future. The cxl_test mock names the same four logs for its
probe-time drain.
2. pci.c: when BIOS owns the memory event logs and a short interrupt
policy has just disabled DCD, return before Set Event Interrupt
Policy and the event buffer allocation. Nothing is left to configure,
and the Set would only have echoed firmware's own settings back.
---
drivers/cxl/cxl.h | 6 +-
drivers/cxl/cxlmem.h | 2 +
drivers/cxl/pci.c | 127 +++++++++++++++++++++++++++++------
tools/testing/cxl/test/mem.c | 5 +-
4 files changed, 114 insertions(+), 26 deletions(-)
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index cab8ce39f465..1add114bc178 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -192,11 +192,7 @@ 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_ALL (CXLDEV_EVENT_STATUS_INFO | \
- CXLDEV_EVENT_STATUS_WARN | \
- CXLDEV_EVENT_STATUS_FAIL | \
- CXLDEV_EVENT_STATUS_FATAL)
+#define CXLDEV_EVENT_STATUS_DCD BIT(4)
/* 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 9fd5af2e41d4..9106affd7c81 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 7b754af2168d..fee3a704b73b 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -510,11 +510,23 @@ static bool cxl_alloc_irq_vectors(struct pci_dev *pdev)
return true;
}
+/* Event logs the driver drains: standard logs when native_cxl */
+static u32 cxl_event_drain_mask(struct pci_host_bridge *host_bridge)
+{
+ if (host_bridge->native_cxl_error)
+ return CXLDEV_EVENT_STATUS_INFO | CXLDEV_EVENT_STATUS_WARN |
+ CXLDEV_EVENT_STATUS_FAIL | CXLDEV_EVENT_STATUS_FATAL;
+ return 0;
+}
+
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);
u32 status;
do {
@@ -523,8 +535,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);
@@ -551,42 +563,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);
@@ -597,7 +629,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,
@@ -633,6 +665,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);
@@ -651,32 +707,57 @@ static bool cxl_event_fw_owns_mem_logs(struct cxl_event_interrupt_policy *policy
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 mask;
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_fw_owns_mem_logs(&policy)) {
+ /*
+ * dcd_settings cannot be sent to a device whose policy is too short
+ * to hold it, so the device would never accept the setting
+ * cxl_irqsetup() arms the DCD interrupt with.
+ */
+ if (cxl_dcd_supported(mds) && policy_size < sizeof(policy)) {
+ dev_warn(mds->cxlds.dev,
+ "DCD supported but interrupt policy is only %zu bytes\n",
+ policy_size);
+ cxl_disable_dcd(mds);
+ /* DCD was the only reason to be here */
+ if (!native_cxl)
+ return 0;
+ }
+
+ if (native_cxl && cxl_event_fw_owns_mem_logs(&policy)) {
dev_err(mds->cxlds.dev,
"FW still in control of Event Logs despite _OSC settings\n");
return -EBUSY;
}
- rc = cxl_event_config_msgnums(mds, &policy);
+ rc = cxl_event_config_msgnums(mds, &policy, native_cxl, policy_size);
if (rc)
return rc;
@@ -684,11 +765,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);
+ mask = cxl_event_drain_mask(host_bridge);
+ if (mask)
+ cxl_mem_get_event_records(mds, mask);
+
+ dev_dbg(mds->cxlds.dev, "Event config : %s DCD %s\n",
+ native_cxl ? "OS" : "BIOS",
+ cxl_dcd_supported(mds) ? "supported" : "not supported");
return 0;
}
diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
index 7b756000a1a6..6ef47265da10 100644
--- a/tools/testing/cxl/test/mem.c
+++ b/tools/testing/cxl/test/mem.c
@@ -1818,7 +1818,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
if (rc)
dev_dbg(dev, "No CXL FWCTL setup\n");
- cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
+ cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_INFO |
+ CXLDEV_EVENT_STATUS_WARN |
+ CXLDEV_EVENT_STATUS_FAIL |
+ CXLDEV_EVENT_STATUS_FATAL);
cxl_mock_test_feat_init(mdata);
return 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v14 7/8] cxl/core: Enforce partition order/simplify partition calls
2026-09-18 20:30 [PATCH v14 0/8] DCD Prep Series Anisa Su
` (5 preceding siblings ...)
2026-09-18 20:30 ` [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
@ 2026-09-18 20:30 ` Anisa Su
2026-09-18 20:30 ` [PATCH v14 8/8] Documentation/cxl: Document DPA partition layout and ordering rules Anisa Su
7 siblings, 0 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-18 20:30 UTC (permalink / raw)
To: linux-cxl
Cc: Dave Jiang, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim
From: Ira Weiny <iweiny@kernel.org>
Device partitions share one flat DPA space in a fixed order: static
volatile capacity at DPA 0, static persistent capacity immediately above
it, and dynamic capacity above all static capacity. The static pair is
mandated by CXL r4.0 8.2.10.9.2.1 "Get Partition Info", Table 8-310: the
device "shall provide this volatile capacity starting at DPA 0" and
"shall provide this persistent capacity starting at the DPA immediately
following the volatile capacity". Dynamic capacity has no such rule.
Table 8-347 only requires the region base to be 256M aligned, so placing
it directly above static capacity is a Linux requirement,
enforced by cxl_configure_dcd().
DPA 0 end
+---------------+---------------+---------------------------+
| ram | pmem | dynamic_ram_1 |
+---------------+---------------+---------------------------+
part[0] part[1] part[2]
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>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
---
drivers/cxl/core/hdm.c | 10 ++++++++++
drivers/cxl/core/memdev.c | 32 +++++++++-----------------------
drivers/cxl/cxlmem.h | 9 +++------
drivers/cxl/mem.c | 2 +-
include/cxl/cxl.h | 20 ++++++++++++++++++++
5 files changed, 43 insertions(+), 30 deletions(-)
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 7f63b86887f4..18e9dfa115fb 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -469,6 +469,16 @@ int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
return 0;
}
+ /* Verify partitions are in expected order. */
+ for (int i = 1; i < info->nr_partitions; i++) {
+ if (info->part[i].mode < info->part[i - 1].mode) {
+ dev_err(dev, "Partition order mismatch: %d (%s) follows %d (%s)\n",
+ i, cxl_mode_name(info->part[i].mode),
+ i - 1, cxl_mode_name(info->part[i - 1].mode));
+ return -EINVAL;
+ }
+ }
+
cxlds->dpa_res = DEFINE_RES_MEM(0, info->size);
for (int i = 0; i < info->nr_partitions; i++) {
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index 8883c8850759..88339e393054 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 9106affd7c81..35268d1c387c 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)
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 43edcde5bce3..b6895302a6ee 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -130,6 +130,26 @@ struct cxl_dpa_perf {
int qos_class;
};
+/*
+ * Partitions share one flat DPA space in a fixed order:
+ *
+ * DPA 0 end
+ * +---------------+---------------+---------------------------+
+ * | ram | pmem | dynamic_ram_1 |
+ * +---------------+---------------+---------------------------+
+ * part[0] part[1] part[2]
+ *
+ * The static ram and pmem partition order is mandated by CXL r4.0
+ * 8.2.10.9.2.1 "Get Partition Info" (opcode 4100h), Table 8-310: volatile
+ * capacity starts at DPA 0 and persistent capacity starts at the DPA
+ * immediately following it. A dynamic partition has no such
+ * requirement -- 8.2.10.9.9.1 "Get Dynamic Capacity Configuration"
+ * (opcode 4800h), Table 8-347, only requires the DC Region Base to be
+ * 256MB aligned -- but Linux follows that precedent and requires it to
+ * start at the DPA immediately following static capacity: after pmem,
+ * after ram on a device with no pmem, or at DPA 0 on a device with no
+ * static capacity at all. cxl_configure_dcd() enforces it.
+ */
enum cxl_partition_mode {
CXL_PARTMODE_RAM,
CXL_PARTMODE_PMEM,
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v14 8/8] Documentation/cxl: Document DPA partition layout and ordering rules
2026-09-18 20:30 [PATCH v14 0/8] DCD Prep Series Anisa Su
` (6 preceding siblings ...)
2026-09-18 20:30 ` [PATCH v14 7/8] cxl/core: Enforce partition order/simplify partition calls Anisa Su
@ 2026-09-18 20:30 ` Anisa Su
2026-09-21 22:02 ` Dave Jiang
7 siblings, 1 reply; 41+ messages in thread
From: Anisa Su @ 2026-09-18 20:30 UTC (permalink / raw)
To: linux-cxl
Cc: Dave Jiang, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Richard Cheng, Ben Cheatham, Anisa Su
DC Partitions complicate DPA ordering. Add a DPA Partitions section to
cxl-driver.rst describing spec-mandated and Linux requirements for the
layout.
maturity-map.rst breaks the DCD entry into sub-items so the map shows
what this series lands: partition enumeration is [1], event handling
and DC-backed DAX regions remain [0] until the follow-on series enables
them. The parent stays [0]; nothing is usable yet.
Suggested-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Anisa Su <anisa.su@samsung.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
---
Changes:
[jonathan]: drop paragraph describing "support for additional
dc partitions may be added later..." to avoid predicting future work in
documentation
[davidlohr]: add DCD sub-items to maturity-map.rst so a multi-step
upstreaming shows users what is there and sets expectations.
---
.../driver-api/cxl/linux/cxl-driver.rst | 35 +++++++++++++++++++
Documentation/driver-api/cxl/maturity-map.rst | 4 +++
2 files changed, 39 insertions(+)
diff --git a/Documentation/driver-api/cxl/linux/cxl-driver.rst b/Documentation/driver-api/cxl/linux/cxl-driver.rst
index dd6dd17dc536..eb4de2a9a9af 100644
--- a/Documentation/driver-api/cxl/linux/cxl-driver.rst
+++ b/Documentation/driver-api/cxl/linux/cxl-driver.rst
@@ -181,6 +181,41 @@ A Memory Device is a discrete base object that is not a port. While the
physical device it belongs to may also host an `endpoint`, the relationship
between an `endpoint` and a `memdev` is not captured in sysfs.
+DPA Partitions
+~~~~~~~~~~~~~~
+A memory device presents its capacity as one flat `Device Physical Address`
+(DPA) space divided into `partitions`, which Linux lays out in a fixed
+order::
+
+ DPA 0 end
+ +---------------+---------------+---------------------------+
+ | ram | pmem | dynamic_ram_1 |
+ +---------------+---------------+---------------------------+
+ part[0] part[1] part[2]
+
+Part of that order is required by the CXL specification and part of it is a
+Linux choice.
+
+The `ram` and `pmem` order is mandated. CXL r4.0 section 8.2.10.9.2.1 "Get
+Partition Info" (4100h), Table 8-310, mandates that volatile capacity starts
+at DPA 0 and pmem starts at the DPA immediately following it.
+
+Dynamic Capacity partitions only need to be 256MB aligned according to
+CXL r4.0 section 8.2.10.9.9.1 "Get Dynamic Capacity Configuration"
+(opcode 4800h), Table 8-347. So a device could leave a gap between ram/pmem
+(static) capacity and its first DC partition, or between one DC partition
+and the next.
+
+Linux chooses not to support gaps between static and dynamic capacity: the
+first DC partition must begin at the DPA immediately following static
+capacity -- after pmem, after ram on a device with no pmem, or at DPA 0 on
+a device with no static capacity at all.
+
+Currently, only one dynamic partition is supported. A device may report up
+to eight (CXL r4.0 Table 8-346); Linux configures the first and exposes it as
+`dynamic_ram_1`. Gaps between DC partitions are not checked, since only the
+first is used.
+
Port Relationships
~~~~~~~~~~~~~~~~~~
In our example described above, there are four host bridges attached to the
diff --git a/Documentation/driver-api/cxl/maturity-map.rst b/Documentation/driver-api/cxl/maturity-map.rst
index 282c1102dd81..fefd31899229 100644
--- a/Documentation/driver-api/cxl/maturity-map.rst
+++ b/Documentation/driver-api/cxl/maturity-map.rst
@@ -150,6 +150,10 @@ Memory-pooling
* [1] Hotplug of LDs (via PCI hotplug)
* [0] Dynamic Capacity Device (DCD) Support
+ * [1] DC partition enumeration / configuration (Get DC Config, CDAT DSMAS, event interrupts)
+ * [0] Extent add / release event handling
+ * [0] DC-backed DAX regions
+
Multi-host sharing
------------------
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts
2026-09-18 20:30 ` [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
@ 2026-09-18 20:43 ` sashiko-bot
2026-09-18 23:38 ` Anisa Su
2026-09-21 22:00 ` Dave Jiang
2026-09-22 5:48 ` Richard Cheng
2 siblings, 1 reply; 41+ messages in thread
From: sashiko-bot @ 2026-09-18 20:43 UTC (permalink / raw)
To: Anisa Su; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] cxl: probe fails on non-native CXL systems if DCD event config fails
--
commit 7a8483673c7e3c3849faf44e013fead5e793c1b6
Author: Ira Weiny <iweiny@kernel.org>
cxl/mem: Configure dynamic capacity interrupts
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.
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 7b754af2168d0..fee3a704b73ba 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
[ ... ]
> @@ -651,32 +707,57 @@ static bool cxl_event_fw_owns_mem_logs(struct cxl_event_interrupt_policy *policy
> 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 mask;
> 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;
[Severity: High]
If native_cxl is false and cxl_dcd_supported(mds) is true, does this early
return abort the entire device probe instead of gracefully degrading?
If the firmware rejects the mailbox command, it looks like this will propagate
the error and fail the probe instead of calling cxl_disable_dcd(mds) and
returning 0 like before.
>
> - if (cxl_event_fw_owns_mem_logs(&policy)) {
> + /*
> + * dcd_settings cannot be sent to a device whose policy is too short
> + * to hold it, so the device would never accept the setting
> + * cxl_irqsetup() arms the DCD interrupt with.
> + */
> + if (cxl_dcd_supported(mds) && policy_size < sizeof(policy)) {
> + dev_warn(mds->cxlds.dev,
> + "DCD supported but interrupt policy is only %zu bytes\n",
> + policy_size);
> + cxl_disable_dcd(mds);
> + /* DCD was the only reason to be here */
> + if (!native_cxl)
> + return 0;
> + }
> +
> + if (native_cxl && cxl_event_fw_owns_mem_logs(&policy)) {
> dev_err(mds->cxlds.dev,
> "FW still in control of Event Logs despite _OSC settings\n");
> return -EBUSY;
> }
>
> - rc = cxl_event_config_msgnums(mds, &policy);
> + rc = cxl_event_config_msgnums(mds, &policy, native_cxl, policy_size);
> if (rc)
> return rc;
[Severity: High]
Similarly here, if the SET command fails on a non-native CXL system, does
this propagate the error and abort the probe?
Should it also disable DCD and return 0 in that scenario?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260918203049.7273-1-anisa.su@samsung.com?part=6
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device
2026-09-18 20:30 ` [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
@ 2026-09-18 20:44 ` sashiko-bot
2026-09-18 22:59 ` Anisa Su
2026-09-21 21:50 ` Dave Jiang
` (2 subsequent siblings)
3 siblings, 1 reply; 41+ messages in thread
From: sashiko-bot @ 2026-09-18 20:44 UTC (permalink / raw)
To: Anisa Su; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] cxl/mem: cxl_configure_dcd() incorrectly treats Total Capacity as static capacity boundary
--
commit 72ce11842054ad85a9e98674e8c97e80cf0a2d21
Author: Ira Weiny <iweiny@kernel.org>
cxl/mem: Read dynamic capacity configuration from the device
This commit implements the Dynamic Capacity (DC) mailbox commands to read
and store CXL DCD configuration information. It validates the first
partition and ensures there are no gaps or overlaps before configuring it.
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index b3419df586b9c..15df8db314d90 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -643,6 +643,54 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
[ ... ]
> +int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> +{
> + struct cxl_dc_partition_info dc_info = { };
> + struct device *dev = mds->cxlds.dev;
> + u64 total;
> + int rc;
> +
> + if (!mds->cxlds.media_ready)
> + return 0;
> +
> + 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);
> + return rc;
> + }
> +
> + 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;
> + }
[Severity: High]
Does info->size here represent the total capacity (volatile + persistent +
dynamic) rather than just the static capacity?
Since cxl_mem_dpa_fetch() initializes info->size to mds->total_bytes
(which is the total capacity), comparing the dynamic partition's base
against this will evaluate to true for spec-compliant devices,
unconditionally rejecting them as overlaps.
> +
> + /* 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;
> + }
[Severity: High]
If info->size includes dynamic capacity, would enforcing that the DCD
partition starts exactly at info->size be physically impossible without
overlapping the unprovisioned space at the end of the DPA?
> +
> + if (check_add_overflow(info->size, dc_info.size, &total)) {
> + dev_err(dev,
> + "DC partition 0 size %#llx overflows DPA space ending at %#llx\n",
> + dc_info.size, info->size);
> + return -EINVAL;
> + }
> +
> + info->size = total;
[Severity: High]
Could this lead to double-counting the dynamic capacity?
Since info->size already includes the dynamic capacity per the CXL spec,
adding dc_info.size here would inflate the value.
If a non-compliant device bypassed the overlap checks, this inflated size
could also cause the strict contiguity check in cxl_dpa_setup() to fail
later during device probe:
drivers/cxl/core/hdm.c:cxl_dpa_setup() {
...
/* Require ordered + contiguous partitions */
if (i) {
const struct cxl_dpa_part_info *prev = &info->part[i - 1];
if (prev->range.end + 1 != part->range.start)
return -EINVAL;
}
...
}
> + dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\n",
> + dc_info.start, dc_info.size);
> + add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260918203049.7273-1-anisa.su@samsung.com?part=2
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device
2026-09-18 20:44 ` sashiko-bot
@ 2026-09-18 22:59 ` Anisa Su
0 siblings, 0 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-18 22:59 UTC (permalink / raw)
To: sashiko-reviews; +Cc: Anisa Su, linux-cxl
On Fri, Sep 18, 2026 at 08:44:28PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] cxl/mem: cxl_configure_dcd() incorrectly treats Total Capacity as static capacity boundary
False Positive
> --
>
> commit 72ce11842054ad85a9e98674e8c97e80cf0a2d21
> Author: Ira Weiny <iweiny@kernel.org>
>
> cxl/mem: Read dynamic capacity configuration from the device
>
> This commit implements the Dynamic Capacity (DC) mailbox commands to read
> and store CXL DCD configuration information. It validates the first
> partition and ensures there are no gaps or overlaps before configuring it.
>
> > diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> > index b3419df586b9c..15df8db314d90 100644
> > --- a/drivers/cxl/core/memdev.c
> > +++ b/drivers/cxl/core/memdev.c
> > @@ -643,6 +643,54 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
>
> [ ... ]
>
> > +int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> > +{
> > + struct cxl_dc_partition_info dc_info = { };
> > + struct device *dev = mds->cxlds.dev;
> > + u64 total;
> > + int rc;
> > +
> > + if (!mds->cxlds.media_ready)
> > + return 0;
> > +
> > + 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);
> > + return rc;
> > + }
> > +
> > + 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;
> > + }
>
> [Severity: High]
> Does info->size here represent the total capacity (volatile + persistent +
> dynamic) rather than just the static capacity?
>
No. The info returned by Identify Memory Device does not include dynamic
capacity.
That's why we need to send Get Dynamic Capacity Configuration to find DC
partitions and their sizes.
> Since cxl_mem_dpa_fetch() initializes info->size to mds->total_bytes
> (which is the total capacity), comparing the dynamic partition's base
> against this will evaluate to true for spec-compliant devices,
> unconditionally rejecting them as overlaps.
>
> > +
> > + /* 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;
> > + }
>
> [Severity: High]
> If info->size includes dynamic capacity, would enforcing that the DCD
> partition starts exactly at info->size be physically impossible without
> overlapping the unprovisioned space at the end of the DPA?
>
info->size does not include dynamic capacity.
> > +
> > + if (check_add_overflow(info->size, dc_info.size, &total)) {
> > + dev_err(dev,
> > + "DC partition 0 size %#llx overflows DPA space ending at %#llx\n",
> > + dc_info.size, info->size);
> > + return -EINVAL;
> > + }
> > +
> > + info->size = total;
>
> [Severity: High]
> Could this lead to double-counting the dynamic capacity?
>
> Since info->size already includes the dynamic capacity per the CXL spec,
> adding dc_info.size here would inflate the value.
>
info->size does not include dynamic capacity -__-
> If a non-compliant device bypassed the overlap checks, this inflated size
> could also cause the strict contiguity check in cxl_dpa_setup() to fail
> later during device probe:
>
> drivers/cxl/core/hdm.c:cxl_dpa_setup() {
> ...
> /* Require ordered + contiguous partitions */
> if (i) {
> const struct cxl_dpa_part_info *prev = &info->part[i - 1];
>
> if (prev->range.end + 1 != part->range.start)
> return -EINVAL;
> }
> ...
> }
>
> > + dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\n",
> > + dc_info.start, dc_info.size);
> > + add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> > +
> > + return 0;
> > +}
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260918203049.7273-1-anisa.su@samsung.com?part=2
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts
2026-09-18 20:43 ` sashiko-bot
@ 2026-09-18 23:38 ` Anisa Su
0 siblings, 0 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-18 23:38 UTC (permalink / raw)
To: sashiko-reviews; +Cc: Anisa Su, linux-cxl
On Fri, Sep 18, 2026 at 08:43:02PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] cxl: probe fails on non-native CXL systems if DCD event config fails
> --
>
> commit 7a8483673c7e3c3849faf44e013fead5e793c1b6
> Author: Ira Weiny <iweiny@kernel.org>
>
> cxl/mem: Configure dynamic capacity interrupts
>
> 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.
>
> > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> > index 7b754af2168d0..fee3a704b73ba 100644
> > --- a/drivers/cxl/pci.c
> > +++ b/drivers/cxl/pci.c
>
> [ ... ]
>
> > @@ -651,32 +707,57 @@ static bool cxl_event_fw_owns_mem_logs(struct cxl_event_interrupt_policy *policy
> > 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 mask;
> > 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;
>
> [Severity: High]
> If native_cxl is false and cxl_dcd_supported(mds) is true, does this early
> return abort the entire device probe instead of gracefully degrading?
>
> If the firmware rejects the mailbox command, it looks like this will propagate
> the error and fail the probe instead of calling cxl_disable_dcd(mds) and
> returning 0 like before.
>
Mailbox errors in other spots already fail probe (Identify, Get Partition Info,
Set Timestamp, etc.).
Also the other places where we disable DCD and move on are for scenarios where the
device is otherwise healthy but missing the DCD IRQ capability (we couldn't get
the IRQ vector for DCD, the interrupt policy doesn't hold the DCD bit, etc.). A
mailbox error isn't the same and maybe better to just fail fast here.
But I'm not against changing this part to turn off DCD and let probe
continue either. Just lazy...
> >
> > - if (cxl_event_fw_owns_mem_logs(&policy)) {
> > + /*
> > + * dcd_settings cannot be sent to a device whose policy is too short
> > + * to hold it, so the device would never accept the setting
> > + * cxl_irqsetup() arms the DCD interrupt with.
> > + */
> > + if (cxl_dcd_supported(mds) && policy_size < sizeof(policy)) {
> > + dev_warn(mds->cxlds.dev,
> > + "DCD supported but interrupt policy is only %zu bytes\n",
> > + policy_size);
> > + cxl_disable_dcd(mds);
> > + /* DCD was the only reason to be here */
> > + if (!native_cxl)
> > + return 0;
> > + }
> > +
> > + if (native_cxl && cxl_event_fw_owns_mem_logs(&policy)) {
> > dev_err(mds->cxlds.dev,
> > "FW still in control of Event Logs despite _OSC settings\n");
> > return -EBUSY;
> > }
> >
> > - rc = cxl_event_config_msgnums(mds, &policy);
> > + rc = cxl_event_config_msgnums(mds, &policy, native_cxl, policy_size);
> > if (rc)
> > return rc;
>
> [Severity: High]
> Similarly here, if the SET command fails on a non-native CXL system, does
> this propagate the error and abort the probe?
>
> Should it also disable DCD and return 0 in that scenario?
>
Same as above for Get.
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260918203049.7273-1-anisa.su@samsung.com?part=6
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device
2026-09-18 20:30 ` [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
2026-09-18 20:44 ` sashiko-bot
@ 2026-09-21 21:50 ` Dave Jiang
2026-09-21 23:19 ` Jonathan Cameron
2026-09-22 3:44 ` Richard Cheng
3 siblings, 0 replies; 41+ messages in thread
From: Dave Jiang @ 2026-09-21 21:50 UTC (permalink / raw)
To: Anisa Su, linux-cxl
Cc: Alison Schofield, Jonathan Cameron, Davidlohr Bueso, Li Ming,
Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny, Anisa Su,
Wonjae Lee, Junhee Park, Heesoo Kim
On 9/18/26 1:30 PM, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
>
> Devices which support Dynamic Capacity (DC) are configured
> via mailbox commands. CXL r4.0 section 9.13.3 describes the use of
> the Get DC Configuration command in order to obtain DCD partition
> characteristics.
>
> 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.
>
> Initial enablement for Linux only supports one dynamic capacity
> partition. Read all the partitions the device reports but validate
> only the first, and configure it as 'dynamic ram 1'.
>
> The specification requires that volatile capacity starts at DPA 0 and pmem
> starts at the DPA immediately following it, but DC partitions only need
> to be 256MB aligned according to CXL r4.0 section 8.2.10.9.9.1 Table 8-347.
> So a device could leave a gap between ram/pmem (static) capacity and its first
> DC partition, or between one DC partition and the next.
>
> However, Linux chooses not to support gaps between static and dynamic
> capacity, and requires the first DC partition to begin at the DPA
> immediately following static capacity.
>
> Based on an original patch by Navneet Singh.
>
> 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: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> ---
> Changes:
> 1. mbox.c: do not refuse DCD when the device reports more than
> CXL_MAX_DC_PARTITIONS. A future spec revision may allow more and
> Linux only maps partition 0, so read only the first 8. A count of
> zero is still refused.
> Jonathan
> 2. mbox.c: reject a decode length that overflows when scaled by
> CXL_CAPACITY_MULTIPLIER. A device reporting 2^36 or more wraps and
> yields a plausible looking partition size rather than an error.
> Richard
> 3. memdev.c: reject a DC partition size that overflows the DPA total.
> The wrapped total reads as zero capacity, which cxl_dpa_setup()
> accepts silently, so the device enumerates with no DPA and no error.
> Richard
> 4. memdev.c: skip DC configuration when the media is not ready, as
> cxl_mem_dpa_fetch() does for static capacity. The DC base was compared
> against a static size forced to 0, disabling DCD with a misleading
> gap message, or configuring dynamic capacity on a DCD-only device
> whose static partitions were withheld.
> ---
> drivers/cxl/core/hdm.c | 2 +
> drivers/cxl/core/mbox.c | 237 ++++++++++++++++++++++++++++++++++++++
> drivers/cxl/core/memdev.c | 48 ++++++++
> drivers/cxl/cxlmem.h | 55 +++++++++
> drivers/cxl/pci.c | 6 +
> include/cxl/cxl.h | 3 +-
> 6 files changed, 350 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 199bb986d674..c96c16cc08e1 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -1349,6 +1349,243 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
> return -EBUSY;
> }
>
> +/*
> + * 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,
> + struct cxl_dc_partition *dev_part)
> +{
> + u64 decode_len = le64_to_cpu(dev_part->decode_length);
> + u64 blk_size = le64_to_cpu(dev_part->block_size);
> + u64 len = le64_to_cpu(dev_part->length);
> + u64 size;
> +
> + /*
> + * 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 = (struct cxl_dc_partition_info) { };
> + dev_dbg(dev, "Partition 0 unavailable for DC\n");
> + return 0;
> + }
> +
> + /*
> + * Decode length is a count of 256M units, so a device reporting 2^36
> + * or more wraps.
> + */
> + if (check_mul_overflow(decode_len, (u64)CXL_CAPACITY_MULTIPLIER, &size)) {
> + dev_err(dev, "DC partition 0 decode length %#llx overflows\n",
> + decode_len);
> + return -EINVAL;
> + }
> +
> + *part = (struct cxl_dc_partition_info) {
> + .start = le64_to_cpu(dev_part->base),
> + .size = size,
> + };
> +
> + /*
> + * Block size is a power of 2 and a multiple of 40h. is_power_of_2()
> + * takes an unsigned long, which truncates blk_size on 32 bit.
> + */
> + if (blk_size == 0 || (blk_size & (blk_size - 1)) ||
> + blk_size % CXL_DCD_BLOCK_LINE_SIZE) {
> + dev_err(dev, "DC partition 0 invalid block size %#llx\n", blk_size);
> + return -EINVAL;
> + }
> +
> + if (part->size == 0) {
> + dev_err(dev, "DC partition 0 zero decode length\n");
> + return -EINVAL;
> + }
> +
> + if (len == 0) {
> + dev_err(dev, "DC partition 0 zero length\n");
> + return -EINVAL;
> + }
> +
> + if (len > part->size) {
> + dev_err(dev, "DC partition 0 length %#llx exceeds decode length %#llx\n",
> + len, part->size);
> + return -EINVAL;
> + }
> +
> + if (!IS_ALIGNED(len, blk_size)) {
> + dev_err(dev, "DC partition 0 length %#llx not a multiple of block size %#llx\n",
> + len, blk_size);
> + return -EINVAL;
> + }
> +
> + if (!IS_ALIGNED(part->start, SZ_256M)) {
> + dev_err(dev, "DC partition 0 base %#llx not aligned to 256M\n",
> + part->start);
> + return -EINVAL;
> + }
> +
> + dev_dbg(dev, "DC partition 0 start %#llx size %#llx blk_size: %#llx\n",
> + part->start, part->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,
> + /* The device must return at least the fixed header */
> + .min_out = sizeof(*dc_resp),
> + };
> + 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 trailing extent/tag counts (CXL r4.0 Table 8-346) are not read */
> + 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 every partition the device reports, but validate only the first:
> + * Linux maps partition 0 and nothing else, so a defect in capacity the
> + * driver never touches is not a reason to refuse the device dynamic
> + * capacity. The remaining entries of @partitions are left unset.
> + *
> + * 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 avail_partitions = 0;
> + u8 start_partition;
> + u8 num_partitions;
> + u8 partition_count;
> + size_t dc_resp_size;
> +
> + /*
> + * Bound requested number of partitions by mailbox payload size. The
> + * 256 byte spec minimum, verified in cxl_pci_setup_mailbox(), keeps
> + * the subtraction below from underflowing.
> + */
> + partition_count = min(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;
> +
> + start_partition = 0;
> + num_partitions = 0;
> + do {
> + int returned, rc;
> +
> + returned = cxl_get_dc_config(mbox, start_partition,
> + partition_count, dc_resp,
> + dc_resp_size);
> + if (returned < 0) {
> + dev_err(dev, "Failed to get DC config: %d\n", returned);
> + return returned;
> + }
> +
> + if (returned == 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;
> + }
> +
> + /* A DCD reports at least one partition (CXL r4.0 Table 8-346) */
> + if (dc_resp->avail_partition_count == 0) {
> + dev_err(dev, "Device reported no DC partitions\n");
> + return -EIO;
> + }
> +
> + avail_partitions = min(dc_resp->avail_partition_count,
> + CXL_MAX_DC_PARTITIONS);
> +
> + /*
> + * Only partition 0 is mapped, so it is the only one checked.
> + * The rest are read to complete the enumeration and dropped.
> + */
> + if (start_partition == 0) {
> + rc = cxl_dc_check(dev, &partitions[0],
> + &dc_resp->partition[0]);
> + if (rc)
> + return rc;
> + }
> +
> + num_partitions += returned;
> + start_partition = num_partitions;
> +
> + } while (num_partitions < avail_partitions);
> +
> + /* Linux only supports the 1st partition; nothing to do if it is unavailable */
> + if (partitions[0].size == 0)
> + return -ENODEV;
> +
> + *dc_info = partitions[0];
> + dev_dbg(dev, "Returning partition 0 %#llx size %#llx\n",
> + dc_info->start, dc_info->size);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
> +
> int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
> {
> struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index b3419df586b9..15df8db314d9 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -643,6 +643,54 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> }
> EXPORT_SYMBOL_NS_GPL(cxl_mem_dpa_fetch, "CXL");
>
> +int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> +{
> + struct cxl_dc_partition_info dc_info = { };
> + struct device *dev = mds->cxlds.dev;
> + u64 total;
> + int rc;
> +
> + if (!mds->cxlds.media_ready)
> + return 0;
> +
> + 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);
> + return rc;
> + }
> +
> + 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;
> + }
> +
> + if (check_add_overflow(info->size, dc_info.size, &total)) {
> + dev_err(dev,
> + "DC partition 0 size %#llx overflows DPA space ending at %#llx\n",
> + dc_info.size, info->size);
> + return -EINVAL;
> + }
> +
> + info->size = total;
> + dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\n",
> + dc_info.start, dc_info.size);
> + add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
> +
>
> /**
> * cxl_set_capacity: initialize dpa by a driver without a mailbox.
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 0c8e23276dc6..4fac65dbb808 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)
> {
> /*
> @@ -689,6 +691,39 @@ struct cxl_mbox_set_shutdown_state_in {
> u8 state;
> } __packed;
>
> +/* See CXL r4.0 Table 8-345 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-346 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-347 */
> + 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;
> @@ -812,9 +847,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);
> +int 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,
> @@ -828,6 +872,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 c7c91e8dc51d..2fe9db4b3d86 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -868,6 +868,12 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> if (rc)
> return rc;
>
> + if (cxl_dcd_supported(mds)) {
> + rc = cxl_configure_dcd(mds, &range_info);
> + if (rc)
> + cxl_disable_dcd(mds);
> + }
> +
> rc = cxl_dpa_setup(cxlds, &range_info);
> if (rc)
> return rc;
> diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
> index 802b143de83d..4ef9511fbbdd 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
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-18 20:30 ` [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions Anisa Su
@ 2026-09-21 21:55 ` Dave Jiang
2026-09-24 5:12 ` Anisa Su
2026-09-21 23:27 ` Jonathan Cameron
2026-09-22 3:53 ` Richard Cheng
2 siblings, 1 reply; 41+ messages in thread
From: Dave Jiang @ 2026-09-21 21:55 UTC (permalink / raw)
To: Anisa Su, linux-cxl
Cc: Alison Schofield, Jonathan Cameron, Davidlohr Bueso, Li Ming,
Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny, Anisa Su,
Wonjae Lee, Junhee Park, Heesoo Kim
On 9/18/26 1:30 PM, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
>
> Additional DCD partition (AKA region) information is contained in the
> DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
Just a nit below. Otherwise
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> ---
> Changes:
> 1. cdat.c: record the DSMAS shareable flag independently of QTG
> discovery. cxl_port_perf_data_calculate() returns -ENOENT when FW
> recommends no QTG for any entry, which skipped the assignment even
> though the CDAT parsed successfully and the DSMAS flag was valid.
> Richard
> ---
> drivers/cxl/core/cdat.c | 49 +++++++++++++++++++++++++++++----------
> drivers/cxl/core/hdm.c | 1 +
> drivers/cxl/core/mbox.c | 9 +++++++
> drivers/cxl/core/memdev.c | 17 ++++++++------
> drivers/cxl/cxlmem.h | 2 ++
> include/cxl/cxl.h | 4 ++++
> 6 files changed, 63 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> index 5c9f07262513..6e7950f12134 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,8 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
> return -ENOMEM;
>
> dent->handle = dsmas->dsmad_handle;
> + /* Shareable is CDAT 1.03 and later, DSMAS Flags bit 3 */
> + 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;
> @@ -255,8 +258,13 @@ static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
> dent->coord[ACCESS_COORDINATE_CPU].write_latency);
> }
>
> -static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> - struct xarray *dsmas_xa)
> +/*
> + * The DSMAS shareable flag is a property of the CDAT entry and is recorded
> + * even if FW returns no QTG recommendations for any entry; only update perf
> + * if those recommendations exist, indicated by @set_perf
> + */
> +static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
> + struct xarray *dsmas_xa, bool set_perf)
Perfer an enum rather than a bool
DJ
> {
> struct device *dev = cxlds->dev;
> struct dsmas_entry *dent;
> @@ -266,18 +274,34 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> bool found = false;
>
> for (int i = 0; i < cxlds->nr_partitions; i++) {
> - struct resource *res = &cxlds->part[i].res;
> + struct cxl_dpa_partition *part = &cxlds->part[i];
> struct range range = {
> - .start = res->start,
> - .end = res->end,
> + .start = part->res.start,
> + .end = part->res.end,
> };
>
> - if (range_contains(&range, &dent->dpa_range)) {
> - update_perf_entry(dev, dent,
> - &cxlds->part[i].perf);
> - found = true;
> + if (!range_contains(&range, &dent->dpa_range))
> + continue;
> +
> + found = true;
> + /*
> + * 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;
> }
> +
> + if (set_perf)
> + update_perf_entry(dev, dent, &part->perf);
> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
> + part->shareable = dent->shareable;
> + break;
> }
>
> if (!found)
> @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
> }
>
> rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> - if (rc) {
> + if (rc)
> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> +
> + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
> + if (rc)
> return;
> - }
>
> - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
> cxl_qos_class_verify(cxlmd);
> cxl_memdev_update_perf(cxlmd);
> }
> 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 c96c16cc08e1..d84a90320081 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -1366,6 +1366,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> u64 decode_len = le64_to_cpu(dev_part->decode_length);
> 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);
> u64 size;
>
> /*
> @@ -1378,6 +1379,13 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> return 0;
> }
>
> + /* 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;
> + }
> +
> /*
> * Decode length is a count of 256M units, so a device reporting 2^36
> * or more wraps.
> @@ -1391,6 +1399,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> *part = (struct cxl_dc_partition_info) {
> .start = le64_to_cpu(dev_part->base),
> .size = size,
> + .handle = handle,
> };
>
> /*
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index 15df8db314d9..8883c8850759 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -594,7 +594,8 @@ bool is_cxl_memdev(const struct device *dev)
> }
> EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "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;
>
> @@ -606,6 +607,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++;
> }
>
> @@ -623,9 +625,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;
> }
>
> @@ -635,9 +637,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;
> }
> @@ -685,7 +687,8 @@ int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> info->size = total;
> dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\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);
>
> return 0;
> }
> @@ -704,7 +707,7 @@ int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
> .size = capacity,
> };
>
> - add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
> + add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM, 0);
> return cxl_dpa_setup(cxlds, &range_info);
> }
> EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 4fac65dbb808..9fd5af2e41d4 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;
> };
> @@ -851,6 +852,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 4ef9511fbbdd..43edcde5bce3 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 that represents this partition
> + * @shareable: Is the partition shareable (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
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts
2026-09-18 20:30 ` [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
2026-09-18 20:43 ` sashiko-bot
@ 2026-09-21 22:00 ` Dave Jiang
2026-09-21 23:42 ` Jonathan Cameron
2026-09-22 5:48 ` Richard Cheng
2 siblings, 1 reply; 41+ messages in thread
From: Dave Jiang @ 2026-09-21 22:00 UTC (permalink / raw)
To: Anisa Su, linux-cxl
Cc: Alison Schofield, Jonathan Cameron, Davidlohr Bueso, Li Ming,
Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny, Anisa Su,
Wonjae Lee, Junhee Park, Heesoo Kim
On 9/18/26 1:30 PM, 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.
>
> The DCD event log is not drained here. cxl_event_drain_mask() reports
> only the logs the driver can service, and the DCD bit is added by
> the patch introducing DCD event handling. Until then a DCD interrupt
> wakes the event thread, which finds no log it owns and returns.
>
> 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>
> 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: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> Reviewed-by: Alison Schofield <alison.schofield@intel.com>
> ---
> Changes:
>
> 1. [jonathan]: get rid of CXL_EVENT_STATUS_ALL and instead opt into
> event logs we want to read individually in cxl_event_drain_mask. This
> makes which logs the driver drains explicit if new logs are added in
> the future. The cxl_test mock names the same four logs for its
> probe-time drain.
> 2. pci.c: when BIOS owns the memory event logs and a short interrupt
> policy has just disabled DCD, return before Set Event Interrupt
> Policy and the event buffer allocation. Nothing is left to configure,
> and the Set would only have echoed firmware's own settings back.
> ---
> drivers/cxl/cxl.h | 6 +-
> drivers/cxl/cxlmem.h | 2 +
> drivers/cxl/pci.c | 127 +++++++++++++++++++++++++++++------
> tools/testing/cxl/test/mem.c | 5 +-
> 4 files changed, 114 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index cab8ce39f465..1add114bc178 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -192,11 +192,7 @@ 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_ALL (CXLDEV_EVENT_STATUS_INFO | \
> - CXLDEV_EVENT_STATUS_WARN | \
> - CXLDEV_EVENT_STATUS_FAIL | \
> - CXLDEV_EVENT_STATUS_FATAL)
> +#define CXLDEV_EVENT_STATUS_DCD BIT(4)
>
> /* 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 9fd5af2e41d4..9106affd7c81 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 7b754af2168d..fee3a704b73b 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -510,11 +510,23 @@ static bool cxl_alloc_irq_vectors(struct pci_dev *pdev)
> return true;
> }
>
> +/* Event logs the driver drains: standard logs when native_cxl */
> +static u32 cxl_event_drain_mask(struct pci_host_bridge *host_bridge)
> +{
> + if (host_bridge->native_cxl_error)
> + return CXLDEV_EVENT_STATUS_INFO | CXLDEV_EVENT_STATUS_WARN |
> + CXLDEV_EVENT_STATUS_FAIL | CXLDEV_EVENT_STATUS_FATAL;
> + return 0;
> +}
> +
> 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);
> u32 status;
>
> do {
> @@ -523,8 +535,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);
> @@ -551,42 +563,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);
> @@ -597,7 +629,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,
> @@ -633,6 +665,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);
> @@ -651,32 +707,57 @@ static bool cxl_event_fw_owns_mem_logs(struct cxl_event_interrupt_policy *policy
> 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 mask;
> 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_fw_owns_mem_logs(&policy)) {
> + /*
> + * dcd_settings cannot be sent to a device whose policy is too short
> + * to hold it, so the device would never accept the setting
> + * cxl_irqsetup() arms the DCD interrupt with.
> + */
> + if (cxl_dcd_supported(mds) && policy_size < sizeof(policy)) {
> + dev_warn(mds->cxlds.dev,
> + "DCD supported but interrupt policy is only %zu bytes\n",
> + policy_size);
> + cxl_disable_dcd(mds);
> + /* DCD was the only reason to be here */
> + if (!native_cxl)
> + return 0;
> + }
> +
> + if (native_cxl && cxl_event_fw_owns_mem_logs(&policy)) {
> dev_err(mds->cxlds.dev,
> "FW still in control of Event Logs despite _OSC settings\n");
> return -EBUSY;
> }
>
> - rc = cxl_event_config_msgnums(mds, &policy);
> + rc = cxl_event_config_msgnums(mds, &policy, native_cxl, policy_size);
> if (rc)
> return rc;
>
> @@ -684,11 +765,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);
> + mask = cxl_event_drain_mask(host_bridge);
> + if (mask)
> + cxl_mem_get_event_records(mds, mask);
> +
> + dev_dbg(mds->cxlds.dev, "Event config : %s DCD %s\n",
> + native_cxl ? "OS" : "BIOS",
> + cxl_dcd_supported(mds) ? "supported" : "not supported");
>
> return 0;
> }
> diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
> index 7b756000a1a6..6ef47265da10 100644
> --- a/tools/testing/cxl/test/mem.c
> +++ b/tools/testing/cxl/test/mem.c
> @@ -1818,7 +1818,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
> if (rc)
> dev_dbg(dev, "No CXL FWCTL setup\n");
>
> - cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
> + cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_INFO |
> + CXLDEV_EVENT_STATUS_WARN |
> + CXLDEV_EVENT_STATUS_FAIL |
> + CXLDEV_EVENT_STATUS_FATAL);
Second time I'm seeing this constructed mask being used in this commit. Maybe create a define for it?
s/CXLDEV_EVENT_STATUS_ALL/CXLDEV_EVENT_STATUS_BASE/ perhaps?
> cxl_mock_test_feat_init(mdata);
>
> return 0;
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 8/8] Documentation/cxl: Document DPA partition layout and ordering rules
2026-09-18 20:30 ` [PATCH v14 8/8] Documentation/cxl: Document DPA partition layout and ordering rules Anisa Su
@ 2026-09-21 22:02 ` Dave Jiang
2026-09-28 21:53 ` Anisa Su
0 siblings, 1 reply; 41+ messages in thread
From: Dave Jiang @ 2026-09-21 22:02 UTC (permalink / raw)
To: Anisa Su, linux-cxl
Cc: Alison Schofield, Jonathan Cameron, Davidlohr Bueso, Li Ming,
Gregory Price, Richard Cheng, Ben Cheatham, Anisa Su
On 9/18/26 1:30 PM, Anisa Su wrote:
> DC Partitions complicate DPA ordering. Add a DPA Partitions section to
> cxl-driver.rst describing spec-mandated and Linux requirements for the
> layout.
>
> maturity-map.rst breaks the DCD entry into sub-items so the map shows
> what this series lands: partition enumeration is [1], event handling
> and DC-backed DAX regions remain [0] until the follow-on series enables
> them. The parent stays [0]; nothing is usable yet.
>
> Suggested-by: Gregory Price <gourry@gourry.net>
> Signed-off-by: Anisa Su <anisa.su@samsung.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
> Reviewed-by: Alison Schofield <alison.schofield@intel.com>
> ---
> Changes:
> [jonathan]: drop paragraph describing "support for additional
> dc partitions may be added later..." to avoid predicting future work in
> documentation
> [davidlohr]: add DCD sub-items to maturity-map.rst so a multi-step
> upstreaming shows users what is there and sets expectations.
> ---
> .../driver-api/cxl/linux/cxl-driver.rst | 35 +++++++++++++++++++
> Documentation/driver-api/cxl/maturity-map.rst | 4 +++
> 2 files changed, 39 insertions(+)
>
> diff --git a/Documentation/driver-api/cxl/linux/cxl-driver.rst b/Documentation/driver-api/cxl/linux/cxl-driver.rst
> index dd6dd17dc536..eb4de2a9a9af 100644
> --- a/Documentation/driver-api/cxl/linux/cxl-driver.rst
> +++ b/Documentation/driver-api/cxl/linux/cxl-driver.rst
> @@ -181,6 +181,41 @@ A Memory Device is a discrete base object that is not a port. While the
> physical device it belongs to may also host an `endpoint`, the relationship
> between an `endpoint` and a `memdev` is not captured in sysfs.
>
> +DPA Partitions
> +~~~~~~~~~~~~~~
> +A memory device presents its capacity as one flat `Device Physical Address`
> +(DPA) space divided into `partitions`, which Linux lays out in a fixed
> +order::
> +
> + DPA 0 end
> + +---------------+---------------+---------------------------+
> + | ram | pmem | dynamic_ram_1 |
> + +---------------+---------------+---------------------------+
> + part[0] part[1] part[2]
> +
> +Part of that order is required by the CXL specification and part of it is a
> +Linux choice.
> +
> +The `ram` and `pmem` order is mandated. CXL r4.0 section 8.2.10.9.2.1 "Get
> +Partition Info" (4100h), Table 8-310, mandates that volatile capacity starts
> +at DPA 0 and pmem starts at the DPA immediately following it.
> +
> +Dynamic Capacity partitions only need to be 256MB aligned according to
> +CXL r4.0 section 8.2.10.9.9.1 "Get Dynamic Capacity Configuration"
> +(opcode 4800h), Table 8-347. So a device could leave a gap between ram/pmem
> +(static) capacity and its first DC partition, or between one DC partition
> +and the next.
> +
> +Linux chooses not to support gaps between static and dynamic capacity: the
> +first DC partition must begin at the DPA immediately following static
> +capacity -- after pmem, after ram on a device with no pmem, or at DPA 0 on
> +a device with no static capacity at all.
> +
> +Currently, only one dynamic partition is supported. A device may report up
> +to eight (CXL r4.0 Table 8-346); Linux configures the first and exposes it as
> +`dynamic_ram_1`. Gaps between DC partitions are not checked, since only the
> +first is used.
> +
> Port Relationships
> ~~~~~~~~~~~~~~~~~~
> In our example described above, there are four host bridges attached to the
> diff --git a/Documentation/driver-api/cxl/maturity-map.rst b/Documentation/driver-api/cxl/maturity-map.rst
> index 282c1102dd81..fefd31899229 100644
> --- a/Documentation/driver-api/cxl/maturity-map.rst
> +++ b/Documentation/driver-api/cxl/maturity-map.rst
Maybe hold off this commit for when everything goes in? Otherwise it's weird that there's documentation but no code.
> @@ -150,6 +150,10 @@ Memory-pooling
> * [1] Hotplug of LDs (via PCI hotplug)
> * [0] Dynamic Capacity Device (DCD) Support
>
> + * [1] DC partition enumeration / configuration (Get DC Config, CDAT DSMAS, event interrupts)
> + * [0] Extent add / release event handling
> + * [0] DC-backed DAX regions
I would just update maturity map once when the entire DCD code is in.
> +
> Multi-host sharing
> ------------------
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device
2026-09-18 20:30 ` [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
2026-09-18 20:44 ` sashiko-bot
2026-09-21 21:50 ` Dave Jiang
@ 2026-09-21 23:19 ` Jonathan Cameron
2026-09-22 3:44 ` Richard Cheng
3 siblings, 0 replies; 41+ messages in thread
From: Jonathan Cameron @ 2026-09-21 23:19 UTC (permalink / raw)
To: Anisa Su
Cc: linux-cxl, Dave Jiang, Alison Schofield, Davidlohr Bueso, Li Ming,
Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny, Anisa Su,
Wonjae Lee, Junhee Park, Heesoo Kim
On Fri, 18 Sep 2026 13:30:11 -0700
Anisa Su <anisa.su887@gmail.com> wrote:
> From: Ira Weiny <iweiny@kernel.org>
>
> Devices which support Dynamic Capacity (DC) are configured
> via mailbox commands. CXL r4.0 section 9.13.3 describes the use of
> the Get DC Configuration command in order to obtain DCD partition
> characteristics.
Wrap has ended up a tiny bit uneven after edits. I'd rewrap whole commit
to 75 chars if you happen to respin for other reasons.
>
> 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.
>
> Initial enablement for Linux only supports one dynamic capacity
> partition. Read all the partitions the device reports but validate
> only the first, and configure it as 'dynamic ram 1'.
>
> The specification requires that volatile capacity starts at DPA 0 and pmem
> starts at the DPA immediately following it, but DC partitions only need
> to be 256MB aligned according to CXL r4.0 section 8.2.10.9.9.1 Table 8-347.
> So a device could leave a gap between ram/pmem (static) capacity and its first
> DC partition, or between one DC partition and the next.
>
> However, Linux chooses not to support gaps between static and dynamic
> capacity, and requires the first DC partition to begin at the DPA
> immediately following static capacity.
>
> Based on an original patch by Navneet Singh.
>
> 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: Alison Schofield <alison.schofield@intel.com>
>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
A personal aim of successfully giving the most trivial feedback ever
is achieved. See below.
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index b3419df586b9..15df8db314d9 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -643,6 +643,54 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> }
> EXPORT_SYMBOL_NS_GPL(cxl_mem_dpa_fetch, "CXL");
>
> +int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> +{
> + struct cxl_dc_partition_info dc_info = { };
> + struct device *dev = mds->cxlds.dev;
> + u64 total;
> + int rc;
> +
> + if (!mds->cxlds.media_ready)
> + return 0;
> +
> + 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);
> + return rc;
> + }
> +
> + 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;
> + }
> +
> + if (check_add_overflow(info->size, dc_info.size, &total)) {
> + dev_err(dev,
> + "DC partition 0 size %#llx overflows DPA space ending at %#llx\n",
> + dc_info.size, info->size);
> + return -EINVAL;
> + }
> +
> + info->size = total;
> + dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\n",
> + dc_info.start, dc_info.size);
> + add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
> +
>
It is already the case that there is a double line here which is not
common in this file. I have no idea why it is here!
If you happen to respin maybe drop it?
> /**
> * cxl_set_capacity: initialize dpa by a driver without a mailbox.
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-18 20:30 ` [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions Anisa Su
2026-09-21 21:55 ` Dave Jiang
@ 2026-09-21 23:27 ` Jonathan Cameron
2026-09-24 5:01 ` Anisa Su
2026-09-22 3:53 ` Richard Cheng
2 siblings, 1 reply; 41+ messages in thread
From: Jonathan Cameron @ 2026-09-21 23:27 UTC (permalink / raw)
To: Anisa Su
Cc: linux-cxl, Dave Jiang, Alison Schofield, Davidlohr Bueso, Li Ming,
Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny, Anisa Su,
Wonjae Lee, Junhee Park, Heesoo Kim
On Fri, 18 Sep 2026 13:30:12 -0700
Anisa Su <anisa.su887@gmail.com> wrote:
> From: Ira Weiny <iweiny@kernel.org>
>
> Additional DCD partition (AKA region) information is contained in the
> DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
>
One little thing inline. Either way
Reviewed-by: Jonathan Cameron <joanthan.cameron@oss.qualcomm.com>
> @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
> }
>
> rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> - if (rc) {
This ended up a bit nasty from an unusual code structure being less
readable than ideal point of view.
> + if (rc)
> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> +
> + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
> + if (rc)
> return;
> - }
Can you make this
rc = cxl_port_perf_data_calculate(port, dmsas_xa);
cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
if (rc) {
dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
return;
}
or just duplicate to keep things simple to read.
rc = cxl_port_perf_data_calculate(port, dmsas_xa);
if (rc) {
dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
cxl_memdev_apply_dsmas(cxlds, dsmas_xa, false);
return;
}
cxl_memdev_apply_dsmas(cxlds, dsmas_xa, true);
...
Second one probably a little better as keeps messages i order if we
happen to get a print from cxl_memdev_apply_dsmas()
>
> - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
> cxl_qos_class_verify(cxlmd);
> cxl_memdev_update_perf(cxlmd);
> }
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts
2026-09-21 22:00 ` Dave Jiang
@ 2026-09-21 23:42 ` Jonathan Cameron
2026-09-22 0:45 ` Dave Jiang
0 siblings, 1 reply; 41+ messages in thread
From: Jonathan Cameron @ 2026-09-21 23:42 UTC (permalink / raw)
To: Dave Jiang
Cc: Anisa Su, linux-cxl, Alison Schofield, Davidlohr Bueso, Li Ming,
Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny, Anisa Su,
Wonjae Lee, Junhee Park, Heesoo Kim
> > diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
> > index 7b756000a1a6..6ef47265da10 100644
> > --- a/tools/testing/cxl/test/mem.c
> > +++ b/tools/testing/cxl/test/mem.c
> > @@ -1818,7 +1818,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
> > if (rc)
> > dev_dbg(dev, "No CXL FWCTL setup\n");
> >
> > - cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
> > + cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_INFO |
> > + CXLDEV_EVENT_STATUS_WARN |
> > + CXLDEV_EVENT_STATUS_FAIL |
> > + CXLDEV_EVENT_STATUS_FATAL);
>
> Second time I'm seeing this constructed mask being used in this commit. Maybe create a define for it?
>
> s/CXLDEV_EVENT_STATUS_ALL/CXLDEV_EVENT_STATUS_BASE/ perhaps?
That was Anisa acting on my feedbakc on previous.
I don't like _ALL because we already have an example of the spec
expanding and the definition becoming messy. But mainly I was
pushing back against _ALL + masking with one element we didn't
want as fragile. So I'm not against _ALL if it is made up of
another define + DCD (for now) and that other define can be
used in places like this.
Bikeshed time... BASE is usually a define for a reg address and it is
is hard to come up with a name for that when it is a mixture of
RAS stuff and INFO which can be a wide range of random nasty
and nice things.
So with the right name seems fine to have what Dave suggests.
I'm just not sure what that name is! Hence I'd just stick
to the long hand option.
Jonathan
>
> > cxl_mock_test_feat_init(mdata);
> >
> > return 0;
>
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts
2026-09-21 23:42 ` Jonathan Cameron
@ 2026-09-22 0:45 ` Dave Jiang
2026-09-22 21:22 ` Anisa Su
0 siblings, 1 reply; 41+ messages in thread
From: Dave Jiang @ 2026-09-22 0:45 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Anisa Su, linux-cxl, Alison Schofield, Davidlohr Bueso, Li Ming,
Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny, Anisa Su,
Wonjae Lee, Junhee Park, Heesoo Kim
On 9/21/26 4:42 PM, Jonathan Cameron wrote:
>
>>> diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
>>> index 7b756000a1a6..6ef47265da10 100644
>>> --- a/tools/testing/cxl/test/mem.c
>>> +++ b/tools/testing/cxl/test/mem.c
>>> @@ -1818,7 +1818,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
>>> if (rc)
>>> dev_dbg(dev, "No CXL FWCTL setup\n");
>>>
>>> - cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
>>> + cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_INFO |
>>> + CXLDEV_EVENT_STATUS_WARN |
>>> + CXLDEV_EVENT_STATUS_FAIL |
>>> + CXLDEV_EVENT_STATUS_FATAL);
>>
>> Second time I'm seeing this constructed mask being used in this commit. Maybe create a define for it?
>>
>> s/CXLDEV_EVENT_STATUS_ALL/CXLDEV_EVENT_STATUS_BASE/ perhaps?
>
> That was Anisa acting on my feedbakc on previous.
> I don't like _ALL because we already have an example of the spec
> expanding and the definition becoming messy. But mainly I was
> pushing back against _ALL + masking with one element we didn't
> want as fragile. So I'm not against _ALL if it is made up of
> another define + DCD (for now) and that other define can be
> used in places like this.
>
> Bikeshed time... BASE is usually a define for a reg address and it is
> is hard to come up with a name for that when it is a mixture of
> RAS stuff and INFO which can be a wide range of random nasty
> and nice things.
>
> So with the right name seems fine to have what Dave suggests.
> I'm just not sure what that name is! Hence I'd just stick
> to the long hand option.
Yeah if we can't come up with anything good then leave it as is.
>
> Jonathan
>
>>
>>> cxl_mock_test_feat_init(mdata);
>>>
>>> return 0;
>>
>>
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device
2026-09-18 20:30 ` [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
` (2 preceding siblings ...)
2026-09-21 23:19 ` Jonathan Cameron
@ 2026-09-22 3:44 ` Richard Cheng
2026-09-24 0:55 ` Jonathan Cameron
3 siblings, 1 reply; 41+ messages in thread
From: Richard Cheng @ 2026-09-22 3:44 UTC (permalink / raw)
To: Anisa Su
Cc: linux-cxl, Dave Jiang, Alison Schofield, Jonathan Cameron,
Davidlohr Bueso, Li Ming, Gregory Price, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim
On Fri, Sep 18, 2026 at 01:30:11PM +0800, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
>
> Devices which support Dynamic Capacity (DC) are configured
> via mailbox commands. CXL r4.0 section 9.13.3 describes the use of
> the Get DC Configuration command in order to obtain DCD partition
> characteristics.
>
> 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.
>
> Initial enablement for Linux only supports one dynamic capacity
> partition. Read all the partitions the device reports but validate
> only the first, and configure it as 'dynamic ram 1'.
>
> The specification requires that volatile capacity starts at DPA 0 and pmem
> starts at the DPA immediately following it, but DC partitions only need
> to be 256MB aligned according to CXL r4.0 section 8.2.10.9.9.1 Table 8-347.
> So a device could leave a gap between ram/pmem (static) capacity and its first
> DC partition, or between one DC partition and the next.
>
> However, Linux chooses not to support gaps between static and dynamic
> capacity, and requires the first DC partition to begin at the DPA
> immediately following static capacity.
>
> Based on an original patch by Navneet Singh.
>
> 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: Alison Schofield <alison.schofield@intel.com>
>
Reviewed-by: Richard Cheng <icheng@nvidia.com>
I wonder if we can have comments in code explaining why linux doesn't support
gaps between static and DC, otherwise this can be a bit confusing why
the 1st DC partition needs to be immediately after the static part, at least
for me.
Best regards,
Richard Cheng.
> ---
> Changes:
> 1. mbox.c: do not refuse DCD when the device reports more than
> CXL_MAX_DC_PARTITIONS. A future spec revision may allow more and
> Linux only maps partition 0, so read only the first 8. A count of
> zero is still refused.
> Jonathan
> 2. mbox.c: reject a decode length that overflows when scaled by
> CXL_CAPACITY_MULTIPLIER. A device reporting 2^36 or more wraps and
> yields a plausible looking partition size rather than an error.
> Richard
> 3. memdev.c: reject a DC partition size that overflows the DPA total.
> The wrapped total reads as zero capacity, which cxl_dpa_setup()
> accepts silently, so the device enumerates with no DPA and no error.
> Richard
> 4. memdev.c: skip DC configuration when the media is not ready, as
> cxl_mem_dpa_fetch() does for static capacity. The DC base was compared
> against a static size forced to 0, disabling DCD with a misleading
> gap message, or configuring dynamic capacity on a DCD-only device
> whose static partitions were withheld.
> ---
> drivers/cxl/core/hdm.c | 2 +
> drivers/cxl/core/mbox.c | 237 ++++++++++++++++++++++++++++++++++++++
> drivers/cxl/core/memdev.c | 48 ++++++++
> drivers/cxl/cxlmem.h | 55 +++++++++
> drivers/cxl/pci.c | 6 +
> include/cxl/cxl.h | 3 +-
> 6 files changed, 350 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 199bb986d674..c96c16cc08e1 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -1349,6 +1349,243 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
> return -EBUSY;
> }
>
> +/*
> + * 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,
> + struct cxl_dc_partition *dev_part)
> +{
> + u64 decode_len = le64_to_cpu(dev_part->decode_length);
> + u64 blk_size = le64_to_cpu(dev_part->block_size);
> + u64 len = le64_to_cpu(dev_part->length);
> + u64 size;
> +
> + /*
> + * 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 = (struct cxl_dc_partition_info) { };
> + dev_dbg(dev, "Partition 0 unavailable for DC\n");
> + return 0;
> + }
> +
> + /*
> + * Decode length is a count of 256M units, so a device reporting 2^36
> + * or more wraps.
> + */
> + if (check_mul_overflow(decode_len, (u64)CXL_CAPACITY_MULTIPLIER, &size)) {
> + dev_err(dev, "DC partition 0 decode length %#llx overflows\n",
> + decode_len);
> + return -EINVAL;
> + }
> +
> + *part = (struct cxl_dc_partition_info) {
> + .start = le64_to_cpu(dev_part->base),
> + .size = size,
> + };
> +
> + /*
> + * Block size is a power of 2 and a multiple of 40h. is_power_of_2()
> + * takes an unsigned long, which truncates blk_size on 32 bit.
> + */
> + if (blk_size == 0 || (blk_size & (blk_size - 1)) ||
> + blk_size % CXL_DCD_BLOCK_LINE_SIZE) {
> + dev_err(dev, "DC partition 0 invalid block size %#llx\n", blk_size);
> + return -EINVAL;
> + }
> +
> + if (part->size == 0) {
> + dev_err(dev, "DC partition 0 zero decode length\n");
> + return -EINVAL;
> + }
> +
> + if (len == 0) {
> + dev_err(dev, "DC partition 0 zero length\n");
> + return -EINVAL;
> + }
> +
> + if (len > part->size) {
> + dev_err(dev, "DC partition 0 length %#llx exceeds decode length %#llx\n",
> + len, part->size);
> + return -EINVAL;
> + }
> +
> + if (!IS_ALIGNED(len, blk_size)) {
> + dev_err(dev, "DC partition 0 length %#llx not a multiple of block size %#llx\n",
> + len, blk_size);
> + return -EINVAL;
> + }
> +
> + if (!IS_ALIGNED(part->start, SZ_256M)) {
> + dev_err(dev, "DC partition 0 base %#llx not aligned to 256M\n",
> + part->start);
> + return -EINVAL;
> + }
> +
> + dev_dbg(dev, "DC partition 0 start %#llx size %#llx blk_size: %#llx\n",
> + part->start, part->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,
> + /* The device must return at least the fixed header */
> + .min_out = sizeof(*dc_resp),
> + };
> + 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 trailing extent/tag counts (CXL r4.0 Table 8-346) are not read */
> + 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 every partition the device reports, but validate only the first:
> + * Linux maps partition 0 and nothing else, so a defect in capacity the
> + * driver never touches is not a reason to refuse the device dynamic
> + * capacity. The remaining entries of @partitions are left unset.
> + *
> + * 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 avail_partitions = 0;
> + u8 start_partition;
> + u8 num_partitions;
> + u8 partition_count;
> + size_t dc_resp_size;
> +
> + /*
> + * Bound requested number of partitions by mailbox payload size. The
> + * 256 byte spec minimum, verified in cxl_pci_setup_mailbox(), keeps
> + * the subtraction below from underflowing.
> + */
> + partition_count = min(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;
> +
> + start_partition = 0;
> + num_partitions = 0;
> + do {
> + int returned, rc;
> +
> + returned = cxl_get_dc_config(mbox, start_partition,
> + partition_count, dc_resp,
> + dc_resp_size);
> + if (returned < 0) {
> + dev_err(dev, "Failed to get DC config: %d\n", returned);
> + return returned;
> + }
> +
> + if (returned == 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;
> + }
> +
> + /* A DCD reports at least one partition (CXL r4.0 Table 8-346) */
> + if (dc_resp->avail_partition_count == 0) {
> + dev_err(dev, "Device reported no DC partitions\n");
> + return -EIO;
> + }
> +
> + avail_partitions = min(dc_resp->avail_partition_count,
> + CXL_MAX_DC_PARTITIONS);
> +
> + /*
> + * Only partition 0 is mapped, so it is the only one checked.
> + * The rest are read to complete the enumeration and dropped.
> + */
> + if (start_partition == 0) {
> + rc = cxl_dc_check(dev, &partitions[0],
> + &dc_resp->partition[0]);
> + if (rc)
> + return rc;
> + }
> +
> + num_partitions += returned;
> + start_partition = num_partitions;
> +
> + } while (num_partitions < avail_partitions);
> +
> + /* Linux only supports the 1st partition; nothing to do if it is unavailable */
> + if (partitions[0].size == 0)
> + return -ENODEV;
> +
> + *dc_info = partitions[0];
> + dev_dbg(dev, "Returning partition 0 %#llx size %#llx\n",
> + dc_info->start, dc_info->size);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_dev_dc_identify, "CXL");
> +
> int cxl_get_dirty_count(struct cxl_memdev_state *mds, u32 *count)
> {
> struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index b3419df586b9..15df8db314d9 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -643,6 +643,54 @@ int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> }
> EXPORT_SYMBOL_NS_GPL(cxl_mem_dpa_fetch, "CXL");
>
> +int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> +{
> + struct cxl_dc_partition_info dc_info = { };
> + struct device *dev = mds->cxlds.dev;
> + u64 total;
> + int rc;
> +
> + if (!mds->cxlds.media_ready)
> + return 0;
> +
> + 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);
> + return rc;
> + }
> +
> + 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;
> + }
> +
> + if (check_add_overflow(info->size, dc_info.size, &total)) {
> + dev_err(dev,
> + "DC partition 0 size %#llx overflows DPA space ending at %#llx\n",
> + dc_info.size, info->size);
> + return -EINVAL;
> + }
> +
> + info->size = total;
> + dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\n",
> + dc_info.start, dc_info.size);
> + add_part(info, dc_info.start, dc_info.size, CXL_PARTMODE_DYNAMIC_RAM_1);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_configure_dcd, "CXL");
> +
>
> /**
> * cxl_set_capacity: initialize dpa by a driver without a mailbox.
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 0c8e23276dc6..4fac65dbb808 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)
> {
> /*
> @@ -689,6 +691,39 @@ struct cxl_mbox_set_shutdown_state_in {
> u8 state;
> } __packed;
>
> +/* See CXL r4.0 Table 8-345 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-346 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-347 */
> + 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;
> @@ -812,9 +847,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);
> +int 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,
> @@ -828,6 +872,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 c7c91e8dc51d..2fe9db4b3d86 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -868,6 +868,12 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> if (rc)
> return rc;
>
> + if (cxl_dcd_supported(mds)) {
> + rc = cxl_configure_dcd(mds, &range_info);
> + if (rc)
> + cxl_disable_dcd(mds);
> + }
> +
> rc = cxl_dpa_setup(cxlds, &range_info);
> if (rc)
> return rc;
> diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
> index 802b143de83d..4ef9511fbbdd 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] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-18 20:30 ` [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions Anisa Su
2026-09-21 21:55 ` Dave Jiang
2026-09-21 23:27 ` Jonathan Cameron
@ 2026-09-22 3:53 ` Richard Cheng
2026-09-22 17:08 ` Dave Jiang
2 siblings, 1 reply; 41+ messages in thread
From: Richard Cheng @ 2026-09-22 3:53 UTC (permalink / raw)
To: Anisa Su
Cc: linux-cxl, Dave Jiang, Alison Schofield, Jonathan Cameron,
Davidlohr Bueso, Li Ming, Gregory Price, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim
On Fri, Sep 18, 2026 at 01:30:12PM +0800, Anisa Su wrote:
> From: Ira Weiny <iweiny@kernel.org>
>
> Additional DCD partition (AKA region) information is contained in the
> DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
>
Hi Anisa,
LGTM but I have limited knowledge for details of device's CDAT.
Other experts would be more suitable to leave their tag.
I have a question here only, I wonder if a CDAT with no DSLBIS supported is
possible ? seems that the implementation has the assumption that device's
CDAT would be always supported with valid DSMAS and DSLBIS.
Best regards,
Richard Cheng.
> ---
> Changes:
> 1. cdat.c: record the DSMAS shareable flag independently of QTG
> discovery. cxl_port_perf_data_calculate() returns -ENOENT when FW
> recommends no QTG for any entry, which skipped the assignment even
> though the CDAT parsed successfully and the DSMAS flag was valid.
> Richard
> ---
> drivers/cxl/core/cdat.c | 49 +++++++++++++++++++++++++++++----------
> drivers/cxl/core/hdm.c | 1 +
> drivers/cxl/core/mbox.c | 9 +++++++
> drivers/cxl/core/memdev.c | 17 ++++++++------
> drivers/cxl/cxlmem.h | 2 ++
> include/cxl/cxl.h | 4 ++++
> 6 files changed, 63 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> index 5c9f07262513..6e7950f12134 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,8 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
> return -ENOMEM;
>
> dent->handle = dsmas->dsmad_handle;
> + /* Shareable is CDAT 1.03 and later, DSMAS Flags bit 3 */
> + 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;
> @@ -255,8 +258,13 @@ static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
> dent->coord[ACCESS_COORDINATE_CPU].write_latency);
> }
>
> -static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> - struct xarray *dsmas_xa)
> +/*
> + * The DSMAS shareable flag is a property of the CDAT entry and is recorded
> + * even if FW returns no QTG recommendations for any entry; only update perf
> + * if those recommendations exist, indicated by @set_perf
> + */
> +static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
> + struct xarray *dsmas_xa, bool set_perf)
> {
> struct device *dev = cxlds->dev;
> struct dsmas_entry *dent;
> @@ -266,18 +274,34 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> bool found = false;
>
> for (int i = 0; i < cxlds->nr_partitions; i++) {
> - struct resource *res = &cxlds->part[i].res;
> + struct cxl_dpa_partition *part = &cxlds->part[i];
> struct range range = {
> - .start = res->start,
> - .end = res->end,
> + .start = part->res.start,
> + .end = part->res.end,
> };
>
> - if (range_contains(&range, &dent->dpa_range)) {
> - update_perf_entry(dev, dent,
> - &cxlds->part[i].perf);
> - found = true;
> + if (!range_contains(&range, &dent->dpa_range))
> + continue;
> +
> + found = true;
> + /*
> + * 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;
> }
> +
> + if (set_perf)
> + update_perf_entry(dev, dent, &part->perf);
> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
> + part->shareable = dent->shareable;
> + break;
> }
>
> if (!found)
> @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
> }
>
> rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> - if (rc) {
> + if (rc)
> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> +
> + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
> + if (rc)
> return;
> - }
>
> - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
> cxl_qos_class_verify(cxlmd);
> cxl_memdev_update_perf(cxlmd);
> }
> 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 c96c16cc08e1..d84a90320081 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -1366,6 +1366,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> u64 decode_len = le64_to_cpu(dev_part->decode_length);
> 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);
> u64 size;
>
> /*
> @@ -1378,6 +1379,13 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> return 0;
> }
>
> + /* 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;
> + }
> +
> /*
> * Decode length is a count of 256M units, so a device reporting 2^36
> * or more wraps.
> @@ -1391,6 +1399,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> *part = (struct cxl_dc_partition_info) {
> .start = le64_to_cpu(dev_part->base),
> .size = size,
> + .handle = handle,
> };
>
> /*
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index 15df8db314d9..8883c8850759 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -594,7 +594,8 @@ bool is_cxl_memdev(const struct device *dev)
> }
> EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "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;
>
> @@ -606,6 +607,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++;
> }
>
> @@ -623,9 +625,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;
> }
>
> @@ -635,9 +637,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;
> }
> @@ -685,7 +687,8 @@ int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> info->size = total;
> dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\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);
>
> return 0;
> }
> @@ -704,7 +707,7 @@ int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
> .size = capacity,
> };
>
> - add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
> + add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM, 0);
> return cxl_dpa_setup(cxlds, &range_info);
> }
> EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 4fac65dbb808..9fd5af2e41d4 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;
> };
> @@ -851,6 +852,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 4ef9511fbbdd..43edcde5bce3 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 that represents this partition
> + * @shareable: Is the partition shareable (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 [flat|nested] 41+ messages in thread
* Re: [PATCH v14 4/8] cxl/events: Split event msgnum configuration from irq setup
2026-09-18 20:30 ` [PATCH v14 4/8] cxl/events: Split event msgnum configuration from irq setup Anisa Su
@ 2026-09-22 5:35 ` Richard Cheng
0 siblings, 0 replies; 41+ messages in thread
From: Richard Cheng @ 2026-09-22 5:35 UTC (permalink / raw)
To: Anisa Su
Cc: linux-cxl, Dave Jiang, Alison Schofield, Jonathan Cameron,
Davidlohr Bueso, Li Ming, Gregory Price, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim, Jonathan Cameron,
Fan Ni
On Fri, Sep 18, 2026 at 01:30:13PM +0800, 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.
>
> 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>
> Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
Reviewed-by: Richard Cheng <icheng@nvidia.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 2fe9db4b3d86..4867657f5526 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -600,35 +600,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;
> @@ -675,11 +671,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 [flat|nested] 41+ messages in thread
* Re: [PATCH v14 5/8] cxl/pci: Factor out interrupt policy check
2026-09-18 20:30 ` [PATCH v14 5/8] cxl/pci: Factor out interrupt policy check Anisa Su
@ 2026-09-22 5:37 ` Richard Cheng
0 siblings, 0 replies; 41+ messages in thread
From: Richard Cheng @ 2026-09-22 5:37 UTC (permalink / raw)
To: Anisa Su
Cc: linux-cxl, Dave Jiang, Alison Schofield, Jonathan Cameron,
Davidlohr Bueso, Li Ming, Gregory Price, Ben Cheatham, Ira Weiny,
Dan Williams, Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim,
Jonathan Cameron, Fan Ni
On Fri, Sep 18, 2026 at 01:30:14PM +0800, 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 check for firmware ownership of memory event logs.
>
> 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>
> Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
> Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Richard Cheng <icheng@nvidia.com>
> ---
> drivers/cxl/pci.c | 13 +++++++++----
> 1 file changed, 9 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 4867657f5526..7b754af2168d 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -640,6 +640,14 @@ static bool cxl_event_int_is_fw(u8 setting)
> return mode == CXL_INT_FW;
> }
>
> +static bool cxl_event_fw_owns_mem_logs(struct cxl_event_interrupt_policy *policy)
> +{
> + return 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);
> +}
> +
> static int cxl_event_config(struct pci_host_bridge *host_bridge,
> struct cxl_memdev_state *mds, bool irq_avail)
> {
> @@ -662,10 +670,7 @@ 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)) {
> + if (cxl_event_fw_owns_mem_logs(&policy)) {
> dev_err(mds->cxlds.dev,
> "FW still in control of Event Logs despite _OSC settings\n");
> return -EBUSY;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts
2026-09-18 20:30 ` [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
2026-09-18 20:43 ` sashiko-bot
2026-09-21 22:00 ` Dave Jiang
@ 2026-09-22 5:48 ` Richard Cheng
2026-09-22 9:15 ` Richard Cheng
2 siblings, 1 reply; 41+ messages in thread
From: Richard Cheng @ 2026-09-22 5:48 UTC (permalink / raw)
To: Anisa Su
Cc: linux-cxl, Dave Jiang, Alison Schofield, Jonathan Cameron,
Davidlohr Bueso, Li Ming, Gregory Price, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim
On Fri, Sep 18, 2026 at 01:30:15PM +0800, 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.
>
> The DCD event log is not drained here. cxl_event_drain_mask() reports
> only the logs the driver can service, and the DCD bit is added by
> the patch introducing DCD event handling. Until then a DCD interrupt
> wakes the event thread, which finds no log it owns and returns.
>
> 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>
> 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: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Richard Cheng <icheng@nvidia.com>
Just a nit below.
> ---
> Changes:
>
> 1. [jonathan]: get rid of CXL_EVENT_STATUS_ALL and instead opt into
> event logs we want to read individually in cxl_event_drain_mask. This
> makes which logs the driver drains explicit if new logs are added in
> the future. The cxl_test mock names the same four logs for its
> probe-time drain.
> 2. pci.c: when BIOS owns the memory event logs and a short interrupt
> policy has just disabled DCD, return before Set Event Interrupt
> Policy and the event buffer allocation. Nothing is left to configure,
> and the Set would only have echoed firmware's own settings back.
> ---
> drivers/cxl/cxl.h | 6 +-
> drivers/cxl/cxlmem.h | 2 +
> drivers/cxl/pci.c | 127 +++++++++++++++++++++++++++++------
> tools/testing/cxl/test/mem.c | 5 +-
> 4 files changed, 114 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index cab8ce39f465..1add114bc178 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -192,11 +192,7 @@ 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_ALL (CXLDEV_EVENT_STATUS_INFO | \
> - CXLDEV_EVENT_STATUS_WARN | \
> - CXLDEV_EVENT_STATUS_FAIL | \
> - CXLDEV_EVENT_STATUS_FATAL)
> +#define CXLDEV_EVENT_STATUS_DCD BIT(4)
>
Can we retain a named mask of the 4 standard memory-event logs?
Instead of repeating the 4-bit expression over the code base, why not
reuse it ?
Best regards,
Richard Cheng.
> /* 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 9fd5af2e41d4..9106affd7c81 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 7b754af2168d..fee3a704b73b 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -510,11 +510,23 @@ static bool cxl_alloc_irq_vectors(struct pci_dev *pdev)
> return true;
> }
>
> +/* Event logs the driver drains: standard logs when native_cxl */
> +static u32 cxl_event_drain_mask(struct pci_host_bridge *host_bridge)
> +{
> + if (host_bridge->native_cxl_error)
> + return CXLDEV_EVENT_STATUS_INFO | CXLDEV_EVENT_STATUS_WARN |
> + CXLDEV_EVENT_STATUS_FAIL | CXLDEV_EVENT_STATUS_FATAL;
> + return 0;
> +}
> +
> 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);
> u32 status;
>
> do {
> @@ -523,8 +535,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);
> @@ -551,42 +563,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);
> @@ -597,7 +629,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,
> @@ -633,6 +665,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);
> @@ -651,32 +707,57 @@ static bool cxl_event_fw_owns_mem_logs(struct cxl_event_interrupt_policy *policy
> 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 mask;
> 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_fw_owns_mem_logs(&policy)) {
> + /*
> + * dcd_settings cannot be sent to a device whose policy is too short
> + * to hold it, so the device would never accept the setting
> + * cxl_irqsetup() arms the DCD interrupt with.
> + */
> + if (cxl_dcd_supported(mds) && policy_size < sizeof(policy)) {
> + dev_warn(mds->cxlds.dev,
> + "DCD supported but interrupt policy is only %zu bytes\n",
> + policy_size);
> + cxl_disable_dcd(mds);
> + /* DCD was the only reason to be here */
> + if (!native_cxl)
> + return 0;
> + }
> +
> + if (native_cxl && cxl_event_fw_owns_mem_logs(&policy)) {
> dev_err(mds->cxlds.dev,
> "FW still in control of Event Logs despite _OSC settings\n");
> return -EBUSY;
> }
>
> - rc = cxl_event_config_msgnums(mds, &policy);
> + rc = cxl_event_config_msgnums(mds, &policy, native_cxl, policy_size);
> if (rc)
> return rc;
>
> @@ -684,11 +765,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);
> + mask = cxl_event_drain_mask(host_bridge);
> + if (mask)
> + cxl_mem_get_event_records(mds, mask);
> +
> + dev_dbg(mds->cxlds.dev, "Event config : %s DCD %s\n",
> + native_cxl ? "OS" : "BIOS",
> + cxl_dcd_supported(mds) ? "supported" : "not supported");
>
> return 0;
> }
> diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
> index 7b756000a1a6..6ef47265da10 100644
> --- a/tools/testing/cxl/test/mem.c
> +++ b/tools/testing/cxl/test/mem.c
> @@ -1818,7 +1818,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
> if (rc)
> dev_dbg(dev, "No CXL FWCTL setup\n");
>
> - cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
> + cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_INFO |
> + CXLDEV_EVENT_STATUS_WARN |
> + CXLDEV_EVENT_STATUS_FAIL |
> + CXLDEV_EVENT_STATUS_FATAL);
> cxl_mock_test_feat_init(mdata);
>
> return 0;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts
2026-09-22 5:48 ` Richard Cheng
@ 2026-09-22 9:15 ` Richard Cheng
0 siblings, 0 replies; 41+ messages in thread
From: Richard Cheng @ 2026-09-22 9:15 UTC (permalink / raw)
To: Anisa Su
Cc: linux-cxl, Dave Jiang, Alison Schofield, Jonathan Cameron,
Davidlohr Bueso, Li Ming, Gregory Price, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim
On Tue, Sep 22, 2026 at 01:48:38PM +0800, Richard Cheng wrote:
> On Fri, Sep 18, 2026 at 01:30:15PM +0800, 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.
> >
> > The DCD event log is not drained here. cxl_event_drain_mask() reports
> > only the logs the driver can service, and the DCD bit is added by
> > the patch introducing DCD event handling. Until then a DCD interrupt
> > wakes the event thread, which finds no log it owns and returns.
> >
> > 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>
> > 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: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> > Reviewed-by: Alison Schofield <alison.schofield@intel.com>
>
> Reviewed-by: Richard Cheng <icheng@nvidia.com>
>
> Just a nit below.
>
> > ---
> > Changes:
> >
> > 1. [jonathan]: get rid of CXL_EVENT_STATUS_ALL and instead opt into
> > event logs we want to read individually in cxl_event_drain_mask. This
> > makes which logs the driver drains explicit if new logs are added in
> > the future. The cxl_test mock names the same four logs for its
> > probe-time drain.
> > 2. pci.c: when BIOS owns the memory event logs and a short interrupt
> > policy has just disabled DCD, return before Set Event Interrupt
> > Policy and the event buffer allocation. Nothing is left to configure,
> > and the Set would only have echoed firmware's own settings back.
> > ---
> > drivers/cxl/cxl.h | 6 +-
> > drivers/cxl/cxlmem.h | 2 +
> > drivers/cxl/pci.c | 127 +++++++++++++++++++++++++++++------
> > tools/testing/cxl/test/mem.c | 5 +-
> > 4 files changed, 114 insertions(+), 26 deletions(-)
> >
> > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > index cab8ce39f465..1add114bc178 100644
> > --- a/drivers/cxl/cxl.h
> > +++ b/drivers/cxl/cxl.h
> > @@ -192,11 +192,7 @@ 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_ALL (CXLDEV_EVENT_STATUS_INFO | \
> > - CXLDEV_EVENT_STATUS_WARN | \
> > - CXLDEV_EVENT_STATUS_FAIL | \
> > - CXLDEV_EVENT_STATUS_FATAL)
> > +#define CXLDEV_EVENT_STATUS_DCD BIT(4)
> >
>
> Can we retain a named mask of the 4 standard memory-event logs?
> Instead of repeating the 4-bit expression over the code base, why not
> reuse it ?
>
> Best regards,
> Richard Cheng.
>
>
I just saw Dave and Jonathan's reply, agree with them.
No need to reply to this one, thanks.
Best regards,
Richard Cheng.
> > /* 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 9fd5af2e41d4..9106affd7c81 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 7b754af2168d..fee3a704b73b 100644
> > --- a/drivers/cxl/pci.c
> > +++ b/drivers/cxl/pci.c
> > @@ -510,11 +510,23 @@ static bool cxl_alloc_irq_vectors(struct pci_dev *pdev)
> > return true;
> > }
> >
> > +/* Event logs the driver drains: standard logs when native_cxl */
> > +static u32 cxl_event_drain_mask(struct pci_host_bridge *host_bridge)
> > +{
> > + if (host_bridge->native_cxl_error)
> > + return CXLDEV_EVENT_STATUS_INFO | CXLDEV_EVENT_STATUS_WARN |
> > + CXLDEV_EVENT_STATUS_FAIL | CXLDEV_EVENT_STATUS_FATAL;
> > + return 0;
> > +}
> > +
> > 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);
> > u32 status;
> >
> > do {
> > @@ -523,8 +535,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);
> > @@ -551,42 +563,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);
> > @@ -597,7 +629,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,
> > @@ -633,6 +665,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);
> > @@ -651,32 +707,57 @@ static bool cxl_event_fw_owns_mem_logs(struct cxl_event_interrupt_policy *policy
> > 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 mask;
> > 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_fw_owns_mem_logs(&policy)) {
> > + /*
> > + * dcd_settings cannot be sent to a device whose policy is too short
> > + * to hold it, so the device would never accept the setting
> > + * cxl_irqsetup() arms the DCD interrupt with.
> > + */
> > + if (cxl_dcd_supported(mds) && policy_size < sizeof(policy)) {
> > + dev_warn(mds->cxlds.dev,
> > + "DCD supported but interrupt policy is only %zu bytes\n",
> > + policy_size);
> > + cxl_disable_dcd(mds);
> > + /* DCD was the only reason to be here */
> > + if (!native_cxl)
> > + return 0;
> > + }
> > +
> > + if (native_cxl && cxl_event_fw_owns_mem_logs(&policy)) {
> > dev_err(mds->cxlds.dev,
> > "FW still in control of Event Logs despite _OSC settings\n");
> > return -EBUSY;
> > }
> >
> > - rc = cxl_event_config_msgnums(mds, &policy);
> > + rc = cxl_event_config_msgnums(mds, &policy, native_cxl, policy_size);
> > if (rc)
> > return rc;
> >
> > @@ -684,11 +765,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);
> > + mask = cxl_event_drain_mask(host_bridge);
> > + if (mask)
> > + cxl_mem_get_event_records(mds, mask);
> > +
> > + dev_dbg(mds->cxlds.dev, "Event config : %s DCD %s\n",
> > + native_cxl ? "OS" : "BIOS",
> > + cxl_dcd_supported(mds) ? "supported" : "not supported");
> >
> > return 0;
> > }
> > diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
> > index 7b756000a1a6..6ef47265da10 100644
> > --- a/tools/testing/cxl/test/mem.c
> > +++ b/tools/testing/cxl/test/mem.c
> > @@ -1818,7 +1818,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
> > if (rc)
> > dev_dbg(dev, "No CXL FWCTL setup\n");
> >
> > - cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
> > + cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_INFO |
> > + CXLDEV_EVENT_STATUS_WARN |
> > + CXLDEV_EVENT_STATUS_FAIL |
> > + CXLDEV_EVENT_STATUS_FATAL);
> > cxl_mock_test_feat_init(mdata);
> >
> > return 0;
> > --
> > 2.43.0
> >
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-22 3:53 ` Richard Cheng
@ 2026-09-22 17:08 ` Dave Jiang
2026-09-22 21:15 ` Anisa Su
0 siblings, 1 reply; 41+ messages in thread
From: Dave Jiang @ 2026-09-22 17:08 UTC (permalink / raw)
To: Richard Cheng, Anisa Su
Cc: linux-cxl, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Ben Cheatham, Ira Weiny, Anisa Su,
Wonjae Lee, Junhee Park, Heesoo Kim
On 9/21/26 8:53 PM, Richard Cheng wrote:
> On Fri, Sep 18, 2026 at 01:30:12PM +0800, Anisa Su wrote:
>> From: Ira Weiny <iweiny@kernel.org>
>>
>> Additional DCD partition (AKA region) information is contained in the
>> DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
>>
>
> Hi Anisa,
>
> LGTM but I have limited knowledge for details of device's CDAT.
> Other experts would be more suitable to leave their tag.
>
> I have a question here only, I wonder if a CDAT with no DSLBIS supported is
> possible ? seems that the implementation has the assumption that device's
> CDAT would be always supported with valid DSMAS and DSLBIS.
According to the spec, the device suppoort DCD shall provide DSMAS (CXL r4.0 9.13.3). DSLBIS is optional.
So I do wonder if we need to add plumbing to disable DCD if DSMAS is missing or malformed.
DJ
>
> Best regards,
> Richard Cheng.
>
>
>> ---
>> Changes:
>> 1. cdat.c: record the DSMAS shareable flag independently of QTG
>> discovery. cxl_port_perf_data_calculate() returns -ENOENT when FW
>> recommends no QTG for any entry, which skipped the assignment even
>> though the CDAT parsed successfully and the DSMAS flag was valid.
>> Richard
>> ---
>> drivers/cxl/core/cdat.c | 49 +++++++++++++++++++++++++++++----------
>> drivers/cxl/core/hdm.c | 1 +
>> drivers/cxl/core/mbox.c | 9 +++++++
>> drivers/cxl/core/memdev.c | 17 ++++++++------
>> drivers/cxl/cxlmem.h | 2 ++
>> include/cxl/cxl.h | 4 ++++
>> 6 files changed, 63 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
>> index 5c9f07262513..6e7950f12134 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,8 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
>> return -ENOMEM;
>>
>> dent->handle = dsmas->dsmad_handle;
>> + /* Shareable is CDAT 1.03 and later, DSMAS Flags bit 3 */
>> + 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;
>> @@ -255,8 +258,13 @@ static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
>> dent->coord[ACCESS_COORDINATE_CPU].write_latency);
>> }
>>
>> -static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
>> - struct xarray *dsmas_xa)
>> +/*
>> + * The DSMAS shareable flag is a property of the CDAT entry and is recorded
>> + * even if FW returns no QTG recommendations for any entry; only update perf
>> + * if those recommendations exist, indicated by @set_perf
>> + */
>> +static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
>> + struct xarray *dsmas_xa, bool set_perf)
>> {
>> struct device *dev = cxlds->dev;
>> struct dsmas_entry *dent;
>> @@ -266,18 +274,34 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
>> bool found = false;
>>
>> for (int i = 0; i < cxlds->nr_partitions; i++) {
>> - struct resource *res = &cxlds->part[i].res;
>> + struct cxl_dpa_partition *part = &cxlds->part[i];
>> struct range range = {
>> - .start = res->start,
>> - .end = res->end,
>> + .start = part->res.start,
>> + .end = part->res.end,
>> };
>>
>> - if (range_contains(&range, &dent->dpa_range)) {
>> - update_perf_entry(dev, dent,
>> - &cxlds->part[i].perf);
>> - found = true;
>> + if (!range_contains(&range, &dent->dpa_range))
>> + continue;
>> +
>> + found = true;
>> + /*
>> + * 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;
>> }
>> +
>> + if (set_perf)
>> + update_perf_entry(dev, dent, &part->perf);
>> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
>> + part->shareable = dent->shareable;
>> + break;
>> }
>>
>> if (!found)
>> @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
>> }
>>
>> rc = cxl_port_perf_data_calculate(port, dsmas_xa);
>> - if (rc) {
>> + if (rc)
>> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
>> +
>> + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
>> + if (rc)
>> return;
>> - }
>>
>> - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
>> cxl_qos_class_verify(cxlmd);
>> cxl_memdev_update_perf(cxlmd);
>> }
>> 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 c96c16cc08e1..d84a90320081 100644
>> --- a/drivers/cxl/core/mbox.c
>> +++ b/drivers/cxl/core/mbox.c
>> @@ -1366,6 +1366,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
>> u64 decode_len = le64_to_cpu(dev_part->decode_length);
>> 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);
>> u64 size;
>>
>> /*
>> @@ -1378,6 +1379,13 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
>> return 0;
>> }
>>
>> + /* 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;
>> + }
>> +
>> /*
>> * Decode length is a count of 256M units, so a device reporting 2^36
>> * or more wraps.
>> @@ -1391,6 +1399,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
>> *part = (struct cxl_dc_partition_info) {
>> .start = le64_to_cpu(dev_part->base),
>> .size = size,
>> + .handle = handle,
>> };
>>
>> /*
>> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
>> index 15df8db314d9..8883c8850759 100644
>> --- a/drivers/cxl/core/memdev.c
>> +++ b/drivers/cxl/core/memdev.c
>> @@ -594,7 +594,8 @@ bool is_cxl_memdev(const struct device *dev)
>> }
>> EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "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;
>>
>> @@ -606,6 +607,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++;
>> }
>>
>> @@ -623,9 +625,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;
>> }
>>
>> @@ -635,9 +637,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;
>> }
>> @@ -685,7 +687,8 @@ int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
>> info->size = total;
>> dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\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);
>>
>> return 0;
>> }
>> @@ -704,7 +707,7 @@ int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
>> .size = capacity,
>> };
>>
>> - add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
>> + add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM, 0);
>> return cxl_dpa_setup(cxlds, &range_info);
>> }
>> EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
>> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
>> index 4fac65dbb808..9fd5af2e41d4 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;
>> };
>> @@ -851,6 +852,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 4ef9511fbbdd..43edcde5bce3 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 that represents this partition
>> + * @shareable: Is the partition shareable (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 [flat|nested] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-22 17:08 ` Dave Jiang
@ 2026-09-22 21:15 ` Anisa Su
2026-09-22 23:05 ` Dave Jiang
0 siblings, 1 reply; 41+ messages in thread
From: Anisa Su @ 2026-09-22 21:15 UTC (permalink / raw)
To: Dave Jiang
Cc: Richard Cheng, Anisa Su, linux-cxl, Alison Schofield,
Jonathan Cameron, Davidlohr Bueso, Li Ming, Gregory Price,
Ben Cheatham, Ira Weiny, Wonjae Lee, Junhee Park, Heesoo Kim
On Tue, Sep 22, 2026 at 10:08:08AM -0700, Dave Jiang wrote:
>
>
> On 9/21/26 8:53 PM, Richard Cheng wrote:
> > On Fri, Sep 18, 2026 at 01:30:12PM +0800, Anisa Su wrote:
> >> From: Ira Weiny <iweiny@kernel.org>
> >>
> >> Additional DCD partition (AKA region) information is contained in the
> >> DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
> >>
> >
> > Hi Anisa,
> >
> > LGTM but I have limited knowledge for details of device's CDAT.
> > Other experts would be more suitable to leave their tag.
> >
> > I have a question here only, I wonder if a CDAT with no DSLBIS supported is
> > possible ? seems that the implementation has the assumption that device's
> > CDAT would be always supported with valid DSMAS and DSLBIS.
>
> According to the spec, the device suppoort DCD shall provide DSMAS (CXL r4.0 9.13.3). DSLBIS is optional.
>
> So I do wonder if we need to add plumbing to disable DCD if DSMAS is missing or malformed.
>
> DJ
>
Can do. DSMAS is missing/malformed in 3 scenarios:
- CDAT is missing
- Failed to parse DSMAS from the CDAT
- No DSMAS entry matched the DC partition.
So we can make a helper function to disable DCD and call from all 3 sites:
+/*
+ * A DCD shall describe its DC partition in a DSMAS entry (CXL r4.0 9.13.3).
+ * Without one the partition's attributes are unknown, so disable DCD.
+ */
+static void cxl_dcd_dsmas_missing(struct cxl_dev_state *cxlds, const char *why)
+{
+ struct cxl_memdev_state *mds;
+
+ if (cxlds->type != CXL_DEVTYPE_CLASSMEM)
+ return;
+
+ mds = to_cxl_memdev_state(cxlds);
+ if (!cxl_dcd_supported(mds) ||
+ !cxl_part_size(cxlds, CXL_PARTMODE_DYNAMIC_RAM_1))
+ return;
+
+ dev_warn(cxlds->dev, "DC partition has no valid DSMAS entry (%s), disabling DCD\n",
+ why);
+ cxl_disable_dcd(mds);
+}
Called if there's no CDAT/fails to parse CDAT:
void cxl_endpoint_parse_cdat(struct cxl_port *port)
int rc;
xa_init(&__dsmas_xa);
- if (!port->cdat.table)
+ if (!port->cdat.table) {
+ cxl_dcd_dsmas_missing(cxlds, "no CDAT");
return;
+ }
rc = cxl_cdat_endpoint_process(port, dsmas_xa);
if (rc < 0) {
dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
+ cxl_dcd_dsmas_missing(cxlds, "CDAT parse failed");
return;
}
Or no DSMAS entry matched the DC partition:
static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
struct device *dev = cxlds->dev;
struct dsmas_entry *dent;
+ bool dc_matched = false;
unsigned long index;
...
if (set_perf)
update_perf_entry(dev, dent, &part->perf);
- if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
+ if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1) {
part->shareable = dent->shareable;
+ dc_matched = true;
+ }
break;
}
...
dev_dbg(dev, "no partition for dsmas dpa: %pra\n",
&dent->dpa_range);
}
+
+ if (!dc_matched)
+ cxl_dcd_dsmas_missing(cxlds, "no matching entry");
}
But currently cxl_cdat_endpoint_process() fails if there's no DSLBIS or it's
empty and DSLBIS is optional for DCD, so need a prep patch to allow empty DSLBIS
and skip cxl_port_perf_data_calculate(port, dsmas_xa) if it's empty.
Something like this:
--- a/drivers/cxl/core/cdat.c
+++ b/drivers/cxl/core/cdat.c
@@ -181,7 +181,7 @@
}
static int cxl_cdat_endpoint_process(struct cxl_port *port,
- struct xarray *dsmas_xa)
+ struct xarray *dsmas_xa, bool *has_dslbis)
{
int rc;
@@ -191,9 +191,13 @@
if (rc)
return rc;
+ /* DSLBIS is optional; without it there is no perf data to apply */
rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler,
dsmas_xa, port->cdat.table, port->cdat.length);
- return cdat_table_parse_output(rc);
+ if (rc < 0)
+ return rc;
+ *has_dslbis = rc > 0;
+ return 0;
}
static int cxl_port_perf_data_calculate(struct cxl_port *port,
@@ -430,19 +434,22 @@
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct xarray __dsmas_xa;
struct xarray *dsmas_xa __free(dsmas) = &__dsmas_xa;
+ bool has_dslbis = false;
int rc;
xa_init(&__dsmas_xa);
if (!port->cdat.table)
return;
- rc = cxl_cdat_endpoint_process(port, dsmas_xa);
+ rc = cxl_cdat_endpoint_process(port, dsmas_xa, &has_dslbis);
if (rc < 0) {
dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
return;
}
- rc = cxl_port_perf_data_calculate(port, dsmas_xa);
+ rc = -ENOENT;
+ if (has_dslbis)
+ rc = cxl_port_perf_data_calculate(port, dsmas_xa);
if (rc)
dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
Would something like this work? (I am also not an expert in CDAT/DSMAS/DSLBIS)
Thanks,
Anisa
> >
> > Best regards,
> > Richard Cheng.
> >
> >
> >> ---
> >> Changes:
> >> 1. cdat.c: record the DSMAS shareable flag independently of QTG
> >> discovery. cxl_port_perf_data_calculate() returns -ENOENT when FW
> >> recommends no QTG for any entry, which skipped the assignment even
> >> though the CDAT parsed successfully and the DSMAS flag was valid.
> >> Richard
> >> ---
> >> drivers/cxl/core/cdat.c | 49 +++++++++++++++++++++++++++++----------
> >> drivers/cxl/core/hdm.c | 1 +
> >> drivers/cxl/core/mbox.c | 9 +++++++
> >> drivers/cxl/core/memdev.c | 17 ++++++++------
> >> drivers/cxl/cxlmem.h | 2 ++
> >> include/cxl/cxl.h | 4 ++++
> >> 6 files changed, 63 insertions(+), 19 deletions(-)
> >>
> >> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> >> index 5c9f07262513..6e7950f12134 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,8 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
> >> return -ENOMEM;
> >>
> >> dent->handle = dsmas->dsmad_handle;
> >> + /* Shareable is CDAT 1.03 and later, DSMAS Flags bit 3 */
> >> + 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;
> >> @@ -255,8 +258,13 @@ static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
> >> dent->coord[ACCESS_COORDINATE_CPU].write_latency);
> >> }
> >>
> >> -static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> >> - struct xarray *dsmas_xa)
> >> +/*
> >> + * The DSMAS shareable flag is a property of the CDAT entry and is recorded
> >> + * even if FW returns no QTG recommendations for any entry; only update perf
> >> + * if those recommendations exist, indicated by @set_perf
> >> + */
> >> +static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
> >> + struct xarray *dsmas_xa, bool set_perf)
> >> {
> >> struct device *dev = cxlds->dev;
> >> struct dsmas_entry *dent;
> >> @@ -266,18 +274,34 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> >> bool found = false;
> >>
> >> for (int i = 0; i < cxlds->nr_partitions; i++) {
> >> - struct resource *res = &cxlds->part[i].res;
> >> + struct cxl_dpa_partition *part = &cxlds->part[i];
> >> struct range range = {
> >> - .start = res->start,
> >> - .end = res->end,
> >> + .start = part->res.start,
> >> + .end = part->res.end,
> >> };
> >>
> >> - if (range_contains(&range, &dent->dpa_range)) {
> >> - update_perf_entry(dev, dent,
> >> - &cxlds->part[i].perf);
> >> - found = true;
> >> + if (!range_contains(&range, &dent->dpa_range))
> >> + continue;
> >> +
> >> + found = true;
> >> + /*
> >> + * 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;
> >> }
> >> +
> >> + if (set_perf)
> >> + update_perf_entry(dev, dent, &part->perf);
> >> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
> >> + part->shareable = dent->shareable;
> >> + break;
> >> }
> >>
> >> if (!found)
> >> @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
> >> }
> >>
> >> rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> >> - if (rc) {
> >> + if (rc)
> >> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> >> +
> >> + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
> >> + if (rc)
> >> return;
> >> - }
> >>
> >> - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
> >> cxl_qos_class_verify(cxlmd);
> >> cxl_memdev_update_perf(cxlmd);
> >> }
> >> 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 c96c16cc08e1..d84a90320081 100644
> >> --- a/drivers/cxl/core/mbox.c
> >> +++ b/drivers/cxl/core/mbox.c
> >> @@ -1366,6 +1366,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> >> u64 decode_len = le64_to_cpu(dev_part->decode_length);
> >> 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);
> >> u64 size;
> >>
> >> /*
> >> @@ -1378,6 +1379,13 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> >> return 0;
> >> }
> >>
> >> + /* 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;
> >> + }
> >> +
> >> /*
> >> * Decode length is a count of 256M units, so a device reporting 2^36
> >> * or more wraps.
> >> @@ -1391,6 +1399,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> >> *part = (struct cxl_dc_partition_info) {
> >> .start = le64_to_cpu(dev_part->base),
> >> .size = size,
> >> + .handle = handle,
> >> };
> >>
> >> /*
> >> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> >> index 15df8db314d9..8883c8850759 100644
> >> --- a/drivers/cxl/core/memdev.c
> >> +++ b/drivers/cxl/core/memdev.c
> >> @@ -594,7 +594,8 @@ bool is_cxl_memdev(const struct device *dev)
> >> }
> >> EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "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;
> >>
> >> @@ -606,6 +607,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++;
> >> }
> >>
> >> @@ -623,9 +625,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;
> >> }
> >>
> >> @@ -635,9 +637,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;
> >> }
> >> @@ -685,7 +687,8 @@ int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> >> info->size = total;
> >> dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\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);
> >>
> >> return 0;
> >> }
> >> @@ -704,7 +707,7 @@ int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
> >> .size = capacity,
> >> };
> >>
> >> - add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
> >> + add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM, 0);
> >> return cxl_dpa_setup(cxlds, &range_info);
> >> }
> >> EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
> >> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> >> index 4fac65dbb808..9fd5af2e41d4 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;
> >> };
> >> @@ -851,6 +852,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 4ef9511fbbdd..43edcde5bce3 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 that represents this partition
> >> + * @shareable: Is the partition shareable (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 [flat|nested] 41+ messages in thread
* Re: [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts
2026-09-22 0:45 ` Dave Jiang
@ 2026-09-22 21:22 ` Anisa Su
2026-09-24 0:58 ` Jonathan Cameron
0 siblings, 1 reply; 41+ messages in thread
From: Anisa Su @ 2026-09-22 21:22 UTC (permalink / raw)
To: Dave Jiang
Cc: Jonathan Cameron, Anisa Su, linux-cxl, Alison Schofield,
Davidlohr Bueso, Li Ming, Gregory Price, Richard Cheng,
Ben Cheatham, Ira Weiny, Wonjae Lee, Junhee Park, Heesoo Kim
On Mon, Sep 21, 2026 at 05:45:56PM -0700, Dave Jiang wrote:
>
>
> On 9/21/26 4:42 PM, Jonathan Cameron wrote:
> >
> >>> diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
> >>> index 7b756000a1a6..6ef47265da10 100644
> >>> --- a/tools/testing/cxl/test/mem.c
> >>> +++ b/tools/testing/cxl/test/mem.c
> >>> @@ -1818,7 +1818,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
> >>> if (rc)
> >>> dev_dbg(dev, "No CXL FWCTL setup\n");
> >>>
> >>> - cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
> >>> + cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_INFO |
> >>> + CXLDEV_EVENT_STATUS_WARN |
> >>> + CXLDEV_EVENT_STATUS_FAIL |
> >>> + CXLDEV_EVENT_STATUS_FATAL);
> >>
> >> Second time I'm seeing this constructed mask being used in this commit. Maybe create a define for it?
> >>
> >> s/CXLDEV_EVENT_STATUS_ALL/CXLDEV_EVENT_STATUS_BASE/ perhaps?
> >
> > That was Anisa acting on my feedbakc on previous.
> > I don't like _ALL because we already have an example of the spec
> > expanding and the definition becoming messy. But mainly I was
> > pushing back against _ALL + masking with one element we didn't
> > want as fragile. So I'm not against _ALL if it is made up of
> > another define + DCD (for now) and that other define can be
> > used in places like this.
> >
> > Bikeshed time... BASE is usually a define for a reg address and it is
> > is hard to come up with a name for that when it is a mixture of
> > RAS stuff and INFO which can be a wide range of random nasty
> > and nice things.
> >
> > So with the right name seems fine to have what Dave suggests.
> > I'm just not sure what that name is! Hence I'd just stick
> > to the long hand option.
>
> Yeah if we can't come up with anything good then leave it as is.
>
CXLDEV_EVENT_STATUS_STANDARD_LOGS...? And just add a comment kind of explaining
what "standard" means:
/* Logs whose ownership follows native_cxl_error; the DCD log is always OS owned */
#define CXLDEV_EVENT_STATUS_STANDARD_LOGS (CXLDEV_EVENT_STATUS_INFO | \
CXLDEV_EVENT_STATUS_WARN | \
CXLDEV_EVENT_STATUS_FAIL | \
CXLDEV_EVENT_STATUS_FATAL)
That's all I can come up with.
- Anisa
> >
> > Jonathan
> >
> >>
> >>> cxl_mock_test_feat_init(mdata);
> >>>
> >>> return 0;
> >>
> >>
> >
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-22 21:15 ` Anisa Su
@ 2026-09-22 23:05 ` Dave Jiang
2026-09-23 0:05 ` Anisa Su
0 siblings, 1 reply; 41+ messages in thread
From: Dave Jiang @ 2026-09-22 23:05 UTC (permalink / raw)
To: Anisa Su
Cc: Richard Cheng, linux-cxl, Alison Schofield, Jonathan Cameron,
Davidlohr Bueso, Li Ming, Gregory Price, Ben Cheatham, Ira Weiny,
Wonjae Lee, Junhee Park, Heesoo Kim
On 9/22/26 2:15 PM, Anisa Su wrote:
> On Tue, Sep 22, 2026 at 10:08:08AM -0700, Dave Jiang wrote:
>>
>>
>> On 9/21/26 8:53 PM, Richard Cheng wrote:
>>> On Fri, Sep 18, 2026 at 01:30:12PM +0800, Anisa Su wrote:
>>>> From: Ira Weiny <iweiny@kernel.org>
>>>>
>>>> Additional DCD partition (AKA region) information is contained in the
>>>> DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
>>>>
>>>
>>> Hi Anisa,
>>>
>>> LGTM but I have limited knowledge for details of device's CDAT.
>>> Other experts would be more suitable to leave their tag.
>>>
>>> I have a question here only, I wonder if a CDAT with no DSLBIS supported is
>>> possible ? seems that the implementation has the assumption that device's
>>> CDAT would be always supported with valid DSMAS and DSLBIS.
>>
>> According to the spec, the device suppoort DCD shall provide DSMAS (CXL r4.0 9.13.3). DSLBIS is optional.
>>
>> So I do wonder if we need to add plumbing to disable DCD if DSMAS is missing or malformed.
>>
>> DJ
>>
> Can do. DSMAS is missing/malformed in 3 scenarios:
> - CDAT is missing
> - Failed to parse DSMAS from the CDAT
> - No DSMAS entry matched the DC partition.
>
> So we can make a helper function to disable DCD and call from all 3 sites:
>
> +/*
> + * A DCD shall describe its DC partition in a DSMAS entry (CXL r4.0 9.13.3).
> + * Without one the partition's attributes are unknown, so disable DCD.
> + */
> +static void cxl_dcd_dsmas_missing(struct cxl_dev_state *cxlds, const char *why)
> +{
> + struct cxl_memdev_state *mds;
> +
> + if (cxlds->type != CXL_DEVTYPE_CLASSMEM)
> + return;
> +
> + mds = to_cxl_memdev_state(cxlds);
> + if (!cxl_dcd_supported(mds) ||
> + !cxl_part_size(cxlds, CXL_PARTMODE_DYNAMIC_RAM_1))
> + return;
> +
> + dev_warn(cxlds->dev, "DC partition has no valid DSMAS entry (%s), disabling DCD\n",
> + why);
> + cxl_disable_dcd(mds);
> +}
>
> Called if there's no CDAT/fails to parse CDAT:
>
> void cxl_endpoint_parse_cdat(struct cxl_port *port)
> int rc;
>
> xa_init(&__dsmas_xa);
> - if (!port->cdat.table)
> + if (!port->cdat.table) {
> + cxl_dcd_dsmas_missing(cxlds, "no CDAT");
> return;
> + }
>
> rc = cxl_cdat_endpoint_process(port, dsmas_xa);
> if (rc < 0) {
> dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
> + cxl_dcd_dsmas_missing(cxlds, "CDAT parse failed");
> return;
> }
>
> Or no DSMAS entry matched the DC partition:
> static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
> struct device *dev = cxlds->dev;
> struct dsmas_entry *dent;
> + bool dc_matched = false;
> unsigned long index;
> ...
> if (set_perf)
> update_perf_entry(dev, dent, &part->perf);
> - if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1) {
> part->shareable = dent->shareable;
> + dc_matched = true;
> + }
> break;
> }
>
> ...
> dev_dbg(dev, "no partition for dsmas dpa: %pra\n",
> &dent->dpa_range);
> }
> +
> + if (!dc_matched)
> + cxl_dcd_dsmas_missing(cxlds, "no matching entry");
> }
>
> But currently cxl_cdat_endpoint_process() fails if there's no DSLBIS or it's
> empty and DSLBIS is optional for DCD, so need a prep patch to allow empty DSLBIS
> and skip cxl_port_perf_data_calculate(port, dsmas_xa) if it's empty.
But perf calc failure does not stop the probe. cxl_endpoint_parse_cdat() returns void. We swallow the error. So I don't think changes are needed around here? You get -ENOENT if no entries from cdat_table_parse_output().
DJ
>
> Something like this:
> --- a/drivers/cxl/core/cdat.c
> +++ b/drivers/cxl/core/cdat.c
> @@ -181,7 +181,7 @@
> }
>
> static int cxl_cdat_endpoint_process(struct cxl_port *port,
> - struct xarray *dsmas_xa)
> + struct xarray *dsmas_xa, bool *has_dslbis)
> {
> int rc;
>
> @@ -191,9 +191,13 @@
> if (rc)
> return rc;
>
> + /* DSLBIS is optional; without it there is no perf data to apply */
> rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler,
> dsmas_xa, port->cdat.table, port->cdat.length);
> - return cdat_table_parse_output(rc);
> + if (rc < 0)
> + return rc;
> + *has_dslbis = rc > 0;
> + return 0;
> }
>
> static int cxl_port_perf_data_calculate(struct cxl_port *port,
> @@ -430,19 +434,22 @@
> struct cxl_dev_state *cxlds = cxlmd->cxlds;
> struct xarray __dsmas_xa;
> struct xarray *dsmas_xa __free(dsmas) = &__dsmas_xa;
> + bool has_dslbis = false;
> int rc;
>
> xa_init(&__dsmas_xa);
> if (!port->cdat.table)
> return;
>
> - rc = cxl_cdat_endpoint_process(port, dsmas_xa);
> + rc = cxl_cdat_endpoint_process(port, dsmas_xa, &has_dslbis);
> if (rc < 0) {
> dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
> return;
> }
>
> - rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> + rc = -ENOENT;
> + if (has_dslbis)
> + rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> if (rc)
> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
>
>
> Would something like this work? (I am also not an expert in CDAT/DSMAS/DSLBIS)
>
> Thanks,
> Anisa
>>>
>>> Best regards,
>>> Richard Cheng.
>>>
>>>
>>>> ---
>>>> Changes:
>>>> 1. cdat.c: record the DSMAS shareable flag independently of QTG
>>>> discovery. cxl_port_perf_data_calculate() returns -ENOENT when FW
>>>> recommends no QTG for any entry, which skipped the assignment even
>>>> though the CDAT parsed successfully and the DSMAS flag was valid.
>>>> Richard
>>>> ---
>>>> drivers/cxl/core/cdat.c | 49 +++++++++++++++++++++++++++++----------
>>>> drivers/cxl/core/hdm.c | 1 +
>>>> drivers/cxl/core/mbox.c | 9 +++++++
>>>> drivers/cxl/core/memdev.c | 17 ++++++++------
>>>> drivers/cxl/cxlmem.h | 2 ++
>>>> include/cxl/cxl.h | 4 ++++
>>>> 6 files changed, 63 insertions(+), 19 deletions(-)
>>>>
>>>> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
>>>> index 5c9f07262513..6e7950f12134 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,8 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
>>>> return -ENOMEM;
>>>>
>>>> dent->handle = dsmas->dsmad_handle;
>>>> + /* Shareable is CDAT 1.03 and later, DSMAS Flags bit 3 */
>>>> + 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;
>>>> @@ -255,8 +258,13 @@ static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
>>>> dent->coord[ACCESS_COORDINATE_CPU].write_latency);
>>>> }
>>>>
>>>> -static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
>>>> - struct xarray *dsmas_xa)
>>>> +/*
>>>> + * The DSMAS shareable flag is a property of the CDAT entry and is recorded
>>>> + * even if FW returns no QTG recommendations for any entry; only update perf
>>>> + * if those recommendations exist, indicated by @set_perf
>>>> + */
>>>> +static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
>>>> + struct xarray *dsmas_xa, bool set_perf)
>>>> {
>>>> struct device *dev = cxlds->dev;
>>>> struct dsmas_entry *dent;
>>>> @@ -266,18 +274,34 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
>>>> bool found = false;
>>>>
>>>> for (int i = 0; i < cxlds->nr_partitions; i++) {
>>>> - struct resource *res = &cxlds->part[i].res;
>>>> + struct cxl_dpa_partition *part = &cxlds->part[i];
>>>> struct range range = {
>>>> - .start = res->start,
>>>> - .end = res->end,
>>>> + .start = part->res.start,
>>>> + .end = part->res.end,
>>>> };
>>>>
>>>> - if (range_contains(&range, &dent->dpa_range)) {
>>>> - update_perf_entry(dev, dent,
>>>> - &cxlds->part[i].perf);
>>>> - found = true;
>>>> + if (!range_contains(&range, &dent->dpa_range))
>>>> + continue;
>>>> +
>>>> + found = true;
>>>> + /*
>>>> + * 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;
>>>> }
>>>> +
>>>> + if (set_perf)
>>>> + update_perf_entry(dev, dent, &part->perf);
>>>> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
>>>> + part->shareable = dent->shareable;
>>>> + break;
>>>> }
>>>>
>>>> if (!found)
>>>> @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
>>>> }
>>>>
>>>> rc = cxl_port_perf_data_calculate(port, dsmas_xa);
>>>> - if (rc) {
>>>> + if (rc)
>>>> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
>>>> +
>>>> + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
>>>> + if (rc)
>>>> return;
>>>> - }
>>>>
>>>> - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
>>>> cxl_qos_class_verify(cxlmd);
>>>> cxl_memdev_update_perf(cxlmd);
>>>> }
>>>> 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 c96c16cc08e1..d84a90320081 100644
>>>> --- a/drivers/cxl/core/mbox.c
>>>> +++ b/drivers/cxl/core/mbox.c
>>>> @@ -1366,6 +1366,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
>>>> u64 decode_len = le64_to_cpu(dev_part->decode_length);
>>>> 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);
>>>> u64 size;
>>>>
>>>> /*
>>>> @@ -1378,6 +1379,13 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
>>>> return 0;
>>>> }
>>>>
>>>> + /* 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;
>>>> + }
>>>> +
>>>> /*
>>>> * Decode length is a count of 256M units, so a device reporting 2^36
>>>> * or more wraps.
>>>> @@ -1391,6 +1399,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
>>>> *part = (struct cxl_dc_partition_info) {
>>>> .start = le64_to_cpu(dev_part->base),
>>>> .size = size,
>>>> + .handle = handle,
>>>> };
>>>>
>>>> /*
>>>> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
>>>> index 15df8db314d9..8883c8850759 100644
>>>> --- a/drivers/cxl/core/memdev.c
>>>> +++ b/drivers/cxl/core/memdev.c
>>>> @@ -594,7 +594,8 @@ bool is_cxl_memdev(const struct device *dev)
>>>> }
>>>> EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "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;
>>>>
>>>> @@ -606,6 +607,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++;
>>>> }
>>>>
>>>> @@ -623,9 +625,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;
>>>> }
>>>>
>>>> @@ -635,9 +637,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;
>>>> }
>>>> @@ -685,7 +687,8 @@ int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
>>>> info->size = total;
>>>> dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\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);
>>>>
>>>> return 0;
>>>> }
>>>> @@ -704,7 +707,7 @@ int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
>>>> .size = capacity,
>>>> };
>>>>
>>>> - add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
>>>> + add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM, 0);
>>>> return cxl_dpa_setup(cxlds, &range_info);
>>>> }
>>>> EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
>>>> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
>>>> index 4fac65dbb808..9fd5af2e41d4 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;
>>>> };
>>>> @@ -851,6 +852,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 4ef9511fbbdd..43edcde5bce3 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 that represents this partition
>>>> + * @shareable: Is the partition shareable (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 [flat|nested] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-22 23:05 ` Dave Jiang
@ 2026-09-23 0:05 ` Anisa Su
2026-09-23 15:35 ` Dave Jiang
0 siblings, 1 reply; 41+ messages in thread
From: Anisa Su @ 2026-09-23 0:05 UTC (permalink / raw)
To: Dave Jiang
Cc: Anisa Su, Richard Cheng, linux-cxl, Alison Schofield,
Jonathan Cameron, Davidlohr Bueso, Li Ming, Gregory Price,
Ben Cheatham, Ira Weiny, Wonjae Lee, Junhee Park, Heesoo Kim
On Tue, Sep 22, 2026 at 04:05:03PM -0700, Dave Jiang wrote:
>
>
> On 9/22/26 2:15 PM, Anisa Su wrote:
> > On Tue, Sep 22, 2026 at 10:08:08AM -0700, Dave Jiang wrote:
> >>
> >>
> >> On 9/21/26 8:53 PM, Richard Cheng wrote:
> >>> On Fri, Sep 18, 2026 at 01:30:12PM +0800, Anisa Su wrote:
> >>>> From: Ira Weiny <iweiny@kernel.org>
> >>>>
> >>>> Additional DCD partition (AKA region) information is contained in the
> >>>> DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
> >>>>
> >>>
> >>> Hi Anisa,
> >>>
> >>> LGTM but I have limited knowledge for details of device's CDAT.
> >>> Other experts would be more suitable to leave their tag.
> >>>
> >>> I have a question here only, I wonder if a CDAT with no DSLBIS supported is
> >>> possible ? seems that the implementation has the assumption that device's
> >>> CDAT would be always supported with valid DSMAS and DSLBIS.
> >>
> >> According to the spec, the device suppoort DCD shall provide DSMAS (CXL r4.0 9.13.3). DSLBIS is optional.
> >>
> >> So I do wonder if we need to add plumbing to disable DCD if DSMAS is missing or malformed.
> >>
> >> DJ
> >>
> > Can do. DSMAS is missing/malformed in 3 scenarios:
> > - CDAT is missing
> > - Failed to parse DSMAS from the CDAT
> > - No DSMAS entry matched the DC partition.
> >
> > So we can make a helper function to disable DCD and call from all 3 sites:
> >
> > +/*
> > + * A DCD shall describe its DC partition in a DSMAS entry (CXL r4.0 9.13.3).
> > + * Without one the partition's attributes are unknown, so disable DCD.
> > + */
> > +static void cxl_dcd_dsmas_missing(struct cxl_dev_state *cxlds, const char *why)
> > +{
> > + struct cxl_memdev_state *mds;
> > +
> > + if (cxlds->type != CXL_DEVTYPE_CLASSMEM)
> > + return;
> > +
> > + mds = to_cxl_memdev_state(cxlds);
> > + if (!cxl_dcd_supported(mds) ||
> > + !cxl_part_size(cxlds, CXL_PARTMODE_DYNAMIC_RAM_1))
> > + return;
> > +
> > + dev_warn(cxlds->dev, "DC partition has no valid DSMAS entry (%s), disabling DCD\n",
> > + why);
> > + cxl_disable_dcd(mds);
> > +}
> >
> > Called if there's no CDAT/fails to parse CDAT:
> >
> > void cxl_endpoint_parse_cdat(struct cxl_port *port)
> > int rc;
> >
> > xa_init(&__dsmas_xa);
> > - if (!port->cdat.table)
> > + if (!port->cdat.table) {
> > + cxl_dcd_dsmas_missing(cxlds, "no CDAT");
> > return;
> > + }
> >
> > rc = cxl_cdat_endpoint_process(port, dsmas_xa);
> > if (rc < 0) {
> > dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
> > + cxl_dcd_dsmas_missing(cxlds, "CDAT parse failed");
> > return;
> > }
> >
> > Or no DSMAS entry matched the DC partition:
> > static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
> > struct device *dev = cxlds->dev;
> > struct dsmas_entry *dent;
> > + bool dc_matched = false;
> > unsigned long index;
> > ...
> > if (set_perf)
> > update_perf_entry(dev, dent, &part->perf);
> > - if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
> > + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1) {
> > part->shareable = dent->shareable;
> > + dc_matched = true;
> > + }
> > break;
> > }
> >
> > ...
> > dev_dbg(dev, "no partition for dsmas dpa: %pra\n",
> > &dent->dpa_range);
> > }
> > +
> > + if (!dc_matched)
> > + cxl_dcd_dsmas_missing(cxlds, "no matching entry");
> > }
> >
> > But currently cxl_cdat_endpoint_process() fails if there's no DSLBIS or it's
> > empty and DSLBIS is optional for DCD, so need a prep patch to allow empty DSLBIS
> > and skip cxl_port_perf_data_calculate(port, dsmas_xa) if it's empty.
>
> But perf calc failure does not stop the probe. cxl_endpoint_parse_cdat() returns void. We swallow the error. So I don't think changes are needed around here? You get -ENOENT if no entries from cdat_table_parse_output().
>
Hmmm my explanation was bad. I'm not worried about the probe, but rather
if -ENOENT is returned by cdat_table_parse_output() due to empty DSLBIS,
then cxl_cdat_endpoint_process() propagates it to cxl_endpoint_parse_cdat(). It
returns early, even though the DSMAS was valid for the DC partition and
the DSLBIS is optional:
rc = cxl_cdat_endpoint_process(port, dsmas_xa);
if (rc < 0) {
dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
return; <---- skips cxl_memdev_set_qos_class, which is where we
read the shareable attr from dsmas
}
rc = cxl_port_perf_data_calculate(port, dsmas_xa);
if (rc) {
dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
return;
}
cxl_memdev_set_qos_class(cxlds, dsmas_xa);
cxl_qos_class_verify(cxlmd);
cxl_memdev_update_perf(cxlmd);
I hope that makes sense?
- Anisa
> DJ
>
> >
> > Something like this:
> > --- a/drivers/cxl/core/cdat.c
> > +++ b/drivers/cxl/core/cdat.c
> > @@ -181,7 +181,7 @@
> > }
> >
> > static int cxl_cdat_endpoint_process(struct cxl_port *port,
> > - struct xarray *dsmas_xa)
> > + struct xarray *dsmas_xa, bool *has_dslbis)
> > {
> > int rc;
> >
> > @@ -191,9 +191,13 @@
> > if (rc)
> > return rc;
> >
> > + /* DSLBIS is optional; without it there is no perf data to apply */
> > rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler,
> > dsmas_xa, port->cdat.table, port->cdat.length);
> > - return cdat_table_parse_output(rc);
> > + if (rc < 0)
> > + return rc;
> > + *has_dslbis = rc > 0;
> > + return 0;
> > }
> >
> > static int cxl_port_perf_data_calculate(struct cxl_port *port,
> > @@ -430,19 +434,22 @@
> > struct cxl_dev_state *cxlds = cxlmd->cxlds;
> > struct xarray __dsmas_xa;
> > struct xarray *dsmas_xa __free(dsmas) = &__dsmas_xa;
> > + bool has_dslbis = false;
> > int rc;
> >
> > xa_init(&__dsmas_xa);
> > if (!port->cdat.table)
> > return;
> >
> > - rc = cxl_cdat_endpoint_process(port, dsmas_xa);
> > + rc = cxl_cdat_endpoint_process(port, dsmas_xa, &has_dslbis);
> > if (rc < 0) {
> > dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
> > return;
> > }
> >
> > - rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> > + rc = -ENOENT;
> > + if (has_dslbis)
> > + rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> > if (rc)
> > dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> >
> >
> > Would something like this work? (I am also not an expert in CDAT/DSMAS/DSLBIS)
> >
> > Thanks,
> > Anisa
> >>>
> >>> Best regards,
> >>> Richard Cheng.
> >>>
> >>>
> >>>> ---
> >>>> Changes:
> >>>> 1. cdat.c: record the DSMAS shareable flag independently of QTG
> >>>> discovery. cxl_port_perf_data_calculate() returns -ENOENT when FW
> >>>> recommends no QTG for any entry, which skipped the assignment even
> >>>> though the CDAT parsed successfully and the DSMAS flag was valid.
> >>>> Richard
> >>>> ---
> >>>> drivers/cxl/core/cdat.c | 49 +++++++++++++++++++++++++++++----------
> >>>> drivers/cxl/core/hdm.c | 1 +
> >>>> drivers/cxl/core/mbox.c | 9 +++++++
> >>>> drivers/cxl/core/memdev.c | 17 ++++++++------
> >>>> drivers/cxl/cxlmem.h | 2 ++
> >>>> include/cxl/cxl.h | 4 ++++
> >>>> 6 files changed, 63 insertions(+), 19 deletions(-)
> >>>>
> >>>> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> >>>> index 5c9f07262513..6e7950f12134 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,8 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
> >>>> return -ENOMEM;
> >>>>
> >>>> dent->handle = dsmas->dsmad_handle;
> >>>> + /* Shareable is CDAT 1.03 and later, DSMAS Flags bit 3 */
> >>>> + 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;
> >>>> @@ -255,8 +258,13 @@ static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
> >>>> dent->coord[ACCESS_COORDINATE_CPU].write_latency);
> >>>> }
> >>>>
> >>>> -static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> >>>> - struct xarray *dsmas_xa)
> >>>> +/*
> >>>> + * The DSMAS shareable flag is a property of the CDAT entry and is recorded
> >>>> + * even if FW returns no QTG recommendations for any entry; only update perf
> >>>> + * if those recommendations exist, indicated by @set_perf
> >>>> + */
> >>>> +static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
> >>>> + struct xarray *dsmas_xa, bool set_perf)
> >>>> {
> >>>> struct device *dev = cxlds->dev;
> >>>> struct dsmas_entry *dent;
> >>>> @@ -266,18 +274,34 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> >>>> bool found = false;
> >>>>
> >>>> for (int i = 0; i < cxlds->nr_partitions; i++) {
> >>>> - struct resource *res = &cxlds->part[i].res;
> >>>> + struct cxl_dpa_partition *part = &cxlds->part[i];
> >>>> struct range range = {
> >>>> - .start = res->start,
> >>>> - .end = res->end,
> >>>> + .start = part->res.start,
> >>>> + .end = part->res.end,
> >>>> };
> >>>>
> >>>> - if (range_contains(&range, &dent->dpa_range)) {
> >>>> - update_perf_entry(dev, dent,
> >>>> - &cxlds->part[i].perf);
> >>>> - found = true;
> >>>> + if (!range_contains(&range, &dent->dpa_range))
> >>>> + continue;
> >>>> +
> >>>> + found = true;
> >>>> + /*
> >>>> + * 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;
> >>>> }
> >>>> +
> >>>> + if (set_perf)
> >>>> + update_perf_entry(dev, dent, &part->perf);
> >>>> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
> >>>> + part->shareable = dent->shareable;
> >>>> + break;
> >>>> }
> >>>>
> >>>> if (!found)
> >>>> @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
> >>>> }
> >>>>
> >>>> rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> >>>> - if (rc) {
> >>>> + if (rc)
> >>>> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> >>>> +
> >>>> + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
> >>>> + if (rc)
> >>>> return;
> >>>> - }
> >>>>
> >>>> - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
> >>>> cxl_qos_class_verify(cxlmd);
> >>>> cxl_memdev_update_perf(cxlmd);
> >>>> }
> >>>> 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 c96c16cc08e1..d84a90320081 100644
> >>>> --- a/drivers/cxl/core/mbox.c
> >>>> +++ b/drivers/cxl/core/mbox.c
> >>>> @@ -1366,6 +1366,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> >>>> u64 decode_len = le64_to_cpu(dev_part->decode_length);
> >>>> 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);
> >>>> u64 size;
> >>>>
> >>>> /*
> >>>> @@ -1378,6 +1379,13 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> >>>> return 0;
> >>>> }
> >>>>
> >>>> + /* 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;
> >>>> + }
> >>>> +
> >>>> /*
> >>>> * Decode length is a count of 256M units, so a device reporting 2^36
> >>>> * or more wraps.
> >>>> @@ -1391,6 +1399,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> >>>> *part = (struct cxl_dc_partition_info) {
> >>>> .start = le64_to_cpu(dev_part->base),
> >>>> .size = size,
> >>>> + .handle = handle,
> >>>> };
> >>>>
> >>>> /*
> >>>> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> >>>> index 15df8db314d9..8883c8850759 100644
> >>>> --- a/drivers/cxl/core/memdev.c
> >>>> +++ b/drivers/cxl/core/memdev.c
> >>>> @@ -594,7 +594,8 @@ bool is_cxl_memdev(const struct device *dev)
> >>>> }
> >>>> EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "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;
> >>>>
> >>>> @@ -606,6 +607,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++;
> >>>> }
> >>>>
> >>>> @@ -623,9 +625,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;
> >>>> }
> >>>>
> >>>> @@ -635,9 +637,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;
> >>>> }
> >>>> @@ -685,7 +687,8 @@ int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> >>>> info->size = total;
> >>>> dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\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);
> >>>>
> >>>> return 0;
> >>>> }
> >>>> @@ -704,7 +707,7 @@ int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
> >>>> .size = capacity,
> >>>> };
> >>>>
> >>>> - add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
> >>>> + add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM, 0);
> >>>> return cxl_dpa_setup(cxlds, &range_info);
> >>>> }
> >>>> EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
> >>>> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> >>>> index 4fac65dbb808..9fd5af2e41d4 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;
> >>>> };
> >>>> @@ -851,6 +852,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 4ef9511fbbdd..43edcde5bce3 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 that represents this partition
> >>>> + * @shareable: Is the partition shareable (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 [flat|nested] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-23 0:05 ` Anisa Su
@ 2026-09-23 15:35 ` Dave Jiang
2026-09-24 4:58 ` Anisa Su
0 siblings, 1 reply; 41+ messages in thread
From: Dave Jiang @ 2026-09-23 15:35 UTC (permalink / raw)
To: Anisa Su
Cc: Richard Cheng, linux-cxl, Alison Schofield, Jonathan Cameron,
Davidlohr Bueso, Li Ming, Gregory Price, Ben Cheatham, Ira Weiny,
Wonjae Lee, Junhee Park, Heesoo Kim
On 9/22/26 5:05 PM, Anisa Su wrote:
> On Tue, Sep 22, 2026 at 04:05:03PM -0700, Dave Jiang wrote:
>>
>>
>> On 9/22/26 2:15 PM, Anisa Su wrote:
>>> On Tue, Sep 22, 2026 at 10:08:08AM -0700, Dave Jiang wrote:
>>>>
>>>>
>>>> On 9/21/26 8:53 PM, Richard Cheng wrote:
>>>>> On Fri, Sep 18, 2026 at 01:30:12PM +0800, Anisa Su wrote:
>>>>>> From: Ira Weiny <iweiny@kernel.org>
>>>>>>
>>>>>> Additional DCD partition (AKA region) information is contained in the
>>>>>> DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
>>>>>>
>>>>>
>>>>> Hi Anisa,
>>>>>
>>>>> LGTM but I have limited knowledge for details of device's CDAT.
>>>>> Other experts would be more suitable to leave their tag.
>>>>>
>>>>> I have a question here only, I wonder if a CDAT with no DSLBIS supported is
>>>>> possible ? seems that the implementation has the assumption that device's
>>>>> CDAT would be always supported with valid DSMAS and DSLBIS.
>>>>
>>>> According to the spec, the device suppoort DCD shall provide DSMAS (CXL r4.0 9.13.3). DSLBIS is optional.
>>>>
>>>> So I do wonder if we need to add plumbing to disable DCD if DSMAS is missing or malformed.
>>>>
>>>> DJ
>>>>
>>> Can do. DSMAS is missing/malformed in 3 scenarios:
>>> - CDAT is missing
>>> - Failed to parse DSMAS from the CDAT
>>> - No DSMAS entry matched the DC partition.
>>>
>>> So we can make a helper function to disable DCD and call from all 3 sites:
>>>
>>> +/*
>>> + * A DCD shall describe its DC partition in a DSMAS entry (CXL r4.0 9.13.3).
>>> + * Without one the partition's attributes are unknown, so disable DCD.
>>> + */
>>> +static void cxl_dcd_dsmas_missing(struct cxl_dev_state *cxlds, const char *why)
>>> +{
>>> + struct cxl_memdev_state *mds;
>>> +
>>> + if (cxlds->type != CXL_DEVTYPE_CLASSMEM)
>>> + return;
>>> +
>>> + mds = to_cxl_memdev_state(cxlds);
>>> + if (!cxl_dcd_supported(mds) ||
>>> + !cxl_part_size(cxlds, CXL_PARTMODE_DYNAMIC_RAM_1))
>>> + return;
>>> +
>>> + dev_warn(cxlds->dev, "DC partition has no valid DSMAS entry (%s), disabling DCD\n",
>>> + why);
>>> + cxl_disable_dcd(mds);
>>> +}
>>>
>>> Called if there's no CDAT/fails to parse CDAT:
>>>
>>> void cxl_endpoint_parse_cdat(struct cxl_port *port)
>>> int rc;
>>>
>>> xa_init(&__dsmas_xa);
>>> - if (!port->cdat.table)
>>> + if (!port->cdat.table) {
>>> + cxl_dcd_dsmas_missing(cxlds, "no CDAT");
>>> return;
>>> + }
>>>
>>> rc = cxl_cdat_endpoint_process(port, dsmas_xa);
>>> if (rc < 0) {
>>> dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
>>> + cxl_dcd_dsmas_missing(cxlds, "CDAT parse failed");
>>> return;
>>> }
>>>
>>> Or no DSMAS entry matched the DC partition:
>>> static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
>>> struct device *dev = cxlds->dev;
>>> struct dsmas_entry *dent;
>>> + bool dc_matched = false;
>>> unsigned long index;
>>> ...
>>> if (set_perf)
>>> update_perf_entry(dev, dent, &part->perf);
>>> - if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
>>> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1) {
>>> part->shareable = dent->shareable;
>>> + dc_matched = true;
>>> + }
>>> break;
>>> }
>>>
>>> ...
>>> dev_dbg(dev, "no partition for dsmas dpa: %pra\n",
>>> &dent->dpa_range);
>>> }
>>> +
>>> + if (!dc_matched)
>>> + cxl_dcd_dsmas_missing(cxlds, "no matching entry");
>>> }
>>>
>>> But currently cxl_cdat_endpoint_process() fails if there's no DSLBIS or it's
>>> empty and DSLBIS is optional for DCD, so need a prep patch to allow empty DSLBIS
>>> and skip cxl_port_perf_data_calculate(port, dsmas_xa) if it's empty.
>>
>> But perf calc failure does not stop the probe. cxl_endpoint_parse_cdat() returns void. We swallow the error. So I don't think changes are needed around here? You get -ENOENT if no entries from cdat_table_parse_output().
>>
> Hmmm my explanation was bad. I'm not worried about the probe, but rather
> if -ENOENT is returned by cdat_table_parse_output() due to empty DSLBIS,
> then cxl_cdat_endpoint_process() propagates it to cxl_endpoint_parse_cdat(). It
> returns early, even though the DSMAS was valid for the DC partition and
> the DSLBIS is optional:
>
> rc = cxl_cdat_endpoint_process(port, dsmas_xa);
> if (rc < 0) {
> dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
> return; <---- skips cxl_memdev_set_qos_class, which is where we
> read the shareable attr from dsmas
> }
>
> rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> if (rc) {
> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> return;
> }
> cxl_memdev_set_qos_class(cxlds, dsmas_xa);
> cxl_qos_class_verify(cxlmd);
> cxl_memdev_update_perf(cxlmd);
>
> I hope that makes sense?
Yeah that makes sense now. What do you think if for the optional handlers if we don't find entries we return MAX_INT? And we just add a comment somewhere that says it means no entires but it's optional? That should reduce the amount of hack around needed right? We can just fixup cdat_table_parse_output().
DJ
>
> - Anisa
>
>> DJ
>>
>>>
>>> Something like this:
>>> --- a/drivers/cxl/core/cdat.c
>>> +++ b/drivers/cxl/core/cdat.c
>>> @@ -181,7 +181,7 @@
>>> }
>>>
>>> static int cxl_cdat_endpoint_process(struct cxl_port *port,
>>> - struct xarray *dsmas_xa)
>>> + struct xarray *dsmas_xa, bool *has_dslbis)
>>> {
>>> int rc;
>>>
>>> @@ -191,9 +191,13 @@
>>> if (rc)
>>> return rc;
>>>
>>> + /* DSLBIS is optional; without it there is no perf data to apply */
>>> rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler,
>>> dsmas_xa, port->cdat.table, port->cdat.length);
>>> - return cdat_table_parse_output(rc);
>>> + if (rc < 0)
>>> + return rc;
>>> + *has_dslbis = rc > 0;
>>> + return 0;
>>> }
>>>
>>> static int cxl_port_perf_data_calculate(struct cxl_port *port,
>>> @@ -430,19 +434,22 @@
>>> struct cxl_dev_state *cxlds = cxlmd->cxlds;
>>> struct xarray __dsmas_xa;
>>> struct xarray *dsmas_xa __free(dsmas) = &__dsmas_xa;
>>> + bool has_dslbis = false;
>>> int rc;
>>>
>>> xa_init(&__dsmas_xa);
>>> if (!port->cdat.table)
>>> return;
>>>
>>> - rc = cxl_cdat_endpoint_process(port, dsmas_xa);
>>> + rc = cxl_cdat_endpoint_process(port, dsmas_xa, &has_dslbis);
>>> if (rc < 0) {
>>> dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
>>> return;
>>> }
>>>
>>> - rc = cxl_port_perf_data_calculate(port, dsmas_xa);
>>> + rc = -ENOENT;
>>> + if (has_dslbis)
>>> + rc = cxl_port_perf_data_calculate(port, dsmas_xa);
>>> if (rc)
>>> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
>>>
>>>
>>> Would something like this work? (I am also not an expert in CDAT/DSMAS/DSLBIS)
>>>
>>> Thanks,
>>> Anisa
>>>>>
>>>>> Best regards,
>>>>> Richard Cheng.
>>>>>
>>>>>
>>>>>> ---
>>>>>> Changes:
>>>>>> 1. cdat.c: record the DSMAS shareable flag independently of QTG
>>>>>> discovery. cxl_port_perf_data_calculate() returns -ENOENT when FW
>>>>>> recommends no QTG for any entry, which skipped the assignment even
>>>>>> though the CDAT parsed successfully and the DSMAS flag was valid.
>>>>>> Richard
>>>>>> ---
>>>>>> drivers/cxl/core/cdat.c | 49 +++++++++++++++++++++++++++++----------
>>>>>> drivers/cxl/core/hdm.c | 1 +
>>>>>> drivers/cxl/core/mbox.c | 9 +++++++
>>>>>> drivers/cxl/core/memdev.c | 17 ++++++++------
>>>>>> drivers/cxl/cxlmem.h | 2 ++
>>>>>> include/cxl/cxl.h | 4 ++++
>>>>>> 6 files changed, 63 insertions(+), 19 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
>>>>>> index 5c9f07262513..6e7950f12134 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,8 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
>>>>>> return -ENOMEM;
>>>>>>
>>>>>> dent->handle = dsmas->dsmad_handle;
>>>>>> + /* Shareable is CDAT 1.03 and later, DSMAS Flags bit 3 */
>>>>>> + 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;
>>>>>> @@ -255,8 +258,13 @@ static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
>>>>>> dent->coord[ACCESS_COORDINATE_CPU].write_latency);
>>>>>> }
>>>>>>
>>>>>> -static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
>>>>>> - struct xarray *dsmas_xa)
>>>>>> +/*
>>>>>> + * The DSMAS shareable flag is a property of the CDAT entry and is recorded
>>>>>> + * even if FW returns no QTG recommendations for any entry; only update perf
>>>>>> + * if those recommendations exist, indicated by @set_perf
>>>>>> + */
>>>>>> +static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
>>>>>> + struct xarray *dsmas_xa, bool set_perf)
>>>>>> {
>>>>>> struct device *dev = cxlds->dev;
>>>>>> struct dsmas_entry *dent;
>>>>>> @@ -266,18 +274,34 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
>>>>>> bool found = false;
>>>>>>
>>>>>> for (int i = 0; i < cxlds->nr_partitions; i++) {
>>>>>> - struct resource *res = &cxlds->part[i].res;
>>>>>> + struct cxl_dpa_partition *part = &cxlds->part[i];
>>>>>> struct range range = {
>>>>>> - .start = res->start,
>>>>>> - .end = res->end,
>>>>>> + .start = part->res.start,
>>>>>> + .end = part->res.end,
>>>>>> };
>>>>>>
>>>>>> - if (range_contains(&range, &dent->dpa_range)) {
>>>>>> - update_perf_entry(dev, dent,
>>>>>> - &cxlds->part[i].perf);
>>>>>> - found = true;
>>>>>> + if (!range_contains(&range, &dent->dpa_range))
>>>>>> + continue;
>>>>>> +
>>>>>> + found = true;
>>>>>> + /*
>>>>>> + * 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;
>>>>>> }
>>>>>> +
>>>>>> + if (set_perf)
>>>>>> + update_perf_entry(dev, dent, &part->perf);
>>>>>> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
>>>>>> + part->shareable = dent->shareable;
>>>>>> + break;
>>>>>> }
>>>>>>
>>>>>> if (!found)
>>>>>> @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
>>>>>> }
>>>>>>
>>>>>> rc = cxl_port_perf_data_calculate(port, dsmas_xa);
>>>>>> - if (rc) {
>>>>>> + if (rc)
>>>>>> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
>>>>>> +
>>>>>> + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
>>>>>> + if (rc)
>>>>>> return;
>>>>>> - }
>>>>>>
>>>>>> - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
>>>>>> cxl_qos_class_verify(cxlmd);
>>>>>> cxl_memdev_update_perf(cxlmd);
>>>>>> }
>>>>>> 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 c96c16cc08e1..d84a90320081 100644
>>>>>> --- a/drivers/cxl/core/mbox.c
>>>>>> +++ b/drivers/cxl/core/mbox.c
>>>>>> @@ -1366,6 +1366,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
>>>>>> u64 decode_len = le64_to_cpu(dev_part->decode_length);
>>>>>> 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);
>>>>>> u64 size;
>>>>>>
>>>>>> /*
>>>>>> @@ -1378,6 +1379,13 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
>>>>>> return 0;
>>>>>> }
>>>>>>
>>>>>> + /* 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;
>>>>>> + }
>>>>>> +
>>>>>> /*
>>>>>> * Decode length is a count of 256M units, so a device reporting 2^36
>>>>>> * or more wraps.
>>>>>> @@ -1391,6 +1399,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
>>>>>> *part = (struct cxl_dc_partition_info) {
>>>>>> .start = le64_to_cpu(dev_part->base),
>>>>>> .size = size,
>>>>>> + .handle = handle,
>>>>>> };
>>>>>>
>>>>>> /*
>>>>>> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
>>>>>> index 15df8db314d9..8883c8850759 100644
>>>>>> --- a/drivers/cxl/core/memdev.c
>>>>>> +++ b/drivers/cxl/core/memdev.c
>>>>>> @@ -594,7 +594,8 @@ bool is_cxl_memdev(const struct device *dev)
>>>>>> }
>>>>>> EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "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;
>>>>>>
>>>>>> @@ -606,6 +607,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++;
>>>>>> }
>>>>>>
>>>>>> @@ -623,9 +625,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;
>>>>>> }
>>>>>>
>>>>>> @@ -635,9 +637,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;
>>>>>> }
>>>>>> @@ -685,7 +687,8 @@ int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
>>>>>> info->size = total;
>>>>>> dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\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);
>>>>>>
>>>>>> return 0;
>>>>>> }
>>>>>> @@ -704,7 +707,7 @@ int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
>>>>>> .size = capacity,
>>>>>> };
>>>>>>
>>>>>> - add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
>>>>>> + add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM, 0);
>>>>>> return cxl_dpa_setup(cxlds, &range_info);
>>>>>> }
>>>>>> EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
>>>>>> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
>>>>>> index 4fac65dbb808..9fd5af2e41d4 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;
>>>>>> };
>>>>>> @@ -851,6 +852,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 4ef9511fbbdd..43edcde5bce3 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 that represents this partition
>>>>>> + * @shareable: Is the partition shareable (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 [flat|nested] 41+ messages in thread
* Re: [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device
2026-09-22 3:44 ` Richard Cheng
@ 2026-09-24 0:55 ` Jonathan Cameron
2026-09-24 7:01 ` Anisa Su
0 siblings, 1 reply; 41+ messages in thread
From: Jonathan Cameron @ 2026-09-24 0:55 UTC (permalink / raw)
To: Richard Cheng
Cc: Anisa Su, linux-cxl, Dave Jiang, Alison Schofield,
Davidlohr Bueso, Li Ming, Gregory Price, Ben Cheatham, Ira Weiny,
Anisa Su, Wonjae Lee, Junhee Park, Heesoo Kim
On Tue, 22 Sep 2026 11:44:16 +0800
Richard Cheng <icheng@nvidia.com> wrote:
> On Fri, Sep 18, 2026 at 01:30:11PM +0800, Anisa Su wrote:
> > From: Ira Weiny <iweiny@kernel.org>
> >
> > Devices which support Dynamic Capacity (DC) are configured
> > via mailbox commands. CXL r4.0 section 9.13.3 describes the use of
> > the Get DC Configuration command in order to obtain DCD partition
> > characteristics.
> >
> > 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.
> >
> > Initial enablement for Linux only supports one dynamic capacity
> > partition. Read all the partitions the device reports but validate
> > only the first, and configure it as 'dynamic ram 1'.
> >
> > The specification requires that volatile capacity starts at DPA 0 and pmem
> > starts at the DPA immediately following it, but DC partitions only need
> > to be 256MB aligned according to CXL r4.0 section 8.2.10.9.9.1 Table 8-347.
> > So a device could leave a gap between ram/pmem (static) capacity and its first
> > DC partition, or between one DC partition and the next.
> >
> > However, Linux chooses not to support gaps between static and dynamic
> > capacity, and requires the first DC partition to begin at the DPA
> > immediately following static capacity.
> >
> > Based on an original patch by Navneet Singh.
> >
> > 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: Alison Schofield <alison.schofield@intel.com>
> >
>
> Reviewed-by: Richard Cheng <icheng@nvidia.com>
>
> I wonder if we can have comments in code explaining why linux doesn't support
> gaps between static and DC, otherwise this can be a bit confusing why
> the 1st DC partition needs to be immediately after the static part, at least
> for me.
Because we are lazy? :)
Or maybe because the more we constrain the hardware that is supported
the less variations we have deal with and test!
Jonathan
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts
2026-09-22 21:22 ` Anisa Su
@ 2026-09-24 0:58 ` Jonathan Cameron
0 siblings, 0 replies; 41+ messages in thread
From: Jonathan Cameron @ 2026-09-24 0:58 UTC (permalink / raw)
To: Anisa Su
Cc: Dave Jiang, linux-cxl, Alison Schofield, Davidlohr Bueso, Li Ming,
Gregory Price, Richard Cheng, Ben Cheatham, Ira Weiny, Wonjae Lee,
Junhee Park, Heesoo Kim
On Tue, 22 Sep 2026 14:22:47 -0700
Anisa Su <anisa.su887@gmail.com> wrote:
> On Mon, Sep 21, 2026 at 05:45:56PM -0700, Dave Jiang wrote:
> >
> >
> > On 9/21/26 4:42 PM, Jonathan Cameron wrote:
> > >
> > >>> diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
> > >>> index 7b756000a1a6..6ef47265da10 100644
> > >>> --- a/tools/testing/cxl/test/mem.c
> > >>> +++ b/tools/testing/cxl/test/mem.c
> > >>> @@ -1818,7 +1818,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
> > >>> if (rc)
> > >>> dev_dbg(dev, "No CXL FWCTL setup\n");
> > >>>
> > >>> - cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
> > >>> + cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_INFO |
> > >>> + CXLDEV_EVENT_STATUS_WARN |
> > >>> + CXLDEV_EVENT_STATUS_FAIL |
> > >>> + CXLDEV_EVENT_STATUS_FATAL);
> > >>
> > >> Second time I'm seeing this constructed mask being used in this commit. Maybe create a define for it?
> > >>
> > >> s/CXLDEV_EVENT_STATUS_ALL/CXLDEV_EVENT_STATUS_BASE/ perhaps?
> > >
> > > That was Anisa acting on my feedbakc on previous.
> > > I don't like _ALL because we already have an example of the spec
> > > expanding and the definition becoming messy. But mainly I was
> > > pushing back against _ALL + masking with one element we didn't
> > > want as fragile. So I'm not against _ALL if it is made up of
> > > another define + DCD (for now) and that other define can be
> > > used in places like this.
> > >
> > > Bikeshed time... BASE is usually a define for a reg address and it is
> > > is hard to come up with a name for that when it is a mixture of
> > > RAS stuff and INFO which can be a wide range of random nasty
> > > and nice things.
> > >
> > > So with the right name seems fine to have what Dave suggests.
> > > I'm just not sure what that name is! Hence I'd just stick
> > > to the long hand option.
> >
> > Yeah if we can't come up with anything good then leave it as is.
> >
> CXLDEV_EVENT_STATUS_STANDARD_LOGS...? And just add a comment kind of explaining
> what "standard" means:
>
> /* Logs whose ownership follows native_cxl_error; the DCD log is always OS owned */
> #define CXLDEV_EVENT_STATUS_STANDARD_LOGS (CXLDEV_EVENT_STATUS_INFO | \
> CXLDEV_EVENT_STATUS_WARN | \
> CXLDEV_EVENT_STATUS_FAIL | \
> CXLDEV_EVENT_STATUS_FATAL)
>
> That's all I can come up with.
I can cope with that
Jonathan
>
> - Anisa
> > >
> > > Jonathan
> > >
> > >>
> > >>> cxl_mock_test_feat_init(mdata);
> > >>>
> > >>> return 0;
> > >>
> > >>
> > >
> >
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-23 15:35 ` Dave Jiang
@ 2026-09-24 4:58 ` Anisa Su
0 siblings, 0 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-24 4:58 UTC (permalink / raw)
To: Dave Jiang
Cc: Anisa Su, Richard Cheng, linux-cxl, Alison Schofield,
Jonathan Cameron, Davidlohr Bueso, Li Ming, Gregory Price,
Ben Cheatham, Ira Weiny, Wonjae Lee, Junhee Park, Heesoo Kim
On Wed, Sep 23, 2026 at 08:35:16AM -0700, Dave Jiang wrote:
>
>
> On 9/22/26 5:05 PM, Anisa Su wrote:
> > On Tue, Sep 22, 2026 at 04:05:03PM -0700, Dave Jiang wrote:
> >>
> >>
> >> On 9/22/26 2:15 PM, Anisa Su wrote:
> >>> On Tue, Sep 22, 2026 at 10:08:08AM -0700, Dave Jiang wrote:
> >>>>
> >>>>
> >>>> On 9/21/26 8:53 PM, Richard Cheng wrote:
> >>>>> On Fri, Sep 18, 2026 at 01:30:12PM +0800, Anisa Su wrote:
> >>>>>> From: Ira Weiny <iweiny@kernel.org>
> >>>>>>
> >>>>>> Additional DCD partition (AKA region) information is contained in the
> >>>>>> DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
> >>>>>>
> >>>>>
> >>>>> Hi Anisa,
> >>>>>
> >>>>> LGTM but I have limited knowledge for details of device's CDAT.
> >>>>> Other experts would be more suitable to leave their tag.
> >>>>>
> >>>>> I have a question here only, I wonder if a CDAT with no DSLBIS supported is
> >>>>> possible ? seems that the implementation has the assumption that device's
> >>>>> CDAT would be always supported with valid DSMAS and DSLBIS.
> >>>>
> >>>> According to the spec, the device suppoort DCD shall provide DSMAS (CXL r4.0 9.13.3). DSLBIS is optional.
> >>>>
> >>>> So I do wonder if we need to add plumbing to disable DCD if DSMAS is missing or malformed.
> >>>>
> >>>> DJ
> >>>>
> >>> Can do. DSMAS is missing/malformed in 3 scenarios:
> >>> - CDAT is missing
> >>> - Failed to parse DSMAS from the CDAT
> >>> - No DSMAS entry matched the DC partition.
> >>>
> >>> So we can make a helper function to disable DCD and call from all 3 sites:
> >>>
> >>> +/*
> >>> + * A DCD shall describe its DC partition in a DSMAS entry (CXL r4.0 9.13.3).
> >>> + * Without one the partition's attributes are unknown, so disable DCD.
> >>> + */
> >>> +static void cxl_dcd_dsmas_missing(struct cxl_dev_state *cxlds, const char *why)
> >>> +{
> >>> + struct cxl_memdev_state *mds;
> >>> +
> >>> + if (cxlds->type != CXL_DEVTYPE_CLASSMEM)
> >>> + return;
> >>> +
> >>> + mds = to_cxl_memdev_state(cxlds);
> >>> + if (!cxl_dcd_supported(mds) ||
> >>> + !cxl_part_size(cxlds, CXL_PARTMODE_DYNAMIC_RAM_1))
> >>> + return;
> >>> +
> >>> + dev_warn(cxlds->dev, "DC partition has no valid DSMAS entry (%s), disabling DCD\n",
> >>> + why);
> >>> + cxl_disable_dcd(mds);
> >>> +}
> >>>
> >>> Called if there's no CDAT/fails to parse CDAT:
> >>>
> >>> void cxl_endpoint_parse_cdat(struct cxl_port *port)
> >>> int rc;
> >>>
> >>> xa_init(&__dsmas_xa);
> >>> - if (!port->cdat.table)
> >>> + if (!port->cdat.table) {
> >>> + cxl_dcd_dsmas_missing(cxlds, "no CDAT");
> >>> return;
> >>> + }
> >>>
> >>> rc = cxl_cdat_endpoint_process(port, dsmas_xa);
> >>> if (rc < 0) {
> >>> dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
> >>> + cxl_dcd_dsmas_missing(cxlds, "CDAT parse failed");
> >>> return;
> >>> }
> >>>
> >>> Or no DSMAS entry matched the DC partition:
> >>> static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
> >>> struct device *dev = cxlds->dev;
> >>> struct dsmas_entry *dent;
> >>> + bool dc_matched = false;
> >>> unsigned long index;
> >>> ...
> >>> if (set_perf)
> >>> update_perf_entry(dev, dent, &part->perf);
> >>> - if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
> >>> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1) {
> >>> part->shareable = dent->shareable;
> >>> + dc_matched = true;
> >>> + }
> >>> break;
> >>> }
> >>>
> >>> ...
> >>> dev_dbg(dev, "no partition for dsmas dpa: %pra\n",
> >>> &dent->dpa_range);
> >>> }
> >>> +
> >>> + if (!dc_matched)
> >>> + cxl_dcd_dsmas_missing(cxlds, "no matching entry");
> >>> }
> >>>
> >>> But currently cxl_cdat_endpoint_process() fails if there's no DSLBIS or it's
> >>> empty and DSLBIS is optional for DCD, so need a prep patch to allow empty DSLBIS
> >>> and skip cxl_port_perf_data_calculate(port, dsmas_xa) if it's empty.
> >>
> >> But perf calc failure does not stop the probe. cxl_endpoint_parse_cdat() returns void. We swallow the error. So I don't think changes are needed around here? You get -ENOENT if no entries from cdat_table_parse_output().
> >>
> > Hmmm my explanation was bad. I'm not worried about the probe, but rather
> > if -ENOENT is returned by cdat_table_parse_output() due to empty DSLBIS,
> > then cxl_cdat_endpoint_process() propagates it to cxl_endpoint_parse_cdat(). It
> > returns early, even though the DSMAS was valid for the DC partition and
> > the DSLBIS is optional:
> >
> > rc = cxl_cdat_endpoint_process(port, dsmas_xa);
> > if (rc < 0) {
> > dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
> > return; <---- skips cxl_memdev_set_qos_class, which is where we
> > read the shareable attr from dsmas
> > }
> >
> > rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> > if (rc) {
> > dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> > return;
> > }
> > cxl_memdev_set_qos_class(cxlds, dsmas_xa);
> > cxl_qos_class_verify(cxlmd);
> > cxl_memdev_update_perf(cxlmd);
> >
> > I hope that makes sense?
>
> Yeah that makes sense now. What do you think if for the optional handlers if we don't find entries we return MAX_INT? And we just add a comment somewhere that says it means no entires but it's optional? That should reduce the amount of hack around needed right? We can just fixup cdat_table_parse_output().
>
> DJ
> >
Hmmm... I tried it but the MAX_INT approach doesn't seem to reduce the
churn by much bc instead of checking if (has_dslbis) like in the prep patch I
proposed originally, we instead check if (rc == INT_MAX).
And cdat_table_parse_output() then needs an
extra input param bool optional so it returns INT_MAX only if DSLBIS is missing.
For the least churn, I think skip cdat_table_parse_output() for DSLBIS and
return 0 if DSLBIS is missing/empty. Then to avoid calculating garbage
check if the DSLBIS attributes are 0 and skip in cxl_port_perf_data_calculate:
@@ cxl_cdat_endpoint_process
+ /* DSLBIS is optional; zero entries is not an error */
rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler,
dsmas_xa, port->cdat.table, port->cdat.length);
- return cdat_table_parse_output(rc);
+ return rc < 0 ? rc : 0;
So then we go on in cxl_endpoint_parse_cdat():
void cxl_endpoint_parse_cdat(struct cxl_port *port)
{ ...
rc = cxl_cdat_endpoint_process(port, dsmas_xa);
if (rc < 0) {
dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
return;
}
rc = cxl_port_perf_data_calculate(port, dsmas_xa); <--- skip for empty DSLBIS
if (rc)
dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
if (rc)
return;
cxl_qos_class_verify(cxlmd);
cxl_memdev_update_perf(cxlmd);
}
So to skip perf_data_calculate for empty DSLBIS:
@@ cxl_port_perf_data_calculate
xa_for_each(dsmas_xa, index, dent) {
int qos_class;
+ /* No DSLBIS for this DSMAS; nothing to hand the QTG _DSM */
+ if (!dent->cdat_coord[ACCESS_COORDINATE_CPU].read_bandwidth &&
+ !dent->cdat_coord[ACCESS_COORDINATE_CPU].write_bandwidth)
+ continue;
+
cxl_coordinates_combine(dent->coord, dent->cdat_coord, ep_c);
So anyway, v15 incoming...
Anisa
> > - Anisa
> >
> >> DJ
> >>
> >>>
> >>> Something like this:
> >>> --- a/drivers/cxl/core/cdat.c
> >>> +++ b/drivers/cxl/core/cdat.c
> >>> @@ -181,7 +181,7 @@
> >>> }
> >>>
> >>> static int cxl_cdat_endpoint_process(struct cxl_port *port,
> >>> - struct xarray *dsmas_xa)
> >>> + struct xarray *dsmas_xa, bool *has_dslbis)
> >>> {
> >>> int rc;
> >>>
> >>> @@ -191,9 +191,13 @@
> >>> if (rc)
> >>> return rc;
> >>>
> >>> + /* DSLBIS is optional; without it there is no perf data to apply */
> >>> rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler,
> >>> dsmas_xa, port->cdat.table, port->cdat.length);
> >>> - return cdat_table_parse_output(rc);
> >>> + if (rc < 0)
> >>> + return rc;
> >>> + *has_dslbis = rc > 0;
> >>> + return 0;
> >>> }
> >>>
> >>> static int cxl_port_perf_data_calculate(struct cxl_port *port,
> >>> @@ -430,19 +434,22 @@
> >>> struct cxl_dev_state *cxlds = cxlmd->cxlds;
> >>> struct xarray __dsmas_xa;
> >>> struct xarray *dsmas_xa __free(dsmas) = &__dsmas_xa;
> >>> + bool has_dslbis = false;
> >>> int rc;
> >>>
> >>> xa_init(&__dsmas_xa);
> >>> if (!port->cdat.table)
> >>> return;
> >>>
> >>> - rc = cxl_cdat_endpoint_process(port, dsmas_xa);
> >>> + rc = cxl_cdat_endpoint_process(port, dsmas_xa, &has_dslbis);
> >>> if (rc < 0) {
> >>> dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc);
> >>> return;
> >>> }
> >>>
> >>> - rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> >>> + rc = -ENOENT;
> >>> + if (has_dslbis)
> >>> + rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> >>> if (rc)
> >>> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> >>>
> >>>
> >>> Would something like this work? (I am also not an expert in CDAT/DSMAS/DSLBIS)
> >>>
> >>> Thanks,
> >>> Anisa
> >>>>>
> >>>>> Best regards,
> >>>>> Richard Cheng.
> >>>>>
> >>>>>
> >>>>>> ---
> >>>>>> Changes:
> >>>>>> 1. cdat.c: record the DSMAS shareable flag independently of QTG
> >>>>>> discovery. cxl_port_perf_data_calculate() returns -ENOENT when FW
> >>>>>> recommends no QTG for any entry, which skipped the assignment even
> >>>>>> though the CDAT parsed successfully and the DSMAS flag was valid.
> >>>>>> Richard
> >>>>>> ---
> >>>>>> drivers/cxl/core/cdat.c | 49 +++++++++++++++++++++++++++++----------
> >>>>>> drivers/cxl/core/hdm.c | 1 +
> >>>>>> drivers/cxl/core/mbox.c | 9 +++++++
> >>>>>> drivers/cxl/core/memdev.c | 17 ++++++++------
> >>>>>> drivers/cxl/cxlmem.h | 2 ++
> >>>>>> include/cxl/cxl.h | 4 ++++
> >>>>>> 6 files changed, 63 insertions(+), 19 deletions(-)
> >>>>>>
> >>>>>> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> >>>>>> index 5c9f07262513..6e7950f12134 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,8 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
> >>>>>> return -ENOMEM;
> >>>>>>
> >>>>>> dent->handle = dsmas->dsmad_handle;
> >>>>>> + /* Shareable is CDAT 1.03 and later, DSMAS Flags bit 3 */
> >>>>>> + 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;
> >>>>>> @@ -255,8 +258,13 @@ static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
> >>>>>> dent->coord[ACCESS_COORDINATE_CPU].write_latency);
> >>>>>> }
> >>>>>>
> >>>>>> -static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> >>>>>> - struct xarray *dsmas_xa)
> >>>>>> +/*
> >>>>>> + * The DSMAS shareable flag is a property of the CDAT entry and is recorded
> >>>>>> + * even if FW returns no QTG recommendations for any entry; only update perf
> >>>>>> + * if those recommendations exist, indicated by @set_perf
> >>>>>> + */
> >>>>>> +static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
> >>>>>> + struct xarray *dsmas_xa, bool set_perf)
> >>>>>> {
> >>>>>> struct device *dev = cxlds->dev;
> >>>>>> struct dsmas_entry *dent;
> >>>>>> @@ -266,18 +274,34 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> >>>>>> bool found = false;
> >>>>>>
> >>>>>> for (int i = 0; i < cxlds->nr_partitions; i++) {
> >>>>>> - struct resource *res = &cxlds->part[i].res;
> >>>>>> + struct cxl_dpa_partition *part = &cxlds->part[i];
> >>>>>> struct range range = {
> >>>>>> - .start = res->start,
> >>>>>> - .end = res->end,
> >>>>>> + .start = part->res.start,
> >>>>>> + .end = part->res.end,
> >>>>>> };
> >>>>>>
> >>>>>> - if (range_contains(&range, &dent->dpa_range)) {
> >>>>>> - update_perf_entry(dev, dent,
> >>>>>> - &cxlds->part[i].perf);
> >>>>>> - found = true;
> >>>>>> + if (!range_contains(&range, &dent->dpa_range))
> >>>>>> + continue;
> >>>>>> +
> >>>>>> + found = true;
> >>>>>> + /*
> >>>>>> + * 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;
> >>>>>> }
> >>>>>> +
> >>>>>> + if (set_perf)
> >>>>>> + update_perf_entry(dev, dent, &part->perf);
> >>>>>> + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
> >>>>>> + part->shareable = dent->shareable;
> >>>>>> + break;
> >>>>>> }
> >>>>>>
> >>>>>> if (!found)
> >>>>>> @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
> >>>>>> }
> >>>>>>
> >>>>>> rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> >>>>>> - if (rc) {
> >>>>>> + if (rc)
> >>>>>> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> >>>>>> +
> >>>>>> + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
> >>>>>> + if (rc)
> >>>>>> return;
> >>>>>> - }
> >>>>>>
> >>>>>> - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
> >>>>>> cxl_qos_class_verify(cxlmd);
> >>>>>> cxl_memdev_update_perf(cxlmd);
> >>>>>> }
> >>>>>> 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 c96c16cc08e1..d84a90320081 100644
> >>>>>> --- a/drivers/cxl/core/mbox.c
> >>>>>> +++ b/drivers/cxl/core/mbox.c
> >>>>>> @@ -1366,6 +1366,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> >>>>>> u64 decode_len = le64_to_cpu(dev_part->decode_length);
> >>>>>> 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);
> >>>>>> u64 size;
> >>>>>>
> >>>>>> /*
> >>>>>> @@ -1378,6 +1379,13 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> >>>>>> return 0;
> >>>>>> }
> >>>>>>
> >>>>>> + /* 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;
> >>>>>> + }
> >>>>>> +
> >>>>>> /*
> >>>>>> * Decode length is a count of 256M units, so a device reporting 2^36
> >>>>>> * or more wraps.
> >>>>>> @@ -1391,6 +1399,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> >>>>>> *part = (struct cxl_dc_partition_info) {
> >>>>>> .start = le64_to_cpu(dev_part->base),
> >>>>>> .size = size,
> >>>>>> + .handle = handle,
> >>>>>> };
> >>>>>>
> >>>>>> /*
> >>>>>> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> >>>>>> index 15df8db314d9..8883c8850759 100644
> >>>>>> --- a/drivers/cxl/core/memdev.c
> >>>>>> +++ b/drivers/cxl/core/memdev.c
> >>>>>> @@ -594,7 +594,8 @@ bool is_cxl_memdev(const struct device *dev)
> >>>>>> }
> >>>>>> EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "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;
> >>>>>>
> >>>>>> @@ -606,6 +607,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++;
> >>>>>> }
> >>>>>>
> >>>>>> @@ -623,9 +625,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;
> >>>>>> }
> >>>>>>
> >>>>>> @@ -635,9 +637,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;
> >>>>>> }
> >>>>>> @@ -685,7 +687,8 @@ int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> >>>>>> info->size = total;
> >>>>>> dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\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);
> >>>>>>
> >>>>>> return 0;
> >>>>>> }
> >>>>>> @@ -704,7 +707,7 @@ int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
> >>>>>> .size = capacity,
> >>>>>> };
> >>>>>>
> >>>>>> - add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
> >>>>>> + add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM, 0);
> >>>>>> return cxl_dpa_setup(cxlds, &range_info);
> >>>>>> }
> >>>>>> EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
> >>>>>> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> >>>>>> index 4fac65dbb808..9fd5af2e41d4 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;
> >>>>>> };
> >>>>>> @@ -851,6 +852,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 4ef9511fbbdd..43edcde5bce3 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 that represents this partition
> >>>>>> + * @shareable: Is the partition shareable (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 [flat|nested] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-21 23:27 ` Jonathan Cameron
@ 2026-09-24 5:01 ` Anisa Su
0 siblings, 0 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-24 5:01 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Anisa Su, linux-cxl, Dave Jiang, Alison Schofield,
Davidlohr Bueso, Li Ming, Gregory Price, Richard Cheng,
Ben Cheatham, Ira Weiny, Wonjae Lee, Junhee Park, Heesoo Kim
On Tue, Sep 22, 2026 at 12:27:43AM +0100, Jonathan Cameron wrote:
> On Fri, 18 Sep 2026 13:30:12 -0700
> Anisa Su <anisa.su887@gmail.com> wrote:
>
> > From: Ira Weiny <iweiny@kernel.org>
> >
> > Additional DCD partition (AKA region) information is contained in the
> > DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
> >
> One little thing inline. Either way
> Reviewed-by: Jonathan Cameron <joanthan.cameron@oss.qualcomm.com>
>
> > @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
> > }
> >
> > rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> > - if (rc) {
>
> This ended up a bit nasty from an unusual code structure being less
> readable than ideal point of view.
>
> > + if (rc)
> > dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> > +
> > + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
> > + if (rc)
> > return;
> > - }
> Can you make this
> rc = cxl_port_perf_data_calculate(port, dmsas_xa);
> cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
> if (rc) {
> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> return;
> }
>
> or just duplicate to keep things simple to read.
>
> rc = cxl_port_perf_data_calculate(port, dmsas_xa);
> if (rc) {
> dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> cxl_memdev_apply_dsmas(cxlds, dsmas_xa, false);
> return;
> }
>
> cxl_memdev_apply_dsmas(cxlds, dsmas_xa, true);
>
> ...
> Second one probably a little better as keeps messages i order if we
> happen to get a print from cxl_memdev_apply_dsmas()
>
>
I can apply this with the whole DSLBIS business discussed with Dave and Richard
in the other thread. RIP review tags... :')
Thanks,
Anisa
>
> >
> > - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
> > cxl_qos_class_verify(cxlmd);
> > cxl_memdev_update_perf(cxlmd);
> > }
>
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions
2026-09-21 21:55 ` Dave Jiang
@ 2026-09-24 5:12 ` Anisa Su
0 siblings, 0 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-24 5:12 UTC (permalink / raw)
To: Dave Jiang
Cc: Anisa Su, linux-cxl, Alison Schofield, Jonathan Cameron,
Davidlohr Bueso, Li Ming, Gregory Price, Richard Cheng,
Ben Cheatham, Ira Weiny, Wonjae Lee, Junhee Park, Heesoo Kim
On Mon, Sep 21, 2026 at 02:55:00PM -0700, Dave Jiang wrote:
>
>
> On 9/18/26 1:30 PM, Anisa Su wrote:
> > From: Ira Weiny <iweiny@kernel.org>
> >
> > Additional DCD partition (AKA region) information is contained in the
> > DSMAS CDAT tables, including performance 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: Alison Schofield <alison.schofield@intel.com>
>
> Just a nit below. Otherwise
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> >
> > ---
> > Changes:
> > 1. cdat.c: record the DSMAS shareable flag independently of QTG
> > discovery. cxl_port_perf_data_calculate() returns -ENOENT when FW
> > recommends no QTG for any entry, which skipped the assignment even
> > though the CDAT parsed successfully and the DSMAS flag was valid.
> > Richard
> > ---
> > drivers/cxl/core/cdat.c | 49 +++++++++++++++++++++++++++++----------
> > drivers/cxl/core/hdm.c | 1 +
> > drivers/cxl/core/mbox.c | 9 +++++++
> > drivers/cxl/core/memdev.c | 17 ++++++++------
> > drivers/cxl/cxlmem.h | 2 ++
> > include/cxl/cxl.h | 4 ++++
> > 6 files changed, 63 insertions(+), 19 deletions(-)
> >
> > diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> > index 5c9f07262513..6e7950f12134 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,8 @@ static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg,
> > return -ENOMEM;
> >
> > dent->handle = dsmas->dsmad_handle;
> > + /* Shareable is CDAT 1.03 and later, DSMAS Flags bit 3 */
> > + 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;
> > @@ -255,8 +258,13 @@ static void update_perf_entry(struct device *dev, struct dsmas_entry *dent,
> > dent->coord[ACCESS_COORDINATE_CPU].write_latency);
> > }
> >
> > -static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> > - struct xarray *dsmas_xa)
> > +/*
> > + * The DSMAS shareable flag is a property of the CDAT entry and is recorded
> > + * even if FW returns no QTG recommendations for any entry; only update perf
> > + * if those recommendations exist, indicated by @set_perf
> > + */
> > +static void cxl_memdev_apply_dsmas(struct cxl_dev_state *cxlds,
> > + struct xarray *dsmas_xa, bool set_perf)
>
> Perfer an enum rather than a bool
>
> DJ
>
Ok, I'll add this enum:
enum cxl_dsmas_apply {
CXL_DSMAS_APPLY_FLAGS, /* shareable etc.; no QTG data */
CXL_DSMAS_APPLY_FLAGS_PERF, /* perf coordinates and qos_class */
};
Thanks,
Anisa
> > {
> > struct device *dev = cxlds->dev;
> > struct dsmas_entry *dent;
> > @@ -266,18 +274,34 @@ static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds,
> > bool found = false;
> >
> > for (int i = 0; i < cxlds->nr_partitions; i++) {
> > - struct resource *res = &cxlds->part[i].res;
> > + struct cxl_dpa_partition *part = &cxlds->part[i];
> > struct range range = {
> > - .start = res->start,
> > - .end = res->end,
> > + .start = part->res.start,
> > + .end = part->res.end,
> > };
> >
> > - if (range_contains(&range, &dent->dpa_range)) {
> > - update_perf_entry(dev, dent,
> > - &cxlds->part[i].perf);
> > - found = true;
> > + if (!range_contains(&range, &dent->dpa_range))
> > + continue;
> > +
> > + found = true;
> > + /*
> > + * 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;
> > }
> > +
> > + if (set_perf)
> > + update_perf_entry(dev, dent, &part->perf);
> > + if (part->mode == CXL_PARTMODE_DYNAMIC_RAM_1)
> > + part->shareable = dent->shareable;
> > + break;
> > }
> >
> > if (!found)
> > @@ -419,12 +443,13 @@ void cxl_endpoint_parse_cdat(struct cxl_port *port)
> > }
> >
> > rc = cxl_port_perf_data_calculate(port, dsmas_xa);
> > - if (rc) {
> > + if (rc)
> > dev_dbg(&port->dev, "Failed to do perf coord calculations.\n");
> > +
> > + cxl_memdev_apply_dsmas(cxlds, dsmas_xa, rc == 0);
> > + if (rc)
> > return;
> > - }
> >
> > - cxl_memdev_set_qos_class(cxlds, dsmas_xa);
> > cxl_qos_class_verify(cxlmd);
> > cxl_memdev_update_perf(cxlmd);
> > }
> > 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 c96c16cc08e1..d84a90320081 100644
> > --- a/drivers/cxl/core/mbox.c
> > +++ b/drivers/cxl/core/mbox.c
> > @@ -1366,6 +1366,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> > u64 decode_len = le64_to_cpu(dev_part->decode_length);
> > 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);
> > u64 size;
> >
> > /*
> > @@ -1378,6 +1379,13 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> > return 0;
> > }
> >
> > + /* 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;
> > + }
> > +
> > /*
> > * Decode length is a count of 256M units, so a device reporting 2^36
> > * or more wraps.
> > @@ -1391,6 +1399,7 @@ static int cxl_dc_check(struct device *dev, struct cxl_dc_partition_info *part,
> > *part = (struct cxl_dc_partition_info) {
> > .start = le64_to_cpu(dev_part->base),
> > .size = size,
> > + .handle = handle,
> > };
> >
> > /*
> > diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> > index 15df8db314d9..8883c8850759 100644
> > --- a/drivers/cxl/core/memdev.c
> > +++ b/drivers/cxl/core/memdev.c
> > @@ -594,7 +594,8 @@ bool is_cxl_memdev(const struct device *dev)
> > }
> > EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "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;
> >
> > @@ -606,6 +607,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++;
> > }
> >
> > @@ -623,9 +625,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;
> > }
> >
> > @@ -635,9 +637,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;
> > }
> > @@ -685,7 +687,8 @@ int cxl_configure_dcd(struct cxl_memdev_state *mds, struct cxl_dpa_info *info)
> > info->size = total;
> > dev_dbg(dev, "Adding dynamic ram partition 1; %#llx size %#llx\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);
> >
> > return 0;
> > }
> > @@ -704,7 +707,7 @@ int cxl_set_capacity(struct cxl_dev_state *cxlds, u64 capacity)
> > .size = capacity,
> > };
> >
> > - add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM);
> > + add_part(&range_info, 0, capacity, CXL_PARTMODE_RAM, 0);
> > return cxl_dpa_setup(cxlds, &range_info);
> > }
> > EXPORT_SYMBOL_NS_GPL(cxl_set_capacity, "CXL");
> > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > index 4fac65dbb808..9fd5af2e41d4 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;
> > };
> > @@ -851,6 +852,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 4ef9511fbbdd..43edcde5bce3 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 that represents this partition
> > + * @shareable: Is the partition shareable (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
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device
2026-09-24 0:55 ` Jonathan Cameron
@ 2026-09-24 7:01 ` Anisa Su
0 siblings, 0 replies; 41+ messages in thread
From: Anisa Su @ 2026-09-24 7:01 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Richard Cheng, Anisa Su, linux-cxl, Dave Jiang, Alison Schofield,
Davidlohr Bueso, Li Ming, Gregory Price, Ben Cheatham, Ira Weiny,
Wonjae Lee, Junhee Park, Heesoo Kim
On Thu, Sep 24, 2026 at 01:55:51AM +0100, Jonathan Cameron wrote:
> On Tue, 22 Sep 2026 11:44:16 +0800
> Richard Cheng <icheng@nvidia.com> wrote:
>
> > On Fri, Sep 18, 2026 at 01:30:11PM +0800, Anisa Su wrote:
> > > From: Ira Weiny <iweiny@kernel.org>
> > >
> > > Devices which support Dynamic Capacity (DC) are configured
> > > via mailbox commands. CXL r4.0 section 9.13.3 describes the use of
> > > the Get DC Configuration command in order to obtain DCD partition
> > > characteristics.
> > >
> > > 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.
> > >
> > > Initial enablement for Linux only supports one dynamic capacity
> > > partition. Read all the partitions the device reports but validate
> > > only the first, and configure it as 'dynamic ram 1'.
> > >
> > > The specification requires that volatile capacity starts at DPA 0 and pmem
> > > starts at the DPA immediately following it, but DC partitions only need
> > > to be 256MB aligned according to CXL r4.0 section 8.2.10.9.9.1 Table 8-347.
> > > So a device could leave a gap between ram/pmem (static) capacity and its first
> > > DC partition, or between one DC partition and the next.
> > >
> > > However, Linux chooses not to support gaps between static and dynamic
> > > capacity, and requires the first DC partition to begin at the DPA
> > > immediately following static capacity.
> > >
> > > Based on an original patch by Navneet Singh.
> > >
> > > 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: Alison Schofield <alison.schofield@intel.com>
> > >
> >
> > Reviewed-by: Richard Cheng <icheng@nvidia.com>
> >
> > I wonder if we can have comments in code explaining why linux doesn't support
> > gaps between static and DC, otherwise this can be a bit confusing why
> > the 1st DC partition needs to be immediately after the static part, at least
> > for me.
>
Hm... Gregory asked the same in v13 so it's definitely a valid
question. It would require some tweaks to dpa resource tree management,
which currently supports skips (dpa not used but belongs to some partition)
but not holes (dpa belonging to no partition). It shouldn't cause too much churn
(although I haven't looked that closely) but can be done later when needed.
I didn't put into the comments/Documentation because like Jonathan mentioned
it's basically because we are lazy.
> Because we are lazy? :)
>
> Or maybe because the more we constrain the hardware that is supported
> the less variations we have deal with and test!
>
Yes x100
> Jonathan
>
Anisa
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 8/8] Documentation/cxl: Document DPA partition layout and ordering rules
2026-09-21 22:02 ` Dave Jiang
@ 2026-09-28 21:53 ` Anisa Su
2026-09-28 22:36 ` Dave Jiang
0 siblings, 1 reply; 41+ messages in thread
From: Anisa Su @ 2026-09-28 21:53 UTC (permalink / raw)
To: Dave Jiang
Cc: Anisa Su, linux-cxl, Alison Schofield, Jonathan Cameron,
Davidlohr Bueso, Li Ming, Gregory Price, Richard Cheng,
Ben Cheatham
On Mon, Sep 21, 2026 at 03:02:49PM -0700, Dave Jiang wrote:
>
>
> On 9/18/26 1:30 PM, Anisa Su wrote:
> > DC Partitions complicate DPA ordering. Add a DPA Partitions section to
> > cxl-driver.rst describing spec-mandated and Linux requirements for the
> > layout.
> >
> > maturity-map.rst breaks the DCD entry into sub-items so the map shows
> > what this series lands: partition enumeration is [1], event handling
> > and DC-backed DAX regions remain [0] until the follow-on series enables
> > them. The parent stays [0]; nothing is usable yet.
> >
> > Suggested-by: Gregory Price <gourry@gourry.net>
> > Signed-off-by: Anisa Su <anisa.su@samsung.com>
> > Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> > Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
> > Reviewed-by: Alison Schofield <alison.schofield@intel.com>
> > ---
> > Changes:
> > [jonathan]: drop paragraph describing "support for additional
> > dc partitions may be added later..." to avoid predicting future work in
> > documentation
> > [davidlohr]: add DCD sub-items to maturity-map.rst so a multi-step
> > upstreaming shows users what is there and sets expectations.
> > ---
> > .../driver-api/cxl/linux/cxl-driver.rst | 35 +++++++++++++++++++
> > Documentation/driver-api/cxl/maturity-map.rst | 4 +++
> > 2 files changed, 39 insertions(+)
> >
> > diff --git a/Documentation/driver-api/cxl/linux/cxl-driver.rst b/Documentation/driver-api/cxl/linux/cxl-driver.rst
> > index dd6dd17dc536..eb4de2a9a9af 100644
> > --- a/Documentation/driver-api/cxl/linux/cxl-driver.rst
> > +++ b/Documentation/driver-api/cxl/linux/cxl-driver.rst
> > @@ -181,6 +181,41 @@ A Memory Device is a discrete base object that is not a port. While the
> > physical device it belongs to may also host an `endpoint`, the relationship
> > between an `endpoint` and a `memdev` is not captured in sysfs.
> >
> > +DPA Partitions
> > +~~~~~~~~~~~~~~
> > +A memory device presents its capacity as one flat `Device Physical Address`
> > +(DPA) space divided into `partitions`, which Linux lays out in a fixed
> > +order::
> > +
> > + DPA 0 end
> > + +---------------+---------------+---------------------------+
> > + | ram | pmem | dynamic_ram_1 |
> > + +---------------+---------------+---------------------------+
> > + part[0] part[1] part[2]
> > +
> > +Part of that order is required by the CXL specification and part of it is a
> > +Linux choice.
> > +
> > +The `ram` and `pmem` order is mandated. CXL r4.0 section 8.2.10.9.2.1 "Get
> > +Partition Info" (4100h), Table 8-310, mandates that volatile capacity starts
> > +at DPA 0 and pmem starts at the DPA immediately following it.
> > +
> > +Dynamic Capacity partitions only need to be 256MB aligned according to
> > +CXL r4.0 section 8.2.10.9.9.1 "Get Dynamic Capacity Configuration"
> > +(opcode 4800h), Table 8-347. So a device could leave a gap between ram/pmem
> > +(static) capacity and its first DC partition, or between one DC partition
> > +and the next.
> > +
> > +Linux chooses not to support gaps between static and dynamic capacity: the
> > +first DC partition must begin at the DPA immediately following static
> > +capacity -- after pmem, after ram on a device with no pmem, or at DPA 0 on
> > +a device with no static capacity at all.
> > +
> > +Currently, only one dynamic partition is supported. A device may report up
> > +to eight (CXL r4.0 Table 8-346); Linux configures the first and exposes it as
> > +`dynamic_ram_1`. Gaps between DC partitions are not checked, since only the
> > +first is used.
> > +
> > Port Relationships
> > ~~~~~~~~~~~~~~~~~~
> > In our example described above, there are four host bridges attached to the
> > diff --git a/Documentation/driver-api/cxl/maturity-map.rst b/Documentation/driver-api/cxl/maturity-map.rst
> > index 282c1102dd81..fefd31899229 100644
> > --- a/Documentation/driver-api/cxl/maturity-map.rst
> > +++ b/Documentation/driver-api/cxl/maturity-map.rst
>
> Maybe hold off this commit for when everything goes in? Otherwise it's weird that there's documentation but no code.
>
On Patch 2 and 8 from v12, which add the dynamic_ram_1 partition and enforce
that there is no gap between static capacity and dynamic, I received feedback
that it's not clear whether this is from the spec or a Linux rule:
patch 2:
https://lore.kernel.org/linux-cxl/anIMLnbG_KmRfjv6@gourry-fedora-PF4VCD3F/
patch 8:
https://lore.kernel.org/linux-cxl/anFCdsSo6dvxfFrZ@aschofie-mobl2.lan/
> > @@ -150,6 +150,10 @@ Memory-pooling
> > * [1] Hotplug of LDs (via PCI hotplug)
> > * [0] Dynamic Capacity Device (DCD) Support
> >
> > + * [1] DC partition enumeration / configuration (Get DC Config, CDAT DSMAS, event interrupts)
> > + * [0] Extent add / release event handling
> > + * [0] DC-backed DAX regions
>
> I would just update maturity map once when the entire DCD code is in.
>
I received the feedback from v13 that if the DCD series is going to be split up,
"the maturity map should indicate what is there and help users understand and set
expectations."
https://lore.kernel.org/linux-cxl/20260908102124.2231730-2-anisa.su@samsung.com/T/#me40889025337fdd9584182bf80cc6dbca45aec9d
But I could squash this with the documentation commit at
the end of the full series? https://lore.kernel.org/linux-cxl/20260625112638.550691-32-anisa.su@samsung.com/
Thanks,
Anisa
> > +
> > Multi-host sharing
> > ------------------
> >
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v14 8/8] Documentation/cxl: Document DPA partition layout and ordering rules
2026-09-28 21:53 ` Anisa Su
@ 2026-09-28 22:36 ` Dave Jiang
0 siblings, 0 replies; 41+ messages in thread
From: Dave Jiang @ 2026-09-28 22:36 UTC (permalink / raw)
To: Anisa Su
Cc: linux-cxl, Alison Schofield, Jonathan Cameron, Davidlohr Bueso,
Li Ming, Gregory Price, Richard Cheng, Ben Cheatham
On 9/28/26 2:53 PM, Anisa Su wrote:
> On Mon, Sep 21, 2026 at 03:02:49PM -0700, Dave Jiang wrote:
>>
>>
>> On 9/18/26 1:30 PM, Anisa Su wrote:
>>> DC Partitions complicate DPA ordering. Add a DPA Partitions section to
>>> cxl-driver.rst describing spec-mandated and Linux requirements for the
>>> layout.
>>>
>>> maturity-map.rst breaks the DCD entry into sub-items so the map shows
>>> what this series lands: partition enumeration is [1], event handling
>>> and DC-backed DAX regions remain [0] until the follow-on series enables
>>> them. The parent stays [0]; nothing is usable yet.
>>>
>>> Suggested-by: Gregory Price <gourry@gourry.net>
>>> Signed-off-by: Anisa Su <anisa.su@samsung.com>
>>> Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
>>> Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
>>> Reviewed-by: Alison Schofield <alison.schofield@intel.com>
>>> ---
>>> Changes:
>>> [jonathan]: drop paragraph describing "support for additional
>>> dc partitions may be added later..." to avoid predicting future work in
>>> documentation
>>> [davidlohr]: add DCD sub-items to maturity-map.rst so a multi-step
>>> upstreaming shows users what is there and sets expectations.
>>> ---
>>> .../driver-api/cxl/linux/cxl-driver.rst | 35 +++++++++++++++++++
>>> Documentation/driver-api/cxl/maturity-map.rst | 4 +++
>>> 2 files changed, 39 insertions(+)
>>>
>>> diff --git a/Documentation/driver-api/cxl/linux/cxl-driver.rst b/Documentation/driver-api/cxl/linux/cxl-driver.rst
>>> index dd6dd17dc536..eb4de2a9a9af 100644
>>> --- a/Documentation/driver-api/cxl/linux/cxl-driver.rst
>>> +++ b/Documentation/driver-api/cxl/linux/cxl-driver.rst
>>> @@ -181,6 +181,41 @@ A Memory Device is a discrete base object that is not a port. While the
>>> physical device it belongs to may also host an `endpoint`, the relationship
>>> between an `endpoint` and a `memdev` is not captured in sysfs.
>>>
>>> +DPA Partitions
>>> +~~~~~~~~~~~~~~
>>> +A memory device presents its capacity as one flat `Device Physical Address`
>>> +(DPA) space divided into `partitions`, which Linux lays out in a fixed
>>> +order::
>>> +
>>> + DPA 0 end
>>> + +---------------+---------------+---------------------------+
>>> + | ram | pmem | dynamic_ram_1 |
>>> + +---------------+---------------+---------------------------+
>>> + part[0] part[1] part[2]
>>> +
>>> +Part of that order is required by the CXL specification and part of it is a
>>> +Linux choice.
>>> +
>>> +The `ram` and `pmem` order is mandated. CXL r4.0 section 8.2.10.9.2.1 "Get
>>> +Partition Info" (4100h), Table 8-310, mandates that volatile capacity starts
>>> +at DPA 0 and pmem starts at the DPA immediately following it.
>>> +
>>> +Dynamic Capacity partitions only need to be 256MB aligned according to
>>> +CXL r4.0 section 8.2.10.9.9.1 "Get Dynamic Capacity Configuration"
>>> +(opcode 4800h), Table 8-347. So a device could leave a gap between ram/pmem
>>> +(static) capacity and its first DC partition, or between one DC partition
>>> +and the next.
>>> +
>>> +Linux chooses not to support gaps between static and dynamic capacity: the
>>> +first DC partition must begin at the DPA immediately following static
>>> +capacity -- after pmem, after ram on a device with no pmem, or at DPA 0 on
>>> +a device with no static capacity at all.
>>> +
>>> +Currently, only one dynamic partition is supported. A device may report up
>>> +to eight (CXL r4.0 Table 8-346); Linux configures the first and exposes it as
>>> +`dynamic_ram_1`. Gaps between DC partitions are not checked, since only the
>>> +first is used.
>>> +
>>> Port Relationships
>>> ~~~~~~~~~~~~~~~~~~
>>> In our example described above, there are four host bridges attached to the
>>> diff --git a/Documentation/driver-api/cxl/maturity-map.rst b/Documentation/driver-api/cxl/maturity-map.rst
>>> index 282c1102dd81..fefd31899229 100644
>>> --- a/Documentation/driver-api/cxl/maturity-map.rst
>>> +++ b/Documentation/driver-api/cxl/maturity-map.rst
>>
>> Maybe hold off this commit for when everything goes in? Otherwise it's weird that there's documentation but no code.
>>
> On Patch 2 and 8 from v12, which add the dynamic_ram_1 partition and enforce
> that there is no gap between static capacity and dynamic, I received feedback
> that it's not clear whether this is from the spec or a Linux rule:
>
> patch 2:
> https://lore.kernel.org/linux-cxl/anIMLnbG_KmRfjv6@gourry-fedora-PF4VCD3F/
>
> patch 8:
> https://lore.kernel.org/linux-cxl/anFCdsSo6dvxfFrZ@aschofie-mobl2.lan/
>>> @@ -150,6 +150,10 @@ Memory-pooling
>>> * [1] Hotplug of LDs (via PCI hotplug)
>>> * [0] Dynamic Capacity Device (DCD) Support
>>>
>>> + * [1] DC partition enumeration / configuration (Get DC Config, CDAT DSMAS, event interrupts)
>>> + * [0] Extent add / release event handling
>>> + * [0] DC-backed DAX regions
>>
>> I would just update maturity map once when the entire DCD code is in.
>>
> I received the feedback from v13 that if the DCD series is going to be split up,
> "the maturity map should indicate what is there and help users understand and set
> expectations."
>
> https://lore.kernel.org/linux-cxl/20260908102124.2231730-2-anisa.su@samsung.com/T/#me40889025337fdd9584182bf80cc6dbca45aec9d
>
> But I could squash this with the documentation commit at
> the end of the full series? https://lore.kernel.org/linux-cxl/20260625112638.550691-32-anisa.su@samsung.com/
Nah just leave it.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
> Thanks,
> Anisa
>>> +
>>> Multi-host sharing
>>> ------------------
>>>
>>
>
^ permalink raw reply [flat|nested] 41+ messages in thread
end of thread, other threads:[~2026-09-28 22:36 UTC | newest]
Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-18 20:30 [PATCH v14 0/8] DCD Prep Series Anisa Su
2026-09-18 20:30 ` [PATCH v14 1/8] cxl/mbox: Flag support for Dynamic Capacity Devices (DCD) Anisa Su
2026-09-18 20:30 ` [PATCH v14 2/8] cxl/mem: Read dynamic capacity configuration from the device Anisa Su
2026-09-18 20:44 ` sashiko-bot
2026-09-18 22:59 ` Anisa Su
2026-09-21 21:50 ` Dave Jiang
2026-09-21 23:19 ` Jonathan Cameron
2026-09-22 3:44 ` Richard Cheng
2026-09-24 0:55 ` Jonathan Cameron
2026-09-24 7:01 ` Anisa Su
2026-09-18 20:30 ` [PATCH v14 3/8] cxl/cdat: Gather DSMAS data for DCD partitions Anisa Su
2026-09-21 21:55 ` Dave Jiang
2026-09-24 5:12 ` Anisa Su
2026-09-21 23:27 ` Jonathan Cameron
2026-09-24 5:01 ` Anisa Su
2026-09-22 3:53 ` Richard Cheng
2026-09-22 17:08 ` Dave Jiang
2026-09-22 21:15 ` Anisa Su
2026-09-22 23:05 ` Dave Jiang
2026-09-23 0:05 ` Anisa Su
2026-09-23 15:35 ` Dave Jiang
2026-09-24 4:58 ` Anisa Su
2026-09-18 20:30 ` [PATCH v14 4/8] cxl/events: Split event msgnum configuration from irq setup Anisa Su
2026-09-22 5:35 ` Richard Cheng
2026-09-18 20:30 ` [PATCH v14 5/8] cxl/pci: Factor out interrupt policy check Anisa Su
2026-09-22 5:37 ` Richard Cheng
2026-09-18 20:30 ` [PATCH v14 6/8] cxl/mem: Configure dynamic capacity interrupts Anisa Su
2026-09-18 20:43 ` sashiko-bot
2026-09-18 23:38 ` Anisa Su
2026-09-21 22:00 ` Dave Jiang
2026-09-21 23:42 ` Jonathan Cameron
2026-09-22 0:45 ` Dave Jiang
2026-09-22 21:22 ` Anisa Su
2026-09-24 0:58 ` Jonathan Cameron
2026-09-22 5:48 ` Richard Cheng
2026-09-22 9:15 ` Richard Cheng
2026-09-18 20:30 ` [PATCH v14 7/8] cxl/core: Enforce partition order/simplify partition calls Anisa Su
2026-09-18 20:30 ` [PATCH v14 8/8] Documentation/cxl: Document DPA partition layout and ordering rules Anisa Su
2026-09-21 22:02 ` Dave Jiang
2026-09-28 21:53 ` Anisa Su
2026-09-28 22:36 ` Dave Jiang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox