* [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem
[not found] <CGME20251119075306epcas5p22a87515de65a3c668275b394cdea83b0@epcas5p2.samsung.com>
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 01/17] nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label Neeraj Kumar
` (16 more replies)
0 siblings, 17 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Introduction:
=============
CXL Persistent Memory (Pmem) devices region, namespace and content must be
persistent across system reboot. In order to achieve this persistency, it
uses Label Storage Area (LSA) to store respective metadata. During system
reboot, stored metadata in LSA is used to bring back the region, namespace
and content of CXL device in its previous state.
CXL specification provides Get_LSA (4102h) and Set_LSA (4103h) mailbox
commands to access the LSA area. nvdimm driver is using same commands to
get/set LSA data.
There are three types of LSA format and these are part of following
specifications:
- v1.1: https://pmem.io/documents/NVDIMM_Namespace_Spec.pdf
- v1.2: https://uefi.org/sites/default/files/resources/UEFI_Spec_2_7.pdf
- v2.1: https://computeexpresslink.org/wp-content/uploads/2024/02/CXL-2.0-Specification.pdf
Basic differences between these LSA formats:
- v1.1: Support Namespace persistency. Size of Namespace Label is 128 bytes
- v1.2: Support Namespace persistency. Size of Namespace Label is 256 bytes
- v2.1: Support Namespace and Region persistency. Size of Namespace and
Region Label is 256 bytes.
Linux nvdimm driver supports only v1.1 and v1.2 LSA format. CXL pmem device
require support of LSA v2.1 format for region and namespace persistency.
Initial support of LSA 2.1 was add in [1].
This patchset adds support of LSA 2.1 in nvdimm and cxl pmem driver.
Patch 1: Introduce NDD_CXL_LABEL flag and Update cxl label index as per v2.1
Patch 2: Skip the need for 'interleave-set cookie' for LSA 2.1 support
Patch 3-9: Introduce region label and update namespace label as per LSA 2.1
Patch 10: Refactor cxl pmem region auto assembly using Dan's Infra [2]
Patch 11-12: Save cxl region info in LSA and region recreation during reboot
Patch 13:14: Segregate out cxl pmem region code from region.c to pmem_region.c
Patch 15: Introduce cxl region addition/deletion attributes
Patch 16-17: Add support of cxl pmem region re-creation from CXL as per LSA 2.1
Pre-requisite:
==============
This patch series has dependency on Dan's Infra [2]
Testing:
========
In order to test this patchset, I also added the support of LSA v2.1 format
in ndctl. ndctl changes are available at [3]. After review, I’ll push in
ndctl repo for community review.
1. Used Qemu using following CXL topology
M2="-object memory-backend-file,id=cxl-mem1,share=on,mem-path=$TMP_DIR/cxltest.raw,size=512M \
-object memory-backend-file,id=cxl-lsa1,share=on,mem-path=$TMP_DIR/lsa.raw,size=1M \
-object memory-backend-file,id=cxl-mem2,share=on,mem-path=$TMP_DIR/cxltest2.raw,size=512M \
-object memory-backend-file,id=cxl-lsa2,share=on,mem-path=$TMP_DIR/lsa2.raw,size=1M \
-device pxb-cxl,bus_nr=10,bus=pcie.0,id=cxl.1 \
-device cxl-rp,port=1,bus=cxl.1,id=root_port11,chassis=0,slot=1 \
-device cxl-type3,bus=root_port11,memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem1,sn=1 \
-device cxl-rp,port=2,bus=cxl.1,id=root_port12,chassis=0,slot=2 \
-device cxl-type3,bus=root_port12,memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem2,sn=2 \
-M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=8k"
2. Create cxl region on both devices
cxl create-region -d decoder0.0 -m mem0
cxl create-region -d decoder0.0 -m mem1
root@QEMUCXL6060pmem:~# cxl list
[
{
"memdevs":[
{
"memdev":"mem0",
"pmem_size":536870912,
"serial":2,
"host":"0000:0c:00.0",
"firmware_version":"BWFW VERSION 00"
},
{
"memdev":"mem1",
"pmem_size":536870912,
"serial":1,
"host":"0000:0b:00.0",
"firmware_version":"BWFW VERSION 00"
}
]
},
{
"regions":[
{
"region":"region0",
"resource":45365592064,
"size":536870912,
"type":"pmem",
"interleave_ways":1,
"interleave_granularity":256,
"decode_state":"commit",
"qos_class_mismatch":true
},
{
"region":"region1",
"resource":45902462976,
"size":536870912,
"type":"pmem",
"interleave_ways":1,
"interleave_granularity":256,
"decode_state":"commit",
"qos_class_mismatch":true
}
]
}
]
3. Re-Start Qemu and we could see cxl region persistency using "cxl list"
4. Create namespace for both regions
root@QEMUCXL6060pmem:~# time ndctl create-namespace --mode=fsdax --region=region0 --size=128M
{
"dev":"namespace0.0",
"mode":"fsdax",
"map":"dev",
"size":"124.00 MiB (130.02 MB)",
"uuid":"8a125dcb-f992-406d-b3ad-be82fc518f05",
"sector_size":512,
"align":2097152,
"blockdev":"pmem0"
}
real 0m31.866s
user 0m0.183s
sys 0m14.855s
root@QEMUCXL6060pmem:~# time ndctl create-namespace --mode=fsdax --region=region1 --size=256M
{
"dev":"namespace1.0",
"mode":"fsdax",
"map":"dev",
"size":"250.00 MiB (262.14 MB)",
"uuid":"8e16d950-c11d-4253-94a0-5b2928926433",
"sector_size":512,
"align":2097152,
"blockdev":"pmem1"
}
real 0m44.359s
user 0m0.196s
sys 0m26.768s
root@QEMUCXL6060pmem:~# time ndctl create-namespace --mode=fsdax --region=region0 --size=256M
{
"dev":"namespace0.1",
"mode":"fsdax",
"map":"dev",
"size":"250.00 MiB (262.14 MB)",
"uuid":"f3170bfe-548a-4ce4-ae00-145a24e70426",
"sector_size":512,
"align":2097152,
"blockdev":"pmem0.1"
}
real 0m44.004s
user 0m0.220s
sys 0m25.711s
root@QEMUCXL6060pmem:~# time ndctl create-namespace --mode=fsdax --region=region1 --size=128M
{
"dev":"namespace1.1",
"mode":"fsdax",
"map":"dev",
"size":"124.00 MiB (130.02 MB)",
"uuid":"6318d2d9-bc78-4896-84f9-6c18c3a8f58c",
"sector_size":512,
"align":2097152,
"blockdev":"pmem1.1"
}
real 0m36.612s
user 0m0.225s
sys 0m16.457s
root@QEMUCXL6060pmem:~# ndctl list
[
{
"dev":"namespace1.0",
"mode":"fsdax",
"map":"dev",
"size":262144000,
"uuid":"e07564eb-6653-4d67-ab07-434c22474001",
"sector_size":512,
"align":2097152,
"blockdev":"pmem1"
},
{
"dev":"namespace1.1",
"mode":"fsdax",
"map":"dev",
"size":130023424,
"uuid":"27dfd65a-c428-426a-8316-f340a59e0671",
"sector_size":512,
"align":2097152,
"blockdev":"pmem1.1"
},
{
"dev":"namespace0.1",
"mode":"fsdax",
"map":"dev",
"size":130023424,
"uuid":"689b4135-668d-4885-b138-b86f27d7602f",
"sector_size":512,
"align":2097152,
"blockdev":"pmem0.1"
},
{
"dev":"namespace0.0",
"mode":"fsdax",
"map":"dev",
"size":262144000,
"uuid":"ed15c67a-842d-4d5d-8996-be3aa24985e4",
"sector_size":512,
"align":2097152,
"blockdev":"pmem0"
}
]
5. Re-Start Qemu and we could see
- Region persistency using "cxl list"
- Namespace persistency using "ndctl list" and cat /proc/iomem
root@QEMUCXL6060pmem:~# cat /proc/iomem
a90000000-b8fffffff : CXL Window 0
a90000000-aafffffff : Persistent Memory
a90000000-aafffffff : region0
a90000000-a97ffffff : namespace0.0
a98000000-aa7ffffff : namespace0.1
ab0000000-acfffffff : Persistent Memory
ab0000000-acfffffff : region1
ab0000000-abfffffff : namespace1.0
ac0000000-ac7ffffff : namespace1.1
- NOTE: We can see some lag in restart. Look at Observesation below
6. Also verify LSA version using "ndctl read-labels -j nmem0"
root@QEMUCXL6060pmem:~# ndctl read-labels -j nmem0
{
"dev":"nmem0",
"index":[
{
"signature":"NAMESPACE_INDEX",
"major":2,
"minor":1,
"labelsize":256,
"seq":2,
"nslot":4090
},
{
"signature":"NAMESPACE_INDEX",
"major":2,
"minor":1,
"labelsize":256,
"seq":1,
"nslot":4090
}
],
"label":[
{
"type":"68bb2c0a-5a77-4937-9f85-3caf41a0f93c",
"uuid":"cbb3fe1e-9345-4ae7-95ca-2638db27ee7d",
"name":"",
"flags":8,
"nrange":1,
"position":0,
"dpa":134217728,
"rawsize":268435456,
"slot":0,
"align":0,
"region_uuid":"50d806c8-fd11-49eb-b19e-77fc67f6364b",
"abstraction_uuid":"266400ba-fb9f-4677-bcb0-968f11d0d225",
"lbasize":512
},
{
"type":"529d7c61-da07-47c4-a93f-ecdf2c06f444",
"uuid":"50d806c8-fd11-49eb-b19e-77fc67f6364b",
"flags":0,
"nlabel":1,
"position":0,
"dpa":0,
"rawsize":536870912,
"hpa":45365592064,
"slot":1,
"interleave granularity":256,
"align":0
},
{
"type":"68bb2c0a-5a77-4937-9f85-3caf41a0f93c",
"uuid":"d9ef8d35-ef84-4111-b302-bce53c94a2ad",
"name":"",
"flags":0,
"nrange":1,
"position":0,
"dpa":0,
"rawsize":134217728,
"slot":2,
"align":0,
"region_uuid":"50d806c8-fd11-49eb-b19e-77fc67f6364b",
"abstraction_uuid":"266400ba-fb9f-4677-bcb0-968f11d0d225",
"lbasize":512
},
{
"type":"68bb2c0a-5a77-4937-9f85-3caf41a0f93c",
"uuid":"cbb3fe1e-9345-4ae7-95ca-2638db27ee7d",
"name":"",
"flags":0,
"nrange":1,
"position":0,
"dpa":134217728,
"rawsize":268435456,
"slot":3,
"align":0,
"region_uuid":"50d806c8-fd11-49eb-b19e-77fc67f6364b",
"abstraction_uuid":"266400ba-fb9f-4677-bcb0-968f11d0d225",
"lbasize":512
}
]
}
read 1 nmem
- NOTE: We have following UUID types as per CXL Spec
"type":"529d7c61-da07-47c4-a93f-ecdf2c06f444" is region label
"type":"68bb2c0a-5a77-4937-9f85-3caf41a0f93c" is namespace label
Limitation (WIP):
================
Current changes only support interleave way == 1
Observation:
============
First time namespace creation using ndctl takes around 10 to 20 second time
while executing "devm_memremap_pages" at [4]
As using this patchset, after auto region creation, namespace creation is
happening in boot sequence (if nvdimm and cxl drivers are static), It is
therefore boot sequence is increased by around 10 to 20 sec.
Changes:
========
Changes in v3->v4
-----------------
[MISC Changes]
- Reduce Patch Series Commits from 20 to 17
- Re-arranged few commits
[PATCH 01/17]
- Rename nvdimm_region_label_supported() [Dave]
- Add Jonathan RB tag
- Add Dave RB tag
[PATCH 02/17]
- Add Dave RB tag
[PATCH 03/17]
- Merge v3 patch 05-06/20 in v4 patch 03/17
- Use pre-parsed namespace and region labels uuids - [Dave]
- Remove dubious typecasting [Jonathan]
- Add 'uuid_t label_uuid' member in ' struct nd_label_ent' [Dave]
- Introduce to_lsa_label() along with to_label()
[PATCH 04/17]
- Re-arrange v3 patch 08/20 in v4 patch 04/17
- Create static string table enumerated by 'enum label_type' [Dave]
- Elaborate commit message [Alison]
- Replace use of to_label() with new to_lsa_label()
[PATCH 07/17]
- Immediate return -EBUSY in region label deletion [Dave]
[PATCH 09/17]
- Return error early and return the success case last [Dave]
[PATCH 10/17]
- Call region discovery via the ops->probe() callback using [2] [Dave]
[PATCH 11/17]
- Conform to existing style in the subsystem [Dave]
- Remove mode from devm_cxl_pmem_add_region() [Dave]
- Fix error return from EINVAL to EOPNOTSUPP [Dave]
[PATCH 13/17]
- Prep patch with just code movement from region.c to pmem_region.c [Dave]
[PATCH 13/17]
- Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c [Dave]
[PATCH 15/17]
- Return error early and return the success case last [Dave]
- Change region_label_state as enum in struct cxl_region_params [Dave]
[PATCH 16/17]
- Caller release the device reference [Dave]
- Add is_endpoint_decoder() check in match_free_ep_decoder [Dave]
- Use dev_dbg() instead of dev_info() in create_pmem_region() [Dave]
- Use dev_warn() instead of dev_info() in create_pmem_region() [Dave]
Previous Series:
----------------
- Find v3 link at [5]
- Find v2 link at [6]
- v1 patch-set was broken. Find the v1 links at [7] and [8]
[1]: https://lore.kernel.org/all/163116432405.2460985.5547867384570123403.stgit@dwillia2-desk3.amr.corp.intel.com/
[2]: https://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl.git/patch/?id=ab70c6227ee6165a562c215d9dcb4a1c55620d5d
[3]: https://github.com/neerajoss/ndctl/commits/linux-cxl/V1_CXL_LSA_2.1_Support/
[4]: https://elixir.bootlin.com/linux/v6.13.7/source/drivers/nvdimm/pmem.c#L520
[5]: https://lore.kernel.org/linux-cxl/20250917134116.1623730-1-s.neeraj@samsung.com/
[6]: https://lore.kernel.org/linux-cxl/20250730121209.303202-1-s.neeraj@samsung.com/
[7] v1 Cover Letter: https://lore.kernel.org/linux-cxl/1931444790.41750165203442.JavaMail.epsvc@epcpadp1new/
[8] v1 Rest Thread: https://lore.kernel.org/linux-cxl/158453976.61750165203630.JavaMail.epsvc@epcpadp1new/
Neeraj Kumar (17):
nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label
nvdimm/label: CXL labels skip the need for 'interleave-set cookie'
nvdimm/label: Add namespace/region label support as per LSA 2.1
nvdimm/label: Include region label in slot validation
nvdimm/label: Skip region label during ns label DPA reservation
nvdimm/label: Preserve region label during namespace creation
nvdimm/label: Add region label delete support
nvdimm/label: Preserve cxl region information from region label
nvdimm/label: Export routine to fetch region information
cxl/mem: Refactor cxl pmem region auto-assembling
cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation
cxl/pmem: Preserve region information into nd_set
cxl/pmem_region: Prep patch to accommodate pmem_region attributes
cxl/pmem_region: Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c
cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion
cxl/pmem_region: Create pmem region using information parsed from LSA
cxl/pmem: Add CXL LSA 2.1 support in cxl pmem
Documentation/ABI/testing/sysfs-bus-cxl | 22 +
drivers/cxl/Kconfig | 15 +
drivers/cxl/core/Makefile | 1 +
drivers/cxl/core/core.h | 35 +-
drivers/cxl/core/pmem_region.c | 390 ++++++++++++++++
drivers/cxl/core/region.c | 364 +++++++--------
drivers/cxl/cxl.h | 41 +-
drivers/cxl/mem.c | 18 +-
drivers/cxl/pci.c | 4 +-
drivers/cxl/pmem.c | 15 +-
drivers/cxl/port.c | 39 +-
drivers/nvdimm/dimm.c | 5 +
drivers/nvdimm/dimm_devs.c | 25 +
drivers/nvdimm/label.c | 579 ++++++++++++++++++++----
drivers/nvdimm/label.h | 18 +-
drivers/nvdimm/namespace_devs.c | 92 +++-
drivers/nvdimm/nd-core.h | 2 +
drivers/nvdimm/nd.h | 79 ++++
drivers/nvdimm/region_devs.c | 10 +
include/linux/libnvdimm.h | 28 ++
tools/testing/cxl/Kbuild | 1 +
21 files changed, 1413 insertions(+), 370 deletions(-)
create mode 100644 drivers/cxl/core/pmem_region.c
base-commit: ea5514e300568cbe8f19431c3e424d4791db8291
prerequisite-patch-id: f6e07504b17ccf39d9486352b6f7305a03897863
prerequisite-patch-id: cd6bad0c8b993bb59369941905f418f3b799c89d
prerequisite-patch-id: 08ebdae0888c0a2631c3b26990679a0261f34f14
prerequisite-patch-id: 42c75ec8c08c7c8518024f46d31eb114f95a1bac
--
2.34.1
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH V4 01/17] nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 02/17] nvdimm/label: CXL labels skip the need for 'interleave-set cookie' Neeraj Kumar
` (15 subsequent siblings)
16 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Prior to LSA 2.1 version, LSA contain only namespace labels. LSA 2.1
introduced in CXL 2.0 Spec, which contain region label along with
namespace label.
NDD_LABELING flag is used for namespace. Introduced NDD_REGION_LABELING
flag for region label. Based on these flags nvdimm driver performs
operation on namespace label or region label.
NDD_REGION_LABELING will be utilized by cxl driver to enable LSA 2.1
region label support
Accordingly updated label index version
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/dimm.c | 1 +
drivers/nvdimm/dimm_devs.c | 7 +++++++
drivers/nvdimm/label.c | 21 +++++++++++++++++----
drivers/nvdimm/nd.h | 1 +
include/linux/libnvdimm.h | 3 +++
5 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c
index 2f6c26cc6a3e..07f5c5d5e537 100644
--- a/drivers/nvdimm/dimm.c
+++ b/drivers/nvdimm/dimm.c
@@ -62,6 +62,7 @@ static int nvdimm_probe(struct device *dev)
if (rc < 0)
dev_dbg(dev, "failed to unlock dimm: %d\n", rc);
+ ndd->cxl = nvdimm_region_label_supported(ndd->dev);
/*
* EACCES failures reading the namespace label-area-properties
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index e1349ef5f8fd..3363a97cc5b5 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -18,6 +18,13 @@
static DEFINE_IDA(dimm_ida);
+bool nvdimm_region_label_supported(struct device *dev)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+
+ return test_bit(NDD_REGION_LABELING, &nvdimm->flags);
+}
+
/*
* Retrieve bus and dimm handle and return if this bus supports
* get_config_data commands
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 04f4a049599a..0a9b6c5cb2c3 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -688,11 +688,24 @@ static int nd_label_write_index(struct nvdimm_drvdata *ndd, int index, u32 seq,
- (unsigned long) to_namespace_index(ndd, 0);
nsindex->labeloff = __cpu_to_le64(offset);
nsindex->nslot = __cpu_to_le32(nslot);
- nsindex->major = __cpu_to_le16(1);
- if (sizeof_namespace_label(ndd) < 256)
+
+ /* Set LSA Label Index Version */
+ if (ndd->cxl) {
+ /* CXL r3.2: Table 9-9 Label Index Block Layout */
+ nsindex->major = __cpu_to_le16(2);
nsindex->minor = __cpu_to_le16(1);
- else
- nsindex->minor = __cpu_to_le16(2);
+ } else {
+ nsindex->major = __cpu_to_le16(1);
+ /*
+ * NVDIMM Namespace Specification
+ * Table 2: Namespace Label Index Block Fields
+ */
+ if (sizeof_namespace_label(ndd) < 256)
+ nsindex->minor = __cpu_to_le16(1);
+ else /* UEFI 2.7: Label Index Block Definitions */
+ nsindex->minor = __cpu_to_le16(2);
+ }
+
nsindex->checksum = __cpu_to_le64(0);
if (flags & ND_NSINDEX_INIT) {
unsigned long *free = (unsigned long *) nsindex->free;
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index b199eea3260e..f631bd84d6f0 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -522,6 +522,7 @@ void nvdimm_set_labeling(struct device *dev);
void nvdimm_set_locked(struct device *dev);
void nvdimm_clear_locked(struct device *dev);
int nvdimm_security_setup_events(struct device *dev);
+bool nvdimm_region_label_supported(struct device *dev);
#if IS_ENABLED(CONFIG_NVDIMM_KEYS)
int nvdimm_security_unlock(struct device *dev);
#else
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 28f086c4a187..5696715c33bb 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -44,6 +44,9 @@ enum {
/* dimm provider wants synchronous registration by __nvdimm_create() */
NDD_REGISTER_SYNC = 8,
+ /* dimm supports region labels (LSA Format 2.1) */
+ NDD_REGION_LABELING = 9,
+
/* need to set a limit somewhere, but yes, this is likely overkill */
ND_IOCTL_MAX_BUFLEN = SZ_4M,
ND_CMD_MAX_ELEM = 5,
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 02/17] nvdimm/label: CXL labels skip the need for 'interleave-set cookie'
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 01/17] nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 03/17] nvdimm/label: Add namespace/region label support as per LSA 2.1 Neeraj Kumar
` (14 subsequent siblings)
16 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
CXL LSA v2.1 utilizes the region labels stored in the LSA for interleave
set configuration instead of interleave-set cookie used in previous LSA
versions. As interleave-set cookie is not required for CXL LSA v2.1 format
so skip its usage for CXL LSA 2.1 format
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Acked-by: Ira Weiny <ira.weiny@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/namespace_devs.c | 8 +++++++-
drivers/nvdimm/region_devs.c | 10 ++++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index a5edcacfe46d..43fdb806532e 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -1678,7 +1678,13 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region,
int rc = 0;
u16 i;
- if (cookie == 0) {
+ /*
+ * CXL LSA v2.1 utilizes the region label stored in the LSA for
+ * interleave set configuration. Whereas EFI LSA v1.1 & v1.2
+ * utilizes interleave-set cookie. i.e, CXL labels skip the
+ * need for 'interleave-set cookie'
+ */
+ if (!ndd->cxl && cookie == 0) {
dev_dbg(&nd_region->dev, "invalid interleave-set-cookie\n");
return ERR_PTR(-ENXIO);
}
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index a5ceaf5db595..269595e6321e 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -841,6 +841,16 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region,
if (!nd_set)
return 0;
+ /*
+ * CXL LSA v2.1 utilizes the region label stored in the LSA for
+ * interleave set configuration. Whereas EFI LSA v1.1 & v1.2
+ * utilizes interleave-set cookie. i.e, CXL labels skip the
+ * need for 'interleave-set cookie'
+ */
+ if (nsindex && __le16_to_cpu(nsindex->major) == 2
+ && __le16_to_cpu(nsindex->minor) == 1)
+ return 0;
+
if (nsindex && __le16_to_cpu(nsindex->major) == 1
&& __le16_to_cpu(nsindex->minor) == 1)
return nd_set->cookie1;
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 03/17] nvdimm/label: Add namespace/region label support as per LSA 2.1
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 01/17] nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 02/17] nvdimm/label: CXL labels skip the need for 'interleave-set cookie' Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 15:51 ` Dave Jiang
2025-12-17 14:31 ` Jonathan Cameron
2025-11-19 7:52 ` [PATCH V4 04/17] nvdimm/label: Include region label in slot validation Neeraj Kumar
` (13 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Modify __pmem_label_update() to update region labels into LSA
CXL 3.2 Spec mentions CXL LSA 2.1 Namespace Labels at section 9.13.2.5
Modified __pmem_label_update() using setter functions to update
namespace label as per CXL LSA 2.1
Create export routine nd_region_label_update() used for creating
region label into LSA. It will be used later from CXL subsystem
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/label.c | 360 ++++++++++++++++++++++++++------
drivers/nvdimm/label.h | 17 +-
drivers/nvdimm/namespace_devs.c | 25 ++-
drivers/nvdimm/nd.h | 66 ++++++
include/linux/libnvdimm.h | 8 +
5 files changed, 406 insertions(+), 70 deletions(-)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 0a9b6c5cb2c3..0d587a5b9f7e 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -22,8 +22,8 @@ static uuid_t nvdimm_btt2_uuid;
static uuid_t nvdimm_pfn_uuid;
static uuid_t nvdimm_dax_uuid;
-static uuid_t cxl_region_uuid;
-static uuid_t cxl_namespace_uuid;
+uuid_t cxl_region_uuid;
+uuid_t cxl_namespace_uuid;
static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0";
@@ -278,15 +278,38 @@ static struct nd_namespace_label *nd_label_base(struct nvdimm_drvdata *ndd)
return base + 2 * sizeof_namespace_index(ndd);
}
+static unsigned long find_slot(struct nvdimm_drvdata *ndd,
+ unsigned long label)
+{
+ unsigned long base;
+
+ base = (unsigned long) nd_label_base(ndd);
+ return (label - base) / sizeof_namespace_label(ndd);
+}
+
+static int to_lsa_slot(struct nvdimm_drvdata *ndd,
+ union nd_lsa_label *lsa_label)
+{
+ return find_slot(ndd, (unsigned long) lsa_label);
+}
+
static int to_slot(struct nvdimm_drvdata *ndd,
- struct nd_namespace_label *nd_label)
+ struct nd_label_ent *label_ent)
+{
+ if (uuid_equal(&cxl_region_uuid, &label_ent->label_uuid))
+ return find_slot(ndd, (unsigned long) label_ent->region_label);
+ else
+ return find_slot(ndd, (unsigned long) label_ent->label);
+}
+
+static union nd_lsa_label *to_lsa_label(struct nvdimm_drvdata *ndd, int slot)
{
unsigned long label, base;
- label = (unsigned long) nd_label;
base = (unsigned long) nd_label_base(ndd);
+ label = base + sizeof_namespace_label(ndd) * slot;
- return (label - base) / sizeof_namespace_label(ndd);
+ return (union nd_lsa_label *) label;
}
static struct nd_namespace_label *to_label(struct nvdimm_drvdata *ndd, int slot)
@@ -381,6 +404,16 @@ static void nsl_calculate_checksum(struct nvdimm_drvdata *ndd,
nsl_set_checksum(ndd, nd_label, sum);
}
+static void region_label_calculate_checksum(struct nvdimm_drvdata *ndd,
+ struct cxl_region_label *region_label)
+{
+ u64 sum;
+
+ region_label_set_checksum(region_label, 0);
+ sum = nd_fletcher64(region_label, sizeof_namespace_label(ndd), 1);
+ region_label_set_checksum(region_label, sum);
+}
+
static bool slot_valid(struct nvdimm_drvdata *ndd,
struct nd_namespace_label *nd_label, u32 slot)
{
@@ -584,7 +617,7 @@ int nd_label_active_count(struct nvdimm_drvdata *ndd)
return count;
}
-struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
+union nd_lsa_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
{
struct nd_namespace_index *nsindex;
unsigned long *free;
@@ -601,7 +634,7 @@ struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
continue;
if (n-- == 0)
- return to_label(ndd, slot);
+ return to_lsa_label(ndd, slot);
}
return NULL;
@@ -737,9 +770,9 @@ static int nd_label_write_index(struct nvdimm_drvdata *ndd, int index, u32 seq,
}
static unsigned long nd_label_offset(struct nvdimm_drvdata *ndd,
- struct nd_namespace_label *nd_label)
+ union nd_lsa_label *lsa_label)
{
- return (unsigned long) nd_label
+ return (unsigned long) lsa_label
- (unsigned long) to_namespace_index(ndd, 0);
}
@@ -823,11 +856,15 @@ static void reap_victim(struct nd_mapping *nd_mapping,
struct nd_label_ent *victim)
{
struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
- u32 slot = to_slot(ndd, victim->label);
+ u32 slot = to_slot(ndd, victim);
dev_dbg(ndd->dev, "free: %d\n", slot);
nd_label_free_slot(ndd, slot);
- victim->label = NULL;
+
+ if (uuid_equal(&cxl_region_uuid, &victim->label_uuid))
+ victim->region_label = NULL;
+ else
+ victim->label = NULL;
}
static void nsl_set_type_guid(struct nvdimm_drvdata *ndd,
@@ -884,26 +921,20 @@ enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd,
return guid_to_nvdimm_cclass(&nd_label->efi.abstraction_guid);
}
-static int __pmem_label_update(struct nd_region *nd_region,
- struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm,
- int pos, unsigned long flags)
+static int namespace_label_update(struct nd_region *nd_region,
+ struct nd_mapping *nd_mapping,
+ struct nd_namespace_pmem *nspm,
+ int pos, u64 flags,
+ struct nd_namespace_label *ns_label,
+ struct nd_namespace_index *nsindex,
+ u32 slot)
{
struct nd_namespace_common *ndns = &nspm->nsio.common;
struct nd_interleave_set *nd_set = nd_region->nd_set;
struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
- struct nd_namespace_label *nd_label;
- struct nd_namespace_index *nsindex;
- struct nd_label_ent *label_ent;
struct nd_label_id label_id;
struct resource *res;
- unsigned long *free;
- u32 nslot, slot;
- size_t offset;
u64 cookie;
- int rc;
-
- if (!preamble_next(ndd, &nsindex, &free, &nslot))
- return -ENXIO;
cookie = nd_region_interleave_set_cookie(nd_region, nsindex);
nd_label_gen_id(&label_id, nspm->uuid, 0);
@@ -916,33 +947,150 @@ static int __pmem_label_update(struct nd_region *nd_region,
return -ENXIO;
}
+ nsl_set_type(ndd, ns_label);
+ nsl_set_uuid(ndd, ns_label, nspm->uuid);
+ nsl_set_name(ndd, ns_label, nspm->alt_name);
+ nsl_set_flags(ndd, ns_label, flags);
+ nsl_set_nlabel(ndd, ns_label, nd_region->ndr_mappings);
+ nsl_set_nrange(ndd, ns_label, 1);
+ nsl_set_position(ndd, ns_label, pos);
+ nsl_set_isetcookie(ndd, ns_label, cookie);
+ nsl_set_rawsize(ndd, ns_label, resource_size(res));
+ nsl_set_lbasize(ndd, ns_label, nspm->lbasize);
+ nsl_set_dpa(ndd, ns_label, res->start);
+ nsl_set_slot(ndd, ns_label, slot);
+ nsl_set_alignment(ndd, ns_label, 0);
+ nsl_set_type_guid(ndd, ns_label, &nd_set->type_guid);
+ nsl_set_region_uuid(ndd, ns_label, &nd_set->uuid);
+ nsl_set_claim_class(ndd, ns_label, ndns->claim_class);
+ nsl_calculate_checksum(ndd, ns_label);
+ nd_dbg_dpa(nd_region, ndd, res, "\n");
+
+ return 0;
+}
+
+static void region_label_update(struct nd_region *nd_region,
+ struct cxl_region_label *region_label,
+ struct nd_mapping *nd_mapping,
+ int pos, u64 flags, u32 slot)
+{
+ struct nd_interleave_set *nd_set = nd_region->nd_set;
+ struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
+
+ /* Set Region Label Format identification UUID */
+ uuid_copy((uuid_t *)region_label->type, &cxl_region_uuid);
+
+ /* Set Current Region Label UUID */
+ export_uuid(region_label->uuid, &nd_set->uuid);
+
+ region_label->flags = __cpu_to_le32(flags);
+ region_label->nlabel = __cpu_to_le16(nd_region->ndr_mappings);
+ region_label->position = __cpu_to_le16(pos);
+ region_label->dpa = __cpu_to_le64(nd_mapping->start);
+ region_label->rawsize = __cpu_to_le64(nd_mapping->size);
+ region_label->hpa = __cpu_to_le64(nd_set->res->start);
+ region_label->slot = __cpu_to_le32(slot);
+ region_label->ig = __cpu_to_le32(nd_set->interleave_granularity);
+ region_label->align = __cpu_to_le32(0);
+
+ /* Update fletcher64 Checksum */
+ region_label_calculate_checksum(ndd, region_label);
+}
+
+static bool is_label_reapable(struct nd_interleave_set *nd_set,
+ struct nd_namespace_pmem *nspm,
+ struct nvdimm_drvdata *ndd,
+ struct nd_label_ent *label_ent,
+ enum label_type ltype,
+ unsigned long *flags)
+{
+ switch (ltype) {
+ case NS_LABEL_TYPE:
+ if (test_and_clear_bit(ND_LABEL_REAP, flags) ||
+ nsl_uuid_equal(ndd, label_ent->label, nspm->uuid))
+ return true;
+
+ break;
+ case RG_LABEL_TYPE:
+ if (region_label_uuid_equal(label_ent->region_label,
+ &nd_set->uuid))
+ return true;
+
+ break;
+ }
+
+ return false;
+}
+
+static bool is_label_present(struct nd_label_ent *label_ent,
+ enum label_type ltype)
+{
+ switch (ltype) {
+ case NS_LABEL_TYPE:
+ if (label_ent->label)
+ return true;
+
+ break;
+ case RG_LABEL_TYPE:
+ if (label_ent->region_label)
+ return true;
+
+ break;
+ }
+
+ return false;
+}
+
+static int __pmem_label_update(struct nd_region *nd_region,
+ struct nd_mapping *nd_mapping,
+ struct nd_namespace_pmem *nspm,
+ int pos, unsigned long flags,
+ enum label_type ltype)
+{
+ struct nd_interleave_set *nd_set = nd_region->nd_set;
+ struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
+ struct nd_namespace_index *nsindex;
+ struct nd_label_ent *label_ent;
+ union nd_lsa_label *lsa_label;
+ unsigned long *free;
+ struct device *dev;
+ u32 nslot, slot;
+ size_t offset;
+ int rc;
+
+ if (!preamble_next(ndd, &nsindex, &free, &nslot))
+ return -ENXIO;
+
/* allocate and write the label to the staging (next) index */
slot = nd_label_alloc_slot(ndd);
if (slot == UINT_MAX)
return -ENXIO;
dev_dbg(ndd->dev, "allocated: %d\n", slot);
- nd_label = to_label(ndd, slot);
- memset(nd_label, 0, sizeof_namespace_label(ndd));
- nsl_set_uuid(ndd, nd_label, nspm->uuid);
- nsl_set_name(ndd, nd_label, nspm->alt_name);
- nsl_set_flags(ndd, nd_label, flags);
- nsl_set_nlabel(ndd, nd_label, nd_region->ndr_mappings);
- nsl_set_nrange(ndd, nd_label, 1);
- nsl_set_position(ndd, nd_label, pos);
- nsl_set_isetcookie(ndd, nd_label, cookie);
- nsl_set_rawsize(ndd, nd_label, resource_size(res));
- nsl_set_lbasize(ndd, nd_label, nspm->lbasize);
- nsl_set_dpa(ndd, nd_label, res->start);
- nsl_set_slot(ndd, nd_label, slot);
- nsl_set_type_guid(ndd, nd_label, &nd_set->type_guid);
- nsl_set_claim_class(ndd, nd_label, ndns->claim_class);
- nsl_calculate_checksum(ndd, nd_label);
- nd_dbg_dpa(nd_region, ndd, res, "\n");
+ lsa_label = to_lsa_label(ndd, slot);
+ memset(lsa_label, 0, sizeof_namespace_label(ndd));
+
+ switch (ltype) {
+ case NS_LABEL_TYPE:
+ dev = &nspm->nsio.common.dev;
+ rc = namespace_label_update(nd_region, nd_mapping,
+ nspm, pos, flags, &lsa_label->ns_label,
+ nsindex, slot);
+ if (rc)
+ return rc;
+
+ break;
+ case RG_LABEL_TYPE:
+ dev = &nd_region->dev;
+ region_label_update(nd_region, &lsa_label->region_label,
+ nd_mapping, pos, flags, slot);
+
+ break;
+ }
/* update label */
- offset = nd_label_offset(ndd, nd_label);
- rc = nvdimm_set_config_data(ndd, offset, nd_label,
+ offset = nd_label_offset(ndd, lsa_label);
+ rc = nvdimm_set_config_data(ndd, offset, lsa_label,
sizeof_namespace_label(ndd));
if (rc < 0)
return rc;
@@ -950,10 +1098,11 @@ static int __pmem_label_update(struct nd_region *nd_region,
/* Garbage collect the previous label */
mutex_lock(&nd_mapping->lock);
list_for_each_entry(label_ent, &nd_mapping->labels, list) {
- if (!label_ent->label)
+ if (!is_label_present(label_ent, ltype))
continue;
- if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags) ||
- nsl_uuid_equal(ndd, label_ent->label, nspm->uuid))
+
+ if (is_label_reapable(nd_set, nspm, ndd, label_ent, ltype,
+ &label_ent->flags))
reap_victim(nd_mapping, label_ent);
}
@@ -961,16 +1110,21 @@ static int __pmem_label_update(struct nd_region *nd_region,
rc = nd_label_write_index(ndd, ndd->ns_next,
nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0);
if (rc == 0) {
- list_for_each_entry(label_ent, &nd_mapping->labels, list)
- if (!label_ent->label) {
- label_ent->label = nd_label;
- nd_label = NULL;
- break;
- }
- dev_WARN_ONCE(&nspm->nsio.common.dev, nd_label,
- "failed to track label: %d\n",
- to_slot(ndd, nd_label));
- if (nd_label)
+ list_for_each_entry(label_ent, &nd_mapping->labels, list) {
+ if (is_label_present(label_ent, ltype))
+ continue;
+
+ if (ltype == NS_LABEL_TYPE)
+ label_ent->label = &lsa_label->ns_label;
+ else
+ label_ent->region_label = &lsa_label->region_label;
+
+ lsa_label = NULL;
+ break;
+ }
+ dev_WARN_ONCE(dev, lsa_label, "failed to track label: %d\n",
+ to_lsa_slot(ndd, lsa_label));
+ if (lsa_label)
rc = -ENXIO;
}
mutex_unlock(&nd_mapping->lock);
@@ -978,7 +1132,8 @@ static int __pmem_label_update(struct nd_region *nd_region,
return rc;
}
-static int init_labels(struct nd_mapping *nd_mapping, int num_labels)
+static int init_labels(struct nd_mapping *nd_mapping, int num_labels,
+ enum label_type ltype)
{
int i, old_num_labels = 0;
struct nd_label_ent *label_ent;
@@ -998,6 +1153,16 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels)
label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL);
if (!label_ent)
return -ENOMEM;
+
+ switch (ltype) {
+ case NS_LABEL_TYPE:
+ uuid_copy(&label_ent->label_uuid, &cxl_namespace_uuid);
+ break;
+ case RG_LABEL_TYPE:
+ uuid_copy(&label_ent->label_uuid, &cxl_region_uuid);
+ break;
+ }
+
mutex_lock(&nd_mapping->lock);
list_add_tail(&label_ent->list, &nd_mapping->labels);
mutex_unlock(&nd_mapping->lock);
@@ -1041,19 +1206,19 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
mutex_lock(&nd_mapping->lock);
list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
- struct nd_namespace_label *nd_label = label_ent->label;
-
- if (!nd_label)
+ if (label_ent->label)
continue;
active++;
- if (!nsl_uuid_equal(ndd, nd_label, uuid))
+ if (!nsl_uuid_equal(ndd, label_ent->label, uuid))
continue;
active--;
- slot = to_slot(ndd, nd_label);
+ slot = to_slot(ndd, label_ent);
nd_label_free_slot(ndd, slot);
dev_dbg(ndd->dev, "free: %d\n", slot);
list_move_tail(&label_ent->list, &list);
- label_ent->label = NULL;
+
+ if (uuid_equal(&cxl_namespace_uuid, &label_ent->label_uuid))
+ label_ent->label = NULL;
}
list_splice_tail_init(&list, &nd_mapping->labels);
@@ -1067,6 +1232,19 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0);
}
+static int find_region_label_count(struct nd_mapping *nd_mapping)
+{
+ struct nd_label_ent *label_ent;
+ int region_label_cnt = 0;
+
+ guard(mutex)(&nd_mapping->lock);
+ list_for_each_entry(label_ent, &nd_mapping->labels, list)
+ if (uuid_equal(&cxl_region_uuid, &label_ent->label_uuid))
+ region_label_cnt++;
+
+ return region_label_cnt;
+}
+
int nd_pmem_namespace_label_update(struct nd_region *nd_region,
struct nd_namespace_pmem *nspm, resource_size_t size)
{
@@ -1075,6 +1253,7 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
for (i = 0; i < nd_region->ndr_mappings; i++) {
struct nd_mapping *nd_mapping = &nd_region->mapping[i];
struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
+ int region_label_cnt = 0;
struct resource *res;
int count = 0;
@@ -1090,12 +1269,20 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
count++;
WARN_ON_ONCE(!count);
- rc = init_labels(nd_mapping, count);
+ region_label_cnt = find_region_label_count(nd_mapping);
+ /*
+ * init_labels() scan labels and allocate new label based
+ * on its second parameter (num_labels). Therefore to
+ * allocate new namespace label also include previously
+ * added region label
+ */
+ rc = init_labels(nd_mapping, count + region_label_cnt,
+ NS_LABEL_TYPE);
if (rc < 0)
return rc;
rc = __pmem_label_update(nd_region, nd_mapping, nspm, i,
- NSLABEL_FLAG_UPDATING);
+ NSLABEL_FLAG_UPDATING, NS_LABEL_TYPE);
if (rc)
return rc;
}
@@ -1107,7 +1294,48 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
for (i = 0; i < nd_region->ndr_mappings; i++) {
struct nd_mapping *nd_mapping = &nd_region->mapping[i];
- rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 0);
+ rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 0,
+ NS_LABEL_TYPE);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+int nd_pmem_region_label_update(struct nd_region *nd_region)
+{
+ int i, rc;
+
+ for (i = 0; i < nd_region->ndr_mappings; i++) {
+ struct nd_mapping *nd_mapping = &nd_region->mapping[i];
+ struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
+ int region_label_cnt = 0;
+
+ /* No need to update region label for non cxl format */
+ if (!ndd->cxl)
+ return 0;
+
+ region_label_cnt = find_region_label_count(nd_mapping);
+ rc = init_labels(nd_mapping, region_label_cnt + 1,
+ RG_LABEL_TYPE);
+ if (rc < 0)
+ return rc;
+
+ rc = __pmem_label_update(nd_region, nd_mapping, NULL, i,
+ NSLABEL_FLAG_UPDATING, RG_LABEL_TYPE);
+ if (rc)
+ return rc;
+ }
+
+ /* Clear the UPDATING flag per UEFI 2.7 expectations */
+ for (i = 0; i < nd_region->ndr_mappings; i++) {
+ struct nd_mapping *nd_mapping = &nd_region->mapping[i];
+ struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
+
+ WARN_ON_ONCE(!ndd->cxl);
+ rc = __pmem_label_update(nd_region, nd_mapping, NULL, i, 0,
+ RG_LABEL_TYPE);
if (rc)
return rc;
}
diff --git a/drivers/nvdimm/label.h b/drivers/nvdimm/label.h
index 0650fb4b9821..f11f54056353 100644
--- a/drivers/nvdimm/label.h
+++ b/drivers/nvdimm/label.h
@@ -30,6 +30,11 @@ enum {
ND_NSINDEX_INIT = 0x1,
};
+enum label_type {
+ RG_LABEL_TYPE,
+ NS_LABEL_TYPE,
+};
+
/**
* struct nd_namespace_index - label set superblock
* @sig: NAMESPACE_INDEX\0
@@ -183,6 +188,15 @@ struct nd_namespace_label {
};
};
+/*
+ * LSA 2.1 format introduces region label, which can also reside
+ * into LSA along with only namespace label as per v1.1 and v1.2
+ */
+union nd_lsa_label {
+ struct nd_namespace_label ns_label;
+ struct cxl_region_label region_label;
+};
+
#define NVDIMM_BTT_GUID "8aed63a2-29a2-4c66-8b12-f05d15d3922a"
#define NVDIMM_BTT2_GUID "18633bfc-1735-4217-8ac9-17239282d3f8"
#define NVDIMM_PFN_GUID "266400ba-fb9f-4677-bcb0-968f11d0d225"
@@ -215,7 +229,7 @@ struct nvdimm_drvdata;
int nd_label_data_init(struct nvdimm_drvdata *ndd);
size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd);
int nd_label_active_count(struct nvdimm_drvdata *ndd);
-struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n);
+union nd_lsa_label *nd_label_active(struct nvdimm_drvdata *ndd, int n);
u32 nd_label_alloc_slot(struct nvdimm_drvdata *ndd);
bool nd_label_free_slot(struct nvdimm_drvdata *ndd, u32 slot);
u32 nd_label_nfree(struct nvdimm_drvdata *ndd);
@@ -223,4 +237,5 @@ struct nd_region;
struct nd_namespace_pmem;
int nd_pmem_namespace_label_update(struct nd_region *nd_region,
struct nd_namespace_pmem *nspm, resource_size_t size);
+int nd_pmem_region_label_update(struct nd_region *nd_region);
#endif /* __LABEL_H__ */
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index 43fdb806532e..b1abbe602a5e 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -232,6 +232,18 @@ static ssize_t __alt_name_store(struct device *dev, const char *buf,
return rc;
}
+int nd_region_label_update(struct nd_region *nd_region)
+{
+ int rc;
+
+ nvdimm_bus_lock(&nd_region->dev);
+ rc = nd_pmem_region_label_update(nd_region);
+ nvdimm_bus_unlock(&nd_region->dev);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(nd_region_label_update);
+
static int nd_namespace_label_update(struct nd_region *nd_region,
struct device *dev)
{
@@ -2122,13 +2134,20 @@ static int init_active_labels(struct nd_region *nd_region)
if (!count)
continue;
for (j = 0; j < count; j++) {
- struct nd_namespace_label *label;
+ union nd_lsa_label *lsa_label;
label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL);
if (!label_ent)
break;
- label = nd_label_active(ndd, j);
- label_ent->label = label;
+
+ lsa_label = nd_label_active(ndd, j);
+ if (is_region_label(ndd, lsa_label)) {
+ label_ent->region_label = &lsa_label->region_label;
+ uuid_copy(&label_ent->label_uuid, &cxl_region_uuid);
+ } else {
+ label_ent->label = &lsa_label->ns_label;
+ uuid_copy(&label_ent->label_uuid, &cxl_namespace_uuid);
+ }
mutex_lock(&nd_mapping->lock);
list_add_tail(&label_ent->list, &nd_mapping->labels);
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index f631bd84d6f0..5fd69c28ffe7 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -14,6 +14,9 @@
#include <linux/nd.h>
#include "label.h"
+extern uuid_t cxl_namespace_uuid;
+extern uuid_t cxl_region_uuid;
+
enum {
/*
* Limits the maximum number of block apertures a dimm can
@@ -295,6 +298,67 @@ static inline const u8 *nsl_uuid_raw(struct nvdimm_drvdata *ndd,
return nd_label->efi.uuid;
}
+static inline void nsl_set_type(struct nvdimm_drvdata *ndd,
+ struct nd_namespace_label *ns_label)
+{
+ if (!(ndd->cxl && ns_label))
+ return;
+
+ uuid_copy((uuid_t *)ns_label->cxl.type, &cxl_namespace_uuid);
+}
+
+static inline void nsl_set_alignment(struct nvdimm_drvdata *ndd,
+ struct nd_namespace_label *ns_label,
+ u32 align)
+{
+ if (!ndd->cxl)
+ return;
+
+ ns_label->cxl.align = __cpu_to_le32(align);
+}
+
+static inline void nsl_set_region_uuid(struct nvdimm_drvdata *ndd,
+ struct nd_namespace_label *ns_label,
+ const uuid_t *uuid)
+{
+ if (!(ndd->cxl && uuid))
+ return;
+
+ export_uuid(ns_label->cxl.region_uuid, uuid);
+}
+
+static inline bool is_region_label(struct nvdimm_drvdata *ndd,
+ union nd_lsa_label *lsa_label)
+{
+ uuid_t *region_type;
+
+ if (!ndd->cxl)
+ return false;
+
+ region_type = (uuid_t *) lsa_label->region_label.type;
+ return uuid_equal(&cxl_region_uuid, region_type);
+}
+
+static inline bool
+region_label_uuid_equal(struct cxl_region_label *region_label,
+ const uuid_t *uuid)
+{
+ return uuid_equal((uuid_t *) region_label->uuid, uuid);
+}
+
+static inline u64
+region_label_get_checksum(struct cxl_region_label *region_label)
+{
+ return __le64_to_cpu(region_label->checksum);
+}
+
+static inline void
+region_label_set_checksum(struct cxl_region_label *region_label,
+ u64 checksum)
+{
+ region_label->checksum = __cpu_to_le64(checksum);
+}
+
bool nsl_validate_type_guid(struct nvdimm_drvdata *ndd,
struct nd_namespace_label *nd_label, guid_t *guid);
enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd,
@@ -376,7 +440,9 @@ enum nd_label_flags {
struct nd_label_ent {
struct list_head list;
unsigned long flags;
+ uuid_t label_uuid;
struct nd_namespace_label *label;
+ struct cxl_region_label *region_label;
};
enum nd_mapping_lock_class {
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 5696715c33bb..2c213b9dac66 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -117,6 +117,13 @@ struct nd_interleave_set {
u64 altcookie;
guid_t type_guid;
+
+ /* v2.1 region label info */
+ uuid_t uuid;
+ int interleave_ways;
+ int interleave_granularity;
+ struct resource *res;
+ int nr_targets;
};
struct nd_mapping_desc {
@@ -307,6 +314,7 @@ int nvdimm_has_flush(struct nd_region *nd_region);
int nvdimm_has_cache(struct nd_region *nd_region);
int nvdimm_in_overwrite(struct nvdimm *nvdimm);
bool is_nvdimm_sync(struct nd_region *nd_region);
+int nd_region_label_update(struct nd_region *nd_region);
static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf,
unsigned int buf_len, int *cmd_rc)
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 04/17] nvdimm/label: Include region label in slot validation
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (2 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 03/17] nvdimm/label: Add namespace/region label support as per LSA 2.1 Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 16:54 ` Dave Jiang
2025-11-19 7:52 ` [PATCH V4 05/17] nvdimm/label: Skip region label during ns label DPA reservation Neeraj Kumar
` (12 subsequent siblings)
16 siblings, 1 reply; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Prior to LSA 2.1 Support, label in slot means only namespace
label. But with LSA 2.1 a label can be either namespace or
region label.
Slot validation routine validates label slot by calculating
label checksum. It was only validating namespace label.
This changeset also validates region label if present.
In previous patch to_lsa_label() was introduced along with
to_label(). to_label() returns only namespace label whereas
to_lsa_label() returns union nd_lsa_label*
In this patch We have converted all usage of to_label()
to to_lsa_label()
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/label.c | 94 ++++++++++++++++++++++++++++--------------
drivers/nvdimm/nd.h | 5 +++
2 files changed, 69 insertions(+), 30 deletions(-)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 0d587a5b9f7e..6ccc51552822 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -312,16 +312,6 @@ static union nd_lsa_label *to_lsa_label(struct nvdimm_drvdata *ndd, int slot)
return (union nd_lsa_label *) label;
}
-static struct nd_namespace_label *to_label(struct nvdimm_drvdata *ndd, int slot)
-{
- unsigned long label, base;
-
- base = (unsigned long) nd_label_base(ndd);
- label = base + sizeof_namespace_label(ndd) * slot;
-
- return (struct nd_namespace_label *) label;
-}
-
#define for_each_clear_bit_le(bit, addr, size) \
for ((bit) = find_next_zero_bit_le((addr), (size), 0); \
(bit) < (size); \
@@ -382,7 +372,7 @@ static bool nsl_validate_checksum(struct nvdimm_drvdata *ndd,
{
u64 sum, sum_save;
- if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum))
+ if (!efi_namespace_label_has(ndd, checksum))
return true;
sum_save = nsl_get_checksum(ndd, nd_label);
@@ -397,13 +387,25 @@ static void nsl_calculate_checksum(struct nvdimm_drvdata *ndd,
{
u64 sum;
- if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum))
+ if (!efi_namespace_label_has(ndd, checksum))
return;
nsl_set_checksum(ndd, nd_label, 0);
sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1);
nsl_set_checksum(ndd, nd_label, sum);
}
+static bool region_label_validate_checksum(struct nvdimm_drvdata *ndd,
+ struct cxl_region_label *region_label)
+{
+ u64 sum, sum_save;
+
+ sum_save = region_label_get_checksum(region_label);
+ region_label_set_checksum(region_label, 0);
+ sum = nd_fletcher64(region_label, sizeof_namespace_label(ndd), 1);
+ region_label_set_checksum(region_label, sum_save);
+ return sum == sum_save;
+}
+
static void region_label_calculate_checksum(struct nvdimm_drvdata *ndd,
struct cxl_region_label *region_label)
{
@@ -415,16 +417,34 @@ static void region_label_calculate_checksum(struct nvdimm_drvdata *ndd,
}
static bool slot_valid(struct nvdimm_drvdata *ndd,
- struct nd_namespace_label *nd_label, u32 slot)
+ union nd_lsa_label *lsa_label, u32 slot)
{
+ struct cxl_region_label *region_label = &lsa_label->region_label;
+ struct nd_namespace_label *nd_label = &lsa_label->ns_label;
+ enum label_type type;
bool valid;
+ static const char * const label_name[] = {
+ [RG_LABEL_TYPE] = "region",
+ [NS_LABEL_TYPE] = "namespace",
+ };
/* check that we are written where we expect to be written */
- if (slot != nsl_get_slot(ndd, nd_label))
- return false;
- valid = nsl_validate_checksum(ndd, nd_label);
+ if (is_region_label(ndd, lsa_label)) {
+ type = RG_LABEL_TYPE;
+ if (slot != region_label_get_slot(region_label))
+ return false;
+ valid = region_label_validate_checksum(ndd, region_label);
+ } else {
+ type = NS_LABEL_TYPE;
+ if (slot != nsl_get_slot(ndd, nd_label))
+ return false;
+ valid = nsl_validate_checksum(ndd, nd_label);
+ }
+
if (!valid)
- dev_dbg(ndd->dev, "fail checksum. slot: %d\n", slot);
+ dev_dbg(ndd->dev, "%s label checksum fail. slot: %d\n",
+ label_name[type], slot);
+
return valid;
}
@@ -440,14 +460,16 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
for_each_clear_bit_le(slot, free, nslot) {
struct nd_namespace_label *nd_label;
struct nd_region *nd_region = NULL;
+ union nd_lsa_label *lsa_label;
struct nd_label_id label_id;
struct resource *res;
uuid_t label_uuid;
u32 flags;
- nd_label = to_label(ndd, slot);
+ lsa_label = to_lsa_label(ndd, slot);
+ nd_label = &lsa_label->ns_label;
- if (!slot_valid(ndd, nd_label, slot))
+ if (!slot_valid(ndd, lsa_label, slot))
continue;
nsl_get_uuid(ndd, nd_label, &label_uuid);
@@ -598,18 +620,30 @@ int nd_label_active_count(struct nvdimm_drvdata *ndd)
return 0;
for_each_clear_bit_le(slot, free, nslot) {
+ struct cxl_region_label *region_label;
struct nd_namespace_label *nd_label;
-
- nd_label = to_label(ndd, slot);
-
- if (!slot_valid(ndd, nd_label, slot)) {
- u32 label_slot = nsl_get_slot(ndd, nd_label);
- u64 size = nsl_get_rawsize(ndd, nd_label);
- u64 dpa = nsl_get_dpa(ndd, nd_label);
+ union nd_lsa_label *lsa_label;
+ u32 lslot;
+ u64 size, dpa;
+
+ lsa_label = to_lsa_label(ndd, slot);
+ nd_label = &lsa_label->ns_label;
+ region_label = &lsa_label->region_label;
+
+ if (!slot_valid(ndd, lsa_label, slot)) {
+ if (is_region_label(ndd, lsa_label)) {
+ lslot = __le32_to_cpu(region_label->slot);
+ size = __le64_to_cpu(region_label->rawsize);
+ dpa = __le64_to_cpu(region_label->dpa);
+ } else {
+ lslot = nsl_get_slot(ndd, nd_label);
+ size = nsl_get_rawsize(ndd, nd_label);
+ dpa = nsl_get_dpa(ndd, nd_label);
+ }
dev_dbg(ndd->dev,
"slot%d invalid slot: %d dpa: %llx size: %llx\n",
- slot, label_slot, dpa, size);
+ slot, lslot, dpa, size);
continue;
}
count++;
@@ -627,10 +661,10 @@ union nd_lsa_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
return NULL;
for_each_clear_bit_le(slot, free, nslot) {
- struct nd_namespace_label *nd_label;
+ union nd_lsa_label *lsa_label;
- nd_label = to_label(ndd, slot);
- if (!slot_valid(ndd, nd_label, slot))
+ lsa_label = to_lsa_label(ndd, slot);
+ if (!slot_valid(ndd, lsa_label, slot))
continue;
if (n-- == 0)
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 5fd69c28ffe7..30c7262d8a26 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -346,6 +346,11 @@ region_label_uuid_equal(struct cxl_region_label *region_label,
return uuid_equal((uuid_t *) region_label->uuid, uuid);
}
+static inline u32 region_label_get_slot(struct cxl_region_label *region_label)
+{
+ return __le32_to_cpu(region_label->slot);
+}
+
static inline u64
region_label_get_checksum(struct cxl_region_label *region_label)
{
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 05/17] nvdimm/label: Skip region label during ns label DPA reservation
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (3 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 04/17] nvdimm/label: Include region label in slot validation Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 17:01 ` Dave Jiang
2025-12-17 14:33 ` Jonathan Cameron
2025-11-19 7:52 ` [PATCH V4 06/17] nvdimm/label: Preserve region label during namespace creation Neeraj Kumar
` (11 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
If Namespace label is present in LSA during nvdimm_probe() then DPA
reservation is required. But this reservation is not required by region
label. Therefore if LSA scanning finds any region label, skip it.
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/label.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 6ccc51552822..e90e48672da3 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -469,6 +469,10 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
lsa_label = to_lsa_label(ndd, slot);
nd_label = &lsa_label->ns_label;
+ /* Skip region label. DPA reservation is for NS label only */
+ if (is_region_label(ndd, lsa_label))
+ continue;
+
if (!slot_valid(ndd, lsa_label, slot))
continue;
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 06/17] nvdimm/label: Preserve region label during namespace creation
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (4 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 05/17] nvdimm/label: Skip region label during ns label DPA reservation Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 18:07 ` Dave Jiang
2025-11-19 7:52 ` [PATCH V4 07/17] nvdimm/label: Add region label delete support Neeraj Kumar
` (10 subsequent siblings)
16 siblings, 1 reply; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
During namespace creation we scan labels present in LSA using
scan_labels(). Currently scan_labels() is only preserving
namespace labels into label_ent list.
In this patch we also preserve region label into label_ent list
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/namespace_devs.c | 47 +++++++++++++++++++++++++++++----
1 file changed, 42 insertions(+), 5 deletions(-)
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index b1abbe602a5e..9450200b4470 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -1999,9 +1999,32 @@ static struct device **scan_labels(struct nd_region *nd_region)
if (count == 0) {
struct nd_namespace_pmem *nspm;
+ for (i = 0; i < nd_region->ndr_mappings; i++) {
+ struct cxl_region_label *region_label;
+ struct nd_label_ent *le, *e;
+ LIST_HEAD(list);
- /* Publish a zero-sized namespace for userspace to configure. */
- nd_mapping_free_labels(nd_mapping);
+ nd_mapping = &nd_region->mapping[i];
+ if (list_empty(&nd_mapping->labels))
+ continue;
+
+ list_for_each_entry_safe(le, e, &nd_mapping->labels,
+ list) {
+ region_label = le->region_label;
+ if (!region_label)
+ continue;
+
+ /* Preserve region labels if present */
+ list_move_tail(&le->list, &list);
+ }
+
+ /*
+ * Publish a zero-sized namespace for userspace
+ * to configure.
+ */
+ nd_mapping_free_labels(nd_mapping);
+ list_splice_init(&list, &nd_mapping->labels);
+ }
nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
if (!nspm)
goto err;
@@ -2013,7 +2036,7 @@ static struct device **scan_labels(struct nd_region *nd_region)
} else if (is_memory(&nd_region->dev)) {
/* clean unselected labels */
for (i = 0; i < nd_region->ndr_mappings; i++) {
- struct list_head *l, *e;
+ struct nd_label_ent *le, *e;
LIST_HEAD(list);
int j;
@@ -2024,10 +2047,24 @@ static struct device **scan_labels(struct nd_region *nd_region)
}
j = count;
- list_for_each_safe(l, e, &nd_mapping->labels) {
+ list_for_each_entry_safe(le, e, &nd_mapping->labels,
+ list) {
+ /* Preserve region labels */
+ if (uuid_equal(&le->label_uuid,
+ &cxl_region_uuid)) {
+ list_move_tail(&le->list, &list);
+ continue;
+ }
+
+ /*
+ * Once preserving selected ns label done
+ * break out of loop
+ */
if (!j--)
break;
- list_move_tail(l, &list);
+
+ /* Preserve selected ns label */
+ list_move_tail(&le->list, &list);
}
nd_mapping_free_labels(nd_mapping);
list_splice_init(&list, &nd_mapping->labels);
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 07/17] nvdimm/label: Add region label delete support
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (5 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 06/17] nvdimm/label: Preserve region label during namespace creation Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 19:50 ` Dave Jiang
2025-12-17 15:05 ` Jonathan Cameron
2025-11-19 7:52 ` [PATCH V4 08/17] nvdimm/label: Preserve cxl region information from region label Neeraj Kumar
` (9 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Create export routine nd_region_label_delete() used for deleting
region label from LSA. It will be used later from CXL subsystem
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/label.c | 76 ++++++++++++++++++++++++++++++---
drivers/nvdimm/label.h | 1 +
drivers/nvdimm/namespace_devs.c | 12 ++++++
drivers/nvdimm/nd.h | 6 +++
include/linux/libnvdimm.h | 1 +
5 files changed, 90 insertions(+), 6 deletions(-)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index e90e48672da3..da55ecd95e2f 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -1225,7 +1225,8 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels,
return max(num_labels, old_num_labels);
}
-static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
+static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid,
+ enum label_type ltype)
{
struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
struct nd_label_ent *label_ent, *e;
@@ -1244,11 +1245,25 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
mutex_lock(&nd_mapping->lock);
list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
- if (label_ent->label)
+ if ((ltype == NS_LABEL_TYPE && !label_ent->label) ||
+ (ltype == RG_LABEL_TYPE && !label_ent->region_label))
continue;
active++;
- if (!nsl_uuid_equal(ndd, label_ent->label, uuid))
- continue;
+
+ switch (ltype) {
+ case NS_LABEL_TYPE:
+ if (!nsl_uuid_equal(ndd, label_ent->label, uuid))
+ continue;
+
+ break;
+ case RG_LABEL_TYPE:
+ if (!region_label_uuid_equal(label_ent->region_label,
+ uuid))
+ continue;
+
+ break;
+ }
+
active--;
slot = to_slot(ndd, label_ent);
nd_label_free_slot(ndd, slot);
@@ -1257,10 +1272,12 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
if (uuid_equal(&cxl_namespace_uuid, &label_ent->label_uuid))
label_ent->label = NULL;
+ else
+ label_ent->region_label = NULL;
}
list_splice_tail_init(&list, &nd_mapping->labels);
- if (active == 0) {
+ if ((ltype == NS_LABEL_TYPE) && (active == 0)) {
nd_mapping_free_labels(nd_mapping);
dev_dbg(ndd->dev, "no more active labels\n");
}
@@ -1296,7 +1313,8 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
int count = 0;
if (size == 0) {
- rc = del_labels(nd_mapping, nspm->uuid);
+ rc = del_labels(nd_mapping, nspm->uuid,
+ NS_LABEL_TYPE);
if (rc)
return rc;
continue;
@@ -1381,6 +1399,52 @@ int nd_pmem_region_label_update(struct nd_region *nd_region)
return 0;
}
+int nd_pmem_region_label_delete(struct nd_region *nd_region)
+{
+ struct nd_interleave_set *nd_set = nd_region->nd_set;
+ struct nd_label_ent *label_ent;
+ int i, rc;
+
+ for (i = 0; i < nd_region->ndr_mappings; i++) {
+ struct nd_mapping *nd_mapping = &nd_region->mapping[i];
+ struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
+
+ /* Find non cxl format supported ndr_mappings */
+ if (!ndd->cxl) {
+ dev_info(&nd_region->dev, "Unsupported region label\n");
+ return -EINVAL;
+ }
+
+ /* Find if any NS label using this region */
+ guard(mutex)(&nd_mapping->lock);
+ list_for_each_entry(label_ent, &nd_mapping->labels, list) {
+ if (!label_ent->label)
+ continue;
+
+ /*
+ * Check if any available NS labels has same
+ * region_uuid in LSA
+ */
+ if (nsl_region_uuid_equal(label_ent->label,
+ &nd_set->uuid)) {
+ dev_dbg(&nd_region->dev,
+ "Region/Namespace label in use\n");
+ return -EBUSY;
+ }
+ }
+ }
+
+ for (i = 0; i < nd_region->ndr_mappings; i++) {
+ struct nd_mapping *nd_mapping = &nd_region->mapping[i];
+
+ rc = del_labels(nd_mapping, &nd_set->uuid, RG_LABEL_TYPE);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
int __init nd_label_init(void)
{
WARN_ON(guid_parse(NVDIMM_BTT_GUID, &nvdimm_btt_guid));
diff --git a/drivers/nvdimm/label.h b/drivers/nvdimm/label.h
index f11f54056353..80a7f7dd8ba7 100644
--- a/drivers/nvdimm/label.h
+++ b/drivers/nvdimm/label.h
@@ -238,4 +238,5 @@ struct nd_namespace_pmem;
int nd_pmem_namespace_label_update(struct nd_region *nd_region,
struct nd_namespace_pmem *nspm, resource_size_t size);
int nd_pmem_region_label_update(struct nd_region *nd_region);
+int nd_pmem_region_label_delete(struct nd_region *nd_region);
#endif /* __LABEL_H__ */
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index 9450200b4470..9299a586bfce 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -244,6 +244,18 @@ int nd_region_label_update(struct nd_region *nd_region)
}
EXPORT_SYMBOL_GPL(nd_region_label_update);
+int nd_region_label_delete(struct nd_region *nd_region)
+{
+ int rc;
+
+ nvdimm_bus_lock(&nd_region->dev);
+ rc = nd_pmem_region_label_delete(nd_region);
+ nvdimm_bus_unlock(&nd_region->dev);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(nd_region_label_delete);
+
static int nd_namespace_label_update(struct nd_region *nd_region,
struct device *dev)
{
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 30c7262d8a26..b241a0b2e314 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -339,6 +339,12 @@ static inline bool is_region_label(struct nvdimm_drvdata *ndd,
return uuid_equal(&cxl_region_uuid, region_type);
}
+static inline bool nsl_region_uuid_equal(struct nd_namespace_label *ns_label,
+ const uuid_t *uuid)
+{
+ return uuid_equal((uuid_t *) ns_label->cxl.region_uuid, uuid);
+}
+
static inline bool
region_label_uuid_equal(struct cxl_region_label *region_label,
const uuid_t *uuid)
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 2c213b9dac66..bbf14a260c93 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -315,6 +315,7 @@ int nvdimm_has_cache(struct nd_region *nd_region);
int nvdimm_in_overwrite(struct nvdimm *nvdimm);
bool is_nvdimm_sync(struct nd_region *nd_region);
int nd_region_label_update(struct nd_region *nd_region);
+int nd_region_label_delete(struct nd_region *nd_region);
static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf,
unsigned int buf_len, int *cmd_rc)
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 08/17] nvdimm/label: Preserve cxl region information from region label
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (6 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 07/17] nvdimm/label: Add region label delete support Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 20:13 ` Dave Jiang
2025-12-17 15:09 ` Jonathan Cameron
2025-11-19 7:52 ` [PATCH V4 09/17] nvdimm/label: Export routine to fetch region information Neeraj Kumar
` (8 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Preserve region information from region label during nvdimm_probe. This
preserved region information is used for creating cxl region to achieve
region persistency across reboot.
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/dimm.c | 4 ++++
drivers/nvdimm/label.c | 40 +++++++++++++++++++++++++++++++++++++++
drivers/nvdimm/nd-core.h | 2 ++
drivers/nvdimm/nd.h | 1 +
include/linux/libnvdimm.h | 14 ++++++++++++++
5 files changed, 61 insertions(+)
diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c
index 07f5c5d5e537..590ec883903d 100644
--- a/drivers/nvdimm/dimm.c
+++ b/drivers/nvdimm/dimm.c
@@ -107,6 +107,10 @@ static int nvdimm_probe(struct device *dev)
if (rc)
goto err;
+ /* Preserve cxl region info if available */
+ if (ndd->cxl)
+ nvdimm_cxl_region_preserve(ndd);
+
return 0;
err:
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index da55ecd95e2f..0f8aea61b504 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -490,6 +490,46 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
return 0;
}
+int nvdimm_cxl_region_preserve(struct nvdimm_drvdata *ndd)
+{
+ struct nvdimm *nvdimm = to_nvdimm(ndd->dev);
+ struct cxl_pmem_region_params *p = &nvdimm->cxl_region_params;
+ struct nd_namespace_index *nsindex;
+ unsigned long *free;
+ u32 nslot, slot;
+
+ if (!preamble_current(ndd, &nsindex, &free, &nslot))
+ return 0; /* no label, nothing to preserve */
+
+ for_each_clear_bit_le(slot, free, nslot) {
+ union nd_lsa_label *lsa_label;
+ struct cxl_region_label *region_label;
+ uuid_t *region_uuid;
+
+ lsa_label = to_lsa_label(ndd, slot);
+ region_label = &lsa_label->region_label;
+ region_uuid = (uuid_t *) ®ion_label->type;
+
+ /* TODO: Currently preserving only one region */
+ if (uuid_equal(&cxl_region_uuid, region_uuid)) {
+ nvdimm->is_region_label = true;
+ import_uuid(&p->uuid, region_label->uuid);
+ p->flags = __le32_to_cpu(region_label->flags);
+ p->nlabel = __le16_to_cpu(region_label->nlabel);
+ p->position = __le16_to_cpu(region_label->position);
+ p->dpa = __le64_to_cpu(region_label->dpa);
+ p->rawsize = __le64_to_cpu(region_label->rawsize);
+ p->hpa = __le64_to_cpu(region_label->hpa);
+ p->slot = __le32_to_cpu(region_label->slot);
+ p->ig = __le32_to_cpu(region_label->ig);
+ p->align = __le32_to_cpu(region_label->align);
+ break;
+ }
+ }
+
+ return 0;
+}
+
int nd_label_data_init(struct nvdimm_drvdata *ndd)
{
size_t config_size, read_size, max_xfer, offset;
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index bfc6bfeb6e24..a73fac81531e 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -46,6 +46,8 @@ struct nvdimm {
} sec;
struct delayed_work dwork;
const struct nvdimm_fw_ops *fw_ops;
+ bool is_region_label;
+ struct cxl_pmem_region_params cxl_region_params;
};
static inline unsigned long nvdimm_security_flags(
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index b241a0b2e314..281d30dd9ba0 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -600,6 +600,7 @@ void nvdimm_set_locked(struct device *dev);
void nvdimm_clear_locked(struct device *dev);
int nvdimm_security_setup_events(struct device *dev);
bool nvdimm_region_label_supported(struct device *dev);
+int nvdimm_cxl_region_preserve(struct nvdimm_drvdata *ndd);
#if IS_ENABLED(CONFIG_NVDIMM_KEYS)
int nvdimm_security_unlock(struct device *dev);
#else
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index bbf14a260c93..07ea2e3f821a 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -108,6 +108,20 @@ struct nd_cmd_desc {
int out_sizes[ND_CMD_MAX_ELEM];
};
+struct cxl_pmem_region_params {
+ uuid_t uuid;
+ u32 flags;
+ u16 nlabel;
+ u16 position;
+ u64 dpa;
+ u64 rawsize;
+ u64 hpa;
+ u32 slot;
+ u32 ig;
+ u32 align;
+ int nr_targets;
+};
+
struct nd_interleave_set {
/* v1.1 definition of the interleave-set-cookie algorithm */
u64 cookie1;
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 09/17] nvdimm/label: Export routine to fetch region information
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (7 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 08/17] nvdimm/label: Preserve cxl region information from region label Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 20:18 ` Dave Jiang
2025-12-17 15:12 ` Jonathan Cameron
2025-11-19 7:52 ` [PATCH V4 10/17] cxl/mem: Refactor cxl pmem region auto-assembling Neeraj Kumar
` (7 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
CXL region information preserved from the LSA needs to be exported for
use by the CXL driver for CXL region re-creation.
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/dimm_devs.c | 18 ++++++++++++++++++
include/linux/libnvdimm.h | 2 ++
2 files changed, 20 insertions(+)
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index 3363a97cc5b5..1474b4e45fcc 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -280,6 +280,24 @@ void *nvdimm_provider_data(struct nvdimm *nvdimm)
}
EXPORT_SYMBOL_GPL(nvdimm_provider_data);
+bool nvdimm_has_cxl_region(struct nvdimm *nvdimm)
+{
+ if (!nvdimm)
+ return false;
+
+ return nvdimm->is_region_label;
+}
+EXPORT_SYMBOL_GPL(nvdimm_has_cxl_region);
+
+void *nvdimm_get_cxl_region_param(struct nvdimm *nvdimm)
+{
+ if (!nvdimm)
+ return NULL;
+
+ return &nvdimm->cxl_region_params;
+}
+EXPORT_SYMBOL_GPL(nvdimm_get_cxl_region_param);
+
static ssize_t commands_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 07ea2e3f821a..3ffd50ab6ac4 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -330,6 +330,8 @@ int nvdimm_in_overwrite(struct nvdimm *nvdimm);
bool is_nvdimm_sync(struct nd_region *nd_region);
int nd_region_label_update(struct nd_region *nd_region);
int nd_region_label_delete(struct nd_region *nd_region);
+bool nvdimm_has_cxl_region(struct nvdimm *nvdimm);
+void *nvdimm_get_cxl_region_param(struct nvdimm *nvdimm);
static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf,
unsigned int buf_len, int *cmd_rc)
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 10/17] cxl/mem: Refactor cxl pmem region auto-assembling
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (8 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 09/17] nvdimm/label: Export routine to fetch region information Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 20:44 ` Dave Jiang
2025-11-19 7:52 ` [PATCH V4 11/17] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation Neeraj Kumar
` (6 subsequent siblings)
16 siblings, 1 reply; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
In 84ec985944ef3, devm_cxl_add_nvdimm() sequence was changed and called
before devm_cxl_add_endpoint(). It's because cxl pmem region auto-assembly
used to get called at last in cxl_endpoint_port_probe(), which requires
cxl_nvd presence.
For cxl region persistency, region creation happens during nvdimm_probe
which need the completion of endpoint probe.
In order to accommodate both cxl pmem region auto-assembly and cxl region
persistency, refactored following
1. Re-Sequence devm_cxl_add_nvdimm() after devm_cxl_add_endpoint(). This
will be called only after successful completion of endpoint probe.
2. Create cxl_region_discovery() which performs pmem region
auto-assembly and remove cxl pmem region auto-assembly from
cxl_endpoint_port_probe()
3. Register cxl_region_discovery() with devm_cxl_add_memdev() which gets
called during cxl_pci_probe() in context of cxl_mem_probe()
4. As cxlmd->ops->probe() calls registered cxl_region_discovery(), so
move devm_cxl_add_nvdimm() before cxlmd->ops->probe(). It gurantees
both the completion of endpoint probe and cxl_nvd presence before
calling cxlmd->ops->probe().
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/core/region.c | 37 +++++++++++++++++++++++++++++++++++++
drivers/cxl/cxl.h | 5 +++++
drivers/cxl/mem.c | 18 +++++++++---------
drivers/cxl/pci.c | 4 +++-
drivers/cxl/port.c | 39 +--------------------------------------
5 files changed, 55 insertions(+), 48 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 2cf5b29cefd2..3c868c4de4ec 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3724,6 +3724,43 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
}
EXPORT_SYMBOL_NS_GPL(cxl_add_to_region, "CXL");
+static int discover_region(struct device *dev, void *unused)
+{
+ struct cxl_endpoint_decoder *cxled;
+ int rc;
+
+ if (!is_endpoint_decoder(dev))
+ return 0;
+
+ cxled = to_cxl_endpoint_decoder(dev);
+ if ((cxled->cxld.flags & CXL_DECODER_F_ENABLE) == 0)
+ return 0;
+
+ if (cxled->state != CXL_DECODER_STATE_AUTO)
+ return 0;
+
+ /*
+ * Region enumeration is opportunistic, if this add-event fails,
+ * continue to the next endpoint decoder.
+ */
+ rc = cxl_add_to_region(cxled);
+ if (rc)
+ dev_dbg(dev, "failed to add to region: %#llx-%#llx\n",
+ cxled->cxld.hpa_range.start, cxled->cxld.hpa_range.end);
+
+ return 0;
+}
+
+int cxl_region_discovery(struct cxl_memdev *cxlmd)
+{
+ struct cxl_port *port = cxlmd->endpoint;
+
+ device_for_each_child(&port->dev, NULL, discover_region);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_region_discovery, "CXL");
+
u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa)
{
struct cxl_region_ref *iter;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index ba17fa86d249..684a0d1b441a 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -904,6 +904,7 @@ struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
int cxl_add_to_region(struct cxl_endpoint_decoder *cxled);
struct cxl_dax_region *to_cxl_dax_region(struct device *dev);
u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa);
+int cxl_region_discovery(struct cxl_memdev *cxlmd);
#else
static inline bool is_cxl_pmem_region(struct device *dev)
{
@@ -926,6 +927,10 @@ static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint,
{
return 0;
}
+static inline int cxl_region_discovery(struct cxl_memdev *cxlmd)
+{
+ return 0;
+}
#endif
void cxl_endpoint_parse_cdat(struct cxl_port *port);
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 13d9e089ecaf..f5e3e2fca86c 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -115,15 +115,6 @@ static int cxl_mem_probe(struct device *dev)
return -ENXIO;
}
- if (cxl_pmem_size(cxlds) && IS_ENABLED(CONFIG_CXL_PMEM)) {
- rc = devm_cxl_add_nvdimm(parent_port, cxlmd);
- if (rc) {
- if (rc == -ENODEV)
- dev_info(dev, "PMEM disabled by platform\n");
- return rc;
- }
- }
-
if (dport->rch)
endpoint_parent = parent_port->uport_dev;
else
@@ -143,6 +134,15 @@ static int cxl_mem_probe(struct device *dev)
return rc;
}
+ if (cxl_pmem_size(cxlds) && IS_ENABLED(CONFIG_CXL_PMEM)) {
+ rc = devm_cxl_add_nvdimm(parent_port, cxlmd);
+ if (rc) {
+ if (rc == -ENODEV)
+ dev_info(dev, "PMEM disabled by platform\n");
+ return rc;
+ }
+ }
+
if (cxlmd->ops) {
rc = cxlmd->ops->probe(cxlmd);
if (rc)
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index e21051d79b25..d56fdfe4b43b 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -907,6 +907,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct cxl_memdev_state *mds;
struct cxl_dev_state *cxlds;
struct cxl_register_map map;
+ struct cxl_memdev_ops ops;
struct cxl_memdev *cxlmd;
int rc, pmu_count;
unsigned int i;
@@ -1006,7 +1007,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (rc)
dev_dbg(&pdev->dev, "No CXL Features discovered\n");
- cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds, NULL);
+ ops.probe = cxl_region_discovery;
+ cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds, &ops);
if (IS_ERR(cxlmd))
return PTR_ERR(cxlmd);
diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
index d5fd0c5ae49b..ad98b2881fed 100644
--- a/drivers/cxl/port.c
+++ b/drivers/cxl/port.c
@@ -31,33 +31,6 @@ static void schedule_detach(void *cxlmd)
schedule_cxl_memdev_detach(cxlmd);
}
-static int discover_region(struct device *dev, void *unused)
-{
- struct cxl_endpoint_decoder *cxled;
- int rc;
-
- if (!is_endpoint_decoder(dev))
- return 0;
-
- cxled = to_cxl_endpoint_decoder(dev);
- if ((cxled->cxld.flags & CXL_DECODER_F_ENABLE) == 0)
- return 0;
-
- if (cxled->state != CXL_DECODER_STATE_AUTO)
- return 0;
-
- /*
- * Region enumeration is opportunistic, if this add-event fails,
- * continue to the next endpoint decoder.
- */
- rc = cxl_add_to_region(cxled);
- if (rc)
- dev_dbg(dev, "failed to add to region: %#llx-%#llx\n",
- cxled->cxld.hpa_range.start, cxled->cxld.hpa_range.end);
-
- return 0;
-}
-
static int cxl_switch_port_probe(struct cxl_port *port)
{
/* Reset nr_dports for rebind of driver */
@@ -83,17 +56,7 @@ static int cxl_endpoint_port_probe(struct cxl_port *port)
if (rc)
return rc;
- rc = devm_cxl_endpoint_decoders_setup(port);
- if (rc)
- return rc;
-
- /*
- * Now that all endpoint decoders are successfully enumerated, try to
- * assemble regions from committed decoders
- */
- device_for_each_child(&port->dev, NULL, discover_region);
-
- return 0;
+ return devm_cxl_endpoint_decoders_setup(port);
}
static int cxl_port_probe(struct device *dev)
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 11/17] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (9 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 10/17] cxl/mem: Refactor cxl pmem region auto-assembling Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 21:33 ` Dave Jiang
2025-12-17 15:28 ` Jonathan Cameron
2025-11-19 7:52 ` [PATCH V4 12/17] cxl/pmem: Preserve region information into nd_set Neeraj Kumar
` (5 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
devm_cxl_pmem_add_region() is used to create cxl region based on region
information scanned from LSA.
devm_cxl_add_region() is used to just allocate cxlr and its fields are
filled later by userspace tool using device attributes (*_store()).
Inspiration for devm_cxl_pmem_add_region() is taken from these device
attributes (_store*) calls. It allocates cxlr and fills information
parsed from LSA and calls device_add(&cxlr->dev) to initiate further
region creation porbes
Rename __create_region() to cxl_create_region(), which will be used
in later patch to create cxl region after fetching region information
from LSA.
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/core/core.h | 12 ++++
drivers/cxl/core/region.c | 124 ++++++++++++++++++++++++++++++++++++--
2 files changed, 131 insertions(+), 5 deletions(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 1fb66132b777..fde96507cb75 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -42,6 +42,10 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port);
struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
u64 dpa);
+struct cxl_region *cxl_create_region(struct cxl_root_decoder *cxlrd,
+ enum cxl_partition_mode mode, int id,
+ struct cxl_pmem_region_params *params,
+ struct cxl_decoder *cxld);
#else
static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
@@ -71,6 +75,14 @@ static inline int cxl_region_init(void)
static inline void cxl_region_exit(void)
{
}
+static inline struct cxl_region *
+cxl_create_region(struct cxl_root_decoder *cxlrd,
+ enum cxl_partition_mode mode, int id,
+ struct cxl_pmem_region_params *params,
+ struct cxl_decoder *cxld)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
#define CXL_REGION_ATTR(x) NULL
#define CXL_REGION_TYPE(x) NULL
#define SET_CXL_REGION_ATTR(x)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 3c868c4de4ec..06a75f0a8e9b 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2618,6 +2618,114 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd,
return ERR_PTR(rc);
}
+static ssize_t alloc_region_hpa(struct cxl_region *cxlr, u64 size)
+{
+ int rc;
+
+ ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
+ if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
+ return rc;
+
+ if (!size)
+ return -EINVAL;
+
+ return alloc_hpa(cxlr, size);
+}
+
+static ssize_t alloc_region_dpa(struct cxl_endpoint_decoder *cxled, u64 size)
+{
+ int rc;
+
+ if (!size)
+ return -EINVAL;
+
+ if (!IS_ALIGNED(size, SZ_256M))
+ return -EINVAL;
+
+ rc = cxl_dpa_free(cxled);
+ if (rc)
+ return rc;
+
+ return cxl_dpa_alloc(cxled, size);
+}
+
+static struct cxl_region *
+devm_cxl_pmem_add_region(struct cxl_root_decoder *cxlrd, int id,
+ struct cxl_pmem_region_params *params,
+ struct cxl_decoder *cxld,
+ enum cxl_decoder_type type)
+{
+ struct cxl_endpoint_decoder *cxled;
+ struct cxl_region_params *p;
+ struct cxl_port *root_port;
+ struct device *dev;
+ int rc;
+
+ struct cxl_region *cxlr __free(put_cxl_region) =
+ cxl_region_alloc(cxlrd, id);
+ if (IS_ERR(cxlr))
+ return cxlr;
+
+ cxlr->mode = CXL_PARTMODE_PMEM;
+ cxlr->type = type;
+
+ dev = &cxlr->dev;
+ rc = dev_set_name(dev, "region%d", id);
+ if (rc)
+ return ERR_PTR(rc);
+
+ p = &cxlr->params;
+ p->uuid = params->uuid;
+ p->interleave_ways = params->nlabel;
+ p->interleave_granularity = params->ig;
+
+ rc = alloc_region_hpa(cxlr, params->rawsize);
+ if (rc)
+ return ERR_PTR(rc);
+
+ cxled = to_cxl_endpoint_decoder(&cxld->dev);
+
+ rc = cxl_dpa_set_part(cxled, CXL_PARTMODE_PMEM);
+ if (rc)
+ return ERR_PTR(rc);
+
+ rc = alloc_region_dpa(cxled, params->rawsize);
+ if (rc)
+ return ERR_PTR(rc);
+
+ /*
+ * TODO: Currently we have support of interleave_way == 1, where
+ * we can only have one region per mem device. It means mem device
+ * position (params->position) will always be 0. It is therefore
+ * attaching only one target at params->position
+ */
+ if (params->position)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ rc = attach_target(cxlr, cxled, params->position, TASK_INTERRUPTIBLE);
+ if (rc)
+ return ERR_PTR(rc);
+
+ rc = __commit(cxlr);
+ if (rc)
+ return ERR_PTR(rc);
+
+ rc = device_add(dev);
+ if (rc)
+ return ERR_PTR(rc);
+
+ root_port = to_cxl_port(cxlrd->cxlsd.cxld.dev.parent);
+ rc = devm_add_action_or_reset(root_port->uport_dev,
+ unregister_region, cxlr);
+ if (rc)
+ return ERR_PTR(rc);
+
+ dev_dbg(root_port->uport_dev, "%s: created %s\n",
+ dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev));
+
+ return no_free_ptr(cxlr);
+}
+
static ssize_t __create_region_show(struct cxl_root_decoder *cxlrd, char *buf)
{
return sysfs_emit(buf, "region%u\n", atomic_read(&cxlrd->region_id));
@@ -2635,8 +2743,10 @@ static ssize_t create_ram_region_show(struct device *dev,
return __create_region_show(to_cxl_root_decoder(dev), buf);
}
-static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd,
- enum cxl_partition_mode mode, int id)
+struct cxl_region *cxl_create_region(struct cxl_root_decoder *cxlrd,
+ enum cxl_partition_mode mode, int id,
+ struct cxl_pmem_region_params *pmem_params,
+ struct cxl_decoder *cxld)
{
int rc;
@@ -2658,6 +2768,9 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd,
return ERR_PTR(-EBUSY);
}
+ if (pmem_params)
+ return devm_cxl_pmem_add_region(cxlrd, id, pmem_params, cxld,
+ CXL_DECODER_HOSTONLYMEM);
return devm_cxl_add_region(cxlrd, id, mode, CXL_DECODER_HOSTONLYMEM);
}
@@ -2672,7 +2785,7 @@ static ssize_t create_region_store(struct device *dev, const char *buf,
if (rc != 1)
return -EINVAL;
- cxlr = __create_region(cxlrd, mode, id);
+ cxlr = cxl_create_region(cxlrd, mode, id, NULL, NULL);
if (IS_ERR(cxlr))
return PTR_ERR(cxlr);
@@ -3641,8 +3754,9 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
struct cxl_region *cxlr;
do {
- cxlr = __create_region(cxlrd, cxlds->part[part].mode,
- atomic_read(&cxlrd->region_id));
+ cxlr = cxl_create_region(cxlrd, cxlds->part[part].mode,
+ atomic_read(&cxlrd->region_id),
+ NULL, NULL);
} while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY);
if (IS_ERR(cxlr)) {
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 12/17] cxl/pmem: Preserve region information into nd_set
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (10 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 11/17] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 22:00 ` Dave Jiang
2025-11-19 7:52 ` [PATCH V4 13/17] cxl/pmem_region: Prep patch to accommodate pmem_region attributes Neeraj Kumar
` (4 subsequent siblings)
16 siblings, 1 reply; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Save region information stored in cxlr to nd_set during
cxl_pmem_region_probe in nd_set. This saved region information is being
stored into LSA, which will be used for cxl region persistence
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/pmem.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c
index e197883690ef..a6eba3572090 100644
--- a/drivers/cxl/pmem.c
+++ b/drivers/cxl/pmem.c
@@ -377,6 +377,7 @@ static int cxl_pmem_region_probe(struct device *dev)
struct nd_mapping_desc mappings[CXL_DECODER_MAX_INTERLEAVE];
struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
struct cxl_region *cxlr = cxlr_pmem->cxlr;
+ struct cxl_region_params *p = &cxlr->params;
struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
struct cxl_pmem_region_info *info = NULL;
struct nd_interleave_set *nd_set;
@@ -465,12 +466,12 @@ static int cxl_pmem_region_probe(struct device *dev)
ndr_desc.num_mappings = cxlr_pmem->nr_mappings;
ndr_desc.mapping = mappings;
- /*
- * TODO enable CXL labels which skip the need for 'interleave-set cookie'
- */
- nd_set->cookie1 =
- nd_fletcher64(info, sizeof(*info) * cxlr_pmem->nr_mappings, 0);
- nd_set->cookie2 = nd_set->cookie1;
+ nd_set->uuid = p->uuid;
+ nd_set->interleave_ways = p->interleave_ways;
+ nd_set->interleave_granularity = p->interleave_granularity;
+ nd_set->res = p->res;
+ nd_set->nr_targets = p->nr_targets;
+
ndr_desc.nd_set = nd_set;
cxlr_pmem->nd_region =
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 13/17] cxl/pmem_region: Prep patch to accommodate pmem_region attributes
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (11 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 12/17] cxl/pmem: Preserve region information into nd_set Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 22:08 ` Dave Jiang
2025-12-17 15:35 ` Jonathan Cameron
2025-11-19 7:52 ` [PATCH V4 14/17] cxl/pmem_region: Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c Neeraj Kumar
` (3 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
For region label update, need to create device attribute, which calls
nvdimm exported routine thus making pmem_region dependent on libnvdimm.
Because of this dependency of pmem region on libnvdimm, segregate pmem
region related code from core/region.c to core/pmem_region.c
This patch has no functionality change. Its just code movement from
core/region.c to core/pmem_region.c
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/core/Makefile | 2 +-
drivers/cxl/core/core.h | 10 ++
drivers/cxl/core/pmem_region.c | 202 +++++++++++++++++++++++++++++++++
drivers/cxl/core/region.c | 188 +-----------------------------
tools/testing/cxl/Kbuild | 2 +-
5 files changed, 215 insertions(+), 189 deletions(-)
create mode 100644 drivers/cxl/core/pmem_region.c
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index 5ad8fef210b5..fe0fcab6d730 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -16,7 +16,7 @@ cxl_core-y += pmu.o
cxl_core-y += cdat.o
cxl_core-y += ras.o
cxl_core-$(CONFIG_TRACING) += trace.o
-cxl_core-$(CONFIG_CXL_REGION) += region.o
+cxl_core-$(CONFIG_CXL_REGION) += region.o pmem_region.o
cxl_core-$(CONFIG_CXL_MCE) += mce.o
cxl_core-$(CONFIG_CXL_FEATURES) += features.o
cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index fde96507cb75..5ebbc3d3dde5 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -46,6 +46,8 @@ struct cxl_region *cxl_create_region(struct cxl_root_decoder *cxlrd,
enum cxl_partition_mode mode, int id,
struct cxl_pmem_region_params *params,
struct cxl_decoder *cxld);
+struct cxl_region *to_cxl_region(struct device *dev);
+int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
#else
static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
@@ -83,6 +85,14 @@ cxl_create_region(struct cxl_root_decoder *cxlrd,
{
return ERR_PTR(-EOPNOTSUPP);
}
+static inline struct cxl_region *to_cxl_region(struct device *dev)
+{
+ return NULL;
+}
+static inline int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
+{
+ return 0;
+}
#define CXL_REGION_ATTR(x) NULL
#define CXL_REGION_TYPE(x) NULL
#define SET_CXL_REGION_ATTR(x)
diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
new file mode 100644
index 000000000000..b45e60f04ff4
--- /dev/null
+++ b/drivers/cxl/core/pmem_region.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2020 Intel Corporation. */
+#include <linux/device.h>
+#include <linux/memregion.h>
+#include <cxlmem.h>
+#include <cxl.h>
+#include "core.h"
+
+/**
+ * DOC: cxl pmem region
+ *
+ * The core CXL PMEM region infrastructure supports persistent memory
+ * region creation using LIBNVDIMM subsystem. It has dependency on
+ * LIBNVDIMM, pmem region need updation of cxl region information into
+ * LSA. LIBNVDIMM dependency is only for pmem region, it is therefore
+ * need this separate file.
+ */
+
+static void cxl_pmem_region_release(struct device *dev)
+{
+ struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
+ int i;
+
+ for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
+ struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd;
+
+ put_device(&cxlmd->dev);
+ }
+
+ kfree(cxlr_pmem);
+}
+
+static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
+ &cxl_base_attribute_group,
+ NULL,
+};
+
+const struct device_type cxl_pmem_region_type = {
+ .name = "cxl_pmem_region",
+ .release = cxl_pmem_region_release,
+ .groups = cxl_pmem_region_attribute_groups,
+};
+
+bool is_cxl_pmem_region(struct device *dev)
+{
+ return dev->type == &cxl_pmem_region_type;
+}
+EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL");
+
+struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
+{
+ if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev),
+ "not a cxl_pmem_region device\n"))
+ return NULL;
+ return container_of(dev, struct cxl_pmem_region, dev);
+}
+EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL");
+
+static struct lock_class_key cxl_pmem_region_key;
+
+static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
+{
+ struct cxl_region_params *p = &cxlr->params;
+ struct cxl_nvdimm_bridge *cxl_nvb;
+ struct device *dev;
+ int i;
+
+ guard(rwsem_read)(&cxl_rwsem.region);
+ if (p->state != CXL_CONFIG_COMMIT)
+ return -ENXIO;
+
+ struct cxl_pmem_region *cxlr_pmem __free(kfree) =
+ kzalloc(struct_size(cxlr_pmem, mapping, p->nr_targets),
+ GFP_KERNEL);
+ if (!cxlr_pmem)
+ return -ENOMEM;
+
+ cxlr_pmem->hpa_range.start = p->res->start;
+ cxlr_pmem->hpa_range.end = p->res->end;
+
+ /* Snapshot the region configuration underneath the cxl_region_rwsem */
+ cxlr_pmem->nr_mappings = p->nr_targets;
+ for (i = 0; i < p->nr_targets; i++) {
+ struct cxl_endpoint_decoder *cxled = p->targets[i];
+ struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
+ struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i];
+
+ /*
+ * Regions never span CXL root devices, so by definition the
+ * bridge for one device is the same for all.
+ */
+ if (i == 0) {
+ cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint);
+ if (!cxl_nvb)
+ return -ENODEV;
+ cxlr->cxl_nvb = cxl_nvb;
+ }
+ m->cxlmd = cxlmd;
+ get_device(&cxlmd->dev);
+ m->start = cxled->dpa_res->start;
+ m->size = resource_size(cxled->dpa_res);
+ m->position = i;
+ }
+
+ dev = &cxlr_pmem->dev;
+ device_initialize(dev);
+ lockdep_set_class(&dev->mutex, &cxl_pmem_region_key);
+ device_set_pm_not_required(dev);
+ dev->parent = &cxlr->dev;
+ dev->bus = &cxl_bus_type;
+ dev->type = &cxl_pmem_region_type;
+ cxlr_pmem->cxlr = cxlr;
+ cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem);
+
+ return 0;
+}
+
+static void cxlr_pmem_unregister(void *_cxlr_pmem)
+{
+ struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem;
+ struct cxl_region *cxlr = cxlr_pmem->cxlr;
+ struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
+
+ /*
+ * Either the bridge is in ->remove() context under the device_lock(),
+ * or cxlr_release_nvdimm() is cancelling the bridge's release action
+ * for @cxlr_pmem and doing it itself (while manually holding the bridge
+ * lock).
+ */
+ device_lock_assert(&cxl_nvb->dev);
+ cxlr->cxlr_pmem = NULL;
+ cxlr_pmem->cxlr = NULL;
+ device_unregister(&cxlr_pmem->dev);
+}
+
+static void cxlr_release_nvdimm(void *_cxlr)
+{
+ struct cxl_region *cxlr = _cxlr;
+ struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
+
+ scoped_guard(device, &cxl_nvb->dev) {
+ if (cxlr->cxlr_pmem)
+ devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister,
+ cxlr->cxlr_pmem);
+ }
+ cxlr->cxl_nvb = NULL;
+ put_device(&cxl_nvb->dev);
+}
+
+/**
+ * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge
+ * @cxlr: parent CXL region for this pmem region bridge device
+ *
+ * Return: 0 on success negative error code on failure.
+ */
+int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
+{
+ struct cxl_pmem_region *cxlr_pmem;
+ struct cxl_nvdimm_bridge *cxl_nvb;
+ struct device *dev;
+ int rc;
+
+ rc = cxl_pmem_region_alloc(cxlr);
+ if (rc)
+ return rc;
+ cxlr_pmem = cxlr->cxlr_pmem;
+ cxl_nvb = cxlr->cxl_nvb;
+
+ dev = &cxlr_pmem->dev;
+ rc = dev_set_name(dev, "pmem_region%d", cxlr->id);
+ if (rc)
+ goto err;
+
+ rc = device_add(dev);
+ if (rc)
+ goto err;
+
+ dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
+ dev_name(dev));
+
+ scoped_guard(device, &cxl_nvb->dev) {
+ if (cxl_nvb->dev.driver)
+ rc = devm_add_action_or_reset(&cxl_nvb->dev,
+ cxlr_pmem_unregister,
+ cxlr_pmem);
+ else
+ rc = -ENXIO;
+ }
+
+ if (rc)
+ goto err_bridge;
+
+ /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */
+ return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr);
+
+err:
+ put_device(dev);
+err_bridge:
+ put_device(&cxl_nvb->dev);
+ cxlr->cxl_nvb = NULL;
+ return rc;
+}
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 06a75f0a8e9b..9798120b208e 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -38,8 +38,6 @@
*/
static nodemask_t nodemask_region_seen = NODE_MASK_NONE;
-static struct cxl_region *to_cxl_region(struct device *dev);
-
#define __ACCESS_ATTR_RO(_level, _name) { \
.attr = { .name = __stringify(_name), .mode = 0444 }, \
.show = _name##_access##_level##_show, \
@@ -2426,7 +2424,7 @@ bool is_cxl_region(struct device *dev)
}
EXPORT_SYMBOL_NS_GPL(is_cxl_region, "CXL");
-static struct cxl_region *to_cxl_region(struct device *dev)
+struct cxl_region *to_cxl_region(struct device *dev)
{
if (dev_WARN_ONCE(dev, dev->type != &cxl_region_type,
"not a cxl_region device\n"))
@@ -2856,46 +2854,6 @@ static ssize_t delete_region_store(struct device *dev,
}
DEVICE_ATTR_WO(delete_region);
-static void cxl_pmem_region_release(struct device *dev)
-{
- struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
- int i;
-
- for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
- struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd;
-
- put_device(&cxlmd->dev);
- }
-
- kfree(cxlr_pmem);
-}
-
-static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
- &cxl_base_attribute_group,
- NULL,
-};
-
-const struct device_type cxl_pmem_region_type = {
- .name = "cxl_pmem_region",
- .release = cxl_pmem_region_release,
- .groups = cxl_pmem_region_attribute_groups,
-};
-
-bool is_cxl_pmem_region(struct device *dev)
-{
- return dev->type == &cxl_pmem_region_type;
-}
-EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL");
-
-struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
-{
- if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev),
- "not a cxl_pmem_region device\n"))
- return NULL;
- return container_of(dev, struct cxl_pmem_region, dev);
-}
-EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL");
-
struct cxl_poison_context {
struct cxl_port *port;
int part;
@@ -3327,64 +3285,6 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
return -ENXIO;
}
-static struct lock_class_key cxl_pmem_region_key;
-
-static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
-{
- struct cxl_region_params *p = &cxlr->params;
- struct cxl_nvdimm_bridge *cxl_nvb;
- struct device *dev;
- int i;
-
- guard(rwsem_read)(&cxl_rwsem.region);
- if (p->state != CXL_CONFIG_COMMIT)
- return -ENXIO;
-
- struct cxl_pmem_region *cxlr_pmem __free(kfree) =
- kzalloc(struct_size(cxlr_pmem, mapping, p->nr_targets), GFP_KERNEL);
- if (!cxlr_pmem)
- return -ENOMEM;
-
- cxlr_pmem->hpa_range.start = p->res->start;
- cxlr_pmem->hpa_range.end = p->res->end;
-
- /* Snapshot the region configuration underneath the cxl_rwsem.region */
- cxlr_pmem->nr_mappings = p->nr_targets;
- for (i = 0; i < p->nr_targets; i++) {
- struct cxl_endpoint_decoder *cxled = p->targets[i];
- struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
- struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i];
-
- /*
- * Regions never span CXL root devices, so by definition the
- * bridge for one device is the same for all.
- */
- if (i == 0) {
- cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint);
- if (!cxl_nvb)
- return -ENODEV;
- cxlr->cxl_nvb = cxl_nvb;
- }
- m->cxlmd = cxlmd;
- get_device(&cxlmd->dev);
- m->start = cxled->dpa_res->start;
- m->size = resource_size(cxled->dpa_res);
- m->position = i;
- }
-
- dev = &cxlr_pmem->dev;
- device_initialize(dev);
- lockdep_set_class(&dev->mutex, &cxl_pmem_region_key);
- device_set_pm_not_required(dev);
- dev->parent = &cxlr->dev;
- dev->bus = &cxl_bus_type;
- dev->type = &cxl_pmem_region_type;
- cxlr_pmem->cxlr = cxlr;
- cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem);
-
- return 0;
-}
-
static void cxl_dax_region_release(struct device *dev)
{
struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev);
@@ -3448,92 +3348,6 @@ static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
return cxlr_dax;
}
-static void cxlr_pmem_unregister(void *_cxlr_pmem)
-{
- struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem;
- struct cxl_region *cxlr = cxlr_pmem->cxlr;
- struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
-
- /*
- * Either the bridge is in ->remove() context under the device_lock(),
- * or cxlr_release_nvdimm() is cancelling the bridge's release action
- * for @cxlr_pmem and doing it itself (while manually holding the bridge
- * lock).
- */
- device_lock_assert(&cxl_nvb->dev);
- cxlr->cxlr_pmem = NULL;
- cxlr_pmem->cxlr = NULL;
- device_unregister(&cxlr_pmem->dev);
-}
-
-static void cxlr_release_nvdimm(void *_cxlr)
-{
- struct cxl_region *cxlr = _cxlr;
- struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
-
- scoped_guard(device, &cxl_nvb->dev) {
- if (cxlr->cxlr_pmem)
- devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister,
- cxlr->cxlr_pmem);
- }
- cxlr->cxl_nvb = NULL;
- put_device(&cxl_nvb->dev);
-}
-
-/**
- * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge
- * @cxlr: parent CXL region for this pmem region bridge device
- *
- * Return: 0 on success negative error code on failure.
- */
-static int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
-{
- struct cxl_pmem_region *cxlr_pmem;
- struct cxl_nvdimm_bridge *cxl_nvb;
- struct device *dev;
- int rc;
-
- rc = cxl_pmem_region_alloc(cxlr);
- if (rc)
- return rc;
- cxlr_pmem = cxlr->cxlr_pmem;
- cxl_nvb = cxlr->cxl_nvb;
-
- dev = &cxlr_pmem->dev;
- rc = dev_set_name(dev, "pmem_region%d", cxlr->id);
- if (rc)
- goto err;
-
- rc = device_add(dev);
- if (rc)
- goto err;
-
- dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
- dev_name(dev));
-
- scoped_guard(device, &cxl_nvb->dev) {
- if (cxl_nvb->dev.driver)
- rc = devm_add_action_or_reset(&cxl_nvb->dev,
- cxlr_pmem_unregister,
- cxlr_pmem);
- else
- rc = -ENXIO;
- }
-
- if (rc)
- goto err_bridge;
-
- /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */
- return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr);
-
-err:
- put_device(dev);
-err_bridge:
- put_device(&cxl_nvb->dev);
- cxlr->cxl_nvb = NULL;
- return rc;
-}
-
static void cxlr_dax_unregister(void *_cxlr_dax)
{
struct cxl_dax_region *cxlr_dax = _cxlr_dax;
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index 0e151d0572d1..ad2496b38fdd 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -59,7 +59,7 @@ cxl_core-y += $(CXL_CORE_SRC)/pmu.o
cxl_core-y += $(CXL_CORE_SRC)/cdat.o
cxl_core-y += $(CXL_CORE_SRC)/ras.o
cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
-cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
+cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/pmem_region.o
cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o
cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 14/17] cxl/pmem_region: Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (12 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 13/17] cxl/pmem_region: Prep patch to accommodate pmem_region attributes Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 22:24 ` Dave Jiang
2025-12-17 15:38 ` Jonathan Cameron
2025-11-19 7:52 ` [PATCH V4 15/17] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion Neeraj Kumar
` (2 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
As pmem region label update/delete has hard dependency on libnvdimm.
It is therefore put core/pmem_region.c under CONFIG_CXL_PMEM_REGION
control. It handles the dependency by selecting CONFIG_LIBNVDIMM
if not enabled.
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/Kconfig | 15 +++++++++++++++
drivers/cxl/core/Makefile | 3 ++-
drivers/cxl/core/core.h | 17 +++++++++++------
drivers/cxl/core/region.c | 2 ++
drivers/cxl/cxl.h | 24 ++++++++++++++----------
tools/testing/cxl/Kbuild | 3 ++-
6 files changed, 46 insertions(+), 18 deletions(-)
diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
index f1361ed6a0d4..307fed8f1f56 100644
--- a/drivers/cxl/Kconfig
+++ b/drivers/cxl/Kconfig
@@ -211,6 +211,21 @@ config CXL_REGION
If unsure say 'y'
+config CXL_PMEM_REGION
+ bool "CXL: Pmem Region Support"
+ default CXL_BUS
+ depends on CXL_REGION
+ depends on ARCH_HAS_PMEM_API
+ depends on PHYS_ADDR_T_64BIT
+ depends on BLK_DEV
+ select LIBNVDIMM
+ help
+ Enable the CXL core to enumerate and provision CXL pmem regions.
+ A CXL pmem region need to update region label into LSA. For LSA
+ update/delete libnvdimm is required.
+
+ If unsure say 'y'
+
config CXL_REGION_INVALIDATION_TEST
bool "CXL: Region Cache Management Bypass (TEST)"
depends on CXL_REGION
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index fe0fcab6d730..399157beb917 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -16,7 +16,8 @@ cxl_core-y += pmu.o
cxl_core-y += cdat.o
cxl_core-y += ras.o
cxl_core-$(CONFIG_TRACING) += trace.o
-cxl_core-$(CONFIG_CXL_REGION) += region.o pmem_region.o
+cxl_core-$(CONFIG_CXL_REGION) += region.o
+cxl_core-$(CONFIG_CXL_PMEM_REGION) += pmem_region.o
cxl_core-$(CONFIG_CXL_MCE) += mce.o
cxl_core-$(CONFIG_CXL_FEATURES) += features.o
cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 5ebbc3d3dde5..beeb9b7527b8 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -34,7 +34,6 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
#define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
#define CXL_REGION_TYPE(x) (&cxl_region_type)
#define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
-#define CXL_PMEM_REGION_TYPE(x) (&cxl_pmem_region_type)
#define CXL_DAX_REGION_TYPE(x) (&cxl_dax_region_type)
int cxl_region_init(void);
void cxl_region_exit(void);
@@ -89,17 +88,23 @@ static inline struct cxl_region *to_cxl_region(struct device *dev)
{
return NULL;
}
-static inline int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
-{
- return 0;
-}
#define CXL_REGION_ATTR(x) NULL
#define CXL_REGION_TYPE(x) NULL
#define SET_CXL_REGION_ATTR(x)
-#define CXL_PMEM_REGION_TYPE(x) NULL
#define CXL_DAX_REGION_TYPE(x) NULL
#endif
+#ifdef CONFIG_CXL_PMEM_REGION
+#define CXL_PMEM_REGION_TYPE(x) (&cxl_pmem_region_type)
+int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
+#else
+#define CXL_PMEM_REGION_TYPE(x) NULL
+static inline int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
+{
+ return 0;
+}
+#endif
+
struct cxl_send_command;
struct cxl_mem_query_commands;
int cxl_query_cmd(struct cxl_mailbox *cxl_mbox,
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 9798120b208e..408e139718f1 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3918,6 +3918,8 @@ static int cxl_region_probe(struct device *dev)
dev_dbg(&cxlr->dev, "CXL EDAC registration for region_id=%d failed\n",
cxlr->id);
+ if (!IS_ENABLED(CONFIG_CXL_PMEM_REGION))
+ return -EINVAL;
return devm_cxl_add_pmem_region(cxlr);
case CXL_PARTMODE_RAM:
rc = devm_cxl_region_edac_register(cxlr);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 684a0d1b441a..6ac3b40cb5ff 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -899,21 +899,11 @@ int devm_cxl_add_nvdimm(struct cxl_port *parent_port, struct cxl_memdev *cxlmd);
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port);
#ifdef CONFIG_CXL_REGION
-bool is_cxl_pmem_region(struct device *dev);
-struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
int cxl_add_to_region(struct cxl_endpoint_decoder *cxled);
struct cxl_dax_region *to_cxl_dax_region(struct device *dev);
u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa);
int cxl_region_discovery(struct cxl_memdev *cxlmd);
#else
-static inline bool is_cxl_pmem_region(struct device *dev)
-{
- return false;
-}
-static inline struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
-{
- return NULL;
-}
static inline int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
{
return 0;
@@ -933,6 +923,20 @@ static inline int cxl_region_discovery(struct cxl_memdev *cxlmd)
}
#endif
+#ifdef CONFIG_CXL_PMEM_REGION
+bool is_cxl_pmem_region(struct device *dev);
+struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
+#else
+static inline bool is_cxl_pmem_region(struct device *dev)
+{
+ return false;
+}
+static inline struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
void cxl_endpoint_parse_cdat(struct cxl_port *port);
void cxl_switch_parse_cdat(struct cxl_dport *dport);
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index ad2496b38fdd..024922326a6b 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -59,7 +59,8 @@ cxl_core-y += $(CXL_CORE_SRC)/pmu.o
cxl_core-y += $(CXL_CORE_SRC)/cdat.o
cxl_core-y += $(CXL_CORE_SRC)/ras.o
cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
-cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/pmem_region.o
+cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
+cxl_core-$(CONFIG_CXL_PMEM_REGION) += $(CXL_CORE_SRC)/pmem_region.o
cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o
cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 15/17] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (13 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 14/17] cxl/pmem_region: Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 23:10 ` Dave Jiang
2025-12-17 15:40 ` Jonathan Cameron
2025-11-19 7:52 ` [PATCH V4 16/17] cxl/pmem_region: Create pmem region using information parsed from LSA Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 17/17] cxl/pmem: Add CXL LSA 2.1 support in cxl pmem Neeraj Kumar
16 siblings, 2 replies; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Using these attributes region label is added/deleted into LSA. These
attributes are called from userspace (ndctl) after region creation.
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
Documentation/ABI/testing/sysfs-bus-cxl | 22 ++++++
drivers/cxl/core/pmem_region.c | 93 ++++++++++++++++++++++++-
drivers/cxl/cxl.h | 7 ++
3 files changed, 121 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl
index c80a1b5a03db..76d79c03dde4 100644
--- a/Documentation/ABI/testing/sysfs-bus-cxl
+++ b/Documentation/ABI/testing/sysfs-bus-cxl
@@ -624,3 +624,25 @@ Description:
The count is persistent across power loss and wraps back to 0
upon overflow. If this file is not present, the device does not
have the necessary support for dirty tracking.
+
+
+What: /sys/bus/cxl/devices/regionZ/pmem_regionZ/region_label_update
+Date: Nov, 2025
+KernelVersion: v6.19
+Contact: linux-cxl@vger.kernel.org
+Description:
+ (RW) Write a boolean 'true' string value to this attribute to
+ update cxl region information into LSA as region label. It
+ uses nvdimm nd_region_label_update() to update cxl region
+ information saved during cxl region creation into LSA. This
+ attribute must be called at last during cxl region creation.
+
+
+What: /sys/bus/cxl/devices/regionZ/pmem_regionZ/region_label_delete
+Date: Nov, 2025
+KernelVersion: v6.19
+Contact: linux-cxl@vger.kernel.org
+Description:
+ (WO) When a boolean 'true' is written to this attribute then
+ pmem_region driver deletes cxl region label from LSA using
+ nvdimm nd_region_label_delete()
diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
index b45e60f04ff4..be4feb73aafc 100644
--- a/drivers/cxl/core/pmem_region.c
+++ b/drivers/cxl/core/pmem_region.c
@@ -30,9 +30,100 @@ static void cxl_pmem_region_release(struct device *dev)
kfree(cxlr_pmem);
}
+static ssize_t region_label_update_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
+ struct cxl_region *cxlr = cxlr_pmem->cxlr;
+ ssize_t rc;
+ bool update;
+
+ rc = kstrtobool(buf, &update);
+ if (rc)
+ return rc;
+
+ ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
+ rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem);
+ if (rc)
+ return rc;
+
+ /* Region not yet committed */
+ if (update && cxlr && cxlr->params.state != CXL_CONFIG_COMMIT) {
+ dev_dbg(dev, "region not committed, can't update into LSA\n");
+ return -ENXIO;
+ }
+
+ if (!cxlr || !cxlr->cxlr_pmem || !cxlr->cxlr_pmem->nd_region)
+ return 0;
+
+ rc = nd_region_label_update(cxlr->cxlr_pmem->nd_region);
+ if (rc)
+ return rc;
+
+ cxlr->params.state_region_label = CXL_REGION_LABEL_ACTIVE;
+
+ return len;
+}
+
+static ssize_t region_label_update_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
+ struct cxl_region *cxlr = cxlr_pmem->cxlr;
+ struct cxl_region_params *p = &cxlr->params;
+ ssize_t rc;
+
+ ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+ rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem);
+ if (rc)
+ return rc;
+
+ return sysfs_emit(buf, "%d\n", p->state_region_label);
+}
+static DEVICE_ATTR_RW(region_label_update);
+
+static ssize_t region_label_delete_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
+ struct cxl_region *cxlr = cxlr_pmem->cxlr;
+ ssize_t rc;
+
+ ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
+ rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem);
+ if (rc)
+ return rc;
+
+ if (!cxlr && !cxlr->cxlr_pmem && !cxlr->cxlr_pmem->nd_region)
+ return 0;
+
+ rc = nd_region_label_delete(cxlr->cxlr_pmem->nd_region);
+ if (rc)
+ return rc;
+
+ cxlr->params.state_region_label = CXL_REGION_LABEL_INACTIVE;
+
+ return len;
+}
+static DEVICE_ATTR_WO(region_label_delete);
+
+static struct attribute *cxl_pmem_region_attrs[] = {
+ &dev_attr_region_label_update.attr,
+ &dev_attr_region_label_delete.attr,
+ NULL
+};
+
+static struct attribute_group cxl_pmem_region_group = {
+ .attrs = cxl_pmem_region_attrs,
+};
+
static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
&cxl_base_attribute_group,
- NULL,
+ &cxl_pmem_region_group,
+ NULL
};
const struct device_type cxl_pmem_region_type = {
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 6ac3b40cb5ff..8c76c4a981bf 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -473,9 +473,15 @@ enum cxl_config_state {
CXL_CONFIG_COMMIT,
};
+enum region_label_state {
+ CXL_REGION_LABEL_INACTIVE,
+ CXL_REGION_LABEL_ACTIVE,
+};
+
/**
* struct cxl_region_params - region settings
* @state: allow the driver to lockdown further parameter changes
+ * @state: region label state
* @uuid: unique id for persistent regions
* @interleave_ways: number of endpoints in the region
* @interleave_granularity: capacity each endpoint contributes to a stripe
@@ -488,6 +494,7 @@ enum cxl_config_state {
*/
struct cxl_region_params {
enum cxl_config_state state;
+ enum region_label_state state_region_label;
uuid_t uuid;
int interleave_ways;
int interleave_granularity;
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 16/17] cxl/pmem_region: Create pmem region using information parsed from LSA
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (14 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 15/17] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 23:37 ` Dave Jiang
2025-11-19 7:52 ` [PATCH V4 17/17] cxl/pmem: Add CXL LSA 2.1 support in cxl pmem Neeraj Kumar
16 siblings, 1 reply; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
create_pmem_region() creates cxl region based on region information
parsed from LSA. This routine required cxl root decoder and endpoint
decoder. Therefore added cxl_find_root_decoder_by_port() and
cxl_find_free_ep_decoder(). These routines find cxl root decoder and
free endpoint decoder on cxl bus using cxl port
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/core/core.h | 4 ++
drivers/cxl/core/pmem_region.c | 97 ++++++++++++++++++++++++++++++++++
drivers/cxl/core/region.c | 13 +++--
drivers/cxl/cxl.h | 5 ++
4 files changed, 115 insertions(+), 4 deletions(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index beeb9b7527b8..dd2efd3deb5e 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -35,6 +35,7 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
#define CXL_REGION_TYPE(x) (&cxl_region_type)
#define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
#define CXL_DAX_REGION_TYPE(x) (&cxl_dax_region_type)
+int verify_free_decoder(struct device *dev);
int cxl_region_init(void);
void cxl_region_exit(void);
int cxl_get_poison_by_endpoint(struct cxl_port *port);
@@ -88,6 +89,9 @@ static inline struct cxl_region *to_cxl_region(struct device *dev)
{
return NULL;
}
+static inline int verify_free_decoder(struct device *dev)
+{
+}
#define CXL_REGION_ATTR(x) NULL
#define CXL_REGION_TYPE(x) NULL
#define SET_CXL_REGION_ATTR(x)
diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
index be4feb73aafc..06665937c180 100644
--- a/drivers/cxl/core/pmem_region.c
+++ b/drivers/cxl/core/pmem_region.c
@@ -291,3 +291,100 @@ int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
cxlr->cxl_nvb = NULL;
return rc;
}
+
+static int match_root_decoder(struct device *dev, const void *data)
+{
+ return is_root_decoder(dev);
+}
+
+/**
+ * cxl_find_root_decoder_by_port() - find a cxl root decoder on cxl bus
+ * @port: any descendant port in CXL port topology
+ *
+ * Caller of this function must call put_device() when done as a device ref
+ * is taken via device_find_child()
+ */
+static struct cxl_root_decoder *cxl_find_root_decoder_by_port(struct cxl_port *port)
+{
+ struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port);
+ struct device *dev;
+
+ if (!cxl_root)
+ return NULL;
+
+ dev = device_find_child(&cxl_root->port.dev, NULL, match_root_decoder);
+ if (!dev)
+ return NULL;
+
+ return to_cxl_root_decoder(dev);
+}
+
+static int match_free_ep_decoder(struct device *dev, const void *data)
+{
+ if (!is_endpoint_decoder(dev))
+ return 0;
+
+ return verify_free_decoder(dev);
+}
+
+/**
+ * cxl_find_endpoint_decoder_by_port() - find a cxl root decoder on cxl bus
+ * @port: any descendant port in CXL port topology
+ *
+ * Caller of this function must call put_device() when done as a device ref
+ * is taken via device_find_child()
+ */
+static struct cxl_decoder *cxl_find_free_ep_decoder(struct cxl_port *port)
+{
+ struct device *dev;
+
+ dev = device_find_child(&port->dev, NULL, match_free_ep_decoder);
+ if (!dev)
+ return NULL;
+
+ return to_cxl_decoder(dev);
+}
+
+void create_pmem_region(struct nvdimm *nvdimm)
+{
+ struct cxl_nvdimm *cxl_nvd;
+ struct cxl_memdev *cxlmd;
+ struct cxl_pmem_region_params *params;
+ struct cxl_region *cxlr;
+
+ if (!nvdimm_has_cxl_region(nvdimm))
+ return;
+
+ lockdep_assert_held(&cxl_rwsem.region);
+ cxl_nvd = nvdimm_provider_data(nvdimm);
+ params = nvdimm_get_cxl_region_param(nvdimm);
+ cxlmd = cxl_nvd->cxlmd;
+
+ /* TODO: Region creation support only for interleave way == 1 */
+ if (!(params->nlabel == 1)) {
+ dev_dbg(&cxlmd->dev,
+ "Region Creation is not supported with iw > 1\n");
+ return;
+ }
+
+ struct cxl_root_decoder *cxlrd __free(put_cxl_root_decoder) =
+ cxl_find_root_decoder_by_port(cxlmd->endpoint);
+ if (!cxlrd) {
+ dev_err(&cxlmd->dev, "CXL root decoder not found\n");
+ return;
+ }
+
+ struct cxl_decoder *cxld __free(put_cxl_decoder) =
+ cxl_find_free_ep_decoder(cxlmd->endpoint);
+ if (!cxlrd) {
+ dev_err(&cxlmd->dev, "CXL endpoint decoder not found\n");
+ return;
+ }
+
+ cxlr = cxl_create_region(cxlrd, CXL_PARTMODE_PMEM,
+ atomic_read(&cxlrd->region_id),
+ params, cxld);
+ if (IS_ERR(cxlr))
+ dev_warn(&cxlmd->dev, "Region Creation failed\n");
+}
+EXPORT_SYMBOL_NS_GPL(create_pmem_region, "CXL");
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 408e139718f1..96f3cf4143b8 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -835,15 +835,12 @@ static int check_commit_order(struct device *dev, void *data)
return 0;
}
-static int match_free_decoder(struct device *dev, const void *data)
+int verify_free_decoder(struct device *dev)
{
struct cxl_port *port = to_cxl_port(dev->parent);
struct cxl_decoder *cxld;
int rc;
- if (!is_switch_decoder(dev))
- return 0;
-
cxld = to_cxl_decoder(dev);
if (cxld->id != port->commit_end + 1)
@@ -867,6 +864,14 @@ static int match_free_decoder(struct device *dev, const void *data)
return 1;
}
+static int match_free_decoder(struct device *dev, const void *data)
+{
+ if (!is_switch_decoder(dev))
+ return 0;
+
+ return verify_free_decoder(dev);
+}
+
static bool spa_maps_hpa(const struct cxl_region_params *p,
const struct range *range)
{
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 8c76c4a981bf..088841a3e238 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -792,6 +792,7 @@ struct cxl_root *find_cxl_root(struct cxl_port *port);
DEFINE_FREE(put_cxl_root, struct cxl_root *, if (_T) put_device(&_T->port.dev))
DEFINE_FREE(put_cxl_port, struct cxl_port *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))
DEFINE_FREE(put_cxl_root_decoder, struct cxl_root_decoder *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->cxlsd.cxld.dev))
+DEFINE_FREE(put_cxl_decoder, struct cxl_decoder *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))
DEFINE_FREE(put_cxl_region, struct cxl_region *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))
int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd);
@@ -933,6 +934,7 @@ static inline int cxl_region_discovery(struct cxl_memdev *cxlmd)
#ifdef CONFIG_CXL_PMEM_REGION
bool is_cxl_pmem_region(struct device *dev);
struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
+void create_pmem_region(struct nvdimm *nvdimm);
#else
static inline bool is_cxl_pmem_region(struct device *dev)
{
@@ -942,6 +944,9 @@ static inline struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
{
return NULL;
}
+static inline void create_pmem_region(struct nvdimm *nvdimm)
+{
+}
#endif
void cxl_endpoint_parse_cdat(struct cxl_port *port);
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V4 17/17] cxl/pmem: Add CXL LSA 2.1 support in cxl pmem
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (15 preceding siblings ...)
2025-11-19 7:52 ` [PATCH V4 16/17] cxl/pmem_region: Create pmem region using information parsed from LSA Neeraj Kumar
@ 2025-11-19 7:52 ` Neeraj Kumar
2025-11-19 23:37 ` Dave Jiang
16 siblings, 1 reply; 58+ messages in thread
From: Neeraj Kumar @ 2025-11-19 7:52 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Add support of CXL LSA 2.1 using NDD_REGION_LABELING flag. It creates
cxl region based on region information parsed from LSA
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/pmem.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c
index a6eba3572090..5970d1792be8 100644
--- a/drivers/cxl/pmem.c
+++ b/drivers/cxl/pmem.c
@@ -135,6 +135,7 @@ static int cxl_nvdimm_probe(struct device *dev)
return rc;
set_bit(NDD_LABELING, &flags);
+ set_bit(NDD_REGION_LABELING, &flags);
set_bit(NDD_REGISTER_SYNC, &flags);
set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
@@ -155,6 +156,7 @@ static int cxl_nvdimm_probe(struct device *dev)
return -ENOMEM;
dev_set_drvdata(dev, nvdimm);
+ create_pmem_region(nvdimm);
return devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 58+ messages in thread
* Re: [PATCH V4 03/17] nvdimm/label: Add namespace/region label support as per LSA 2.1
2025-11-19 7:52 ` [PATCH V4 03/17] nvdimm/label: Add namespace/region label support as per LSA 2.1 Neeraj Kumar
@ 2025-11-19 15:51 ` Dave Jiang
2026-01-09 11:46 ` Neeraj Kumar
2025-12-17 14:31 ` Jonathan Cameron
1 sibling, 1 reply; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 15:51 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> Modify __pmem_label_update() to update region labels into LSA
>
> CXL 3.2 Spec mentions CXL LSA 2.1 Namespace Labels at section 9.13.2.5
> Modified __pmem_label_update() using setter functions to update
> namespace label as per CXL LSA 2.1
>
> Create export routine nd_region_label_update() used for creating
> region label into LSA. It will be used later from CXL subsystem
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
A few nits below, otherwise
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/nvdimm/label.c | 360 ++++++++++++++++++++++++++------
> drivers/nvdimm/label.h | 17 +-
> drivers/nvdimm/namespace_devs.c | 25 ++-
> drivers/nvdimm/nd.h | 66 ++++++
> include/linux/libnvdimm.h | 8 +
> 5 files changed, 406 insertions(+), 70 deletions(-)
>
> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
> index 0a9b6c5cb2c3..0d587a5b9f7e 100644
> --- a/drivers/nvdimm/label.c
> +++ b/drivers/nvdimm/label.c
> @@ -22,8 +22,8 @@ static uuid_t nvdimm_btt2_uuid;
> static uuid_t nvdimm_pfn_uuid;
> static uuid_t nvdimm_dax_uuid;
>
> -static uuid_t cxl_region_uuid;
> -static uuid_t cxl_namespace_uuid;
> +uuid_t cxl_region_uuid;
> +uuid_t cxl_namespace_uuid;
>
> static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0";
>
> @@ -278,15 +278,38 @@ static struct nd_namespace_label *nd_label_base(struct nvdimm_drvdata *ndd)
> return base + 2 * sizeof_namespace_index(ndd);
> }
>
> +static unsigned long find_slot(struct nvdimm_drvdata *ndd,
> + unsigned long label)
> +{
> + unsigned long base;
> +
> + base = (unsigned long) nd_label_base(ndd);
No need for space after casting
> + return (label - base) / sizeof_namespace_label(ndd);
> +}
> +
> +static int to_lsa_slot(struct nvdimm_drvdata *ndd,
> + union nd_lsa_label *lsa_label)
> +{
> + return find_slot(ndd, (unsigned long) lsa_label);
> +}
> +
> static int to_slot(struct nvdimm_drvdata *ndd,
> - struct nd_namespace_label *nd_label)
> + struct nd_label_ent *label_ent)
> +{
> + if (uuid_equal(&cxl_region_uuid, &label_ent->label_uuid))
> + return find_slot(ndd, (unsigned long) label_ent->region_label);
> + else
> + return find_slot(ndd, (unsigned long) label_ent->label);
> +}
> +
> +static union nd_lsa_label *to_lsa_label(struct nvdimm_drvdata *ndd, int slot)
> {
> unsigned long label, base;
>
> - label = (unsigned long) nd_label;
> base = (unsigned long) nd_label_base(ndd);
> + label = base + sizeof_namespace_label(ndd) * slot;
>
> - return (label - base) / sizeof_namespace_label(ndd);
> + return (union nd_lsa_label *) label;
> }
>
> static struct nd_namespace_label *to_label(struct nvdimm_drvdata *ndd, int slot)
> @@ -381,6 +404,16 @@ static void nsl_calculate_checksum(struct nvdimm_drvdata *ndd,
> nsl_set_checksum(ndd, nd_label, sum);
> }
>
> +static void region_label_calculate_checksum(struct nvdimm_drvdata *ndd,
> + struct cxl_region_label *region_label)
> +{
> + u64 sum;
> +
> + region_label_set_checksum(region_label, 0);
> + sum = nd_fletcher64(region_label, sizeof_namespace_label(ndd), 1);
> + region_label_set_checksum(region_label, sum);
> +}
> +
> static bool slot_valid(struct nvdimm_drvdata *ndd,
> struct nd_namespace_label *nd_label, u32 slot)
> {
> @@ -584,7 +617,7 @@ int nd_label_active_count(struct nvdimm_drvdata *ndd)
> return count;
> }
>
> -struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
> +union nd_lsa_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
> {
> struct nd_namespace_index *nsindex;
> unsigned long *free;
> @@ -601,7 +634,7 @@ struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
> continue;
>
> if (n-- == 0)
> - return to_label(ndd, slot);
> + return to_lsa_label(ndd, slot);
> }
>
> return NULL;
> @@ -737,9 +770,9 @@ static int nd_label_write_index(struct nvdimm_drvdata *ndd, int index, u32 seq,
> }
>
> static unsigned long nd_label_offset(struct nvdimm_drvdata *ndd,
> - struct nd_namespace_label *nd_label)
> + union nd_lsa_label *lsa_label)
> {
> - return (unsigned long) nd_label
> + return (unsigned long) lsa_label
> - (unsigned long) to_namespace_index(ndd, 0);
> }
>
> @@ -823,11 +856,15 @@ static void reap_victim(struct nd_mapping *nd_mapping,
> struct nd_label_ent *victim)
> {
> struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> - u32 slot = to_slot(ndd, victim->label);
> + u32 slot = to_slot(ndd, victim);
>
> dev_dbg(ndd->dev, "free: %d\n", slot);
> nd_label_free_slot(ndd, slot);
> - victim->label = NULL;
> +
> + if (uuid_equal(&cxl_region_uuid, &victim->label_uuid))
> + victim->region_label = NULL;
> + else
> + victim->label = NULL;
> }
>
> static void nsl_set_type_guid(struct nvdimm_drvdata *ndd,
> @@ -884,26 +921,20 @@ enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd,
> return guid_to_nvdimm_cclass(&nd_label->efi.abstraction_guid);
> }
>
> -static int __pmem_label_update(struct nd_region *nd_region,
> - struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm,
> - int pos, unsigned long flags)
> +static int namespace_label_update(struct nd_region *nd_region,
> + struct nd_mapping *nd_mapping,
> + struct nd_namespace_pmem *nspm,
> + int pos, u64 flags,
> + struct nd_namespace_label *ns_label,
> + struct nd_namespace_index *nsindex,
> + u32 slot)
> {
> struct nd_namespace_common *ndns = &nspm->nsio.common;
> struct nd_interleave_set *nd_set = nd_region->nd_set;
> struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> - struct nd_namespace_label *nd_label;
> - struct nd_namespace_index *nsindex;
> - struct nd_label_ent *label_ent;
> struct nd_label_id label_id;
> struct resource *res;
> - unsigned long *free;
> - u32 nslot, slot;
> - size_t offset;
> u64 cookie;
> - int rc;
> -
> - if (!preamble_next(ndd, &nsindex, &free, &nslot))
> - return -ENXIO;
>
> cookie = nd_region_interleave_set_cookie(nd_region, nsindex);
> nd_label_gen_id(&label_id, nspm->uuid, 0);
> @@ -916,33 +947,150 @@ static int __pmem_label_update(struct nd_region *nd_region,
> return -ENXIO;
> }
>
> + nsl_set_type(ndd, ns_label);
> + nsl_set_uuid(ndd, ns_label, nspm->uuid);
> + nsl_set_name(ndd, ns_label, nspm->alt_name);
> + nsl_set_flags(ndd, ns_label, flags);
> + nsl_set_nlabel(ndd, ns_label, nd_region->ndr_mappings);
> + nsl_set_nrange(ndd, ns_label, 1);
> + nsl_set_position(ndd, ns_label, pos);
> + nsl_set_isetcookie(ndd, ns_label, cookie);
> + nsl_set_rawsize(ndd, ns_label, resource_size(res));
> + nsl_set_lbasize(ndd, ns_label, nspm->lbasize);
> + nsl_set_dpa(ndd, ns_label, res->start);
> + nsl_set_slot(ndd, ns_label, slot);
> + nsl_set_alignment(ndd, ns_label, 0);
> + nsl_set_type_guid(ndd, ns_label, &nd_set->type_guid);
> + nsl_set_region_uuid(ndd, ns_label, &nd_set->uuid);
> + nsl_set_claim_class(ndd, ns_label, ndns->claim_class);
> + nsl_calculate_checksum(ndd, ns_label);
> + nd_dbg_dpa(nd_region, ndd, res, "\n");
> +
> + return 0;
> +}
> +
> +static void region_label_update(struct nd_region *nd_region,
> + struct cxl_region_label *region_label,
> + struct nd_mapping *nd_mapping,
> + int pos, u64 flags, u32 slot)
> +{
> + struct nd_interleave_set *nd_set = nd_region->nd_set;
> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> +
> + /* Set Region Label Format identification UUID */
> + uuid_copy((uuid_t *)region_label->type, &cxl_region_uuid);
> +
> + /* Set Current Region Label UUID */
> + export_uuid(region_label->uuid, &nd_set->uuid);
> +
> + region_label->flags = __cpu_to_le32(flags);
> + region_label->nlabel = __cpu_to_le16(nd_region->ndr_mappings);
> + region_label->position = __cpu_to_le16(pos);
> + region_label->dpa = __cpu_to_le64(nd_mapping->start);
> + region_label->rawsize = __cpu_to_le64(nd_mapping->size);
> + region_label->hpa = __cpu_to_le64(nd_set->res->start);
> + region_label->slot = __cpu_to_le32(slot);
> + region_label->ig = __cpu_to_le32(nd_set->interleave_granularity);
> + region_label->align = __cpu_to_le32(0);
> +
> + /* Update fletcher64 Checksum */
> + region_label_calculate_checksum(ndd, region_label);
> +}
> +
> +static bool is_label_reapable(struct nd_interleave_set *nd_set,
> + struct nd_namespace_pmem *nspm,
> + struct nvdimm_drvdata *ndd,
> + struct nd_label_ent *label_ent,
> + enum label_type ltype,
> + unsigned long *flags)
> +{
> + switch (ltype) {
> + case NS_LABEL_TYPE:
> + if (test_and_clear_bit(ND_LABEL_REAP, flags) ||
> + nsl_uuid_equal(ndd, label_ent->label, nspm->uuid))
> + return true;
> +
> + break;
> + case RG_LABEL_TYPE:
> + if (region_label_uuid_equal(label_ent->region_label,
> + &nd_set->uuid))
> + return true;
> +
> + break;
> + }
> +
> + return false;
> +}
> +
> +static bool is_label_present(struct nd_label_ent *label_ent,
> + enum label_type ltype)
> +{
> + switch (ltype) {
> + case NS_LABEL_TYPE:
> + if (label_ent->label)
> + return true;
> +
> + break;
> + case RG_LABEL_TYPE:
> + if (label_ent->region_label)
> + return true;
> +
> + break;
> + }
> +
> + return false;
> +}
> +
> +static int __pmem_label_update(struct nd_region *nd_region,
> + struct nd_mapping *nd_mapping,
> + struct nd_namespace_pmem *nspm,
> + int pos, unsigned long flags,
> + enum label_type ltype)
> +{
> + struct nd_interleave_set *nd_set = nd_region->nd_set;
> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> + struct nd_namespace_index *nsindex;
> + struct nd_label_ent *label_ent;
> + union nd_lsa_label *lsa_label;
> + unsigned long *free;
> + struct device *dev;
> + u32 nslot, slot;
> + size_t offset;
> + int rc;
> +
> + if (!preamble_next(ndd, &nsindex, &free, &nslot))
> + return -ENXIO;
> +
> /* allocate and write the label to the staging (next) index */
> slot = nd_label_alloc_slot(ndd);
> if (slot == UINT_MAX)
> return -ENXIO;
> dev_dbg(ndd->dev, "allocated: %d\n", slot);
>
> - nd_label = to_label(ndd, slot);
> - memset(nd_label, 0, sizeof_namespace_label(ndd));
> - nsl_set_uuid(ndd, nd_label, nspm->uuid);
> - nsl_set_name(ndd, nd_label, nspm->alt_name);
> - nsl_set_flags(ndd, nd_label, flags);
> - nsl_set_nlabel(ndd, nd_label, nd_region->ndr_mappings);
> - nsl_set_nrange(ndd, nd_label, 1);
> - nsl_set_position(ndd, nd_label, pos);
> - nsl_set_isetcookie(ndd, nd_label, cookie);
> - nsl_set_rawsize(ndd, nd_label, resource_size(res));
> - nsl_set_lbasize(ndd, nd_label, nspm->lbasize);
> - nsl_set_dpa(ndd, nd_label, res->start);
> - nsl_set_slot(ndd, nd_label, slot);
> - nsl_set_type_guid(ndd, nd_label, &nd_set->type_guid);
> - nsl_set_claim_class(ndd, nd_label, ndns->claim_class);
> - nsl_calculate_checksum(ndd, nd_label);
> - nd_dbg_dpa(nd_region, ndd, res, "\n");
> + lsa_label = to_lsa_label(ndd, slot);
> + memset(lsa_label, 0, sizeof_namespace_label(ndd));
> +
> + switch (ltype) {
> + case NS_LABEL_TYPE:
> + dev = &nspm->nsio.common.dev;
> + rc = namespace_label_update(nd_region, nd_mapping,
> + nspm, pos, flags, &lsa_label->ns_label,
> + nsindex, slot);
> + if (rc)
> + return rc;
> +
> + break;
> + case RG_LABEL_TYPE:
> + dev = &nd_region->dev;
> + region_label_update(nd_region, &lsa_label->region_label,
> + nd_mapping, pos, flags, slot);
inconsistent tabbing this one vs the case above. Should pick the one that conforms to the rest of this file. And yeah I get it. The tab formatting under nvdimm is different than CXL and it's a pain going between the two.
DJ
> +
> + break;
> + }
>
> /* update label */
> - offset = nd_label_offset(ndd, nd_label);
> - rc = nvdimm_set_config_data(ndd, offset, nd_label,
> + offset = nd_label_offset(ndd, lsa_label);
> + rc = nvdimm_set_config_data(ndd, offset, lsa_label,
> sizeof_namespace_label(ndd));
> if (rc < 0)
> return rc;
> @@ -950,10 +1098,11 @@ static int __pmem_label_update(struct nd_region *nd_region,
> /* Garbage collect the previous label */
> mutex_lock(&nd_mapping->lock);
> list_for_each_entry(label_ent, &nd_mapping->labels, list) {
> - if (!label_ent->label)
> + if (!is_label_present(label_ent, ltype))
> continue;
> - if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags) ||
> - nsl_uuid_equal(ndd, label_ent->label, nspm->uuid))
> +
> + if (is_label_reapable(nd_set, nspm, ndd, label_ent, ltype,
> + &label_ent->flags))
> reap_victim(nd_mapping, label_ent);
> }
>
> @@ -961,16 +1110,21 @@ static int __pmem_label_update(struct nd_region *nd_region,
> rc = nd_label_write_index(ndd, ndd->ns_next,
> nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0);
> if (rc == 0) {
> - list_for_each_entry(label_ent, &nd_mapping->labels, list)
> - if (!label_ent->label) {
> - label_ent->label = nd_label;
> - nd_label = NULL;
> - break;
> - }
> - dev_WARN_ONCE(&nspm->nsio.common.dev, nd_label,
> - "failed to track label: %d\n",
> - to_slot(ndd, nd_label));
> - if (nd_label)
> + list_for_each_entry(label_ent, &nd_mapping->labels, list) {
> + if (is_label_present(label_ent, ltype))
> + continue;
> +
> + if (ltype == NS_LABEL_TYPE)
> + label_ent->label = &lsa_label->ns_label;
> + else
> + label_ent->region_label = &lsa_label->region_label;
> +
> + lsa_label = NULL;
> + break;
> + }
> + dev_WARN_ONCE(dev, lsa_label, "failed to track label: %d\n",
> + to_lsa_slot(ndd, lsa_label));
> + if (lsa_label)
> rc = -ENXIO;
> }
> mutex_unlock(&nd_mapping->lock);
> @@ -978,7 +1132,8 @@ static int __pmem_label_update(struct nd_region *nd_region,
> return rc;
> }
>
> -static int init_labels(struct nd_mapping *nd_mapping, int num_labels)
> +static int init_labels(struct nd_mapping *nd_mapping, int num_labels,
> + enum label_type ltype)
> {
> int i, old_num_labels = 0;
> struct nd_label_ent *label_ent;
> @@ -998,6 +1153,16 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels)
> label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL);
> if (!label_ent)
> return -ENOMEM;
> +
> + switch (ltype) {
> + case NS_LABEL_TYPE:
> + uuid_copy(&label_ent->label_uuid, &cxl_namespace_uuid);
> + break;
> + case RG_LABEL_TYPE:
> + uuid_copy(&label_ent->label_uuid, &cxl_region_uuid);
> + break;
> + }
> +
> mutex_lock(&nd_mapping->lock);
> list_add_tail(&label_ent->list, &nd_mapping->labels);
> mutex_unlock(&nd_mapping->lock);
> @@ -1041,19 +1206,19 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
>
> mutex_lock(&nd_mapping->lock);
> list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
> - struct nd_namespace_label *nd_label = label_ent->label;
> -
> - if (!nd_label)
> + if (label_ent->label)
> continue;
> active++;
> - if (!nsl_uuid_equal(ndd, nd_label, uuid))
> + if (!nsl_uuid_equal(ndd, label_ent->label, uuid))
> continue;
> active--;
> - slot = to_slot(ndd, nd_label);
> + slot = to_slot(ndd, label_ent);
> nd_label_free_slot(ndd, slot);
> dev_dbg(ndd->dev, "free: %d\n", slot);
> list_move_tail(&label_ent->list, &list);
> - label_ent->label = NULL;
> +
> + if (uuid_equal(&cxl_namespace_uuid, &label_ent->label_uuid))
> + label_ent->label = NULL;
> }
> list_splice_tail_init(&list, &nd_mapping->labels);
>
> @@ -1067,6 +1232,19 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
> nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0);
> }
>
> +static int find_region_label_count(struct nd_mapping *nd_mapping)
> +{
> + struct nd_label_ent *label_ent;
> + int region_label_cnt = 0;
> +
> + guard(mutex)(&nd_mapping->lock);
> + list_for_each_entry(label_ent, &nd_mapping->labels, list)
> + if (uuid_equal(&cxl_region_uuid, &label_ent->label_uuid))
> + region_label_cnt++;
> +
> + return region_label_cnt;
> +}
> +
> int nd_pmem_namespace_label_update(struct nd_region *nd_region,
> struct nd_namespace_pmem *nspm, resource_size_t size)
> {
> @@ -1075,6 +1253,7 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
> for (i = 0; i < nd_region->ndr_mappings; i++) {
> struct nd_mapping *nd_mapping = &nd_region->mapping[i];
> struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> + int region_label_cnt = 0;
> struct resource *res;
> int count = 0;
>
> @@ -1090,12 +1269,20 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
> count++;
> WARN_ON_ONCE(!count);
>
> - rc = init_labels(nd_mapping, count);
> + region_label_cnt = find_region_label_count(nd_mapping);
> + /*
> + * init_labels() scan labels and allocate new label based
> + * on its second parameter (num_labels). Therefore to
> + * allocate new namespace label also include previously
> + * added region label
> + */
> + rc = init_labels(nd_mapping, count + region_label_cnt,
> + NS_LABEL_TYPE);
> if (rc < 0)
> return rc;
>
> rc = __pmem_label_update(nd_region, nd_mapping, nspm, i,
> - NSLABEL_FLAG_UPDATING);
> + NSLABEL_FLAG_UPDATING, NS_LABEL_TYPE);
> if (rc)
> return rc;
> }
> @@ -1107,7 +1294,48 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
> for (i = 0; i < nd_region->ndr_mappings; i++) {
> struct nd_mapping *nd_mapping = &nd_region->mapping[i];
>
> - rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 0);
> + rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 0,
> + NS_LABEL_TYPE);
> + if (rc)
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +int nd_pmem_region_label_update(struct nd_region *nd_region)
> +{
> + int i, rc;
> +
> + for (i = 0; i < nd_region->ndr_mappings; i++) {
> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> + int region_label_cnt = 0;
> +
> + /* No need to update region label for non cxl format */
> + if (!ndd->cxl)
> + return 0;
> +
> + region_label_cnt = find_region_label_count(nd_mapping);
> + rc = init_labels(nd_mapping, region_label_cnt + 1,
> + RG_LABEL_TYPE);
> + if (rc < 0)
> + return rc;
> +
> + rc = __pmem_label_update(nd_region, nd_mapping, NULL, i,
> + NSLABEL_FLAG_UPDATING, RG_LABEL_TYPE);
> + if (rc)
> + return rc;
> + }
> +
> + /* Clear the UPDATING flag per UEFI 2.7 expectations */
> + for (i = 0; i < nd_region->ndr_mappings; i++) {
> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> +
> + WARN_ON_ONCE(!ndd->cxl);
> + rc = __pmem_label_update(nd_region, nd_mapping, NULL, i, 0,
> + RG_LABEL_TYPE);
> if (rc)
> return rc;
> }
> diff --git a/drivers/nvdimm/label.h b/drivers/nvdimm/label.h
> index 0650fb4b9821..f11f54056353 100644
> --- a/drivers/nvdimm/label.h
> +++ b/drivers/nvdimm/label.h
> @@ -30,6 +30,11 @@ enum {
> ND_NSINDEX_INIT = 0x1,
> };
>
> +enum label_type {
> + RG_LABEL_TYPE,
> + NS_LABEL_TYPE,
> +};
> +
> /**
> * struct nd_namespace_index - label set superblock
> * @sig: NAMESPACE_INDEX\0
> @@ -183,6 +188,15 @@ struct nd_namespace_label {
> };
> };
>
> +/*
> + * LSA 2.1 format introduces region label, which can also reside
> + * into LSA along with only namespace label as per v1.1 and v1.2
> + */
> +union nd_lsa_label {
> + struct nd_namespace_label ns_label;
> + struct cxl_region_label region_label;
> +};
> +
> #define NVDIMM_BTT_GUID "8aed63a2-29a2-4c66-8b12-f05d15d3922a"
> #define NVDIMM_BTT2_GUID "18633bfc-1735-4217-8ac9-17239282d3f8"
> #define NVDIMM_PFN_GUID "266400ba-fb9f-4677-bcb0-968f11d0d225"
> @@ -215,7 +229,7 @@ struct nvdimm_drvdata;
> int nd_label_data_init(struct nvdimm_drvdata *ndd);
> size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd);
> int nd_label_active_count(struct nvdimm_drvdata *ndd);
> -struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n);
> +union nd_lsa_label *nd_label_active(struct nvdimm_drvdata *ndd, int n);
> u32 nd_label_alloc_slot(struct nvdimm_drvdata *ndd);
> bool nd_label_free_slot(struct nvdimm_drvdata *ndd, u32 slot);
> u32 nd_label_nfree(struct nvdimm_drvdata *ndd);
> @@ -223,4 +237,5 @@ struct nd_region;
> struct nd_namespace_pmem;
> int nd_pmem_namespace_label_update(struct nd_region *nd_region,
> struct nd_namespace_pmem *nspm, resource_size_t size);
> +int nd_pmem_region_label_update(struct nd_region *nd_region);
> #endif /* __LABEL_H__ */
> diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
> index 43fdb806532e..b1abbe602a5e 100644
> --- a/drivers/nvdimm/namespace_devs.c
> +++ b/drivers/nvdimm/namespace_devs.c
> @@ -232,6 +232,18 @@ static ssize_t __alt_name_store(struct device *dev, const char *buf,
> return rc;
> }
>
> +int nd_region_label_update(struct nd_region *nd_region)
> +{
> + int rc;
> +
> + nvdimm_bus_lock(&nd_region->dev);
> + rc = nd_pmem_region_label_update(nd_region);
> + nvdimm_bus_unlock(&nd_region->dev);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL_GPL(nd_region_label_update);
> +
> static int nd_namespace_label_update(struct nd_region *nd_region,
> struct device *dev)
> {
> @@ -2122,13 +2134,20 @@ static int init_active_labels(struct nd_region *nd_region)
> if (!count)
> continue;
> for (j = 0; j < count; j++) {
> - struct nd_namespace_label *label;
> + union nd_lsa_label *lsa_label;
>
> label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL);
> if (!label_ent)
> break;
> - label = nd_label_active(ndd, j);
> - label_ent->label = label;
> +
> + lsa_label = nd_label_active(ndd, j);
> + if (is_region_label(ndd, lsa_label)) {
> + label_ent->region_label = &lsa_label->region_label;
> + uuid_copy(&label_ent->label_uuid, &cxl_region_uuid);
> + } else {
> + label_ent->label = &lsa_label->ns_label;
> + uuid_copy(&label_ent->label_uuid, &cxl_namespace_uuid);
> + }
>
> mutex_lock(&nd_mapping->lock);
> list_add_tail(&label_ent->list, &nd_mapping->labels);
> diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
> index f631bd84d6f0..5fd69c28ffe7 100644
> --- a/drivers/nvdimm/nd.h
> +++ b/drivers/nvdimm/nd.h
> @@ -14,6 +14,9 @@
> #include <linux/nd.h>
> #include "label.h"
>
> +extern uuid_t cxl_namespace_uuid;
> +extern uuid_t cxl_region_uuid;
> +
> enum {
> /*
> * Limits the maximum number of block apertures a dimm can
> @@ -295,6 +298,67 @@ static inline const u8 *nsl_uuid_raw(struct nvdimm_drvdata *ndd,
> return nd_label->efi.uuid;
> }
>
> +static inline void nsl_set_type(struct nvdimm_drvdata *ndd,
> + struct nd_namespace_label *ns_label)
> +{
> + if (!(ndd->cxl && ns_label))
> + return;
> +
> + uuid_copy((uuid_t *)ns_label->cxl.type, &cxl_namespace_uuid);
> +}
> +
> +static inline void nsl_set_alignment(struct nvdimm_drvdata *ndd,
> + struct nd_namespace_label *ns_label,
> + u32 align)
> +{
> + if (!ndd->cxl)
> + return;
> +
> + ns_label->cxl.align = __cpu_to_le32(align);
> +}
> +
> +static inline void nsl_set_region_uuid(struct nvdimm_drvdata *ndd,
> + struct nd_namespace_label *ns_label,
> + const uuid_t *uuid)
> +{
> + if (!(ndd->cxl && uuid))
> + return;
> +
> + export_uuid(ns_label->cxl.region_uuid, uuid);
> +}
> +
> +static inline bool is_region_label(struct nvdimm_drvdata *ndd,
> + union nd_lsa_label *lsa_label)
> +{
> + uuid_t *region_type;
> +
> + if (!ndd->cxl)
> + return false;
> +
> + region_type = (uuid_t *) lsa_label->region_label.type;
> + return uuid_equal(&cxl_region_uuid, region_type);
> +}
> +
> +static inline bool
> +region_label_uuid_equal(struct cxl_region_label *region_label,
> + const uuid_t *uuid)
> +{
> + return uuid_equal((uuid_t *) region_label->uuid, uuid);
> +}
> +
> +static inline u64
> +region_label_get_checksum(struct cxl_region_label *region_label)
> +{
> + return __le64_to_cpu(region_label->checksum);
> +}
> +
> +static inline void
> +region_label_set_checksum(struct cxl_region_label *region_label,
> + u64 checksum)
> +{
> + region_label->checksum = __cpu_to_le64(checksum);
> +}
> +
> bool nsl_validate_type_guid(struct nvdimm_drvdata *ndd,
> struct nd_namespace_label *nd_label, guid_t *guid);
> enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd,
> @@ -376,7 +440,9 @@ enum nd_label_flags {
> struct nd_label_ent {
> struct list_head list;
> unsigned long flags;
> + uuid_t label_uuid;
> struct nd_namespace_label *label;
> + struct cxl_region_label *region_label;
> };
>
> enum nd_mapping_lock_class {
> diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
> index 5696715c33bb..2c213b9dac66 100644
> --- a/include/linux/libnvdimm.h
> +++ b/include/linux/libnvdimm.h
> @@ -117,6 +117,13 @@ struct nd_interleave_set {
> u64 altcookie;
>
> guid_t type_guid;
> +
> + /* v2.1 region label info */
> + uuid_t uuid;
> + int interleave_ways;
> + int interleave_granularity;
> + struct resource *res;
> + int nr_targets;
> };
>
> struct nd_mapping_desc {
> @@ -307,6 +314,7 @@ int nvdimm_has_flush(struct nd_region *nd_region);
> int nvdimm_has_cache(struct nd_region *nd_region);
> int nvdimm_in_overwrite(struct nvdimm *nvdimm);
> bool is_nvdimm_sync(struct nd_region *nd_region);
> +int nd_region_label_update(struct nd_region *nd_region);
>
> static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf,
> unsigned int buf_len, int *cmd_rc)
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 04/17] nvdimm/label: Include region label in slot validation
2025-11-19 7:52 ` [PATCH V4 04/17] nvdimm/label: Include region label in slot validation Neeraj Kumar
@ 2025-11-19 16:54 ` Dave Jiang
0 siblings, 0 replies; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 16:54 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> Prior to LSA 2.1 Support, label in slot means only namespace
> label. But with LSA 2.1 a label can be either namespace or
> region label.
>
> Slot validation routine validates label slot by calculating
> label checksum. It was only validating namespace label.
> This changeset also validates region label if present.
>
> In previous patch to_lsa_label() was introduced along with
> to_label(). to_label() returns only namespace label whereas
> to_lsa_label() returns union nd_lsa_label*
>
> In this patch We have converted all usage of to_label()
> to to_lsa_label()
>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/nvdimm/label.c | 94 ++++++++++++++++++++++++++++--------------
> drivers/nvdimm/nd.h | 5 +++
> 2 files changed, 69 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
> index 0d587a5b9f7e..6ccc51552822 100644
> --- a/drivers/nvdimm/label.c
> +++ b/drivers/nvdimm/label.c
> @@ -312,16 +312,6 @@ static union nd_lsa_label *to_lsa_label(struct nvdimm_drvdata *ndd, int slot)
> return (union nd_lsa_label *) label;
> }
>
> -static struct nd_namespace_label *to_label(struct nvdimm_drvdata *ndd, int slot)
> -{
> - unsigned long label, base;
> -
> - base = (unsigned long) nd_label_base(ndd);
> - label = base + sizeof_namespace_label(ndd) * slot;
> -
> - return (struct nd_namespace_label *) label;
> -}
> -
> #define for_each_clear_bit_le(bit, addr, size) \
> for ((bit) = find_next_zero_bit_le((addr), (size), 0); \
> (bit) < (size); \
> @@ -382,7 +372,7 @@ static bool nsl_validate_checksum(struct nvdimm_drvdata *ndd,
> {
> u64 sum, sum_save;
>
> - if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum))
> + if (!efi_namespace_label_has(ndd, checksum))
> return true;
>
> sum_save = nsl_get_checksum(ndd, nd_label);
> @@ -397,13 +387,25 @@ static void nsl_calculate_checksum(struct nvdimm_drvdata *ndd,
> {
> u64 sum;
>
> - if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum))
> + if (!efi_namespace_label_has(ndd, checksum))
> return;
> nsl_set_checksum(ndd, nd_label, 0);
> sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1);
> nsl_set_checksum(ndd, nd_label, sum);
> }
>
> +static bool region_label_validate_checksum(struct nvdimm_drvdata *ndd,
> + struct cxl_region_label *region_label)
> +{
> + u64 sum, sum_save;
> +
> + sum_save = region_label_get_checksum(region_label);
> + region_label_set_checksum(region_label, 0);
> + sum = nd_fletcher64(region_label, sizeof_namespace_label(ndd), 1);
> + region_label_set_checksum(region_label, sum_save);
> + return sum == sum_save;
> +}
> +
> static void region_label_calculate_checksum(struct nvdimm_drvdata *ndd,
> struct cxl_region_label *region_label)
> {
> @@ -415,16 +417,34 @@ static void region_label_calculate_checksum(struct nvdimm_drvdata *ndd,
> }
>
> static bool slot_valid(struct nvdimm_drvdata *ndd,
> - struct nd_namespace_label *nd_label, u32 slot)
> + union nd_lsa_label *lsa_label, u32 slot)
> {
> + struct cxl_region_label *region_label = &lsa_label->region_label;
> + struct nd_namespace_label *nd_label = &lsa_label->ns_label;
> + enum label_type type;
> bool valid;
> + static const char * const label_name[] = {
> + [RG_LABEL_TYPE] = "region",
> + [NS_LABEL_TYPE] = "namespace",
> + };
>
> /* check that we are written where we expect to be written */
> - if (slot != nsl_get_slot(ndd, nd_label))
> - return false;
> - valid = nsl_validate_checksum(ndd, nd_label);
> + if (is_region_label(ndd, lsa_label)) {
> + type = RG_LABEL_TYPE;
> + if (slot != region_label_get_slot(region_label))
> + return false;
> + valid = region_label_validate_checksum(ndd, region_label);
> + } else {
> + type = NS_LABEL_TYPE;
> + if (slot != nsl_get_slot(ndd, nd_label))
> + return false;
> + valid = nsl_validate_checksum(ndd, nd_label);
> + }
> +
> if (!valid)
> - dev_dbg(ndd->dev, "fail checksum. slot: %d\n", slot);
> + dev_dbg(ndd->dev, "%s label checksum fail. slot: %d\n",
> + label_name[type], slot);
> +
> return valid;
> }
>
> @@ -440,14 +460,16 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
> for_each_clear_bit_le(slot, free, nslot) {
> struct nd_namespace_label *nd_label;
> struct nd_region *nd_region = NULL;
> + union nd_lsa_label *lsa_label;
> struct nd_label_id label_id;
> struct resource *res;
> uuid_t label_uuid;
> u32 flags;
>
> - nd_label = to_label(ndd, slot);
> + lsa_label = to_lsa_label(ndd, slot);
> + nd_label = &lsa_label->ns_label;
>
> - if (!slot_valid(ndd, nd_label, slot))
> + if (!slot_valid(ndd, lsa_label, slot))
> continue;
>
> nsl_get_uuid(ndd, nd_label, &label_uuid);
> @@ -598,18 +620,30 @@ int nd_label_active_count(struct nvdimm_drvdata *ndd)
> return 0;
>
> for_each_clear_bit_le(slot, free, nslot) {
> + struct cxl_region_label *region_label;
> struct nd_namespace_label *nd_label;
> -
> - nd_label = to_label(ndd, slot);
> -
> - if (!slot_valid(ndd, nd_label, slot)) {
> - u32 label_slot = nsl_get_slot(ndd, nd_label);
> - u64 size = nsl_get_rawsize(ndd, nd_label);
> - u64 dpa = nsl_get_dpa(ndd, nd_label);
> + union nd_lsa_label *lsa_label;
> + u32 lslot;
> + u64 size, dpa;
> +
> + lsa_label = to_lsa_label(ndd, slot);
> + nd_label = &lsa_label->ns_label;
> + region_label = &lsa_label->region_label;
> +
> + if (!slot_valid(ndd, lsa_label, slot)) {
> + if (is_region_label(ndd, lsa_label)) {
> + lslot = __le32_to_cpu(region_label->slot);
> + size = __le64_to_cpu(region_label->rawsize);
> + dpa = __le64_to_cpu(region_label->dpa);
> + } else {
> + lslot = nsl_get_slot(ndd, nd_label);
> + size = nsl_get_rawsize(ndd, nd_label);
> + dpa = nsl_get_dpa(ndd, nd_label);
> + }
>
> dev_dbg(ndd->dev,
> "slot%d invalid slot: %d dpa: %llx size: %llx\n",
> - slot, label_slot, dpa, size);
> + slot, lslot, dpa, size);
> continue;
> }
> count++;
> @@ -627,10 +661,10 @@ union nd_lsa_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
> return NULL;
>
> for_each_clear_bit_le(slot, free, nslot) {
> - struct nd_namespace_label *nd_label;
> + union nd_lsa_label *lsa_label;
>
> - nd_label = to_label(ndd, slot);
> - if (!slot_valid(ndd, nd_label, slot))
> + lsa_label = to_lsa_label(ndd, slot);
> + if (!slot_valid(ndd, lsa_label, slot))
> continue;
>
> if (n-- == 0)
> diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
> index 5fd69c28ffe7..30c7262d8a26 100644
> --- a/drivers/nvdimm/nd.h
> +++ b/drivers/nvdimm/nd.h
> @@ -346,6 +346,11 @@ region_label_uuid_equal(struct cxl_region_label *region_label,
> return uuid_equal((uuid_t *) region_label->uuid, uuid);
> }
>
> +static inline u32 region_label_get_slot(struct cxl_region_label *region_label)
> +{
> + return __le32_to_cpu(region_label->slot);
> +}
> +
> static inline u64
> region_label_get_checksum(struct cxl_region_label *region_label)
> {
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 05/17] nvdimm/label: Skip region label during ns label DPA reservation
2025-11-19 7:52 ` [PATCH V4 05/17] nvdimm/label: Skip region label during ns label DPA reservation Neeraj Kumar
@ 2025-11-19 17:01 ` Dave Jiang
2025-12-17 14:33 ` Jonathan Cameron
1 sibling, 0 replies; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 17:01 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> If Namespace label is present in LSA during nvdimm_probe() then DPA
> reservation is required. But this reservation is not required by region
> label. Therefore if LSA scanning finds any region label, skip it.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/nvdimm/label.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
> index 6ccc51552822..e90e48672da3 100644
> --- a/drivers/nvdimm/label.c
> +++ b/drivers/nvdimm/label.c
> @@ -469,6 +469,10 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
> lsa_label = to_lsa_label(ndd, slot);
> nd_label = &lsa_label->ns_label;
>
> + /* Skip region label. DPA reservation is for NS label only */
> + if (is_region_label(ndd, lsa_label))
> + continue;
> +
> if (!slot_valid(ndd, lsa_label, slot))
> continue;
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 06/17] nvdimm/label: Preserve region label during namespace creation
2025-11-19 7:52 ` [PATCH V4 06/17] nvdimm/label: Preserve region label during namespace creation Neeraj Kumar
@ 2025-11-19 18:07 ` Dave Jiang
0 siblings, 0 replies; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 18:07 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> During namespace creation we scan labels present in LSA using
> scan_labels(). Currently scan_labels() is only preserving
> namespace labels into label_ent list.
>
> In this patch we also preserve region label into label_ent list
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/nvdimm/namespace_devs.c | 47 +++++++++++++++++++++++++++++----
> 1 file changed, 42 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
> index b1abbe602a5e..9450200b4470 100644
> --- a/drivers/nvdimm/namespace_devs.c
> +++ b/drivers/nvdimm/namespace_devs.c
> @@ -1999,9 +1999,32 @@ static struct device **scan_labels(struct nd_region *nd_region)
>
> if (count == 0) {
> struct nd_namespace_pmem *nspm;
> + for (i = 0; i < nd_region->ndr_mappings; i++) {
> + struct cxl_region_label *region_label;
> + struct nd_label_ent *le, *e;
> + LIST_HEAD(list);
>
> - /* Publish a zero-sized namespace for userspace to configure. */
> - nd_mapping_free_labels(nd_mapping);
> + nd_mapping = &nd_region->mapping[i];
> + if (list_empty(&nd_mapping->labels))
> + continue;
> +
> + list_for_each_entry_safe(le, e, &nd_mapping->labels,
> + list) {
> + region_label = le->region_label;
> + if (!region_label)
> + continue;
> +
> + /* Preserve region labels if present */
> + list_move_tail(&le->list, &list);
> + }
> +
> + /*
> + * Publish a zero-sized namespace for userspace
> + * to configure.
> + */
> + nd_mapping_free_labels(nd_mapping);
> + list_splice_init(&list, &nd_mapping->labels);
> + }
> nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
> if (!nspm)
> goto err;
> @@ -2013,7 +2036,7 @@ static struct device **scan_labels(struct nd_region *nd_region)
> } else if (is_memory(&nd_region->dev)) {
> /* clean unselected labels */
> for (i = 0; i < nd_region->ndr_mappings; i++) {
> - struct list_head *l, *e;
> + struct nd_label_ent *le, *e;
> LIST_HEAD(list);
> int j;
>
> @@ -2024,10 +2047,24 @@ static struct device **scan_labels(struct nd_region *nd_region)
> }
>
> j = count;
> - list_for_each_safe(l, e, &nd_mapping->labels) {
> + list_for_each_entry_safe(le, e, &nd_mapping->labels,
> + list) {
> + /* Preserve region labels */
> + if (uuid_equal(&le->label_uuid,
> + &cxl_region_uuid)) {
> + list_move_tail(&le->list, &list);
> + continue;
> + }
> +
> + /*
> + * Once preserving selected ns label done
> + * break out of loop
> + */
> if (!j--)
> break;
> - list_move_tail(l, &list);
> +
> + /* Preserve selected ns label */
> + list_move_tail(&le->list, &list);
> }
> nd_mapping_free_labels(nd_mapping);
> list_splice_init(&list, &nd_mapping->labels);
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 07/17] nvdimm/label: Add region label delete support
2025-11-19 7:52 ` [PATCH V4 07/17] nvdimm/label: Add region label delete support Neeraj Kumar
@ 2025-11-19 19:50 ` Dave Jiang
2026-01-09 11:56 ` Neeraj Kumar
2025-12-17 15:05 ` Jonathan Cameron
1 sibling, 1 reply; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 19:50 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> Create export routine nd_region_label_delete() used for deleting
> region label from LSA. It will be used later from CXL subsystem
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Just one small thing below, otherwise
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/nvdimm/label.c | 76 ++++++++++++++++++++++++++++++---
> drivers/nvdimm/label.h | 1 +
> drivers/nvdimm/namespace_devs.c | 12 ++++++
> drivers/nvdimm/nd.h | 6 +++
> include/linux/libnvdimm.h | 1 +
> 5 files changed, 90 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
> index e90e48672da3..da55ecd95e2f 100644
> --- a/drivers/nvdimm/label.c
> +++ b/drivers/nvdimm/label.c
> @@ -1225,7 +1225,8 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels,
> return max(num_labels, old_num_labels);
> }
>
> -static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
> +static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid,
> + enum label_type ltype)
> {
> struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> struct nd_label_ent *label_ent, *e;
> @@ -1244,11 +1245,25 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
>
> mutex_lock(&nd_mapping->lock);
> list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
> - if (label_ent->label)
> + if ((ltype == NS_LABEL_TYPE && !label_ent->label) ||
> + (ltype == RG_LABEL_TYPE && !label_ent->region_label))
> continue;
> active++;
> - if (!nsl_uuid_equal(ndd, label_ent->label, uuid))
> - continue;
> +
> + switch (ltype) {
> + case NS_LABEL_TYPE:
> + if (!nsl_uuid_equal(ndd, label_ent->label, uuid))
> + continue;
> +
> + break;
> + case RG_LABEL_TYPE:
> + if (!region_label_uuid_equal(label_ent->region_label,
> + uuid))
> + continue;
> +
> + break;
> + }
> +
> active--;
> slot = to_slot(ndd, label_ent);
> nd_label_free_slot(ndd, slot);
> @@ -1257,10 +1272,12 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
>
> if (uuid_equal(&cxl_namespace_uuid, &label_ent->label_uuid))
> label_ent->label = NULL;
> + else
> + label_ent->region_label = NULL;
> }
> list_splice_tail_init(&list, &nd_mapping->labels);
>
> - if (active == 0) {
> + if ((ltype == NS_LABEL_TYPE) && (active == 0)) {
> nd_mapping_free_labels(nd_mapping);
> dev_dbg(ndd->dev, "no more active labels\n");
> }
> @@ -1296,7 +1313,8 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
> int count = 0;
>
> if (size == 0) {
> - rc = del_labels(nd_mapping, nspm->uuid);
> + rc = del_labels(nd_mapping, nspm->uuid,
> + NS_LABEL_TYPE);
> if (rc)
> return rc;
> continue;
> @@ -1381,6 +1399,52 @@ int nd_pmem_region_label_update(struct nd_region *nd_region)
> return 0;
> }
>
> +int nd_pmem_region_label_delete(struct nd_region *nd_region)
> +{
> + struct nd_interleave_set *nd_set = nd_region->nd_set;
> + struct nd_label_ent *label_ent;
> + int i, rc;
> +
> + for (i = 0; i < nd_region->ndr_mappings; i++) {
> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> +
> + /* Find non cxl format supported ndr_mappings */
> + if (!ndd->cxl) {
> + dev_info(&nd_region->dev, "Unsupported region label\n");
> + return -EINVAL;
> + }
> +
> + /* Find if any NS label using this region */
> + guard(mutex)(&nd_mapping->lock);
> + list_for_each_entry(label_ent, &nd_mapping->labels, list) {
> + if (!label_ent->label)
> + continue;
> +
> + /*
> + * Check if any available NS labels has same
> + * region_uuid in LSA
> + */
> + if (nsl_region_uuid_equal(label_ent->label,
> + &nd_set->uuid)) {
> + dev_dbg(&nd_region->dev,
> + "Region/Namespace label in use\n");
> + return -EBUSY;
> + }
> + }
> + }
> +
> + for (i = 0; i < nd_region->ndr_mappings; i++) {
> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
> +
> + rc = del_labels(nd_mapping, &nd_set->uuid, RG_LABEL_TYPE);
> + if (rc)
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> int __init nd_label_init(void)
> {
> WARN_ON(guid_parse(NVDIMM_BTT_GUID, &nvdimm_btt_guid));
> diff --git a/drivers/nvdimm/label.h b/drivers/nvdimm/label.h
> index f11f54056353..80a7f7dd8ba7 100644
> --- a/drivers/nvdimm/label.h
> +++ b/drivers/nvdimm/label.h
> @@ -238,4 +238,5 @@ struct nd_namespace_pmem;
> int nd_pmem_namespace_label_update(struct nd_region *nd_region,
> struct nd_namespace_pmem *nspm, resource_size_t size);
> int nd_pmem_region_label_update(struct nd_region *nd_region);
> +int nd_pmem_region_label_delete(struct nd_region *nd_region);
> #endif /* __LABEL_H__ */
> diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
> index 9450200b4470..9299a586bfce 100644
> --- a/drivers/nvdimm/namespace_devs.c
> +++ b/drivers/nvdimm/namespace_devs.c
> @@ -244,6 +244,18 @@ int nd_region_label_update(struct nd_region *nd_region)
> }
> EXPORT_SYMBOL_GPL(nd_region_label_update);
>
> +int nd_region_label_delete(struct nd_region *nd_region)
> +{
> + int rc;
> +
> + nvdimm_bus_lock(&nd_region->dev);
You can use the new nvdimm_bus guard() now.
guard(nvdimm_bus)(&nd_region->dev);
DJ
> + rc = nd_pmem_region_label_delete(nd_region);
> + nvdimm_bus_unlock(&nd_region->dev);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL_GPL(nd_region_label_delete);
> +
> static int nd_namespace_label_update(struct nd_region *nd_region,
> struct device *dev)
> {
> diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
> index 30c7262d8a26..b241a0b2e314 100644
> --- a/drivers/nvdimm/nd.h
> +++ b/drivers/nvdimm/nd.h
> @@ -339,6 +339,12 @@ static inline bool is_region_label(struct nvdimm_drvdata *ndd,
> return uuid_equal(&cxl_region_uuid, region_type);
> }
>
> +static inline bool nsl_region_uuid_equal(struct nd_namespace_label *ns_label,
> + const uuid_t *uuid)
> +{
> + return uuid_equal((uuid_t *) ns_label->cxl.region_uuid, uuid);
> +}
> +
> static inline bool
> region_label_uuid_equal(struct cxl_region_label *region_label,
> const uuid_t *uuid)
> diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
> index 2c213b9dac66..bbf14a260c93 100644
> --- a/include/linux/libnvdimm.h
> +++ b/include/linux/libnvdimm.h
> @@ -315,6 +315,7 @@ int nvdimm_has_cache(struct nd_region *nd_region);
> int nvdimm_in_overwrite(struct nvdimm *nvdimm);
> bool is_nvdimm_sync(struct nd_region *nd_region);
> int nd_region_label_update(struct nd_region *nd_region);
> +int nd_region_label_delete(struct nd_region *nd_region);
>
> static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf,
> unsigned int buf_len, int *cmd_rc)
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 08/17] nvdimm/label: Preserve cxl region information from region label
2025-11-19 7:52 ` [PATCH V4 08/17] nvdimm/label: Preserve cxl region information from region label Neeraj Kumar
@ 2025-11-19 20:13 ` Dave Jiang
2026-01-09 12:03 ` Neeraj Kumar
2025-12-17 15:09 ` Jonathan Cameron
1 sibling, 1 reply; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 20:13 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> Preserve region information from region label during nvdimm_probe. This
> preserved region information is used for creating cxl region to achieve
> region persistency across reboot.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Assume there's a plan to add >1 region labels preservation in the next step?
> ---
> drivers/nvdimm/dimm.c | 4 ++++
> drivers/nvdimm/label.c | 40 +++++++++++++++++++++++++++++++++++++++
> drivers/nvdimm/nd-core.h | 2 ++
> drivers/nvdimm/nd.h | 1 +
> include/linux/libnvdimm.h | 14 ++++++++++++++
> 5 files changed, 61 insertions(+)
>
> diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c
> index 07f5c5d5e537..590ec883903d 100644
> --- a/drivers/nvdimm/dimm.c
> +++ b/drivers/nvdimm/dimm.c
> @@ -107,6 +107,10 @@ static int nvdimm_probe(struct device *dev)
> if (rc)
> goto err;
>
> + /* Preserve cxl region info if available */
> + if (ndd->cxl)
> + nvdimm_cxl_region_preserve(ndd);
> +
> return 0;
>
> err:
> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
> index da55ecd95e2f..0f8aea61b504 100644
> --- a/drivers/nvdimm/label.c
> +++ b/drivers/nvdimm/label.c
> @@ -490,6 +490,46 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
> return 0;
> }
>
> +int nvdimm_cxl_region_preserve(struct nvdimm_drvdata *ndd)
> +{
> + struct nvdimm *nvdimm = to_nvdimm(ndd->dev);
> + struct cxl_pmem_region_params *p = &nvdimm->cxl_region_params;
> + struct nd_namespace_index *nsindex;
> + unsigned long *free;
> + u32 nslot, slot;
> +
> + if (!preamble_current(ndd, &nsindex, &free, &nslot))
> + return 0; /* no label, nothing to preserve */
> +
> + for_each_clear_bit_le(slot, free, nslot) {
> + union nd_lsa_label *lsa_label;
> + struct cxl_region_label *region_label;
> + uuid_t *region_uuid;
> +
> + lsa_label = to_lsa_label(ndd, slot);
> + region_label = &lsa_label->region_label;
> + region_uuid = (uuid_t *) ®ion_label->type;
> +
> + /* TODO: Currently preserving only one region */
> + if (uuid_equal(&cxl_region_uuid, region_uuid)) {
> + nvdimm->is_region_label = true;
> + import_uuid(&p->uuid, region_label->uuid);
> + p->flags = __le32_to_cpu(region_label->flags);
> + p->nlabel = __le16_to_cpu(region_label->nlabel);
> + p->position = __le16_to_cpu(region_label->position);
> + p->dpa = __le64_to_cpu(region_label->dpa);
> + p->rawsize = __le64_to_cpu(region_label->rawsize);
> + p->hpa = __le64_to_cpu(region_label->hpa);
> + p->slot = __le32_to_cpu(region_label->slot);
> + p->ig = __le32_to_cpu(region_label->ig);
> + p->align = __le32_to_cpu(region_label->align);
> + break;
> + }
> + }
> +
> + return 0;
> +}
> +
> int nd_label_data_init(struct nvdimm_drvdata *ndd)
> {
> size_t config_size, read_size, max_xfer, offset;
> diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
> index bfc6bfeb6e24..a73fac81531e 100644
> --- a/drivers/nvdimm/nd-core.h
> +++ b/drivers/nvdimm/nd-core.h
> @@ -46,6 +46,8 @@ struct nvdimm {
> } sec;
> struct delayed_work dwork;
> const struct nvdimm_fw_ops *fw_ops;
> + bool is_region_label;
> + struct cxl_pmem_region_params cxl_region_params;
> };
>
> static inline unsigned long nvdimm_security_flags(
> diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
> index b241a0b2e314..281d30dd9ba0 100644
> --- a/drivers/nvdimm/nd.h
> +++ b/drivers/nvdimm/nd.h
> @@ -600,6 +600,7 @@ void nvdimm_set_locked(struct device *dev);
> void nvdimm_clear_locked(struct device *dev);
> int nvdimm_security_setup_events(struct device *dev);
> bool nvdimm_region_label_supported(struct device *dev);
> +int nvdimm_cxl_region_preserve(struct nvdimm_drvdata *ndd);
> #if IS_ENABLED(CONFIG_NVDIMM_KEYS)
> int nvdimm_security_unlock(struct device *dev);
> #else
> diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
> index bbf14a260c93..07ea2e3f821a 100644
> --- a/include/linux/libnvdimm.h
> +++ b/include/linux/libnvdimm.h
> @@ -108,6 +108,20 @@ struct nd_cmd_desc {
> int out_sizes[ND_CMD_MAX_ELEM];
> };
>
> +struct cxl_pmem_region_params {
> + uuid_t uuid;
> + u32 flags;
> + u16 nlabel;
> + u16 position;
> + u64 dpa;
> + u64 rawsize;
> + u64 hpa;
> + u32 slot;
> + u32 ig;
> + u32 align;
> + int nr_targets;
> +};
> +
> struct nd_interleave_set {
> /* v1.1 definition of the interleave-set-cookie algorithm */
> u64 cookie1;
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 09/17] nvdimm/label: Export routine to fetch region information
2025-11-19 7:52 ` [PATCH V4 09/17] nvdimm/label: Export routine to fetch region information Neeraj Kumar
@ 2025-11-19 20:18 ` Dave Jiang
2025-12-17 15:12 ` Jonathan Cameron
1 sibling, 0 replies; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 20:18 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> CXL region information preserved from the LSA needs to be exported for
> use by the CXL driver for CXL region re-creation.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/nvdimm/dimm_devs.c | 18 ++++++++++++++++++
> include/linux/libnvdimm.h | 2 ++
> 2 files changed, 20 insertions(+)
>
> diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
> index 3363a97cc5b5..1474b4e45fcc 100644
> --- a/drivers/nvdimm/dimm_devs.c
> +++ b/drivers/nvdimm/dimm_devs.c
> @@ -280,6 +280,24 @@ void *nvdimm_provider_data(struct nvdimm *nvdimm)
> }
> EXPORT_SYMBOL_GPL(nvdimm_provider_data);
>
> +bool nvdimm_has_cxl_region(struct nvdimm *nvdimm)
> +{
> + if (!nvdimm)
> + return false;
> +
> + return nvdimm->is_region_label;
> +}
> +EXPORT_SYMBOL_GPL(nvdimm_has_cxl_region);
> +
> +void *nvdimm_get_cxl_region_param(struct nvdimm *nvdimm)
> +{
> + if (!nvdimm)
> + return NULL;
> +
> + return &nvdimm->cxl_region_params;
> +}
> +EXPORT_SYMBOL_GPL(nvdimm_get_cxl_region_param);
> +
> static ssize_t commands_show(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
> index 07ea2e3f821a..3ffd50ab6ac4 100644
> --- a/include/linux/libnvdimm.h
> +++ b/include/linux/libnvdimm.h
> @@ -330,6 +330,8 @@ int nvdimm_in_overwrite(struct nvdimm *nvdimm);
> bool is_nvdimm_sync(struct nd_region *nd_region);
> int nd_region_label_update(struct nd_region *nd_region);
> int nd_region_label_delete(struct nd_region *nd_region);
> +bool nvdimm_has_cxl_region(struct nvdimm *nvdimm);
> +void *nvdimm_get_cxl_region_param(struct nvdimm *nvdimm);
>
> static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf,
> unsigned int buf_len, int *cmd_rc)
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 10/17] cxl/mem: Refactor cxl pmem region auto-assembling
2025-11-19 7:52 ` [PATCH V4 10/17] cxl/mem: Refactor cxl pmem region auto-assembling Neeraj Kumar
@ 2025-11-19 20:44 ` Dave Jiang
2026-01-09 12:10 ` Neeraj Kumar
0 siblings, 1 reply; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 20:44 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> In 84ec985944ef3, devm_cxl_add_nvdimm() sequence was changed and called
> before devm_cxl_add_endpoint(). It's because cxl pmem region auto-assembly
> used to get called at last in cxl_endpoint_port_probe(), which requires
> cxl_nvd presence.
>
> For cxl region persistency, region creation happens during nvdimm_probe
> which need the completion of endpoint probe.
>
> In order to accommodate both cxl pmem region auto-assembly and cxl region
> persistency, refactored following
>
> 1. Re-Sequence devm_cxl_add_nvdimm() after devm_cxl_add_endpoint(). This
> will be called only after successful completion of endpoint probe.
>
> 2. Create cxl_region_discovery() which performs pmem region
> auto-assembly and remove cxl pmem region auto-assembly from
> cxl_endpoint_port_probe()
>
> 3. Register cxl_region_discovery() with devm_cxl_add_memdev() which gets
> called during cxl_pci_probe() in context of cxl_mem_probe()
>
> 4. As cxlmd->ops->probe() calls registered cxl_region_discovery(), so
> move devm_cxl_add_nvdimm() before cxlmd->ops->probe(). It gurantees
s/gurantees/guarantees/
> both the completion of endpoint probe and cxl_nvd presence before
> calling cxlmd->ops->probe().
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/cxl/core/region.c | 37 +++++++++++++++++++++++++++++++++++++
> drivers/cxl/cxl.h | 5 +++++
> drivers/cxl/mem.c | 18 +++++++++---------
> drivers/cxl/pci.c | 4 +++-
> drivers/cxl/port.c | 39 +--------------------------------------
> 5 files changed, 55 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 2cf5b29cefd2..3c868c4de4ec 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3724,6 +3724,43 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
> }
> EXPORT_SYMBOL_NS_GPL(cxl_add_to_region, "CXL");
>
> +static int discover_region(struct device *dev, void *unused)
> +{
> + struct cxl_endpoint_decoder *cxled;
> + int rc;
> +
> + if (!is_endpoint_decoder(dev))
> + return 0;
> +
> + cxled = to_cxl_endpoint_decoder(dev);
> + if ((cxled->cxld.flags & CXL_DECODER_F_ENABLE) == 0)
> + return 0;
> +
> + if (cxled->state != CXL_DECODER_STATE_AUTO)
> + return 0;
> +
> + /*
> + * Region enumeration is opportunistic, if this add-event fails,
> + * continue to the next endpoint decoder.
> + */
> + rc = cxl_add_to_region(cxled);
> + if (rc)
> + dev_dbg(dev, "failed to add to region: %#llx-%#llx\n",
> + cxled->cxld.hpa_range.start, cxled->cxld.hpa_range.end);
> +
> + return 0;
> +}
> +
> +int cxl_region_discovery(struct cxl_memdev *cxlmd)
> +{
> + struct cxl_port *port = cxlmd->endpoint;
> +
> + device_for_each_child(&port->dev, NULL, discover_region);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_region_discovery, "CXL");
> +
> u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa)
> {
> struct cxl_region_ref *iter;
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index ba17fa86d249..684a0d1b441a 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -904,6 +904,7 @@ struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
> int cxl_add_to_region(struct cxl_endpoint_decoder *cxled);
> struct cxl_dax_region *to_cxl_dax_region(struct device *dev);
> u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa);
> +int cxl_region_discovery(struct cxl_memdev *cxlmd);
> #else
> static inline bool is_cxl_pmem_region(struct device *dev)
> {
> @@ -926,6 +927,10 @@ static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint,
> {
> return 0;
> }
> +static inline int cxl_region_discovery(struct cxl_memdev *cxlmd)
> +{
> + return 0;
> +}
> #endif
>
> void cxl_endpoint_parse_cdat(struct cxl_port *port);
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 13d9e089ecaf..f5e3e2fca86c 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -115,15 +115,6 @@ static int cxl_mem_probe(struct device *dev)
> return -ENXIO;
> }
>
> - if (cxl_pmem_size(cxlds) && IS_ENABLED(CONFIG_CXL_PMEM)) {
> - rc = devm_cxl_add_nvdimm(parent_port, cxlmd);
> - if (rc) {
> - if (rc == -ENODEV)
> - dev_info(dev, "PMEM disabled by platform\n");
> - return rc;
> - }
> - }
> -
> if (dport->rch)
> endpoint_parent = parent_port->uport_dev;
> else
> @@ -143,6 +134,15 @@ static int cxl_mem_probe(struct device *dev)
> return rc;
> }
>
> + if (cxl_pmem_size(cxlds) && IS_ENABLED(CONFIG_CXL_PMEM)) {
> + rc = devm_cxl_add_nvdimm(parent_port, cxlmd);
> + if (rc) {
> + if (rc == -ENODEV)
> + dev_info(dev, "PMEM disabled by platform\n");
> + return rc;
> + }
> + }
> +
> if (cxlmd->ops) {
> rc = cxlmd->ops->probe(cxlmd);
> if (rc)
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index e21051d79b25..d56fdfe4b43b 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -907,6 +907,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> struct cxl_memdev_state *mds;
> struct cxl_dev_state *cxlds;
> struct cxl_register_map map;
> + struct cxl_memdev_ops ops;
> struct cxl_memdev *cxlmd;
> int rc, pmu_count;
> unsigned int i;
> @@ -1006,7 +1007,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> if (rc)
> dev_dbg(&pdev->dev, "No CXL Features discovered\n");
>
> - cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds, NULL);
> + ops.probe = cxl_region_discovery;
> + cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds, &ops);
> if (IS_ERR(cxlmd))
> return PTR_ERR(cxlmd);
>
> diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
> index d5fd0c5ae49b..ad98b2881fed 100644
> --- a/drivers/cxl/port.c
> +++ b/drivers/cxl/port.c
> @@ -31,33 +31,6 @@ static void schedule_detach(void *cxlmd)
> schedule_cxl_memdev_detach(cxlmd);
> }
>
> -static int discover_region(struct device *dev, void *unused)
> -{
> - struct cxl_endpoint_decoder *cxled;
> - int rc;
> -
> - if (!is_endpoint_decoder(dev))
> - return 0;
> -
> - cxled = to_cxl_endpoint_decoder(dev);
> - if ((cxled->cxld.flags & CXL_DECODER_F_ENABLE) == 0)
> - return 0;
> -
> - if (cxled->state != CXL_DECODER_STATE_AUTO)
> - return 0;
> -
> - /*
> - * Region enumeration is opportunistic, if this add-event fails,
> - * continue to the next endpoint decoder.
> - */
> - rc = cxl_add_to_region(cxled);
> - if (rc)
> - dev_dbg(dev, "failed to add to region: %#llx-%#llx\n",
> - cxled->cxld.hpa_range.start, cxled->cxld.hpa_range.end);
> -
> - return 0;
> -}
> -
> static int cxl_switch_port_probe(struct cxl_port *port)
> {
> /* Reset nr_dports for rebind of driver */
> @@ -83,17 +56,7 @@ static int cxl_endpoint_port_probe(struct cxl_port *port)
> if (rc)
> return rc;
>
> - rc = devm_cxl_endpoint_decoders_setup(port);
> - if (rc)
> - return rc;
> -
> - /*
> - * Now that all endpoint decoders are successfully enumerated, try to
> - * assemble regions from committed decoders
> - */
> - device_for_each_child(&port->dev, NULL, discover_region);
> -
> - return 0;
> + return devm_cxl_endpoint_decoders_setup(port);
> }
>
> static int cxl_port_probe(struct device *dev)
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 11/17] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation
2025-11-19 7:52 ` [PATCH V4 11/17] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation Neeraj Kumar
@ 2025-11-19 21:33 ` Dave Jiang
2026-01-09 12:13 ` Neeraj Kumar
2025-12-17 15:28 ` Jonathan Cameron
1 sibling, 1 reply; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 21:33 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> devm_cxl_pmem_add_region() is used to create cxl region based on region
> information scanned from LSA.
>
> devm_cxl_add_region() is used to just allocate cxlr and its fields are
> filled later by userspace tool using device attributes (*_store()).
>
> Inspiration for devm_cxl_pmem_add_region() is taken from these device
> attributes (_store*) calls. It allocates cxlr and fills information
> parsed from LSA and calls device_add(&cxlr->dev) to initiate further
> region creation porbes
>
> Rename __create_region() to cxl_create_region(), which will be used
> in later patch to create cxl region after fetching region information
> from LSA.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
small comment below, otherwise
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/cxl/core/core.h | 12 ++++
> drivers/cxl/core/region.c | 124 ++++++++++++++++++++++++++++++++++++--
> 2 files changed, 131 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 1fb66132b777..fde96507cb75 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -42,6 +42,10 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port);
> struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
> u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> u64 dpa);
> +struct cxl_region *cxl_create_region(struct cxl_root_decoder *cxlrd,
> + enum cxl_partition_mode mode, int id,
> + struct cxl_pmem_region_params *params,
> + struct cxl_decoder *cxld);
>
> #else
> static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
> @@ -71,6 +75,14 @@ static inline int cxl_region_init(void)
> static inline void cxl_region_exit(void)
> {
> }
> +static inline struct cxl_region *
> +cxl_create_region(struct cxl_root_decoder *cxlrd,
> + enum cxl_partition_mode mode, int id,
> + struct cxl_pmem_region_params *params,
> + struct cxl_decoder *cxld)
> +{
> + return ERR_PTR(-EOPNOTSUPP);
> +}
> #define CXL_REGION_ATTR(x) NULL
> #define CXL_REGION_TYPE(x) NULL
> #define SET_CXL_REGION_ATTR(x)
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 3c868c4de4ec..06a75f0a8e9b 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -2618,6 +2618,114 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd,
> return ERR_PTR(rc);
> }
>
> +static ssize_t alloc_region_hpa(struct cxl_region *cxlr, u64 size)
> +{
> + int rc;
> +
> + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
> + return rc;
> +
> + if (!size)
> + return -EINVAL;
Why not do this check before acquiring the lock?
DJ
> +
> + return alloc_hpa(cxlr, size);
> +}
> +
> +static ssize_t alloc_region_dpa(struct cxl_endpoint_decoder *cxled, u64 size)
> +{
> + int rc;
> +
> + if (!size)
> + return -EINVAL;
> +
> + if (!IS_ALIGNED(size, SZ_256M))
> + return -EINVAL;
> +
> + rc = cxl_dpa_free(cxled);
> + if (rc)
> + return rc;
> +
> + return cxl_dpa_alloc(cxled, size);
> +}
> +
> +static struct cxl_region *
> +devm_cxl_pmem_add_region(struct cxl_root_decoder *cxlrd, int id,
> + struct cxl_pmem_region_params *params,
> + struct cxl_decoder *cxld,
> + enum cxl_decoder_type type)
> +{
> + struct cxl_endpoint_decoder *cxled;
> + struct cxl_region_params *p;
> + struct cxl_port *root_port;
> + struct device *dev;
> + int rc;
> +
> + struct cxl_region *cxlr __free(put_cxl_region) =
> + cxl_region_alloc(cxlrd, id);
> + if (IS_ERR(cxlr))
> + return cxlr;
> +
> + cxlr->mode = CXL_PARTMODE_PMEM;
> + cxlr->type = type;
> +
> + dev = &cxlr->dev;
> + rc = dev_set_name(dev, "region%d", id);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + p = &cxlr->params;
> + p->uuid = params->uuid;
> + p->interleave_ways = params->nlabel;
> + p->interleave_granularity = params->ig;
> +
> + rc = alloc_region_hpa(cxlr, params->rawsize);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + cxled = to_cxl_endpoint_decoder(&cxld->dev);
> +
> + rc = cxl_dpa_set_part(cxled, CXL_PARTMODE_PMEM);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + rc = alloc_region_dpa(cxled, params->rawsize);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + /*
> + * TODO: Currently we have support of interleave_way == 1, where
> + * we can only have one region per mem device. It means mem device
> + * position (params->position) will always be 0. It is therefore
> + * attaching only one target at params->position
> + */
> + if (params->position)
> + return ERR_PTR(-EOPNOTSUPP);
> +
> + rc = attach_target(cxlr, cxled, params->position, TASK_INTERRUPTIBLE);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + rc = __commit(cxlr);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + rc = device_add(dev);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + root_port = to_cxl_port(cxlrd->cxlsd.cxld.dev.parent);
> + rc = devm_add_action_or_reset(root_port->uport_dev,
> + unregister_region, cxlr);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + dev_dbg(root_port->uport_dev, "%s: created %s\n",
> + dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev));
> +
> + return no_free_ptr(cxlr);
> +}
> +
> static ssize_t __create_region_show(struct cxl_root_decoder *cxlrd, char *buf)
> {
> return sysfs_emit(buf, "region%u\n", atomic_read(&cxlrd->region_id));
> @@ -2635,8 +2743,10 @@ static ssize_t create_ram_region_show(struct device *dev,
> return __create_region_show(to_cxl_root_decoder(dev), buf);
> }
>
> -static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd,
> - enum cxl_partition_mode mode, int id)
> +struct cxl_region *cxl_create_region(struct cxl_root_decoder *cxlrd,
> + enum cxl_partition_mode mode, int id,
> + struct cxl_pmem_region_params *pmem_params,
> + struct cxl_decoder *cxld)
> {
> int rc;
>
> @@ -2658,6 +2768,9 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd,
> return ERR_PTR(-EBUSY);
> }
>
> + if (pmem_params)
> + return devm_cxl_pmem_add_region(cxlrd, id, pmem_params, cxld,
> + CXL_DECODER_HOSTONLYMEM);
> return devm_cxl_add_region(cxlrd, id, mode, CXL_DECODER_HOSTONLYMEM);
> }
>
> @@ -2672,7 +2785,7 @@ static ssize_t create_region_store(struct device *dev, const char *buf,
> if (rc != 1)
> return -EINVAL;
>
> - cxlr = __create_region(cxlrd, mode, id);
> + cxlr = cxl_create_region(cxlrd, mode, id, NULL, NULL);
> if (IS_ERR(cxlr))
> return PTR_ERR(cxlr);
>
> @@ -3641,8 +3754,9 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
> struct cxl_region *cxlr;
>
> do {
> - cxlr = __create_region(cxlrd, cxlds->part[part].mode,
> - atomic_read(&cxlrd->region_id));
> + cxlr = cxl_create_region(cxlrd, cxlds->part[part].mode,
> + atomic_read(&cxlrd->region_id),
> + NULL, NULL);
> } while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY);
>
> if (IS_ERR(cxlr)) {
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 12/17] cxl/pmem: Preserve region information into nd_set
2025-11-19 7:52 ` [PATCH V4 12/17] cxl/pmem: Preserve region information into nd_set Neeraj Kumar
@ 2025-11-19 22:00 ` Dave Jiang
0 siblings, 0 replies; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 22:00 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> Save region information stored in cxlr to nd_set during
> cxl_pmem_region_probe in nd_set. This saved region information is being
> stored into LSA, which will be used for cxl region persistence
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/cxl/pmem.c | 13 +++++++------
> 1 file changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c
> index e197883690ef..a6eba3572090 100644
> --- a/drivers/cxl/pmem.c
> +++ b/drivers/cxl/pmem.c
> @@ -377,6 +377,7 @@ static int cxl_pmem_region_probe(struct device *dev)
> struct nd_mapping_desc mappings[CXL_DECODER_MAX_INTERLEAVE];
> struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
> struct cxl_region *cxlr = cxlr_pmem->cxlr;
> + struct cxl_region_params *p = &cxlr->params;
> struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
> struct cxl_pmem_region_info *info = NULL;
> struct nd_interleave_set *nd_set;
> @@ -465,12 +466,12 @@ static int cxl_pmem_region_probe(struct device *dev)
> ndr_desc.num_mappings = cxlr_pmem->nr_mappings;
> ndr_desc.mapping = mappings;
>
> - /*
> - * TODO enable CXL labels which skip the need for 'interleave-set cookie'
> - */
> - nd_set->cookie1 =
> - nd_fletcher64(info, sizeof(*info) * cxlr_pmem->nr_mappings, 0);
> - nd_set->cookie2 = nd_set->cookie1;
> + nd_set->uuid = p->uuid;
> + nd_set->interleave_ways = p->interleave_ways;
> + nd_set->interleave_granularity = p->interleave_granularity;
> + nd_set->res = p->res;
> + nd_set->nr_targets = p->nr_targets;
> +
> ndr_desc.nd_set = nd_set;
>
> cxlr_pmem->nd_region =
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 13/17] cxl/pmem_region: Prep patch to accommodate pmem_region attributes
2025-11-19 7:52 ` [PATCH V4 13/17] cxl/pmem_region: Prep patch to accommodate pmem_region attributes Neeraj Kumar
@ 2025-11-19 22:08 ` Dave Jiang
2025-12-17 15:35 ` Jonathan Cameron
1 sibling, 0 replies; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 22:08 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> For region label update, need to create device attribute, which calls
> nvdimm exported routine thus making pmem_region dependent on libnvdimm.
> Because of this dependency of pmem region on libnvdimm, segregate pmem
> region related code from core/region.c to core/pmem_region.c
>
> This patch has no functionality change. Its just code movement from
> core/region.c to core/pmem_region.c
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/cxl/core/Makefile | 2 +-
> drivers/cxl/core/core.h | 10 ++
> drivers/cxl/core/pmem_region.c | 202 +++++++++++++++++++++++++++++++++
> drivers/cxl/core/region.c | 188 +-----------------------------
> tools/testing/cxl/Kbuild | 2 +-
> 5 files changed, 215 insertions(+), 189 deletions(-)
> create mode 100644 drivers/cxl/core/pmem_region.c
>
> diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
> index 5ad8fef210b5..fe0fcab6d730 100644
> --- a/drivers/cxl/core/Makefile
> +++ b/drivers/cxl/core/Makefile
> @@ -16,7 +16,7 @@ cxl_core-y += pmu.o
> cxl_core-y += cdat.o
> cxl_core-y += ras.o
> cxl_core-$(CONFIG_TRACING) += trace.o
> -cxl_core-$(CONFIG_CXL_REGION) += region.o
> +cxl_core-$(CONFIG_CXL_REGION) += region.o pmem_region.o
> cxl_core-$(CONFIG_CXL_MCE) += mce.o
> cxl_core-$(CONFIG_CXL_FEATURES) += features.o
> cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index fde96507cb75..5ebbc3d3dde5 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -46,6 +46,8 @@ struct cxl_region *cxl_create_region(struct cxl_root_decoder *cxlrd,
> enum cxl_partition_mode mode, int id,
> struct cxl_pmem_region_params *params,
> struct cxl_decoder *cxld);
> +struct cxl_region *to_cxl_region(struct device *dev);
> +int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
>
> #else
> static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
> @@ -83,6 +85,14 @@ cxl_create_region(struct cxl_root_decoder *cxlrd,
> {
> return ERR_PTR(-EOPNOTSUPP);
> }
> +static inline struct cxl_region *to_cxl_region(struct device *dev)
> +{
> + return NULL;
> +}
> +static inline int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
> +{
> + return 0;
> +}
> #define CXL_REGION_ATTR(x) NULL
> #define CXL_REGION_TYPE(x) NULL
> #define SET_CXL_REGION_ATTR(x)
> diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
> new file mode 100644
> index 000000000000..b45e60f04ff4
> --- /dev/null
> +++ b/drivers/cxl/core/pmem_region.c
> @@ -0,0 +1,202 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2020 Intel Corporation. */
> +#include <linux/device.h>
> +#include <linux/memregion.h>
> +#include <cxlmem.h>
> +#include <cxl.h>
> +#include "core.h"
> +
> +/**
> + * DOC: cxl pmem region
> + *
> + * The core CXL PMEM region infrastructure supports persistent memory
> + * region creation using LIBNVDIMM subsystem. It has dependency on
> + * LIBNVDIMM, pmem region need updation of cxl region information into
> + * LSA. LIBNVDIMM dependency is only for pmem region, it is therefore
> + * need this separate file.
> + */
> +
> +static void cxl_pmem_region_release(struct device *dev)
> +{
> + struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
> + int i;
> +
> + for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
> + struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd;
> +
> + put_device(&cxlmd->dev);
> + }
> +
> + kfree(cxlr_pmem);
> +}
> +
> +static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
> + &cxl_base_attribute_group,
> + NULL,
> +};
> +
> +const struct device_type cxl_pmem_region_type = {
> + .name = "cxl_pmem_region",
> + .release = cxl_pmem_region_release,
> + .groups = cxl_pmem_region_attribute_groups,
> +};
> +
> +bool is_cxl_pmem_region(struct device *dev)
> +{
> + return dev->type == &cxl_pmem_region_type;
> +}
> +EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL");
> +
> +struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
> +{
> + if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev),
> + "not a cxl_pmem_region device\n"))
> + return NULL;
> + return container_of(dev, struct cxl_pmem_region, dev);
> +}
> +EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL");
> +
> +static struct lock_class_key cxl_pmem_region_key;
> +
> +static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
> +{
> + struct cxl_region_params *p = &cxlr->params;
> + struct cxl_nvdimm_bridge *cxl_nvb;
> + struct device *dev;
> + int i;
> +
> + guard(rwsem_read)(&cxl_rwsem.region);
> + if (p->state != CXL_CONFIG_COMMIT)
> + return -ENXIO;
> +
> + struct cxl_pmem_region *cxlr_pmem __free(kfree) =
> + kzalloc(struct_size(cxlr_pmem, mapping, p->nr_targets),
> + GFP_KERNEL);
> + if (!cxlr_pmem)
> + return -ENOMEM;
> +
> + cxlr_pmem->hpa_range.start = p->res->start;
> + cxlr_pmem->hpa_range.end = p->res->end;
> +
> + /* Snapshot the region configuration underneath the cxl_region_rwsem */
> + cxlr_pmem->nr_mappings = p->nr_targets;
> + for (i = 0; i < p->nr_targets; i++) {
> + struct cxl_endpoint_decoder *cxled = p->targets[i];
> + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> + struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i];
> +
> + /*
> + * Regions never span CXL root devices, so by definition the
> + * bridge for one device is the same for all.
> + */
> + if (i == 0) {
> + cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint);
> + if (!cxl_nvb)
> + return -ENODEV;
> + cxlr->cxl_nvb = cxl_nvb;
> + }
> + m->cxlmd = cxlmd;
> + get_device(&cxlmd->dev);
> + m->start = cxled->dpa_res->start;
> + m->size = resource_size(cxled->dpa_res);
> + m->position = i;
> + }
> +
> + dev = &cxlr_pmem->dev;
> + device_initialize(dev);
> + lockdep_set_class(&dev->mutex, &cxl_pmem_region_key);
> + device_set_pm_not_required(dev);
> + dev->parent = &cxlr->dev;
> + dev->bus = &cxl_bus_type;
> + dev->type = &cxl_pmem_region_type;
> + cxlr_pmem->cxlr = cxlr;
> + cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem);
> +
> + return 0;
> +}
> +
> +static void cxlr_pmem_unregister(void *_cxlr_pmem)
> +{
> + struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem;
> + struct cxl_region *cxlr = cxlr_pmem->cxlr;
> + struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
> +
> + /*
> + * Either the bridge is in ->remove() context under the device_lock(),
> + * or cxlr_release_nvdimm() is cancelling the bridge's release action
> + * for @cxlr_pmem and doing it itself (while manually holding the bridge
> + * lock).
> + */
> + device_lock_assert(&cxl_nvb->dev);
> + cxlr->cxlr_pmem = NULL;
> + cxlr_pmem->cxlr = NULL;
> + device_unregister(&cxlr_pmem->dev);
> +}
> +
> +static void cxlr_release_nvdimm(void *_cxlr)
> +{
> + struct cxl_region *cxlr = _cxlr;
> + struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
> +
> + scoped_guard(device, &cxl_nvb->dev) {
> + if (cxlr->cxlr_pmem)
> + devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister,
> + cxlr->cxlr_pmem);
> + }
> + cxlr->cxl_nvb = NULL;
> + put_device(&cxl_nvb->dev);
> +}
> +
> +/**
> + * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge
> + * @cxlr: parent CXL region for this pmem region bridge device
> + *
> + * Return: 0 on success negative error code on failure.
> + */
> +int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
> +{
> + struct cxl_pmem_region *cxlr_pmem;
> + struct cxl_nvdimm_bridge *cxl_nvb;
> + struct device *dev;
> + int rc;
> +
> + rc = cxl_pmem_region_alloc(cxlr);
> + if (rc)
> + return rc;
> + cxlr_pmem = cxlr->cxlr_pmem;
> + cxl_nvb = cxlr->cxl_nvb;
> +
> + dev = &cxlr_pmem->dev;
> + rc = dev_set_name(dev, "pmem_region%d", cxlr->id);
> + if (rc)
> + goto err;
> +
> + rc = device_add(dev);
> + if (rc)
> + goto err;
> +
> + dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
> + dev_name(dev));
> +
> + scoped_guard(device, &cxl_nvb->dev) {
> + if (cxl_nvb->dev.driver)
> + rc = devm_add_action_or_reset(&cxl_nvb->dev,
> + cxlr_pmem_unregister,
> + cxlr_pmem);
> + else
> + rc = -ENXIO;
> + }
> +
> + if (rc)
> + goto err_bridge;
> +
> + /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */
> + return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr);
> +
> +err:
> + put_device(dev);
> +err_bridge:
> + put_device(&cxl_nvb->dev);
> + cxlr->cxl_nvb = NULL;
> + return rc;
> +}
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 06a75f0a8e9b..9798120b208e 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -38,8 +38,6 @@
> */
> static nodemask_t nodemask_region_seen = NODE_MASK_NONE;
>
> -static struct cxl_region *to_cxl_region(struct device *dev);
> -
> #define __ACCESS_ATTR_RO(_level, _name) { \
> .attr = { .name = __stringify(_name), .mode = 0444 }, \
> .show = _name##_access##_level##_show, \
> @@ -2426,7 +2424,7 @@ bool is_cxl_region(struct device *dev)
> }
> EXPORT_SYMBOL_NS_GPL(is_cxl_region, "CXL");
>
> -static struct cxl_region *to_cxl_region(struct device *dev)
> +struct cxl_region *to_cxl_region(struct device *dev)
> {
> if (dev_WARN_ONCE(dev, dev->type != &cxl_region_type,
> "not a cxl_region device\n"))
> @@ -2856,46 +2854,6 @@ static ssize_t delete_region_store(struct device *dev,
> }
> DEVICE_ATTR_WO(delete_region);
>
> -static void cxl_pmem_region_release(struct device *dev)
> -{
> - struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
> - int i;
> -
> - for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
> - struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd;
> -
> - put_device(&cxlmd->dev);
> - }
> -
> - kfree(cxlr_pmem);
> -}
> -
> -static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
> - &cxl_base_attribute_group,
> - NULL,
> -};
> -
> -const struct device_type cxl_pmem_region_type = {
> - .name = "cxl_pmem_region",
> - .release = cxl_pmem_region_release,
> - .groups = cxl_pmem_region_attribute_groups,
> -};
> -
> -bool is_cxl_pmem_region(struct device *dev)
> -{
> - return dev->type == &cxl_pmem_region_type;
> -}
> -EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL");
> -
> -struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
> -{
> - if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev),
> - "not a cxl_pmem_region device\n"))
> - return NULL;
> - return container_of(dev, struct cxl_pmem_region, dev);
> -}
> -EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL");
> -
> struct cxl_poison_context {
> struct cxl_port *port;
> int part;
> @@ -3327,64 +3285,6 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
> return -ENXIO;
> }
>
> -static struct lock_class_key cxl_pmem_region_key;
> -
> -static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
> -{
> - struct cxl_region_params *p = &cxlr->params;
> - struct cxl_nvdimm_bridge *cxl_nvb;
> - struct device *dev;
> - int i;
> -
> - guard(rwsem_read)(&cxl_rwsem.region);
> - if (p->state != CXL_CONFIG_COMMIT)
> - return -ENXIO;
> -
> - struct cxl_pmem_region *cxlr_pmem __free(kfree) =
> - kzalloc(struct_size(cxlr_pmem, mapping, p->nr_targets), GFP_KERNEL);
> - if (!cxlr_pmem)
> - return -ENOMEM;
> -
> - cxlr_pmem->hpa_range.start = p->res->start;
> - cxlr_pmem->hpa_range.end = p->res->end;
> -
> - /* Snapshot the region configuration underneath the cxl_rwsem.region */
> - cxlr_pmem->nr_mappings = p->nr_targets;
> - for (i = 0; i < p->nr_targets; i++) {
> - struct cxl_endpoint_decoder *cxled = p->targets[i];
> - struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> - struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i];
> -
> - /*
> - * Regions never span CXL root devices, so by definition the
> - * bridge for one device is the same for all.
> - */
> - if (i == 0) {
> - cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint);
> - if (!cxl_nvb)
> - return -ENODEV;
> - cxlr->cxl_nvb = cxl_nvb;
> - }
> - m->cxlmd = cxlmd;
> - get_device(&cxlmd->dev);
> - m->start = cxled->dpa_res->start;
> - m->size = resource_size(cxled->dpa_res);
> - m->position = i;
> - }
> -
> - dev = &cxlr_pmem->dev;
> - device_initialize(dev);
> - lockdep_set_class(&dev->mutex, &cxl_pmem_region_key);
> - device_set_pm_not_required(dev);
> - dev->parent = &cxlr->dev;
> - dev->bus = &cxl_bus_type;
> - dev->type = &cxl_pmem_region_type;
> - cxlr_pmem->cxlr = cxlr;
> - cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem);
> -
> - return 0;
> -}
> -
> static void cxl_dax_region_release(struct device *dev)
> {
> struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev);
> @@ -3448,92 +3348,6 @@ static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
> return cxlr_dax;
> }
>
> -static void cxlr_pmem_unregister(void *_cxlr_pmem)
> -{
> - struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem;
> - struct cxl_region *cxlr = cxlr_pmem->cxlr;
> - struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
> -
> - /*
> - * Either the bridge is in ->remove() context under the device_lock(),
> - * or cxlr_release_nvdimm() is cancelling the bridge's release action
> - * for @cxlr_pmem and doing it itself (while manually holding the bridge
> - * lock).
> - */
> - device_lock_assert(&cxl_nvb->dev);
> - cxlr->cxlr_pmem = NULL;
> - cxlr_pmem->cxlr = NULL;
> - device_unregister(&cxlr_pmem->dev);
> -}
> -
> -static void cxlr_release_nvdimm(void *_cxlr)
> -{
> - struct cxl_region *cxlr = _cxlr;
> - struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
> -
> - scoped_guard(device, &cxl_nvb->dev) {
> - if (cxlr->cxlr_pmem)
> - devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister,
> - cxlr->cxlr_pmem);
> - }
> - cxlr->cxl_nvb = NULL;
> - put_device(&cxl_nvb->dev);
> -}
> -
> -/**
> - * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge
> - * @cxlr: parent CXL region for this pmem region bridge device
> - *
> - * Return: 0 on success negative error code on failure.
> - */
> -static int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
> -{
> - struct cxl_pmem_region *cxlr_pmem;
> - struct cxl_nvdimm_bridge *cxl_nvb;
> - struct device *dev;
> - int rc;
> -
> - rc = cxl_pmem_region_alloc(cxlr);
> - if (rc)
> - return rc;
> - cxlr_pmem = cxlr->cxlr_pmem;
> - cxl_nvb = cxlr->cxl_nvb;
> -
> - dev = &cxlr_pmem->dev;
> - rc = dev_set_name(dev, "pmem_region%d", cxlr->id);
> - if (rc)
> - goto err;
> -
> - rc = device_add(dev);
> - if (rc)
> - goto err;
> -
> - dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
> - dev_name(dev));
> -
> - scoped_guard(device, &cxl_nvb->dev) {
> - if (cxl_nvb->dev.driver)
> - rc = devm_add_action_or_reset(&cxl_nvb->dev,
> - cxlr_pmem_unregister,
> - cxlr_pmem);
> - else
> - rc = -ENXIO;
> - }
> -
> - if (rc)
> - goto err_bridge;
> -
> - /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */
> - return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr);
> -
> -err:
> - put_device(dev);
> -err_bridge:
> - put_device(&cxl_nvb->dev);
> - cxlr->cxl_nvb = NULL;
> - return rc;
> -}
> -
> static void cxlr_dax_unregister(void *_cxlr_dax)
> {
> struct cxl_dax_region *cxlr_dax = _cxlr_dax;
> diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
> index 0e151d0572d1..ad2496b38fdd 100644
> --- a/tools/testing/cxl/Kbuild
> +++ b/tools/testing/cxl/Kbuild
> @@ -59,7 +59,7 @@ cxl_core-y += $(CXL_CORE_SRC)/pmu.o
> cxl_core-y += $(CXL_CORE_SRC)/cdat.o
> cxl_core-y += $(CXL_CORE_SRC)/ras.o
> cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
> -cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
> +cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/pmem_region.o
> cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o
> cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
> cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 14/17] cxl/pmem_region: Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c
2025-11-19 7:52 ` [PATCH V4 14/17] cxl/pmem_region: Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c Neeraj Kumar
@ 2025-11-19 22:24 ` Dave Jiang
2025-12-17 15:38 ` Jonathan Cameron
1 sibling, 0 replies; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 22:24 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> As pmem region label update/delete has hard dependency on libnvdimm.
> It is therefore put core/pmem_region.c under CONFIG_CXL_PMEM_REGION
> control. It handles the dependency by selecting CONFIG_LIBNVDIMM
> if not enabled.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/cxl/Kconfig | 15 +++++++++++++++
> drivers/cxl/core/Makefile | 3 ++-
> drivers/cxl/core/core.h | 17 +++++++++++------
> drivers/cxl/core/region.c | 2 ++
> drivers/cxl/cxl.h | 24 ++++++++++++++----------
> tools/testing/cxl/Kbuild | 3 ++-
> 6 files changed, 46 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
> index f1361ed6a0d4..307fed8f1f56 100644
> --- a/drivers/cxl/Kconfig
> +++ b/drivers/cxl/Kconfig
> @@ -211,6 +211,21 @@ config CXL_REGION
>
> If unsure say 'y'
>
> +config CXL_PMEM_REGION
> + bool "CXL: Pmem Region Support"
> + default CXL_BUS
> + depends on CXL_REGION
> + depends on ARCH_HAS_PMEM_API
> + depends on PHYS_ADDR_T_64BIT
> + depends on BLK_DEV
> + select LIBNVDIMM
> + help
> + Enable the CXL core to enumerate and provision CXL pmem regions.
> + A CXL pmem region need to update region label into LSA. For LSA
> + update/delete libnvdimm is required.
> +
> + If unsure say 'y'
> +
> config CXL_REGION_INVALIDATION_TEST
> bool "CXL: Region Cache Management Bypass (TEST)"
> depends on CXL_REGION
> diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
> index fe0fcab6d730..399157beb917 100644
> --- a/drivers/cxl/core/Makefile
> +++ b/drivers/cxl/core/Makefile
> @@ -16,7 +16,8 @@ cxl_core-y += pmu.o
> cxl_core-y += cdat.o
> cxl_core-y += ras.o
> cxl_core-$(CONFIG_TRACING) += trace.o
> -cxl_core-$(CONFIG_CXL_REGION) += region.o pmem_region.o
> +cxl_core-$(CONFIG_CXL_REGION) += region.o
> +cxl_core-$(CONFIG_CXL_PMEM_REGION) += pmem_region.o
> cxl_core-$(CONFIG_CXL_MCE) += mce.o
> cxl_core-$(CONFIG_CXL_FEATURES) += features.o
> cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 5ebbc3d3dde5..beeb9b7527b8 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -34,7 +34,6 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
> #define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
> #define CXL_REGION_TYPE(x) (&cxl_region_type)
> #define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
> -#define CXL_PMEM_REGION_TYPE(x) (&cxl_pmem_region_type)
> #define CXL_DAX_REGION_TYPE(x) (&cxl_dax_region_type)
> int cxl_region_init(void);
> void cxl_region_exit(void);
> @@ -89,17 +88,23 @@ static inline struct cxl_region *to_cxl_region(struct device *dev)
> {
> return NULL;
> }
> -static inline int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
> -{
> - return 0;
> -}
> #define CXL_REGION_ATTR(x) NULL
> #define CXL_REGION_TYPE(x) NULL
> #define SET_CXL_REGION_ATTR(x)
> -#define CXL_PMEM_REGION_TYPE(x) NULL
> #define CXL_DAX_REGION_TYPE(x) NULL
> #endif
>
> +#ifdef CONFIG_CXL_PMEM_REGION
> +#define CXL_PMEM_REGION_TYPE(x) (&cxl_pmem_region_type)
> +int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
> +#else
> +#define CXL_PMEM_REGION_TYPE(x) NULL
> +static inline int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
> +{
> + return 0;
> +}
> +#endif
> +
> struct cxl_send_command;
> struct cxl_mem_query_commands;
> int cxl_query_cmd(struct cxl_mailbox *cxl_mbox,
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 9798120b208e..408e139718f1 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3918,6 +3918,8 @@ static int cxl_region_probe(struct device *dev)
> dev_dbg(&cxlr->dev, "CXL EDAC registration for region_id=%d failed\n",
> cxlr->id);
>
> + if (!IS_ENABLED(CONFIG_CXL_PMEM_REGION))
> + return -EINVAL;
> return devm_cxl_add_pmem_region(cxlr);
> case CXL_PARTMODE_RAM:
> rc = devm_cxl_region_edac_register(cxlr);
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 684a0d1b441a..6ac3b40cb5ff 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -899,21 +899,11 @@ int devm_cxl_add_nvdimm(struct cxl_port *parent_port, struct cxl_memdev *cxlmd);
> struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port);
>
> #ifdef CONFIG_CXL_REGION
> -bool is_cxl_pmem_region(struct device *dev);
> -struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
> int cxl_add_to_region(struct cxl_endpoint_decoder *cxled);
> struct cxl_dax_region *to_cxl_dax_region(struct device *dev);
> u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa);
> int cxl_region_discovery(struct cxl_memdev *cxlmd);
> #else
> -static inline bool is_cxl_pmem_region(struct device *dev)
> -{
> - return false;
> -}
> -static inline struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
> -{
> - return NULL;
> -}
> static inline int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
> {
> return 0;
> @@ -933,6 +923,20 @@ static inline int cxl_region_discovery(struct cxl_memdev *cxlmd)
> }
> #endif
>
> +#ifdef CONFIG_CXL_PMEM_REGION
> +bool is_cxl_pmem_region(struct device *dev);
> +struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
> +#else
> +static inline bool is_cxl_pmem_region(struct device *dev)
> +{
> + return false;
> +}
> +static inline struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
> +{
> + return NULL;
> +}
> +#endif
> +
> void cxl_endpoint_parse_cdat(struct cxl_port *port);
> void cxl_switch_parse_cdat(struct cxl_dport *dport);
>
> diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
> index ad2496b38fdd..024922326a6b 100644
> --- a/tools/testing/cxl/Kbuild
> +++ b/tools/testing/cxl/Kbuild
> @@ -59,7 +59,8 @@ cxl_core-y += $(CXL_CORE_SRC)/pmu.o
> cxl_core-y += $(CXL_CORE_SRC)/cdat.o
> cxl_core-y += $(CXL_CORE_SRC)/ras.o
> cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
> -cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/pmem_region.o
> +cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
> +cxl_core-$(CONFIG_CXL_PMEM_REGION) += $(CXL_CORE_SRC)/pmem_region.o
> cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o
> cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
> cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 15/17] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion
2025-11-19 7:52 ` [PATCH V4 15/17] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion Neeraj Kumar
@ 2025-11-19 23:10 ` Dave Jiang
2026-01-09 12:31 ` Neeraj Kumar
2025-12-17 15:40 ` Jonathan Cameron
1 sibling, 1 reply; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 23:10 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> Using these attributes region label is added/deleted into LSA. These
> attributes are called from userspace (ndctl) after region creation.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
> ---
> Documentation/ABI/testing/sysfs-bus-cxl | 22 ++++++
> drivers/cxl/core/pmem_region.c | 93 ++++++++++++++++++++++++-
> drivers/cxl/cxl.h | 7 ++
> 3 files changed, 121 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl
> index c80a1b5a03db..76d79c03dde4 100644
> --- a/Documentation/ABI/testing/sysfs-bus-cxl
> +++ b/Documentation/ABI/testing/sysfs-bus-cxl
> @@ -624,3 +624,25 @@ Description:
> The count is persistent across power loss and wraps back to 0
> upon overflow. If this file is not present, the device does not
> have the necessary support for dirty tracking.
> +
> +
> +What: /sys/bus/cxl/devices/regionZ/pmem_regionZ/region_label_update
> +Date: Nov, 2025
> +KernelVersion: v6.19
> +Contact: linux-cxl@vger.kernel.org
> +Description:
> + (RW) Write a boolean 'true' string value to this attribute to
> + update cxl region information into LSA as region label. It
> + uses nvdimm nd_region_label_update() to update cxl region
> + information saved during cxl region creation into LSA. This
> + attribute must be called at last during cxl region creation.
Please consider:
attribute must be written last during cxl region creation.
No need to mention kernel specifics like function names. Just give general description of what the attribute does. Same for the next attribute below.
Also, does this attribute needs to be readable? The documentation above does not explain the read attribute if so.
DJ
> +
> +
> +What: /sys/bus/cxl/devices/regionZ/pmem_regionZ/region_label_delete
> +Date: Nov, 2025
> +KernelVersion: v6.19
> +Contact: linux-cxl@vger.kernel.org
> +Description:
> + (WO) When a boolean 'true' is written to this attribute then
> + pmem_region driver deletes cxl region label from LSA using
> + nvdimm nd_region_label_delete()
> diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
> index b45e60f04ff4..be4feb73aafc 100644
> --- a/drivers/cxl/core/pmem_region.c
> +++ b/drivers/cxl/core/pmem_region.c
> @@ -30,9 +30,100 @@ static void cxl_pmem_region_release(struct device *dev)
> kfree(cxlr_pmem);
> }
>
> +static ssize_t region_label_update_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
> + struct cxl_region *cxlr = cxlr_pmem->cxlr;
> + ssize_t rc;
> + bool update;
> +
> + rc = kstrtobool(buf, &update);
> + if (rc)
> + return rc;
> +
> + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> + rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem);
> + if (rc)
> + return rc;
> +
> + /* Region not yet committed */
> + if (update && cxlr && cxlr->params.state != CXL_CONFIG_COMMIT) {
> + dev_dbg(dev, "region not committed, can't update into LSA\n");
> + return -ENXIO;
> + }
> +
> + if (!cxlr || !cxlr->cxlr_pmem || !cxlr->cxlr_pmem->nd_region)
> + return 0;
> +
> + rc = nd_region_label_update(cxlr->cxlr_pmem->nd_region);
> + if (rc)
> + return rc;
> +
> + cxlr->params.state_region_label = CXL_REGION_LABEL_ACTIVE;
> +
> + return len;
> +}
> +
> +static ssize_t region_label_update_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
> + struct cxl_region *cxlr = cxlr_pmem->cxlr;
> + struct cxl_region_params *p = &cxlr->params;
> + ssize_t rc;
> +
> + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> + rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem);
> + if (rc)
> + return rc;
> +
> + return sysfs_emit(buf, "%d\n", p->state_region_label);
> +}
> +static DEVICE_ATTR_RW(region_label_update);
> +
> +static ssize_t region_label_delete_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
> + struct cxl_region *cxlr = cxlr_pmem->cxlr;
> + ssize_t rc;
> +
> + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> + rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem);
> + if (rc)
> + return rc;
> +
> + if (!cxlr && !cxlr->cxlr_pmem && !cxlr->cxlr_pmem->nd_region)
> + return 0;
> +
> + rc = nd_region_label_delete(cxlr->cxlr_pmem->nd_region);
> + if (rc)
> + return rc;
> +
> + cxlr->params.state_region_label = CXL_REGION_LABEL_INACTIVE;
> +
> + return len;
> +}
> +static DEVICE_ATTR_WO(region_label_delete);
> +
> +static struct attribute *cxl_pmem_region_attrs[] = {
> + &dev_attr_region_label_update.attr,
> + &dev_attr_region_label_delete.attr,
> + NULL
> +};
> +
> +static struct attribute_group cxl_pmem_region_group = {
> + .attrs = cxl_pmem_region_attrs,
> +};
> +
> static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
> &cxl_base_attribute_group,
> - NULL,
> + &cxl_pmem_region_group,
> + NULL
> };
>
> const struct device_type cxl_pmem_region_type = {
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 6ac3b40cb5ff..8c76c4a981bf 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -473,9 +473,15 @@ enum cxl_config_state {
> CXL_CONFIG_COMMIT,
> };
>
> +enum region_label_state {
> + CXL_REGION_LABEL_INACTIVE,
> + CXL_REGION_LABEL_ACTIVE,
> +};
> +
> /**
> * struct cxl_region_params - region settings
> * @state: allow the driver to lockdown further parameter changes
> + * @state: region label state
> * @uuid: unique id for persistent regions
> * @interleave_ways: number of endpoints in the region
> * @interleave_granularity: capacity each endpoint contributes to a stripe
> @@ -488,6 +494,7 @@ enum cxl_config_state {
> */
> struct cxl_region_params {
> enum cxl_config_state state;
> + enum region_label_state state_region_label;
> uuid_t uuid;
> int interleave_ways;
> int interleave_granularity;
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 16/17] cxl/pmem_region: Create pmem region using information parsed from LSA
2025-11-19 7:52 ` [PATCH V4 16/17] cxl/pmem_region: Create pmem region using information parsed from LSA Neeraj Kumar
@ 2025-11-19 23:37 ` Dave Jiang
2026-01-09 12:37 ` Neeraj Kumar
0 siblings, 1 reply; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 23:37 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> create_pmem_region() creates cxl region based on region information
> parsed from LSA. This routine required cxl root decoder and endpoint
> decoder. Therefore added cxl_find_root_decoder_by_port() and
> cxl_find_free_ep_decoder(). These routines find cxl root decoder and
> free endpoint decoder on cxl bus using cxl port
Please consider:
create_pmem_region() creates CXL region based on region information
parsed from the Label Storage Area (LSA). This routine requires cxl root
decoder and endpoint decoder. Add cxl_find_root_decoder_by_port()
and cxl_find_free_ep_decoder() to find the root decoder and a free
endpoint decoder respectively.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
> ---
> drivers/cxl/core/core.h | 4 ++
> drivers/cxl/core/pmem_region.c | 97 ++++++++++++++++++++++++++++++++++
> drivers/cxl/core/region.c | 13 +++--
> drivers/cxl/cxl.h | 5 ++
> 4 files changed, 115 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index beeb9b7527b8..dd2efd3deb5e 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -35,6 +35,7 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
> #define CXL_REGION_TYPE(x) (&cxl_region_type)
> #define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
> #define CXL_DAX_REGION_TYPE(x) (&cxl_dax_region_type)
> +int verify_free_decoder(struct device *dev);
> int cxl_region_init(void);
> void cxl_region_exit(void);
> int cxl_get_poison_by_endpoint(struct cxl_port *port);
> @@ -88,6 +89,9 @@ static inline struct cxl_region *to_cxl_region(struct device *dev)
> {
> return NULL;
> }
> +static inline int verify_free_decoder(struct device *dev)
> +{
this function needs to return something
> +}
> #define CXL_REGION_ATTR(x) NULL
> #define CXL_REGION_TYPE(x) NULL
> #define SET_CXL_REGION_ATTR(x)
> diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
> index be4feb73aafc..06665937c180 100644
> --- a/drivers/cxl/core/pmem_region.c
> +++ b/drivers/cxl/core/pmem_region.c
> @@ -291,3 +291,100 @@ int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
> cxlr->cxl_nvb = NULL;
> return rc;
> }
> +
> +static int match_root_decoder(struct device *dev, const void *data)
> +{
> + return is_root_decoder(dev);
Is it suppose to just grab the first root decoder? If so the function should be match_first_root_decoder(). However, should the root decoder cover the region it's trying to match to? Should there be some checks to see if the region fits under the root decoder range? Also, should it not check the root decoder flags to see if it has CXL_DECODER_F_PMEM set so the CFMWS can cover PMEM?
> +}
> +
> +/**
> + * cxl_find_root_decoder_by_port() - find a cxl root decoder on cxl bus
> + * @port: any descendant port in CXL port topology
> + *
> + * Caller of this function must call put_device() when done as a device ref
> + * is taken via device_find_child()
> + */
> +static struct cxl_root_decoder *cxl_find_root_decoder_by_port(struct cxl_port *port)
> +{
> + struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port);
> + struct device *dev;
> +
> + if (!cxl_root)
> + return NULL;
> +
> + dev = device_find_child(&cxl_root->port.dev, NULL, match_root_decoder);
> + if (!dev)
> + return NULL;
> +
> + return to_cxl_root_decoder(dev);
> +}
> +
> +static int match_free_ep_decoder(struct device *dev, const void *data)
> +{
> + if (!is_endpoint_decoder(dev))
> + return 0;
> +
> + return verify_free_decoder(dev);
> +}
> +
> +/**
> + * cxl_find_endpoint_decoder_by_port() - find a cxl root decoder on cxl bus
> + * @port: any descendant port in CXL port topology
> + *
> + * Caller of this function must call put_device() when done as a device ref
> + * is taken via device_find_child()
> + */
> +static struct cxl_decoder *cxl_find_free_ep_decoder(struct cxl_port *port)
> +{
> + struct device *dev;
> +
> + dev = device_find_child(&port->dev, NULL, match_free_ep_decoder);
> + if (!dev)
> + return NULL;
> +
> + return to_cxl_decoder(dev);
> +}
> +
> +void create_pmem_region(struct nvdimm *nvdimm)
> +{
> + struct cxl_nvdimm *cxl_nvd;
> + struct cxl_memdev *cxlmd;
> + struct cxl_pmem_region_params *params;
> + struct cxl_region *cxlr;
> +
> + if (!nvdimm_has_cxl_region(nvdimm))
> + return;
> +
> + lockdep_assert_held(&cxl_rwsem.region);
> + cxl_nvd = nvdimm_provider_data(nvdimm);
> + params = nvdimm_get_cxl_region_param(nvdimm);
> + cxlmd = cxl_nvd->cxlmd;
> +
> + /* TODO: Region creation support only for interleave way == 1 */
> + if (!(params->nlabel == 1)) {
> + dev_dbg(&cxlmd->dev,
> + "Region Creation is not supported with iw > 1\n");
> + return;
> + }
> +
> + struct cxl_root_decoder *cxlrd __free(put_cxl_root_decoder) =
> + cxl_find_root_decoder_by_port(cxlmd->endpoint);
> + if (!cxlrd) {
> + dev_err(&cxlmd->dev, "CXL root decoder not found\n");
> + return;
> + }
> +
> + struct cxl_decoder *cxld __free(put_cxl_decoder) =
> + cxl_find_free_ep_decoder(cxlmd->endpoint);
> + if (!cxlrd) {
> + dev_err(&cxlmd->dev, "CXL endpoint decoder not found\n");
> + return;
> + }
> +
> + cxlr = cxl_create_region(cxlrd, CXL_PARTMODE_PMEM,
> + atomic_read(&cxlrd->region_id),
> + params, cxld);
> + if (IS_ERR(cxlr))
> + dev_warn(&cxlmd->dev, "Region Creation failed\n");
> +}
> +EXPORT_SYMBOL_NS_GPL(create_pmem_region, "CXL");
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 408e139718f1..96f3cf4143b8 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -835,15 +835,12 @@ static int check_commit_order(struct device *dev, void *data)
> return 0;
> }
>
> -static int match_free_decoder(struct device *dev, const void *data)
> +int verify_free_decoder(struct device *dev)
I would call it is_free_decoder() instead. Probably ok to return bool instead of int.
DJ
> {
> struct cxl_port *port = to_cxl_port(dev->parent);
> struct cxl_decoder *cxld;
> int rc;
>
> - if (!is_switch_decoder(dev))
> - return 0;
> -
> cxld = to_cxl_decoder(dev);
>
> if (cxld->id != port->commit_end + 1)
> @@ -867,6 +864,14 @@ static int match_free_decoder(struct device *dev, const void *data)
> return 1;
> }
>
> +static int match_free_decoder(struct device *dev, const void *data)
> +{
> + if (!is_switch_decoder(dev))
> + return 0;
> +
> + return verify_free_decoder(dev);
> +}
> +
> static bool spa_maps_hpa(const struct cxl_region_params *p,
> const struct range *range)
> {
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 8c76c4a981bf..088841a3e238 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -792,6 +792,7 @@ struct cxl_root *find_cxl_root(struct cxl_port *port);
> DEFINE_FREE(put_cxl_root, struct cxl_root *, if (_T) put_device(&_T->port.dev))
> DEFINE_FREE(put_cxl_port, struct cxl_port *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))
> DEFINE_FREE(put_cxl_root_decoder, struct cxl_root_decoder *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->cxlsd.cxld.dev))
> +DEFINE_FREE(put_cxl_decoder, struct cxl_decoder *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))
> DEFINE_FREE(put_cxl_region, struct cxl_region *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))
>
> int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd);
> @@ -933,6 +934,7 @@ static inline int cxl_region_discovery(struct cxl_memdev *cxlmd)
> #ifdef CONFIG_CXL_PMEM_REGION
> bool is_cxl_pmem_region(struct device *dev);
> struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
> +void create_pmem_region(struct nvdimm *nvdimm);
> #else
> static inline bool is_cxl_pmem_region(struct device *dev)
> {
> @@ -942,6 +944,9 @@ static inline struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
> {
> return NULL;
> }
> +static inline void create_pmem_region(struct nvdimm *nvdimm)
> +{
> +}
> #endif
>
> void cxl_endpoint_parse_cdat(struct cxl_port *port);
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 17/17] cxl/pmem: Add CXL LSA 2.1 support in cxl pmem
2025-11-19 7:52 ` [PATCH V4 17/17] cxl/pmem: Add CXL LSA 2.1 support in cxl pmem Neeraj Kumar
@ 2025-11-19 23:37 ` Dave Jiang
0 siblings, 0 replies; 58+ messages in thread
From: Dave Jiang @ 2025-11-19 23:37 UTC (permalink / raw)
To: Neeraj Kumar, linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel
On 11/19/25 12:52 AM, Neeraj Kumar wrote:
> Add support of CXL LSA 2.1 using NDD_REGION_LABELING flag. It creates
> cxl region based on region information parsed from LSA
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
> drivers/cxl/pmem.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c
> index a6eba3572090..5970d1792be8 100644
> --- a/drivers/cxl/pmem.c
> +++ b/drivers/cxl/pmem.c
> @@ -135,6 +135,7 @@ static int cxl_nvdimm_probe(struct device *dev)
> return rc;
>
> set_bit(NDD_LABELING, &flags);
> + set_bit(NDD_REGION_LABELING, &flags);
> set_bit(NDD_REGISTER_SYNC, &flags);
> set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
> set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
> @@ -155,6 +156,7 @@ static int cxl_nvdimm_probe(struct device *dev)
> return -ENOMEM;
>
> dev_set_drvdata(dev, nvdimm);
> + create_pmem_region(nvdimm);
> return devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm);
> }
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 03/17] nvdimm/label: Add namespace/region label support as per LSA 2.1
2025-11-19 7:52 ` [PATCH V4 03/17] nvdimm/label: Add namespace/region label support as per LSA 2.1 Neeraj Kumar
2025-11-19 15:51 ` Dave Jiang
@ 2025-12-17 14:31 ` Jonathan Cameron
2026-01-09 11:51 ` Neeraj Kumar
1 sibling, 1 reply; 58+ messages in thread
From: Jonathan Cameron @ 2025-12-17 14:31 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 19 Nov 2025 13:22:41 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> Modify __pmem_label_update() to update region labels into LSA
>
> CXL 3.2 Spec mentions CXL LSA 2.1 Namespace Labels at section 9.13.2.5
> Modified __pmem_label_update() using setter functions to update
> namespace label as per CXL LSA 2.1
>
> Create export routine nd_region_label_update() used for creating
> region label into LSA. It will be used later from CXL subsystem
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Hi Neeraj,
A few things inline from a fresh read.
Thanks,
Jonathan
> ---
> drivers/nvdimm/label.c | 360 ++++++++++++++++++++++++++------
> drivers/nvdimm/label.h | 17 +-
> drivers/nvdimm/namespace_devs.c | 25 ++-
> drivers/nvdimm/nd.h | 66 ++++++
> include/linux/libnvdimm.h | 8 +
> 5 files changed, 406 insertions(+), 70 deletions(-)
>
> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
> index 0a9b6c5cb2c3..0d587a5b9f7e 100644
> --- a/drivers/nvdimm/label.c
> +++ b/drivers/nvdimm/label.c
> @@ -978,7 +1132,8 @@ static int __pmem_label_update(struct nd_region *nd_region,
> return rc;
> }
> int nd_pmem_namespace_label_update(struct nd_region *nd_region,
> struct nd_namespace_pmem *nspm, resource_size_t size)
> {
> @@ -1075,6 +1253,7 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
> for (i = 0; i < nd_region->ndr_mappings; i++) {
> struct nd_mapping *nd_mapping = &nd_region->mapping[i];
> struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> + int region_label_cnt = 0;
Always initialized anyway before use I think. So no need to do it here.
> struct resource *res;
> int count = 0;
>
> @@ -1090,12 +1269,20 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
> count++;
> WARN_ON_ONCE(!count);
>
> - rc = init_labels(nd_mapping, count);
> + region_label_cnt = find_region_label_count(nd_mapping);
> + /*
> + * init_labels() scan labels and allocate new label based
> + * on its second parameter (num_labels). Therefore to
> + * allocate new namespace label also include previously
> + * added region label
> + */
> + rc = init_labels(nd_mapping, count + region_label_cnt,
> + NS_LABEL_TYPE);
> if (rc < 0)
> return rc;
>
> rc = __pmem_label_update(nd_region, nd_mapping, nspm, i,
> - NSLABEL_FLAG_UPDATING);
> + NSLABEL_FLAG_UPDATING, NS_LABEL_TYPE);
> if (rc)
> return rc;
> }
> +int nd_pmem_region_label_update(struct nd_region *nd_region)
> +{
> + int i, rc;
> +
> + for (i = 0; i < nd_region->ndr_mappings; i++) {
> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> + int region_label_cnt = 0;
Seems to always be initialized before use anyway so no need to do it here.
> +
> + /* No need to update region label for non cxl format */
> + if (!ndd->cxl)
> + return 0;
> +
> + region_label_cnt = find_region_label_count(nd_mapping);
> + rc = init_labels(nd_mapping, region_label_cnt + 1,
> + RG_LABEL_TYPE);
> + if (rc < 0)
> + return rc;
> +
> + rc = __pmem_label_update(nd_region, nd_mapping, NULL, i,
> + NSLABEL_FLAG_UPDATING, RG_LABEL_TYPE);
> + if (rc)
> + return rc;
> + }
> +
> + /* Clear the UPDATING flag per UEFI 2.7 expectations */
> + for (i = 0; i < nd_region->ndr_mappings; i++) {
> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> +
> + WARN_ON_ONCE(!ndd->cxl);
> + rc = __pmem_label_update(nd_region, nd_mapping, NULL, i, 0,
> + RG_LABEL_TYPE);
> if (rc)
> return rc;
> }
> diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
> index 43fdb806532e..b1abbe602a5e 100644
> --- a/drivers/nvdimm/namespace_devs.c
> +++ b/drivers/nvdimm/namespace_devs.c
> @@ -232,6 +232,18 @@ static ssize_t __alt_name_store(struct device *dev, const char *buf,
> return rc;
> }
>
> +int nd_region_label_update(struct nd_region *nd_region)
> +{
> + int rc;
> +
> + nvdimm_bus_lock(&nd_region->dev);
> + rc = nd_pmem_region_label_update(nd_region);
> + nvdimm_bus_unlock(&nd_region->dev);
In line with much of the nvdimm stuff I'd use guard and save a couple of lines.
guard(nvdimm_bus)(&nd_region->dev);
return nd_pmem_region_label_update(nd_region);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL_GPL(nd_region_label_update);
> diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
> index f631bd84d6f0..5fd69c28ffe7 100644
> --- a/drivers/nvdimm/nd.h
> +++ b/drivers/nvdimm/nd.h
> @@ -14,6 +14,9 @@
> #include <linux/nd.h>
> #include "label.h"
>
> +extern uuid_t cxl_namespace_uuid;
> +extern uuid_t cxl_region_uuid;
> +
> enum {
> /*
> * Limits the maximum number of block apertures a dimm can
> @@ -295,6 +298,67 @@ static inline const u8 *nsl_uuid_raw(struct nvdimm_drvdata *ndd,
> return nd_label->efi.uuid;
> }
>
> +static inline void nsl_set_type(struct nvdimm_drvdata *ndd,
> + struct nd_namespace_label *ns_label)
> +{
> + if (!(ndd->cxl && ns_label))
> + return;
> +
> + uuid_copy((uuid_t *)ns_label->cxl.type, &cxl_namespace_uuid);
uuid_import() perhaps more appropriate given it is coming(I think)
from a __u8 &.
> +}
> +
> +
> +static inline bool is_region_label(struct nvdimm_drvdata *ndd,
> + union nd_lsa_label *lsa_label)
> +{
> + uuid_t *region_type;
> +
> + if (!ndd->cxl)
> + return false;
> +
> + region_type = (uuid_t *) lsa_label->region_label.type;
> + return uuid_equal(&cxl_region_uuid, region_type)
I'd match style of next function and not have the local variable.
return uuid_equal(&cxl_region_uuid,
(uuid_t *)lsa_label->region_label.type);
> +}
> +
> +static inline bool
> +region_label_uuid_equal(struct cxl_region_label *region_label,
> + const uuid_t *uuid)
> +{
> + return uuid_equal((uuid_t *) region_label->uuid, uuid);
Dave pointed out that there shouldn't be a space after the cast.
Make sure you catch all of these.
> +}
> +
> +static inline u64
> +region_label_get_checksum(struct cxl_region_label *region_label)
> +{
> + return __le64_to_cpu(region_label->checksum);
> +}
> +
> +static inline void
> +region_label_set_checksum(struct cxl_region_label *region_label,
> + u64 checksum)
> +{
> + region_label->checksum = __cpu_to_le64(checksum);
> +}
Perhaps add a little justification to the patch description on why these
get/set are helpful? Seems like just setting them directly would perhaps
be fine as all call sites can see the structure definition anyway?
> +
> bool nsl_validate_type_guid(struct nvdimm_drvdata *ndd,
> struct nd_namespace_label *nd_label, guid_t *guid);
> enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd,
> @@ -376,7 +440,9 @@ enum nd_label_flags {
> struct nd_label_ent {
> struct list_head list;
> unsigned long flags;
> + uuid_t label_uuid;
> struct nd_namespace_label *label;
> + struct cxl_region_label *region_label;
> };
>
> enum nd_mapping_lock_class {
> diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
> index 5696715c33bb..2c213b9dac66 100644
> --- a/include/linux/libnvdimm.h
> +++ b/include/linux/libnvdimm.h
> @@ -117,6 +117,13 @@ struct nd_interleave_set {
> u64 altcookie;
>
> guid_t type_guid;
> +
> + /* v2.1 region label info */
> + uuid_t uuid;
> + int interleave_ways;
> + int interleave_granularity;
> + struct resource *res;
> + int nr_targets;
> };
>
> struct nd_mapping_desc {
> @@ -307,6 +314,7 @@ int nvdimm_has_flush(struct nd_region *nd_region);
> int nvdimm_has_cache(struct nd_region *nd_region);
> int nvdimm_in_overwrite(struct nvdimm *nvdimm);
> bool is_nvdimm_sync(struct nd_region *nd_region);
> +int nd_region_label_update(struct nd_region *nd_region);
>
> static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf,
> unsigned int buf_len, int *cmd_rc)
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 05/17] nvdimm/label: Skip region label during ns label DPA reservation
2025-11-19 7:52 ` [PATCH V4 05/17] nvdimm/label: Skip region label during ns label DPA reservation Neeraj Kumar
2025-11-19 17:01 ` Dave Jiang
@ 2025-12-17 14:33 ` Jonathan Cameron
2026-01-09 11:53 ` Neeraj Kumar
1 sibling, 1 reply; 58+ messages in thread
From: Jonathan Cameron @ 2025-12-17 14:33 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 19 Nov 2025 13:22:43 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> If Namespace label is present in LSA during nvdimm_probe() then DPA
> reservation is required. But this reservation is not required by region
> label. Therefore if LSA scanning finds any region label, skip it.
It would be good to have a little more explanation of why these two
types of label put different requirements here.
I'd rather not go spec diving to find out!
Otherwise LGTM.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
> ---
> drivers/nvdimm/label.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
> index 6ccc51552822..e90e48672da3 100644
> --- a/drivers/nvdimm/label.c
> +++ b/drivers/nvdimm/label.c
> @@ -469,6 +469,10 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
> lsa_label = to_lsa_label(ndd, slot);
> nd_label = &lsa_label->ns_label;
>
> + /* Skip region label. DPA reservation is for NS label only */
> + if (is_region_label(ndd, lsa_label))
> + continue;
> +
> if (!slot_valid(ndd, lsa_label, slot))
> continue;
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 07/17] nvdimm/label: Add region label delete support
2025-11-19 7:52 ` [PATCH V4 07/17] nvdimm/label: Add region label delete support Neeraj Kumar
2025-11-19 19:50 ` Dave Jiang
@ 2025-12-17 15:05 ` Jonathan Cameron
2026-01-09 11:58 ` Neeraj Kumar
1 sibling, 1 reply; 58+ messages in thread
From: Jonathan Cameron @ 2025-12-17 15:05 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 19 Nov 2025 13:22:45 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> Create export routine nd_region_label_delete() used for deleting
> region label from LSA. It will be used later from CXL subsystem
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
A few minor things inline.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> ---
> drivers/nvdimm/label.c | 76 ++++++++++++++++++++++++++++++---
> drivers/nvdimm/label.h | 1 +
> drivers/nvdimm/namespace_devs.c | 12 ++++++
> drivers/nvdimm/nd.h | 6 +++
> include/linux/libnvdimm.h | 1 +
> 5 files changed, 90 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
> index e90e48672da3..da55ecd95e2f 100644
> --- a/drivers/nvdimm/label.c
> +++ b/drivers/nvdimm/label.c
> @@ -1225,7 +1225,8 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels,
> return max(num_labels, old_num_labels);
> }
>
> -static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
> +static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid,
> + enum label_type ltype)
> {
> struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> struct nd_label_ent *label_ent, *e;
> @@ -1244,11 +1245,25 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
>
> mutex_lock(&nd_mapping->lock);
> list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
> - if (label_ent->label)
> + if ((ltype == NS_LABEL_TYPE && !label_ent->label) ||
> + (ltype == RG_LABEL_TYPE && !label_ent->region_label))
> continue;
> active++;
> - if (!nsl_uuid_equal(ndd, label_ent->label, uuid))
> - continue;
> +
> + switch (ltype) {
> + case NS_LABEL_TYPE:
> + if (!nsl_uuid_equal(ndd, label_ent->label, uuid))
> + continue;
> +
> + break;
> + case RG_LABEL_TYPE:
> + if (!region_label_uuid_equal(label_ent->region_label,
> + uuid))
Align after equal( or just go a bit long on this line to improve readability.
> + continue;
> +
> + break;
> + }
> +
> @@ -1381,6 +1399,52 @@ int nd_pmem_region_label_update(struct nd_region *nd_region)
> return 0;
> }
>
> +int nd_pmem_region_label_delete(struct nd_region *nd_region)
> +{
> + struct nd_interleave_set *nd_set = nd_region->nd_set;
> + struct nd_label_ent *label_ent;
> + int i, rc;
> +
> + for (i = 0; i < nd_region->ndr_mappings; i++) {
> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
> +
> + /* Find non cxl format supported ndr_mappings */
> + if (!ndd->cxl) {
> + dev_info(&nd_region->dev, "Unsupported region label\n");
> + return -EINVAL;
> + }
> +
> + /* Find if any NS label using this region */
> + guard(mutex)(&nd_mapping->lock);
> + list_for_each_entry(label_ent, &nd_mapping->labels, list) {
> + if (!label_ent->label)
> + continue;
> +
> + /*
> + * Check if any available NS labels has same
> + * region_uuid in LSA
> + */
> + if (nsl_region_uuid_equal(label_ent->label,
> + &nd_set->uuid)) {
> + dev_dbg(&nd_region->dev,
> + "Region/Namespace label in use\n");
> + return -EBUSY;
> + }
> + }
> + }
> +
> + for (i = 0; i < nd_region->ndr_mappings; i++) {
> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
Unless this gets more complex later, I'd not bother with this local variable.
> +
> + rc = del_labels(nd_mapping, &nd_set->uuid, RG_LABEL_TYPE);
> + if (rc)
> + return rc;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
> index 9450200b4470..9299a586bfce 100644
> --- a/drivers/nvdimm/namespace_devs.c
> +++ b/drivers/nvdimm/namespace_devs.c
> @@ -244,6 +244,18 @@ int nd_region_label_update(struct nd_region *nd_region)
> }
> EXPORT_SYMBOL_GPL(nd_region_label_update);
>
> +int nd_region_label_delete(struct nd_region *nd_region)
> +{
> + int rc;
> +
> + nvdimm_bus_lock(&nd_region->dev);
Similar to earlier. Use the guard() for this to simplify things a little.
> + rc = nd_pmem_region_label_delete(nd_region);
> + nvdimm_bus_unlock(&nd_region->dev);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL_GPL(nd_region_label_delete);
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 08/17] nvdimm/label: Preserve cxl region information from region label
2025-11-19 7:52 ` [PATCH V4 08/17] nvdimm/label: Preserve cxl region information from region label Neeraj Kumar
2025-11-19 20:13 ` Dave Jiang
@ 2025-12-17 15:09 ` Jonathan Cameron
2026-01-09 12:06 ` Neeraj Kumar
1 sibling, 1 reply; 58+ messages in thread
From: Jonathan Cameron @ 2025-12-17 15:09 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 19 Nov 2025 13:22:46 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> Preserve region information from region label during nvdimm_probe. This
> preserved region information is used for creating cxl region to achieve
> region persistency across reboot.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Trivial comments inline. As Dave asked, what's the plan for multiple?
Add some brief notes to the patch description on this.
Thanks,
Jonathan
> ---
> drivers/nvdimm/dimm.c | 4 ++++
> drivers/nvdimm/label.c | 40 +++++++++++++++++++++++++++++++++++++++
> drivers/nvdimm/nd-core.h | 2 ++
> drivers/nvdimm/nd.h | 1 +
> include/linux/libnvdimm.h | 14 ++++++++++++++
> 5 files changed, 61 insertions(+)
>
> diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c
> index 07f5c5d5e537..590ec883903d 100644
> --- a/drivers/nvdimm/dimm.c
> +++ b/drivers/nvdimm/dimm.c
> @@ -107,6 +107,10 @@ static int nvdimm_probe(struct device *dev)
> if (rc)
> goto err;
>
> + /* Preserve cxl region info if available */
> + if (ndd->cxl)
> + nvdimm_cxl_region_preserve(ndd);
> +
> return 0;
>
> err:
> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
> index da55ecd95e2f..0f8aea61b504 100644
> --- a/drivers/nvdimm/label.c
> +++ b/drivers/nvdimm/label.c
> @@ -490,6 +490,46 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
> return 0;
> }
>
> +int nvdimm_cxl_region_preserve(struct nvdimm_drvdata *ndd)
> +{
> + struct nvdimm *nvdimm = to_nvdimm(ndd->dev);
> + struct cxl_pmem_region_params *p = &nvdimm->cxl_region_params;
> + struct nd_namespace_index *nsindex;
> + unsigned long *free;
> + u32 nslot, slot;
> +
> + if (!preamble_current(ndd, &nsindex, &free, &nslot))
> + return 0; /* no label, nothing to preserve */
> +
> + for_each_clear_bit_le(slot, free, nslot) {
> + union nd_lsa_label *lsa_label;
> + struct cxl_region_label *region_label;
> + uuid_t *region_uuid;
> +
> + lsa_label = to_lsa_label(ndd, slot);
> + region_label = &lsa_label->region_label;
> + region_uuid = (uuid_t *) ®ion_label->type;
> +
union nd_lsa_label *lsa_label = to_lsa_label(ndd, slot);
struct cxl_region_label *region_label = &lsa_label->region_label;
//I'd go long on thi sone as only just over 80 and helps readability.
uuid_t *region_uuid = (uuid_t *)®ion_label->type;
Saves a fine lines and there doesn't seem to be an obvious reason
not to do so.
> + /* TODO: Currently preserving only one region */
> + if (uuid_equal(&cxl_region_uuid, region_uuid)) {
> + nvdimm->is_region_label = true;
> + import_uuid(&p->uuid, region_label->uuid);
> + p->flags = __le32_to_cpu(region_label->flags);
> + p->nlabel = __le16_to_cpu(region_label->nlabel);
> + p->position = __le16_to_cpu(region_label->position);
> + p->dpa = __le64_to_cpu(region_label->dpa);
> + p->rawsize = __le64_to_cpu(region_label->rawsize);
> + p->hpa = __le64_to_cpu(region_label->hpa);
> + p->slot = __le32_to_cpu(region_label->slot);
> + p->ig = __le32_to_cpu(region_label->ig);
> + p->align = __le32_to_cpu(region_label->align);
> + break;
> + }
> + }
> +
> + return 0;
> +}
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 09/17] nvdimm/label: Export routine to fetch region information
2025-11-19 7:52 ` [PATCH V4 09/17] nvdimm/label: Export routine to fetch region information Neeraj Kumar
2025-11-19 20:18 ` Dave Jiang
@ 2025-12-17 15:12 ` Jonathan Cameron
2026-01-09 12:09 ` Neeraj Kumar
1 sibling, 1 reply; 58+ messages in thread
From: Jonathan Cameron @ 2025-12-17 15:12 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 19 Nov 2025 13:22:47 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> CXL region information preserved from the LSA needs to be exported for
> use by the CXL driver for CXL region re-creation.
To me it feels like the !nvdimm checks may be excessive in an interface
that makes no sense if NULL is passed in.
Perhaps drop those?
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
> ---
> drivers/nvdimm/dimm_devs.c | 18 ++++++++++++++++++
> include/linux/libnvdimm.h | 2 ++
> 2 files changed, 20 insertions(+)
>
> diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
> index 3363a97cc5b5..1474b4e45fcc 100644
> --- a/drivers/nvdimm/dimm_devs.c
> +++ b/drivers/nvdimm/dimm_devs.c
> @@ -280,6 +280,24 @@ void *nvdimm_provider_data(struct nvdimm *nvdimm)
> }
> EXPORT_SYMBOL_GPL(nvdimm_provider_data);
>
> +bool nvdimm_has_cxl_region(struct nvdimm *nvdimm)
> +{
> + if (!nvdimm)
> + return false;
Seems a bit odd that this would ever get called on !nvdimm.
Is that protection worth adding?
> +
> + return nvdimm->is_region_label;
> +}
> +EXPORT_SYMBOL_GPL(nvdimm_has_cxl_region);
> +
> +void *nvdimm_get_cxl_region_param(struct nvdimm *nvdimm)
> +{
> + if (!nvdimm)
This feels a little more plausible as defense but is this
needed?
> + return NULL;
> +
> + return &nvdimm->cxl_region_params;
> +}
> +EXPORT_SYMBOL_GPL(nvdimm_get_cxl_region_param);
> +
> static ssize_t commands_show(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
> index 07ea2e3f821a..3ffd50ab6ac4 100644
> --- a/include/linux/libnvdimm.h
> +++ b/include/linux/libnvdimm.h
> @@ -330,6 +330,8 @@ int nvdimm_in_overwrite(struct nvdimm *nvdimm);
> bool is_nvdimm_sync(struct nd_region *nd_region);
> int nd_region_label_update(struct nd_region *nd_region);
> int nd_region_label_delete(struct nd_region *nd_region);
> +bool nvdimm_has_cxl_region(struct nvdimm *nvdimm);
> +void *nvdimm_get_cxl_region_param(struct nvdimm *nvdimm);
>
> static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf,
> unsigned int buf_len, int *cmd_rc)
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 11/17] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation
2025-11-19 7:52 ` [PATCH V4 11/17] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation Neeraj Kumar
2025-11-19 21:33 ` Dave Jiang
@ 2025-12-17 15:28 ` Jonathan Cameron
2026-01-09 12:22 ` Neeraj Kumar
1 sibling, 1 reply; 58+ messages in thread
From: Jonathan Cameron @ 2025-12-17 15:28 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 19 Nov 2025 13:22:49 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> devm_cxl_pmem_add_region() is used to create cxl region based on region
> information scanned from LSA.
>
> devm_cxl_add_region() is used to just allocate cxlr and its fields are
> filled later by userspace tool using device attributes (*_store()).
>
> Inspiration for devm_cxl_pmem_add_region() is taken from these device
> attributes (_store*) calls. It allocates cxlr and fills information
> parsed from LSA and calls device_add(&cxlr->dev) to initiate further
> region creation porbes
>
> Rename __create_region() to cxl_create_region(), which will be used
> in later patch to create cxl region after fetching region information
> from LSA.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
I think there is an underflow of the device reference count in an error
path. See below.
Jonathan
> +static struct cxl_region *
> +devm_cxl_pmem_add_region(struct cxl_root_decoder *cxlrd, int id,
> + struct cxl_pmem_region_params *params,
> + struct cxl_decoder *cxld,
> + enum cxl_decoder_type type)
> +{
> + struct cxl_endpoint_decoder *cxled;
> + struct cxl_region_params *p;
> + struct cxl_port *root_port;
> + struct device *dev;
> + int rc;
> +
> + struct cxl_region *cxlr __free(put_cxl_region) =
> + cxl_region_alloc(cxlrd, id);
It can be tricky to get the use of __free() when related
to devices that are being registered right. I'm not sure it
is quite correct here.
> + if (IS_ERR(cxlr))
> + return cxlr;
> +
> + cxlr->mode = CXL_PARTMODE_PMEM;
> + cxlr->type = type;
> +
> + dev = &cxlr->dev;
> + rc = dev_set_name(dev, "region%d", id);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + p = &cxlr->params;
> + p->uuid = params->uuid;
> + p->interleave_ways = params->nlabel;
> + p->interleave_granularity = params->ig;
> +
> + rc = alloc_region_hpa(cxlr, params->rawsize);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + cxled = to_cxl_endpoint_decoder(&cxld->dev);
> +
> + rc = cxl_dpa_set_part(cxled, CXL_PARTMODE_PMEM);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + rc = alloc_region_dpa(cxled, params->rawsize);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + /*
> + * TODO: Currently we have support of interleave_way == 1, where
> + * we can only have one region per mem device. It means mem device
> + * position (params->position) will always be 0. It is therefore
> + * attaching only one target at params->position
> + */
> + if (params->position)
> + return ERR_PTR(-EOPNOTSUPP);
> +
> + rc = attach_target(cxlr, cxled, params->position, TASK_INTERRUPTIBLE);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + rc = __commit(cxlr);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + rc = device_add(dev);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + root_port = to_cxl_port(cxlrd->cxlsd.cxld.dev.parent);
> + rc = devm_add_action_or_reset(root_port->uport_dev,
> + unregister_region, cxlr);
> + if (rc)
In this path the __free(put_cxl_region) will put once.
The unregister_region will both unregister and put. The
dev_add_action_or_reset() will have called unregister_region()
Which does both device_del() and a put on cxlr->dev.
I might have missed another reference but at first glance at least
this underflows.
Note the different error path for the devm_add_action_or_reset
in current devm_cxl_add_region() which is there because there isn't
another reference count to decrement.
Various ways to solve this. A common one is to separate the
allocation and adding stuff into another function (with __free as
you have here) and call that from here, leaving this outer wrapper
just doing the devm_add_action_or_reset() if everything else
has succeeded and hence no need for the outer function to do any
other reference coutn handling. Or just don't use __free() as
is done in devm_cxl_add_region()
> + return ERR_PTR(rc);
> +
> + dev_dbg(root_port->uport_dev, "%s: created %s\n",
> + dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev));
> +
> + return no_free_ptr(cxlr);
> +}
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 13/17] cxl/pmem_region: Prep patch to accommodate pmem_region attributes
2025-11-19 7:52 ` [PATCH V4 13/17] cxl/pmem_region: Prep patch to accommodate pmem_region attributes Neeraj Kumar
2025-11-19 22:08 ` Dave Jiang
@ 2025-12-17 15:35 ` Jonathan Cameron
2026-01-09 12:26 ` Neeraj Kumar
1 sibling, 1 reply; 58+ messages in thread
From: Jonathan Cameron @ 2025-12-17 15:35 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 19 Nov 2025 13:22:51 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> For region label update, need to create device attribute, which calls
> nvdimm exported routine thus making pmem_region dependent on libnvdimm.
> Because of this dependency of pmem region on libnvdimm, segregate pmem
> region related code from core/region.c to core/pmem_region.c
>
> This patch has no functionality change. Its just code movement from
> core/region.c to core/pmem_region.c
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Minor stuff below.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> #define SET_CXL_REGION_ATTR(x)
> diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
> new file mode 100644
> index 000000000000..b45e60f04ff4
> --- /dev/null
> +++ b/drivers/cxl/core/pmem_region.c
> @@ -0,0 +1,202 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2020 Intel Corporation. */
> +#include <linux/device.h>
> +#include <linux/memregion.h>
> +#include <cxlmem.h>
> +#include <cxl.h>
> +#include "core.h"
> +
> +/**
> + * DOC: cxl pmem region
> + *
> + * The core CXL PMEM region infrastructure supports persistent memory
> + * region creation using LIBNVDIMM subsystem. It has dependency on
> + * LIBNVDIMM, pmem region need updation of cxl region information into
Perhaps reword as:
pmem region needs to update the cxl region information in the LSA.
> + * LSA. LIBNVDIMM dependency is only for pmem region, it is therefore
> + * need this separate file.
This seems like an explanation for the patch. Not sure we need it
in the final code. Anyone who considers changing this will rapidly
spot that in the build files.
...
> +static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
> + &cxl_base_attribute_group,
> + NULL,
Whilst here, perhaps drop that trailing , there shouldn't be one on a terminating
entry like this.
> +};
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 14/17] cxl/pmem_region: Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c
2025-11-19 7:52 ` [PATCH V4 14/17] cxl/pmem_region: Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c Neeraj Kumar
2025-11-19 22:24 ` Dave Jiang
@ 2025-12-17 15:38 ` Jonathan Cameron
2026-01-09 12:29 ` Neeraj Kumar
1 sibling, 1 reply; 58+ messages in thread
From: Jonathan Cameron @ 2025-12-17 15:38 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 19 Nov 2025 13:22:52 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> As pmem region label update/delete has hard dependency on libnvdimm.
> It is therefore put core/pmem_region.c under CONFIG_CXL_PMEM_REGION
> control. It handles the dependency by selecting CONFIG_LIBNVDIMM
> if not enabled.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 5ebbc3d3dde5..beeb9b7527b8 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -34,7 +34,6 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
> #define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
> #define CXL_REGION_TYPE(x) (&cxl_region_type)
> #define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
> -#define CXL_PMEM_REGION_TYPE(x) (&cxl_pmem_region_type)
> #define CXL_DAX_REGION_TYPE(x) (&cxl_dax_region_type)
> int cxl_region_init(void);
> void cxl_region_exit(void);
> @@ -89,17 +88,23 @@ static inline struct cxl_region *to_cxl_region(struct device *dev)
> {
> return NULL;
> }
> -static inline int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
> -{
> - return 0;
> -}
> #define CXL_REGION_ATTR(x) NULL
> #define CXL_REGION_TYPE(x) NULL
> #define SET_CXL_REGION_ATTR(x)
> -#define CXL_PMEM_REGION_TYPE(x) NULL
> #define CXL_DAX_REGION_TYPE(x) NULL
> #endif
>
> +#ifdef CONFIG_CXL_PMEM_REGION
> +#define CXL_PMEM_REGION_TYPE(x) (&cxl_pmem_region_type)
> +int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
> +#else
> +#define CXL_PMEM_REGION_TYPE(x) NULL
> +static inline int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
> +{
> + return 0;
> +}
> +#endif
> +
> struct cxl_send_command;
> struct cxl_mem_query_commands;
> int cxl_query_cmd(struct cxl_mailbox *cxl_mbox,
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 9798120b208e..408e139718f1 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3918,6 +3918,8 @@ static int cxl_region_probe(struct device *dev)
> dev_dbg(&cxlr->dev, "CXL EDAC registration for region_id=%d failed\n",
> cxlr->id);
>
> + if (!IS_ENABLED(CONFIG_CXL_PMEM_REGION))
> + return -EINVAL;
> return devm_cxl_add_pmem_region(cxlr);
Why not have the stub return -EINVAL if it never makes sense to call without
the CONFIG being enabled?
> case CXL_PARTMODE_RAM:
> rc = devm_cxl_region_edac_register(cxlr);
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 15/17] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion
2025-11-19 7:52 ` [PATCH V4 15/17] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion Neeraj Kumar
2025-11-19 23:10 ` Dave Jiang
@ 2025-12-17 15:40 ` Jonathan Cameron
2026-01-09 12:32 ` Neeraj Kumar
1 sibling, 1 reply; 58+ messages in thread
From: Jonathan Cameron @ 2025-12-17 15:40 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 19 Nov 2025 13:22:53 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> Using these attributes region label is added/deleted into LSA. These
> attributes are called from userspace (ndctl) after region creation.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
One quick addition to what Dave called out.
Thanks,
Jonathan
> diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
> index b45e60f04ff4..be4feb73aafc 100644
> --- a/drivers/cxl/core/pmem_region.c
> +++ b/drivers/cxl/core/pmem_region.c
> @@ -30,9 +30,100 @@ static void cxl_pmem_region_release(struct device *dev)
> kfree(cxlr_pmem);
> }
>
> +static ssize_t region_label_update_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
> + struct cxl_region *cxlr = cxlr_pmem->cxlr;
> + ssize_t rc;
> + bool update;
> +
> + rc = kstrtobool(buf, &update);
> + if (rc)
> + return rc;
> +
> + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> + rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem);
> + if (rc)
I'd stick to one style for these. Elsewhere you have
if ((rc = ACQUIRE_ERR())
> + return rc;
> +
> + /* Region not yet committed */
> + if (update && cxlr && cxlr->params.state != CXL_CONFIG_COMMIT) {
> + dev_dbg(dev, "region not committed, can't update into LSA\n");
> + return -ENXIO;
> + }
> +
> + if (!cxlr || !cxlr->cxlr_pmem || !cxlr->cxlr_pmem->nd_region)
> + return 0;
> +
> + rc = nd_region_label_update(cxlr->cxlr_pmem->nd_region);
> + if (rc)
> + return rc;
> +
> + cxlr->params.state_region_label = CXL_REGION_LABEL_ACTIVE;
> +
> + return len;
> +}
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 03/17] nvdimm/label: Add namespace/region label support as per LSA 2.1
2025-11-19 15:51 ` Dave Jiang
@ 2026-01-09 11:46 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 11:46 UTC (permalink / raw)
To: Dave Jiang
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 2703 bytes --]
On 19/11/25 08:51AM, Dave Jiang wrote:
>
>
>On 11/19/25 12:52 AM, Neeraj Kumar wrote:
>> Modify __pmem_label_update() to update region labels into LSA
>>
>> CXL 3.2 Spec mentions CXL LSA 2.1 Namespace Labels at section 9.13.2.5
>> Modified __pmem_label_update() using setter functions to update
>> namespace label as per CXL LSA 2.1
>>
>> Create export routine nd_region_label_update() used for creating
>> region label into LSA. It will be used later from CXL subsystem
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>
>A few nits below, otherwise
>Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
>
>> ---
>> drivers/nvdimm/label.c | 360 ++++++++++++++++++++++++++------
>> drivers/nvdimm/label.h | 17 +-
>> drivers/nvdimm/namespace_devs.c | 25 ++-
>> drivers/nvdimm/nd.h | 66 ++++++
>> include/linux/libnvdimm.h | 8 +
>> 5 files changed, 406 insertions(+), 70 deletions(-)
>>
>> +static unsigned long find_slot(struct nvdimm_drvdata *ndd,
>> + unsigned long label)
>> +{
>> + unsigned long base;
>> +
>> + base = (unsigned long) nd_label_base(ndd);
>
>No need for space after casting
Fixed it in V5
>
>> + return (label - base) / sizeof_namespace_label(ndd);
>> +}
<snip>
>> +static int __pmem_label_update(struct nd_region *nd_region,
>> + struct nd_mapping *nd_mapping,
>> + struct nd_namespace_pmem *nspm,
>> + int pos, unsigned long flags,
>> + enum label_type ltype)
>> +{
>> + struct nd_interleave_set *nd_set = nd_region->nd_set;
>> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
>> + struct nd_namespace_index *nsindex;
>> + struct nd_label_ent *label_ent;
>> + union nd_lsa_label *lsa_label;
>> + unsigned long *free;
>> + struct device *dev;
>> + u32 nslot, slot;
>> + size_t offset;
>> + int rc;
>> +
>> - nd_dbg_dpa(nd_region, ndd, res, "\n");
>> + lsa_label = to_lsa_label(ndd, slot);
>> + memset(lsa_label, 0, sizeof_namespace_label(ndd));
>> +
>> + switch (ltype) {
>> + case NS_LABEL_TYPE:
>> + dev = &nspm->nsio.common.dev;
>> + rc = namespace_label_update(nd_region, nd_mapping,
>> + nspm, pos, flags, &lsa_label->ns_label,
>> + nsindex, slot);
>> + if (rc)
>> + return rc;
>> +
>> + break;
>> + case RG_LABEL_TYPE:
>> + dev = &nd_region->dev;
>> + region_label_update(nd_region, &lsa_label->region_label,
>> + nd_mapping, pos, flags, slot);
>
>inconsistent tabbing this one vs the case above. Should pick the one that conforms to the rest of this file. And yeah I get it. The tab formatting under nvdimm is different than CXL and it's a pain going between the two.
>
>DJ
Thanks Dave, I have fixed it in V5, Will be sending it soon
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 03/17] nvdimm/label: Add namespace/region label support as per LSA 2.1
2025-12-17 14:31 ` Jonathan Cameron
@ 2026-01-09 11:51 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 11:51 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 7229 bytes --]
On 17/12/25 02:31PM, Jonathan Cameron wrote:
>On Wed, 19 Nov 2025 13:22:41 +0530
>Neeraj Kumar <s.neeraj@samsung.com> wrote:
>
>> Modify __pmem_label_update() to update region labels into LSA
>>
>> CXL 3.2 Spec mentions CXL LSA 2.1 Namespace Labels at section 9.13.2.5
>> Modified __pmem_label_update() using setter functions to update
>> namespace label as per CXL LSA 2.1
>>
>> Create export routine nd_region_label_update() used for creating
>> region label into LSA. It will be used later from CXL subsystem
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>Hi Neeraj,
>
>A few things inline from a fresh read.
>
>Thanks,
>
>Jonathan
>
>> ---
>> drivers/nvdimm/label.c | 360 ++++++++++++++++++++++++++------
>> drivers/nvdimm/label.h | 17 +-
>> drivers/nvdimm/namespace_devs.c | 25 ++-
>> drivers/nvdimm/nd.h | 66 ++++++
>> include/linux/libnvdimm.h | 8 +
>> 5 files changed, 406 insertions(+), 70 deletions(-)
>>
>> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
>> index 0a9b6c5cb2c3..0d587a5b9f7e 100644
>> --- a/drivers/nvdimm/label.c
>> +++ b/drivers/nvdimm/label.c
>
>
>
>
>> @@ -978,7 +1132,8 @@ static int __pmem_label_update(struct nd_region *nd_region,
>> return rc;
>> }
>
>> int nd_pmem_namespace_label_update(struct nd_region *nd_region,
>> struct nd_namespace_pmem *nspm, resource_size_t size)
>> {
>> @@ -1075,6 +1253,7 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
>> for (i = 0; i < nd_region->ndr_mappings; i++) {
>> struct nd_mapping *nd_mapping = &nd_region->mapping[i];
>> struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
>> + int region_label_cnt = 0;
>
>Always initialized anyway before use I think. So no need to do it here.
Fixed it in V5
>
>> struct resource *res;
>> int count = 0;
>>
>> @@ -1090,12 +1269,20 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
>> count++;
>> WARN_ON_ONCE(!count);
>>
>> - rc = init_labels(nd_mapping, count);
>> + region_label_cnt = find_region_label_count(nd_mapping);
>> + /*
>> + * init_labels() scan labels and allocate new label based
>> + * on its second parameter (num_labels). Therefore to
>> + * allocate new namespace label also include previously
>> + * added region label
>> + */
>> + rc = init_labels(nd_mapping, count + region_label_cnt,
>> + NS_LABEL_TYPE);
>> if (rc < 0)
>> return rc;
>>
>> rc = __pmem_label_update(nd_region, nd_mapping, nspm, i,
>> - NSLABEL_FLAG_UPDATING);
>> + NSLABEL_FLAG_UPDATING, NS_LABEL_TYPE);
>> if (rc)
>> return rc;
>> }
>
>> +int nd_pmem_region_label_update(struct nd_region *nd_region)
>> +{
>> + int i, rc;
>> +
>> + for (i = 0; i < nd_region->ndr_mappings; i++) {
>> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
>> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
>> + int region_label_cnt = 0;
>
>Seems to always be initialized before use anyway so no need to do it here.
Fixed it in V5
>
>> +
>> + /* No need to update region label for non cxl format */
>> + if (!ndd->cxl)
>> + return 0;
>> +
>> + region_label_cnt = find_region_label_count(nd_mapping);
>> + rc = init_labels(nd_mapping, region_label_cnt + 1,
>> + RG_LABEL_TYPE);
>> + if (rc < 0)
>> + return rc;
>> +
>> + rc = __pmem_label_update(nd_region, nd_mapping, NULL, i,
>> + NSLABEL_FLAG_UPDATING, RG_LABEL_TYPE);
>> + if (rc)
>> + return rc;
>> + }
>> +
>> + /* Clear the UPDATING flag per UEFI 2.7 expectations */
>> + for (i = 0; i < nd_region->ndr_mappings; i++) {
>> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
>> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
>> +
>> + WARN_ON_ONCE(!ndd->cxl);
>> + rc = __pmem_label_update(nd_region, nd_mapping, NULL, i, 0,
>> + RG_LABEL_TYPE);
>> if (rc)
>> return rc;
>> }
>
>> diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
>> index 43fdb806532e..b1abbe602a5e 100644
>> --- a/drivers/nvdimm/namespace_devs.c
>> +++ b/drivers/nvdimm/namespace_devs.c
>> @@ -232,6 +232,18 @@ static ssize_t __alt_name_store(struct device *dev, const char *buf,
>> return rc;
>> }
>>
>> +int nd_region_label_update(struct nd_region *nd_region)
>> +{
>> + int rc;
>> +
>> + nvdimm_bus_lock(&nd_region->dev);
>> + rc = nd_pmem_region_label_update(nd_region);
>> + nvdimm_bus_unlock(&nd_region->dev);
>
>In line with much of the nvdimm stuff I'd use guard and save a couple of lines.
>
> guard(nvdimm_bus)(&nd_region->dev);
> return nd_pmem_region_label_update(nd_region);
>
Fixed it in V5
>> +
>> + return rc;
>> +}
>> +EXPORT_SYMBOL_GPL(nd_region_label_update);
>
>> diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
>> index f631bd84d6f0..5fd69c28ffe7 100644
>> --- a/drivers/nvdimm/nd.h
>> +++ b/drivers/nvdimm/nd.h
>> @@ -14,6 +14,9 @@
>> #include <linux/nd.h>
>> #include "label.h"
>>
>> +extern uuid_t cxl_namespace_uuid;
>> +extern uuid_t cxl_region_uuid;
>> +
>> enum {
>> /*
>> * Limits the maximum number of block apertures a dimm can
>> @@ -295,6 +298,67 @@ static inline const u8 *nsl_uuid_raw(struct nvdimm_drvdata *ndd,
>> return nd_label->efi.uuid;
>> }
>>
>> +static inline void nsl_set_type(struct nvdimm_drvdata *ndd,
>> + struct nd_namespace_label *ns_label)
>> +{
>> + if (!(ndd->cxl && ns_label))
>> + return;
>> +
>> + uuid_copy((uuid_t *)ns_label->cxl.type, &cxl_namespace_uuid);
>
>uuid_import() perhaps more appropriate given it is coming(I think)
>from a __u8 &.
>
Yes we can avoid extra typecasting in uuid_copy using uuid_export().
Actually cxl_namespace_uuid is of type uuid_t and ns_label->cxl.type is of type __u8
So export_uuid() is appropriate here than uuid_inport()
I have fixed it in V5
>> +}
>> +
>
>
>> +
>> +static inline bool is_region_label(struct nvdimm_drvdata *ndd,
>> + union nd_lsa_label *lsa_label)
>> +{
>> + uuid_t *region_type;
>> +
>> + if (!ndd->cxl)
>> + return false;
>> +
>> + region_type = (uuid_t *) lsa_label->region_label.type;
>> + return uuid_equal(&cxl_region_uuid, region_type)
>
>I'd match style of next function and not have the local variable.
>
> return uuid_equal(&cxl_region_uuid,
> (uuid_t *)lsa_label->region_label.type);
Fixed it in V5
>> +}
>> +
>> +static inline bool
>> +region_label_uuid_equal(struct cxl_region_label *region_label,
>> + const uuid_t *uuid)
>> +{
>> + return uuid_equal((uuid_t *) region_label->uuid, uuid);
>
>Dave pointed out that there shouldn't be a space after the cast.
>Make sure you catch all of these.
>
Fixed it in V5
>> +}
>> +
>> +static inline u64
>> +region_label_get_checksum(struct cxl_region_label *region_label)
>> +{
>> + return __le64_to_cpu(region_label->checksum);
>> +}
>> +
>> +static inline void
>> +region_label_set_checksum(struct cxl_region_label *region_label,
>> + u64 checksum)
>> +{
>> + region_label->checksum = __cpu_to_le64(checksum);
>> +}
>
>Perhaps add a little justification to the patch description on why these
>get/set are helpful? Seems like just setting them directly would perhaps
>be fine as all call sites can see the structure definition anyway?
>
Thanks Jonathan, I have used them directly instead of extra setter function in V5
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 05/17] nvdimm/label: Skip region label during ns label DPA reservation
2025-12-17 14:33 ` Jonathan Cameron
@ 2026-01-09 11:53 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 11:53 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 625 bytes --]
On 17/12/25 02:33PM, Jonathan Cameron wrote:
>On Wed, 19 Nov 2025 13:22:43 +0530
>Neeraj Kumar <s.neeraj@samsung.com> wrote:
>
>> If Namespace label is present in LSA during nvdimm_probe() then DPA
>> reservation is required. But this reservation is not required by region
>> label. Therefore if LSA scanning finds any region label, skip it.
>
>It would be good to have a little more explanation of why these two
>types of label put different requirements here.
>
>I'd rather not go spec diving to find out!
>
>Otherwise LGTM.
Thanks Jonathan for review, I have added further explanation in commit message
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 07/17] nvdimm/label: Add region label delete support
2025-11-19 19:50 ` Dave Jiang
@ 2026-01-09 11:56 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 11:56 UTC (permalink / raw)
To: Dave Jiang
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 984 bytes --]
On 19/11/25 12:50PM, Dave Jiang wrote:
>
>
>On 11/19/25 12:52 AM, Neeraj Kumar wrote:
>> Create export routine nd_region_label_delete() used for deleting
>> region label from LSA. It will be used later from CXL subsystem
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>
>Just one small thing below, otherwise
>Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
>> ---
>> drivers/nvdimm/label.c | 76 ++++++++++++++++++++++++++++++---
>> drivers/nvdimm/label.h | 1 +
>> drivers/nvdimm/namespace_devs.c | 12 ++++++
>> drivers/nvdimm/nd.h | 6 +++
>> include/linux/libnvdimm.h | 1 +
>> 5 files changed, 90 insertions(+), 6 deletions(-)
>>
<snip>
>>
>> +int nd_region_label_delete(struct nd_region *nd_region)
>> +{
>> + int rc;
>> +
>> + nvdimm_bus_lock(&nd_region->dev);
>
>You can use the new nvdimm_bus guard() now.
>
>guard(nvdimm_bus)(&nd_region->dev);
>
>DJ
Hi Dave, Thanks for RB tag, I have fixed it in V5.
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 07/17] nvdimm/label: Add region label delete support
2025-12-17 15:05 ` Jonathan Cameron
@ 2026-01-09 11:58 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 11:58 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 3632 bytes --]
On 17/12/25 03:05PM, Jonathan Cameron wrote:
>On Wed, 19 Nov 2025 13:22:45 +0530
>Neeraj Kumar <s.neeraj@samsung.com> wrote:
>
>> Create export routine nd_region_label_delete() used for deleting
>> region label from LSA. It will be used later from CXL subsystem
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>A few minor things inline.
>
>Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
>
>> ---
>> drivers/nvdimm/label.c | 76 ++++++++++++++++++++++++++++++---
>> drivers/nvdimm/label.h | 1 +
>> drivers/nvdimm/namespace_devs.c | 12 ++++++
>> drivers/nvdimm/nd.h | 6 +++
>> include/linux/libnvdimm.h | 1 +
>> 5 files changed, 90 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
>> index e90e48672da3..da55ecd95e2f 100644
>> --- a/drivers/nvdimm/label.c
>> +++ b/drivers/nvdimm/label.c
>> @@ -1225,7 +1225,8 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels,
>> return max(num_labels, old_num_labels);
>> }
>>
>> -static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
>> +static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid,
>> + enum label_type ltype)
>> {
>> struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
>> struct nd_label_ent *label_ent, *e;
>> @@ -1244,11 +1245,25 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
>>
>> mutex_lock(&nd_mapping->lock);
>> list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
>> - if (label_ent->label)
>> + if ((ltype == NS_LABEL_TYPE && !label_ent->label) ||
>> + (ltype == RG_LABEL_TYPE && !label_ent->region_label))
>> continue;
>> active++;
>> - if (!nsl_uuid_equal(ndd, label_ent->label, uuid))
>> - continue;
>> +
>> + switch (ltype) {
>> + case NS_LABEL_TYPE:
>> + if (!nsl_uuid_equal(ndd, label_ent->label, uuid))
>> + continue;
>> +
>> + break;
>> + case RG_LABEL_TYPE:
>> + if (!region_label_uuid_equal(label_ent->region_label,
>> + uuid))
>
>Align after equal( or just go a bit long on this line to improve readability.
Fixed it in V5
>
>> + continue;
>> +
>> + break;
>> + }
>> +
>
>> @@ -1381,6 +1399,52 @@ int nd_pmem_region_label_update(struct nd_region *nd_region)
>> return 0;
>> }
>>
>> +int nd_pmem_region_label_delete(struct nd_region *nd_region)
>> +{
>> + struct nd_interleave_set *nd_set = nd_region->nd_set;
>> + struct nd_label_ent *label_ent;
>> + int i, rc;
>> +
>> + for (i = 0; i < nd_region->ndr_mappings; i++) {
>> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
>> + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
>> +
>> + /* Find non cxl format supported ndr_mappings */
>> + if (!ndd->cxl) {
>> + dev_info(&nd_region->dev, "Unsupported region label\n");
>> + return -EINVAL;
>> + }
>> +
>> + /* Find if any NS label using this region */
>> + guard(mutex)(&nd_mapping->lock);
>> + list_for_each_entry(label_ent, &nd_mapping->labels, list) {
>> + if (!label_ent->label)
>> + continue;
>> +
>> + /*
>> + * Check if any available NS labels has same
>> + * region_uuid in LSA
>> + */
>> + if (nsl_region_uuid_equal(label_ent->label,
>> + &nd_set->uuid)) {
>> + dev_dbg(&nd_region->dev,
>> + "Region/Namespace label in use\n");
>> + return -EBUSY;
>> + }
>> + }
>> + }
>> +
>> + for (i = 0; i < nd_region->ndr_mappings; i++) {
>> + struct nd_mapping *nd_mapping = &nd_region->mapping[i];
>Unless this gets more complex later, I'd not bother with this local variable.
Thanks Jonathan for review, i have fixed it in V5.
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 08/17] nvdimm/label: Preserve cxl region information from region label
2025-11-19 20:13 ` Dave Jiang
@ 2026-01-09 12:03 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:03 UTC (permalink / raw)
To: Dave Jiang
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 545 bytes --]
On 19/11/25 01:13PM, Dave Jiang wrote:
>
>
>On 11/19/25 12:52 AM, Neeraj Kumar wrote:
>> Preserve region information from region label during nvdimm_probe. This
>> preserved region information is used for creating cxl region to achieve
>> region persistency across reboot.
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>
>Reviewed-by: Dave Jiang <dave.jiang@intel.com>
>
>Assume there's a plan to add >1 region labels preservation in the next step?
Thanks Dave for RB tag. Yes, I will prepare in next patch series.
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 08/17] nvdimm/label: Preserve cxl region information from region label
2025-12-17 15:09 ` Jonathan Cameron
@ 2026-01-09 12:06 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:06 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 2137 bytes --]
On 17/12/25 03:09PM, Jonathan Cameron wrote:
>On Wed, 19 Nov 2025 13:22:46 +0530
>Neeraj Kumar <s.neeraj@samsung.com> wrote:
>
>> Preserve region information from region label during nvdimm_probe. This
>> preserved region information is used for creating cxl region to achieve
>> region persistency across reboot.
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>Trivial comments inline. As Dave asked, what's the plan for multiple?
Multi-interleave support will require some more efforts on top of this change
So will take it in another series.
Yes, I will prepare in next patch series
>Add some brief notes to the patch description on this.
Yes, I have updated it in commit description
>
>Thanks,
>
>Jonathan
>
>> ---
>> drivers/nvdimm/dimm.c | 4 ++++
>> drivers/nvdimm/label.c | 40 +++++++++++++++++++++++++++++++++++++++
>> drivers/nvdimm/nd-core.h | 2 ++
>> drivers/nvdimm/nd.h | 1 +
>> include/linux/libnvdimm.h | 14 ++++++++++++++
>> 5 files changed, 61 insertions(+)
>>
>>
<snip>
>> +int nvdimm_cxl_region_preserve(struct nvdimm_drvdata *ndd)
>> +{
>> + struct nvdimm *nvdimm = to_nvdimm(ndd->dev);
>> + struct cxl_pmem_region_params *p = &nvdimm->cxl_region_params;
>> + struct nd_namespace_index *nsindex;
>> + unsigned long *free;
>> + u32 nslot, slot;
>> +
>> + if (!preamble_current(ndd, &nsindex, &free, &nslot))
>> + return 0; /* no label, nothing to preserve */
>> +
>> + for_each_clear_bit_le(slot, free, nslot) {
>> + union nd_lsa_label *lsa_label;
>> + struct cxl_region_label *region_label;
>> + uuid_t *region_uuid;
>> +
>> + lsa_label = to_lsa_label(ndd, slot);
>> + region_label = &lsa_label->region_label;
>> + region_uuid = (uuid_t *) ®ion_label->type;
>> +
> union nd_lsa_label *lsa_label = to_lsa_label(ndd, slot);
> struct cxl_region_label *region_label = &lsa_label->region_label;
>//I'd go long on thi sone as only just over 80 and helps readability.
> uuid_t *region_uuid = (uuid_t *)®ion_label->type;
>
>Saves a fine lines and there doesn't seem to be an obvious reason
>not to do so.
I have fixed in V5, Will be sending it soon.
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 09/17] nvdimm/label: Export routine to fetch region information
2025-12-17 15:12 ` Jonathan Cameron
@ 2026-01-09 12:09 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:09 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 1429 bytes --]
On 17/12/25 03:12PM, Jonathan Cameron wrote:
>On Wed, 19 Nov 2025 13:22:47 +0530
>Neeraj Kumar <s.neeraj@samsung.com> wrote:
>
>> CXL region information preserved from the LSA needs to be exported for
>> use by the CXL driver for CXL region re-creation.
>To me it feels like the !nvdimm checks may be excessive in an interface
>that makes no sense if NULL is passed in.
>Perhaps drop those?
>
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>> ---
>> drivers/nvdimm/dimm_devs.c | 18 ++++++++++++++++++
>> include/linux/libnvdimm.h | 2 ++
>> 2 files changed, 20 insertions(+)
>>
>> diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
>> index 3363a97cc5b5..1474b4e45fcc 100644
>> --- a/drivers/nvdimm/dimm_devs.c
>> +++ b/drivers/nvdimm/dimm_devs.c
>> @@ -280,6 +280,24 @@ void *nvdimm_provider_data(struct nvdimm *nvdimm)
>> }
>> EXPORT_SYMBOL_GPL(nvdimm_provider_data);
>>
>> +bool nvdimm_has_cxl_region(struct nvdimm *nvdimm)
>> +{
>> + if (!nvdimm)
>> + return false;
>
>Seems a bit odd that this would ever get called on !nvdimm.
>Is that protection worth adding?
>
>> +
>> + return nvdimm->is_region_label;
>> +}
>> +EXPORT_SYMBOL_GPL(nvdimm_has_cxl_region);
>> +
>> +void *nvdimm_get_cxl_region_param(struct nvdimm *nvdimm)
>> +{
>> + if (!nvdimm)
>
>This feels a little more plausible as defense but is this
>needed?
Yes we can avoid this check, I have fixed it in V5.
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 10/17] cxl/mem: Refactor cxl pmem region auto-assembling
2025-11-19 20:44 ` Dave Jiang
@ 2026-01-09 12:10 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:10 UTC (permalink / raw)
To: Dave Jiang
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 1491 bytes --]
On 19/11/25 01:44PM, Dave Jiang wrote:
>
>
>On 11/19/25 12:52 AM, Neeraj Kumar wrote:
>> In 84ec985944ef3, devm_cxl_add_nvdimm() sequence was changed and called
>> before devm_cxl_add_endpoint(). It's because cxl pmem region auto-assembly
>> used to get called at last in cxl_endpoint_port_probe(), which requires
>> cxl_nvd presence.
>>
>> For cxl region persistency, region creation happens during nvdimm_probe
>> which need the completion of endpoint probe.
>>
>> In order to accommodate both cxl pmem region auto-assembly and cxl region
>> persistency, refactored following
>>
>> 1. Re-Sequence devm_cxl_add_nvdimm() after devm_cxl_add_endpoint(). This
>> will be called only after successful completion of endpoint probe.
>>
>> 2. Create cxl_region_discovery() which performs pmem region
>> auto-assembly and remove cxl pmem region auto-assembly from
>> cxl_endpoint_port_probe()
>>
>> 3. Register cxl_region_discovery() with devm_cxl_add_memdev() which gets
>> called during cxl_pci_probe() in context of cxl_mem_probe()
>>
>> 4. As cxlmd->ops->probe() calls registered cxl_region_discovery(), so
>> move devm_cxl_add_nvdimm() before cxlmd->ops->probe(). It gurantees
>
>s/gurantees/guarantees/
Fixed it in V5
>
>> both the completion of endpoint probe and cxl_nvd presence before
>> calling cxlmd->ops->probe().
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>
>Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Thanks Dave for RB tag
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 11/17] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation
2025-11-19 21:33 ` Dave Jiang
@ 2026-01-09 12:13 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:13 UTC (permalink / raw)
To: Dave Jiang
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 1529 bytes --]
On 19/11/25 02:33PM, Dave Jiang wrote:
>
>
>On 11/19/25 12:52 AM, Neeraj Kumar wrote:
>> devm_cxl_pmem_add_region() is used to create cxl region based on region
>> information scanned from LSA.
>>
>> devm_cxl_add_region() is used to just allocate cxlr and its fields are
>> filled later by userspace tool using device attributes (*_store()).
>>
>> Inspiration for devm_cxl_pmem_add_region() is taken from these device
>> attributes (_store*) calls. It allocates cxlr and fills information
>> parsed from LSA and calls device_add(&cxlr->dev) to initiate further
>> region creation porbes
>>
>> Rename __create_region() to cxl_create_region(), which will be used
>> in later patch to create cxl region after fetching region information
>> from LSA.
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>
>small comment below, otherwise
>Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Thanks Dave for RB tag.
>
>> ---
>> drivers/cxl/core/core.h | 12 ++++
>> drivers/cxl/core/region.c | 124 ++++++++++++++++++++++++++++++++++++--
>> 2 files changed, 131 insertions(+), 5 deletions(-)
>>
>> --- a/drivers/cxl/core/region.c
>> +++ b/drivers/cxl/core/region.c
>>
>> +static ssize_t alloc_region_hpa(struct cxl_region *cxlr, u64 size)
>> +{
>> + int rc;
>> +
>> + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
>> + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>> + return rc;
>> +
>> + if (!size)
>> + return -EINVAL;
>
>Why not do this check before acquiring the lock?
>
>DJ
Fixed it in V5.
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 11/17] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation
2025-12-17 15:28 ` Jonathan Cameron
@ 2026-01-09 12:22 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:22 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 4340 bytes --]
On 17/12/25 03:28PM, Jonathan Cameron wrote:
>On Wed, 19 Nov 2025 13:22:49 +0530
>Neeraj Kumar <s.neeraj@samsung.com> wrote:
>
>> devm_cxl_pmem_add_region() is used to create cxl region based on region
>> information scanned from LSA.
>>
>> devm_cxl_add_region() is used to just allocate cxlr and its fields are
>> filled later by userspace tool using device attributes (*_store()).
>>
>> Inspiration for devm_cxl_pmem_add_region() is taken from these device
>> attributes (_store*) calls. It allocates cxlr and fills information
>> parsed from LSA and calls device_add(&cxlr->dev) to initiate further
>> region creation porbes
>>
>> Rename __create_region() to cxl_create_region(), which will be used
>> in later patch to create cxl region after fetching region information
>> from LSA.
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>
>I think there is an underflow of the device reference count in an error
>path. See below.
>
>Jonathan
>
>> +static struct cxl_region *
>> +devm_cxl_pmem_add_region(struct cxl_root_decoder *cxlrd, int id,
>> + struct cxl_pmem_region_params *params,
>> + struct cxl_decoder *cxld,
>> + enum cxl_decoder_type type)
>> +{
>> + struct cxl_endpoint_decoder *cxled;
>> + struct cxl_region_params *p;
>> + struct cxl_port *root_port;
>> + struct device *dev;
>> + int rc;
>> +
>> + struct cxl_region *cxlr __free(put_cxl_region) =
>> + cxl_region_alloc(cxlrd, id);
>It can be tricky to get the use of __free() when related
>to devices that are being registered right. I'm not sure it
>is quite correct here.
>
>> + if (IS_ERR(cxlr))
>> + return cxlr;
>> +
>> + cxlr->mode = CXL_PARTMODE_PMEM;
>> + cxlr->type = type;
>> +
>> + dev = &cxlr->dev;
>> + rc = dev_set_name(dev, "region%d", id);
>> + if (rc)
>> + return ERR_PTR(rc);
>> +
>> + p = &cxlr->params;
>> + p->uuid = params->uuid;
>> + p->interleave_ways = params->nlabel;
>> + p->interleave_granularity = params->ig;
>> +
>> + rc = alloc_region_hpa(cxlr, params->rawsize);
>> + if (rc)
>> + return ERR_PTR(rc);
>> +
>> + cxled = to_cxl_endpoint_decoder(&cxld->dev);
>> +
>> + rc = cxl_dpa_set_part(cxled, CXL_PARTMODE_PMEM);
>> + if (rc)
>> + return ERR_PTR(rc);
>> +
>> + rc = alloc_region_dpa(cxled, params->rawsize);
>> + if (rc)
>> + return ERR_PTR(rc);
>> +
>> + /*
>> + * TODO: Currently we have support of interleave_way == 1, where
>> + * we can only have one region per mem device. It means mem device
>> + * position (params->position) will always be 0. It is therefore
>> + * attaching only one target at params->position
>> + */
>> + if (params->position)
>> + return ERR_PTR(-EOPNOTSUPP);
>> +
>> + rc = attach_target(cxlr, cxled, params->position, TASK_INTERRUPTIBLE);
>> + if (rc)
>> + return ERR_PTR(rc);
>> +
>> + rc = __commit(cxlr);
>> + if (rc)
>> + return ERR_PTR(rc);
>> +
>> + rc = device_add(dev);
>> + if (rc)
>> + return ERR_PTR(rc);
>> +
>> + root_port = to_cxl_port(cxlrd->cxlsd.cxld.dev.parent);
>> + rc = devm_add_action_or_reset(root_port->uport_dev,
>> + unregister_region, cxlr);
>> + if (rc)
>In this path the __free(put_cxl_region) will put once.
>The unregister_region will both unregister and put. The
>dev_add_action_or_reset() will have called unregister_region()
>Which does both device_del() and a put on cxlr->dev.
>
>I might have missed another reference but at first glance at least
>this underflows.
>
>Note the different error path for the devm_add_action_or_reset
>in current devm_cxl_add_region() which is there because there isn't
>another reference count to decrement.
>
>Various ways to solve this. A common one is to separate the
>allocation and adding stuff into another function (with __free as
>you have here) and call that from here, leaving this outer wrapper
>just doing the devm_add_action_or_reset() if everything else
>has succeeded and hence no need for the outer function to do any
>other reference coutn handling. Or just don't use __free() as
>is done in devm_cxl_add_region()
>
I have used __free() based on Dave's review comment in V2[1] to
avoid extra gotos. Thanks for catching this reference underflow.
I have fixed it in V5 as per your suggestion.
I have used separate routine cxl_pmem_region_prep() where i have used __free().
[1]: https://lore.kernel.org/linux-cxl/148912029.181757055784505.JavaMail.epsvc@epcpadp2new/
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 13/17] cxl/pmem_region: Prep patch to accommodate pmem_region attributes
2025-12-17 15:35 ` Jonathan Cameron
@ 2026-01-09 12:26 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:26 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 2127 bytes --]
On 17/12/25 03:35PM, Jonathan Cameron wrote:
>On Wed, 19 Nov 2025 13:22:51 +0530
>Neeraj Kumar <s.neeraj@samsung.com> wrote:
>
>> For region label update, need to create device attribute, which calls
>> nvdimm exported routine thus making pmem_region dependent on libnvdimm.
>> Because of this dependency of pmem region on libnvdimm, segregate pmem
>> region related code from core/region.c to core/pmem_region.c
>>
>> This patch has no functionality change. Its just code movement from
>> core/region.c to core/pmem_region.c
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>
>Minor stuff below.
>Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Thanks Jonathan for RB tag.
>
>
>> #define SET_CXL_REGION_ATTR(x)
>> diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
>> new file mode 100644
>> index 000000000000..b45e60f04ff4
>> --- /dev/null
>> +++ b/drivers/cxl/core/pmem_region.c
>> @@ -0,0 +1,202 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/* Copyright(c) 2020 Intel Corporation. */
>> +#include <linux/device.h>
>> +#include <linux/memregion.h>
>> +#include <cxlmem.h>
>> +#include <cxl.h>
>> +#include "core.h"
>> +
>> +/**
>> + * DOC: cxl pmem region
>> + *
>> + * The core CXL PMEM region infrastructure supports persistent memory
>> + * region creation using LIBNVDIMM subsystem. It has dependency on
>> + * LIBNVDIMM, pmem region need updation of cxl region information into
>
>Perhaps reword as:
>
>pmem region needs to update the cxl region information in the LSA.
>
Fixed it accrodingly in V5
>> + * LSA. LIBNVDIMM dependency is only for pmem region, it is therefore
>> + * need this separate file.
>
>This seems like an explanation for the patch. Not sure we need it
>in the final code. Anyone who considers changing this will rapidly
>spot that in the build files.
>
>...
Fixed it in V5
>
>> +static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
>> + &cxl_base_attribute_group,
>> + NULL,
>
>Whilst here, perhaps drop that trailing , there shouldn't be one on a terminating
>entry like this.
Fixed it in V5
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 14/17] cxl/pmem_region: Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c
2025-12-17 15:38 ` Jonathan Cameron
@ 2026-01-09 12:29 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:29 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 1496 bytes --]
On 17/12/25 03:38PM, Jonathan Cameron wrote:
>On Wed, 19 Nov 2025 13:22:52 +0530
>Neeraj Kumar <s.neeraj@samsung.com> wrote:
>
>> As pmem region label update/delete has hard dependency on libnvdimm.
>> It is therefore put core/pmem_region.c under CONFIG_CXL_PMEM_REGION
>> control. It handles the dependency by selecting CONFIG_LIBNVDIMM
>> if not enabled.
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>
>
>> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
>> index 5ebbc3d3dde5..beeb9b7527b8 100644
>> --- a/drivers/cxl/core/core.h
>> +++ b/drivers/cxl/core/core.h
>> @@ -89,17 +88,23 @@ static inline struct cxl_region *to_cxl_region(struct device *dev)
>> {
>> return NULL;
>> }
>> -static inline int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
>> -{
>> - return 0;
>> -}
>> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
>> index 9798120b208e..408e139718f1 100644
>> --- a/drivers/cxl/core/region.c
>> +++ b/drivers/cxl/core/region.c
>> @@ -3918,6 +3918,8 @@ static int cxl_region_probe(struct device *dev)
>> dev_dbg(&cxlr->dev, "CXL EDAC registration for region_id=%d failed\n",
>> cxlr->id);
>>
>> + if (!IS_ENABLED(CONFIG_CXL_PMEM_REGION))
>> + return -EINVAL;
>> return devm_cxl_add_pmem_region(cxlr);
>
>Why not have the stub return -EINVAL if it never makes sense to call without
>the CONFIG being enabled?
I have removed this config check and handled it in
devm_cxl_add_pmem_region() itself.
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 15/17] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion
2025-11-19 23:10 ` Dave Jiang
@ 2026-01-09 12:31 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:31 UTC (permalink / raw)
To: Dave Jiang
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 1906 bytes --]
On 19/11/25 04:10PM, Dave Jiang wrote:
>
>
>On 11/19/25 12:52 AM, Neeraj Kumar wrote:
>> Using these attributes region label is added/deleted into LSA. These
>> attributes are called from userspace (ndctl) after region creation.
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>> ---
>> Documentation/ABI/testing/sysfs-bus-cxl | 22 ++++++
>> drivers/cxl/core/pmem_region.c | 93 ++++++++++++++++++++++++-
>> drivers/cxl/cxl.h | 7 ++
>> 3 files changed, 121 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl
>> index c80a1b5a03db..76d79c03dde4 100644
>> --- a/Documentation/ABI/testing/sysfs-bus-cxl
>> +++ b/Documentation/ABI/testing/sysfs-bus-cxl
>> @@ -624,3 +624,25 @@ Description:
>> The count is persistent across power loss and wraps back to 0
>> upon overflow. If this file is not present, the device does not
>> have the necessary support for dirty tracking.
>> +
>> +
>> +What: /sys/bus/cxl/devices/regionZ/pmem_regionZ/region_label_update
>> +Date: Nov, 2025
>> +KernelVersion: v6.19
>> +Contact: linux-cxl@vger.kernel.org
>> +Description:
>> + (RW) Write a boolean 'true' string value to this attribute to
>> + update cxl region information into LSA as region label. It
>> + uses nvdimm nd_region_label_update() to update cxl region
>> + information saved during cxl region creation into LSA. This
>> + attribute must be called at last during cxl region creation.
>
>Please consider:
>attribute must be written last during cxl region creation.
>
>No need to mention kernel specifics like function names. Just give general description of what the attribute does. Same for the next attribute below.
>
>Also, does this attribute needs to be readable? The documentation above does not explain the read attribute if so.
>
>DJ
Fixed it in V5
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 15/17] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion
2025-12-17 15:40 ` Jonathan Cameron
@ 2026-01-09 12:32 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:32 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 1337 bytes --]
On 17/12/25 03:40PM, Jonathan Cameron wrote:
>On Wed, 19 Nov 2025 13:22:53 +0530
>Neeraj Kumar <s.neeraj@samsung.com> wrote:
>
>> Using these attributes region label is added/deleted into LSA. These
>> attributes are called from userspace (ndctl) after region creation.
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>One quick addition to what Dave called out.
>
>Thanks,
>
>Jonathan
>
>> diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
>> index b45e60f04ff4..be4feb73aafc 100644
>> --- a/drivers/cxl/core/pmem_region.c
>> +++ b/drivers/cxl/core/pmem_region.c
>> @@ -30,9 +30,100 @@ static void cxl_pmem_region_release(struct device *dev)
>> kfree(cxlr_pmem);
>> }
>>
>> +static ssize_t region_label_update_store(struct device *dev,
>> + struct device_attribute *attr,
>> + const char *buf, size_t len)
>> +{
>> + struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
>> + struct cxl_region *cxlr = cxlr_pmem->cxlr;
>> + ssize_t rc;
>> + bool update;
>> +
>> + rc = kstrtobool(buf, &update);
>> + if (rc)
>> + return rc;
>> +
>> + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
>> + rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem);
>> + if (rc)
>I'd stick to one style for these. Elsewhere you have
> if ((rc = ACQUIRE_ERR())
>
Fixed it accordingly in V5
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V4 16/17] cxl/pmem_region: Create pmem region using information parsed from LSA
2025-11-19 23:37 ` Dave Jiang
@ 2026-01-09 12:37 ` Neeraj Kumar
0 siblings, 0 replies; 58+ messages in thread
From: Neeraj Kumar @ 2026-01-09 12:37 UTC (permalink / raw)
To: Dave Jiang
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel, cpgs
[-- Attachment #1: Type: text/plain, Size: 3143 bytes --]
On 19/11/25 04:37PM, Dave Jiang wrote:
>
>
>On 11/19/25 12:52 AM, Neeraj Kumar wrote:
>> create_pmem_region() creates cxl region based on region information
>> parsed from LSA. This routine required cxl root decoder and endpoint
>> decoder. Therefore added cxl_find_root_decoder_by_port() and
>> cxl_find_free_ep_decoder(). These routines find cxl root decoder and
>> free endpoint decoder on cxl bus using cxl port
>
>Please consider:
>create_pmem_region() creates CXL region based on region information
>parsed from the Label Storage Area (LSA). This routine requires cxl root
>decoder and endpoint decoder. Add cxl_find_root_decoder_by_port()
>and cxl_find_free_ep_decoder() to find the root decoder and a free
>endpoint decoder respectively.
>
Fixed it accordingly in V5
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>> ---
>> drivers/cxl/core/core.h | 4 ++
>> drivers/cxl/core/pmem_region.c | 97 ++++++++++++++++++++++++++++++++++
>> drivers/cxl/core/region.c | 13 +++--
>> drivers/cxl/cxl.h | 5 ++
>> 4 files changed, 115 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
>> index beeb9b7527b8..dd2efd3deb5e 100644
>> --- a/drivers/cxl/core/core.h
>> +++ b/drivers/cxl/core/core.h
>> @@ -35,6 +35,7 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
>> #define CXL_REGION_TYPE(x) (&cxl_region_type)
>> #define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
>> #define CXL_DAX_REGION_TYPE(x) (&cxl_dax_region_type)
>> +int verify_free_decoder(struct device *dev);
>> int cxl_region_init(void);
>> void cxl_region_exit(void);
>> int cxl_get_poison_by_endpoint(struct cxl_port *port);
>> @@ -88,6 +89,9 @@ static inline struct cxl_region *to_cxl_region(struct device *dev)
>> {
>> return NULL;
>> }
>> +static inline int verify_free_decoder(struct device *dev)
>> +{
>
>this function needs to return something
Thanks for catching this. Fixed it in V5
>
>> +}
>> #define CXL_REGION_ATTR(x) NULL
>> #define CXL_REGION_TYPE(x) NULL
>> #define SET_CXL_REGION_ATTR(x)
>> diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
>> index be4feb73aafc..06665937c180 100644
>> --- a/drivers/cxl/core/pmem_region.c
>> +++ b/drivers/cxl/core/pmem_region.c
>> @@ -291,3 +291,100 @@ int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
>> cxlr->cxl_nvb = NULL;
>> return rc;
>> }
>> +
>> +static int match_root_decoder(struct device *dev, const void *data)
>> +{
>> + return is_root_decoder(dev);
>
>Is it suppose to just grab the first root decoder? If so the function should be match_first_root_decoder(). However, should the root decoder cover the region it's trying to match to? Should there be some checks to see if the region fits under the root decoder range? Also, should it not check the root decoder flags to see if it has CXL_DECODER_F_PMEM set so the CFMWS can cover PMEM?
>
Yes Dave, Here we should check as you suggested. Also currently its only
considering only first decoder but it should return the root decoder
associated with particular endpoint decoder. I have fixed this in V5.
Regards,
Neeraj
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 58+ messages in thread
end of thread, other threads:[~2026-01-11 15:28 UTC | newest]
Thread overview: 58+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CGME20251119075306epcas5p22a87515de65a3c668275b394cdea83b0@epcas5p2.samsung.com>
2025-11-19 7:52 ` [PATCH V4 00/17] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 01/17] nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 02/17] nvdimm/label: CXL labels skip the need for 'interleave-set cookie' Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 03/17] nvdimm/label: Add namespace/region label support as per LSA 2.1 Neeraj Kumar
2025-11-19 15:51 ` Dave Jiang
2026-01-09 11:46 ` Neeraj Kumar
2025-12-17 14:31 ` Jonathan Cameron
2026-01-09 11:51 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 04/17] nvdimm/label: Include region label in slot validation Neeraj Kumar
2025-11-19 16:54 ` Dave Jiang
2025-11-19 7:52 ` [PATCH V4 05/17] nvdimm/label: Skip region label during ns label DPA reservation Neeraj Kumar
2025-11-19 17:01 ` Dave Jiang
2025-12-17 14:33 ` Jonathan Cameron
2026-01-09 11:53 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 06/17] nvdimm/label: Preserve region label during namespace creation Neeraj Kumar
2025-11-19 18:07 ` Dave Jiang
2025-11-19 7:52 ` [PATCH V4 07/17] nvdimm/label: Add region label delete support Neeraj Kumar
2025-11-19 19:50 ` Dave Jiang
2026-01-09 11:56 ` Neeraj Kumar
2025-12-17 15:05 ` Jonathan Cameron
2026-01-09 11:58 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 08/17] nvdimm/label: Preserve cxl region information from region label Neeraj Kumar
2025-11-19 20:13 ` Dave Jiang
2026-01-09 12:03 ` Neeraj Kumar
2025-12-17 15:09 ` Jonathan Cameron
2026-01-09 12:06 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 09/17] nvdimm/label: Export routine to fetch region information Neeraj Kumar
2025-11-19 20:18 ` Dave Jiang
2025-12-17 15:12 ` Jonathan Cameron
2026-01-09 12:09 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 10/17] cxl/mem: Refactor cxl pmem region auto-assembling Neeraj Kumar
2025-11-19 20:44 ` Dave Jiang
2026-01-09 12:10 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 11/17] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation Neeraj Kumar
2025-11-19 21:33 ` Dave Jiang
2026-01-09 12:13 ` Neeraj Kumar
2025-12-17 15:28 ` Jonathan Cameron
2026-01-09 12:22 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 12/17] cxl/pmem: Preserve region information into nd_set Neeraj Kumar
2025-11-19 22:00 ` Dave Jiang
2025-11-19 7:52 ` [PATCH V4 13/17] cxl/pmem_region: Prep patch to accommodate pmem_region attributes Neeraj Kumar
2025-11-19 22:08 ` Dave Jiang
2025-12-17 15:35 ` Jonathan Cameron
2026-01-09 12:26 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 14/17] cxl/pmem_region: Introduce CONFIG_CXL_PMEM_REGION for core/pmem_region.c Neeraj Kumar
2025-11-19 22:24 ` Dave Jiang
2025-12-17 15:38 ` Jonathan Cameron
2026-01-09 12:29 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 15/17] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion Neeraj Kumar
2025-11-19 23:10 ` Dave Jiang
2026-01-09 12:31 ` Neeraj Kumar
2025-12-17 15:40 ` Jonathan Cameron
2026-01-09 12:32 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 16/17] cxl/pmem_region: Create pmem region using information parsed from LSA Neeraj Kumar
2025-11-19 23:37 ` Dave Jiang
2026-01-09 12:37 ` Neeraj Kumar
2025-11-19 7:52 ` [PATCH V4 17/17] cxl/pmem: Add CXL LSA 2.1 support in cxl pmem Neeraj Kumar
2025-11-19 23:37 ` Dave Jiang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox