* [PATCH V3 01/20] nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 14:35 ` Jonathan Cameron
2025-09-17 13:29 ` [PATCH V3 03/20] nvdimm/label: Modify nd_label_base() signature Neeraj Kumar
` (17 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 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
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 91d9163ee303..bda22cb94e5b 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_check_region_label_format(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 21498d461fde..918c3db93195 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -18,6 +18,13 @@
static DEFINE_IDA(dimm_ida);
+bool nvdimm_check_region_label_format(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 cc5c8f3f81e8..158809c2be9e 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_check_region_label_format(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] 32+ messages in thread* Re: [PATCH V3 01/20] nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label
2025-09-17 13:29 ` [PATCH V3 01/20] nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label Neeraj Kumar
@ 2025-09-17 14:35 ` Jonathan Cameron
0 siblings, 0 replies; 32+ messages in thread
From: Jonathan Cameron @ 2025-09-17 14:35 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 17 Sep 2025 18:59:21 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> 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
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH V3 03/20] nvdimm/label: Modify nd_label_base() signature
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 01/20] nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 14:38 ` Jonathan Cameron
2025-09-17 13:29 ` [PATCH V3 04/20] nvdimm/label: Update mutex_lock() with guard(mutex)() Neeraj Kumar
` (16 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
nd_label_base() was being used after typecasting with 'unsigned long'. Thus
modified nd_label_base() to return 'unsigned long' instead of 'struct
nd_namespace_label *'
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/label.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 0a9b6c5cb2c3..668e1e146229 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -271,11 +271,11 @@ static void nd_label_copy(struct nvdimm_drvdata *ndd,
memcpy(dst, src, sizeof_namespace_index(ndd));
}
-static struct nd_namespace_label *nd_label_base(struct nvdimm_drvdata *ndd)
+static unsigned long nd_label_base(struct nvdimm_drvdata *ndd)
{
void *base = to_namespace_index(ndd, 0);
- return base + 2 * sizeof_namespace_index(ndd);
+ return (unsigned long) (base + 2 * sizeof_namespace_index(ndd));
}
static int to_slot(struct nvdimm_drvdata *ndd,
@@ -284,7 +284,7 @@ static int to_slot(struct nvdimm_drvdata *ndd,
unsigned long label, base;
label = (unsigned long) nd_label;
- base = (unsigned long) nd_label_base(ndd);
+ base = nd_label_base(ndd);
return (label - base) / sizeof_namespace_label(ndd);
}
@@ -293,7 +293,7 @@ static struct nd_namespace_label *to_label(struct nvdimm_drvdata *ndd, int slot)
{
unsigned long label, base;
- base = (unsigned long) nd_label_base(ndd);
+ base = nd_label_base(ndd);
label = base + sizeof_namespace_label(ndd) * slot;
return (struct nd_namespace_label *) label;
@@ -684,7 +684,7 @@ static int nd_label_write_index(struct nvdimm_drvdata *ndd, int index, u32 seq,
nd_label_next_nsindex(index))
- (unsigned long) to_namespace_index(ndd, 0);
nsindex->otheroff = __cpu_to_le64(offset);
- offset = (unsigned long) nd_label_base(ndd)
+ offset = nd_label_base(ndd)
- (unsigned long) to_namespace_index(ndd, 0);
nsindex->labeloff = __cpu_to_le64(offset);
nsindex->nslot = __cpu_to_le32(nslot);
--
2.34.1
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH V3 03/20] nvdimm/label: Modify nd_label_base() signature
2025-09-17 13:29 ` [PATCH V3 03/20] nvdimm/label: Modify nd_label_base() signature Neeraj Kumar
@ 2025-09-17 14:38 ` Jonathan Cameron
0 siblings, 0 replies; 32+ messages in thread
From: Jonathan Cameron @ 2025-09-17 14:38 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 17 Sep 2025 18:59:23 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> nd_label_base() was being used after typecasting with 'unsigned long'. Thus
> modified nd_label_base() to return 'unsigned long' instead of 'struct
> nd_namespace_label *'
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
I'm not that fussy either way on this one, but it seems
a reasonable thing to do.
Jonathan
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH V3 04/20] nvdimm/label: Update mutex_lock() with guard(mutex)()
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 01/20] nvdimm/label: Introduce NDD_REGION_LABELING flag to set region label Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 03/20] nvdimm/label: Modify nd_label_base() signature Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 14:46 ` Jonathan Cameron
2025-09-17 13:29 ` [PATCH V3 05/20] nvdimm/namespace_label: Add namespace label changes as per CXL LSA v2.1 Neeraj Kumar
` (15 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Updated mutex_lock() with guard(mutex)()
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/label.c | 36 +++++++++++++++++-------------------
1 file changed, 17 insertions(+), 19 deletions(-)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 668e1e146229..3235562d0e1c 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -948,7 +948,7 @@ static int __pmem_label_update(struct nd_region *nd_region,
return rc;
/* Garbage collect the previous label */
- mutex_lock(&nd_mapping->lock);
+ guard(mutex)(&nd_mapping->lock);
list_for_each_entry(label_ent, &nd_mapping->labels, list) {
if (!label_ent->label)
continue;
@@ -960,20 +960,20 @@ static int __pmem_label_update(struct nd_region *nd_region,
/* update index */
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)
- rc = -ENXIO;
- }
- mutex_unlock(&nd_mapping->lock);
+ if (rc)
+ return rc;
+
+ 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)
+ rc = -ENXIO;
return rc;
}
@@ -998,9 +998,8 @@ 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;
- mutex_lock(&nd_mapping->lock);
+ guard(mutex)(&nd_mapping->lock);
list_add_tail(&label_ent->list, &nd_mapping->labels);
- mutex_unlock(&nd_mapping->lock);
}
if (ndd->ns_current == -1 || ndd->ns_next == -1)
@@ -1039,7 +1038,7 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
if (!preamble_next(ndd, &nsindex, &free, &nslot))
return 0;
- mutex_lock(&nd_mapping->lock);
+ guard(mutex)(&nd_mapping->lock);
list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
struct nd_namespace_label *nd_label = label_ent->label;
@@ -1061,7 +1060,6 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
nd_mapping_free_labels(nd_mapping);
dev_dbg(ndd->dev, "no more active labels\n");
}
- mutex_unlock(&nd_mapping->lock);
return nd_label_write_index(ndd, ndd->ns_next,
nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0);
--
2.34.1
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH V3 04/20] nvdimm/label: Update mutex_lock() with guard(mutex)()
2025-09-17 13:29 ` [PATCH V3 04/20] nvdimm/label: Update mutex_lock() with guard(mutex)() Neeraj Kumar
@ 2025-09-17 14:46 ` Jonathan Cameron
2025-09-22 12:51 ` Neeraj Kumar
0 siblings, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-09-17 14:46 UTC (permalink / raw)
To: Neeraj Kumar
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
On Wed, 17 Sep 2025 18:59:24 +0530
Neeraj Kumar <s.neeraj@samsung.com> wrote:
> Updated mutex_lock() with guard(mutex)()
Say why.
>
> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
> ---
> drivers/nvdimm/label.c | 36 +++++++++++++++++-------------------
> 1 file changed, 17 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
> index 668e1e146229..3235562d0e1c 100644
> --- a/drivers/nvdimm/label.c
> +++ b/drivers/nvdimm/label.c
> @@ -948,7 +948,7 @@ static int __pmem_label_update(struct nd_region *nd_region,
> return rc;
>
> /* Garbage collect the previous label */
> - mutex_lock(&nd_mapping->lock);
> + guard(mutex)(&nd_mapping->lock);
> list_for_each_entry(label_ent, &nd_mapping->labels, list) {
> if (!label_ent->label)
> continue;
> @@ -960,20 +960,20 @@ static int __pmem_label_update(struct nd_region *nd_region,
> /* update index */
> 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)
> - rc = -ENXIO;
> - }
> - mutex_unlock(&nd_mapping->lock);
> + if (rc)
> + return rc;
> +
> + list_for_each_entry(label_ent, &nd_mapping->labels, list)
> + if (!label_ent->label) {
> + label_ent->label = nd_label;
> + nd_label = NULL;
> + break;
Perhaps it will change in later patches, but you could have done
if (!label_ent->label) {
label_ent->label = nd_label;
return;
}
as nothing else happens if we find a match.
> + }
> + dev_WARN_ONCE(&nspm->nsio.common.dev, nd_label,
> + "failed to track label: %d\n",
> + to_slot(ndd, nd_label));
> + if (nd_label)
> + rc = -ENXIO;
>
> return rc;
> }
> @@ -998,9 +998,8 @@ 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;
> - mutex_lock(&nd_mapping->lock);
> + guard(mutex)(&nd_mapping->lock);
> list_add_tail(&label_ent->list, &nd_mapping->labels);
> - mutex_unlock(&nd_mapping->lock);
Not sure I'd bother with cases like this but harmless.
> }
>
> if (ndd->ns_current == -1 || ndd->ns_next == -1)
> @@ -1039,7 +1038,7 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
> if (!preamble_next(ndd, &nsindex, &free, &nslot))
> return 0;
>
> - mutex_lock(&nd_mapping->lock);
> + guard(mutex)(&nd_mapping->lock);
> list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
> struct nd_namespace_label *nd_label = label_ent->label;
>
> @@ -1061,7 +1060,6 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
> nd_mapping_free_labels(nd_mapping);
> dev_dbg(ndd->dev, "no more active labels\n");
> }
> - mutex_unlock(&nd_mapping->lock);
This is a potential functional change as the lock is held for longer than before.
nd_label_write_index is not trivial so reviewing if that is safe is not trivial.
The benefit is small so far (maybe that changes in later patches) so I would not
make the change.
>
> return nd_label_write_index(ndd, ndd->ns_next,
> nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0);
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH V3 04/20] nvdimm/label: Update mutex_lock() with guard(mutex)()
2025-09-17 14:46 ` Jonathan Cameron
@ 2025-09-22 12:51 ` Neeraj Kumar
0 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-22 12:51 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-cxl, nvdimm, linux-kernel, gost.dev, a.manzanares, vishak.g,
neeraj.kernel
[-- Attachment #1: Type: text/plain, Size: 2460 bytes --]
On 17/09/25 03:46PM, Jonathan Cameron wrote:
>On Wed, 17 Sep 2025 18:59:24 +0530
>Neeraj Kumar <s.neeraj@samsung.com> wrote:
>
>> Updated mutex_lock() with guard(mutex)()
>
>Say why.
Sure, I will update it in next patch-set.
>>
>> Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
>> ---
>> drivers/nvdimm/label.c | 36 +++++++++++++++++-------------------
>> 1 file changed, 17 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
>> index 668e1e146229..3235562d0e1c 100644
>> --- a/drivers/nvdimm/label.c
>> +++ b/drivers/nvdimm/label.c
>> @@ -948,7 +948,7 @@ static int __pmem_label_update(struct nd_region *nd_region,
>> return rc;
>> + list_for_each_entry(label_ent, &nd_mapping->labels, list)
>> + if (!label_ent->label) {
>> + label_ent->label = nd_label;
>> + nd_label = NULL;
>> + break;
>
>Perhaps it will change in later patches, but you could have done
> if (!label_ent->label) {
> label_ent->label = nd_label;
> return;
> }
>as nothing else happens if we find a match.
Yes, I have updated it in later patch. I will update it here itself.
>
>> + }
>> @@ -998,9 +998,8 @@ 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;
>> - mutex_lock(&nd_mapping->lock);
>> + guard(mutex)(&nd_mapping->lock);
>> list_add_tail(&label_ent->list, &nd_mapping->labels);
>> - mutex_unlock(&nd_mapping->lock);
>
>Not sure I'd bother with cases like this but harmless.
>
>> }
>>
>> - mutex_lock(&nd_mapping->lock);
>> + guard(mutex)(&nd_mapping->lock);
>> list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
>> struct nd_namespace_label *nd_label = label_ent->label;
>>
>> @@ -1061,7 +1060,6 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
>> nd_mapping_free_labels(nd_mapping);
>> dev_dbg(ndd->dev, "no more active labels\n");
>> }
>> - mutex_unlock(&nd_mapping->lock);
>This is a potential functional change as the lock is held for longer than before.
>nd_label_write_index is not trivial so reviewing if that is safe is not trivial.
>
>The benefit is small so far (maybe that changes in later patches) so I would not
>make the change.
Sure, I will revert it back in next patch-set
Regards,
Neeraj
>
>
>
>>
>> return nd_label_write_index(ndd, ndd->ns_next,
>> nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0);
>
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH V3 05/20] nvdimm/namespace_label: Add namespace label changes as per CXL LSA v2.1
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (2 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 04/20] nvdimm/label: Update mutex_lock() with guard(mutex)() Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 06/20] nvdimm/region_label: Add region label update support Neeraj Kumar
` (14 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
CXL 3.2 Spec mentions CXL LSA 2.1 Namespace Labels at section 9.13.2.5
Modified __pmem_label_update function using setter functions to update
namespace label as per CXL LSA 2.1
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/label.c | 3 +++
drivers/nvdimm/nd.h | 23 +++++++++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 3235562d0e1c..182f8c9a01bf 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -924,6 +924,7 @@ static int __pmem_label_update(struct nd_region *nd_region,
nd_label = to_label(ndd, slot);
memset(nd_label, 0, sizeof_namespace_label(ndd));
+ nsl_set_type(ndd, nd_label);
nsl_set_uuid(ndd, nd_label, nspm->uuid);
nsl_set_name(ndd, nd_label, nspm->alt_name);
nsl_set_flags(ndd, nd_label, flags);
@@ -935,7 +936,9 @@ static int __pmem_label_update(struct nd_region *nd_region,
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_alignment(ndd, nd_label, 0);
nsl_set_type_guid(ndd, nd_label, &nd_set->type_guid);
+ nsl_set_region_uuid(ndd, nd_label, NULL);
nsl_set_claim_class(ndd, nd_label, ndns->claim_class);
nsl_calculate_checksum(ndd, nd_label);
nd_dbg_dpa(nd_region, ndd, res, "\n");
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 158809c2be9e..e362611d82cc 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -295,6 +295,29 @@ 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)
+ uuid_parse(CXL_NAMESPACE_UUID, (uuid_t *) ns_label->cxl.type);
+}
+
+static inline void nsl_set_alignment(struct nvdimm_drvdata *ndd,
+ struct nd_namespace_label *ns_label,
+ u32 align)
+{
+ if (ndd->cxl)
+ 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)
+ export_uuid(ns_label->cxl.region_uuid, uuid);
+}
+
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,
--
2.34.1
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH V3 06/20] nvdimm/region_label: Add region label update support
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (3 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 05/20] nvdimm/namespace_label: Add namespace label changes as per CXL LSA v2.1 Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 07/20] nvdimm/region_label: Add region label delete support Neeraj Kumar
` (13 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Modified __pmem_label_update() to update region labels into LSA
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/label.c | 269 ++++++++++++++++++++++++++------
drivers/nvdimm/label.h | 15 ++
drivers/nvdimm/namespace_devs.c | 12 ++
drivers/nvdimm/nd.h | 38 ++++-
include/linux/libnvdimm.h | 8 +
5 files changed, 289 insertions(+), 53 deletions(-)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 182f8c9a01bf..209c73f6b7e7 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -381,6 +381,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)
{
@@ -884,26 +894,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,36 +920,131 @@ 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_parse(CXL_REGION_UUID, (uuid_t *) region_label->type);
+
+ /* 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,
+ union nd_lsa_label *label,
+ 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->ns_label, nspm->uuid))
+ return true;
+
+ break;
+ case RG_LABEL_TYPE:
+ if (region_label_uuid_equal(&label->region_label,
+ &nd_set->uuid))
+ 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_type(ndd, nd_label);
- 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_alignment(ndd, nd_label, 0);
- nsl_set_type_guid(ndd, nd_label, &nd_set->type_guid);
- nsl_set_region_uuid(ndd, nd_label, NULL);
- 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 = (union nd_lsa_label *) to_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->ns_label);
+ rc = nvdimm_set_config_data(ndd, offset, lsa_label,
sizeof_namespace_label(ndd));
if (rc < 0)
return rc;
@@ -955,8 +1054,10 @@ static int __pmem_label_update(struct nd_region *nd_region,
list_for_each_entry(label_ent, &nd_mapping->labels, list) {
if (!label_ent->label)
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,
+ (union nd_lsa_label *) label_ent->label,
+ ltype, &label_ent->flags))
reap_victim(nd_mapping, label_ent);
}
@@ -966,19 +1067,20 @@ static int __pmem_label_update(struct nd_region *nd_region,
if (rc)
return rc;
- 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)
- rc = -ENXIO;
+ list_for_each_entry(label_ent, &nd_mapping->labels, list) {
+ if (label_ent->label)
+ continue;
- return rc;
+ label_ent->label = &lsa_label->ns_label;
+ lsa_label = NULL;
+ break;
+ }
+ dev_WARN_ONCE(dev, lsa_label, "failed to track label: %d\n",
+ to_slot(ndd, &lsa_label->ns_label));
+ if (lsa_label)
+ return -ENXIO;
+
+ return 0;
}
static int init_labels(struct nd_mapping *nd_mapping, int num_labels)
@@ -1068,6 +1170,21 @@ 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 nvdimm_drvdata *ndd,
+ 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 (is_region_label(ndd,
+ (union nd_lsa_label *) label_ent->label))
+ 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)
{
@@ -1076,6 +1193,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;
@@ -1091,12 +1209,19 @@ 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(ndd, 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);
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;
}
@@ -1108,7 +1233,47 @@ 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(ndd, nd_mapping);
+ rc = init_labels(nd_mapping, region_label_cnt + 1);
+ 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..284e2a763b49 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"
@@ -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 3271b1c8569a..559f822ef24f 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)
{
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index e362611d82cc..f04c042dcfa9 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -318,6 +318,39 @@ static inline void nsl_set_region_uuid(struct nvdimm_drvdata *ndd,
export_uuid(ns_label->cxl.region_uuid, uuid);
}
+static inline bool is_region_label(struct nvdimm_drvdata *ndd,
+ union nd_lsa_label *nd_label)
+{
+ uuid_t region_type, *ns_type;
+
+ if (!ndd->cxl || !nd_label)
+ return false;
+
+ uuid_parse(CXL_REGION_UUID, ®ion_type);
+ ns_type = (uuid_t *) nd_label->ns_label.cxl.type;
+ return uuid_equal(®ion_type, ns_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,
@@ -399,7 +432,10 @@ enum nd_label_flags {
struct nd_label_ent {
struct list_head list;
unsigned long flags;
- struct nd_namespace_label *label;
+ union {
+ 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] 32+ messages in thread* [PATCH V3 07/20] nvdimm/region_label: Add region label delete support
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (4 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 06/20] nvdimm/region_label: Add region label update support Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 08/20] nvdimm/label: Include region label in slot validation Neeraj Kumar
` (12 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Added LSA v2.1 format region label deletion routine. This function is
used to delete region label from LSA
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/label.c | 79 ++++++++++++++++++++++++++++++---
drivers/nvdimm/label.h | 1 +
drivers/nvdimm/namespace_devs.c | 12 +++++
drivers/nvdimm/nd.h | 6 +++
include/linux/libnvdimm.h | 1 +
5 files changed, 92 insertions(+), 7 deletions(-)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 209c73f6b7e7..d33db96ba8ba 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -1126,11 +1126,13 @@ 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;
struct nd_namespace_index *nsindex;
+ union nd_lsa_label *nd_label;
unsigned long *free;
LIST_HEAD(list);
u32 nslot, slot;
@@ -1145,15 +1147,28 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
guard(mutex)(&nd_mapping->lock);
list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
- struct nd_namespace_label *nd_label = label_ent->label;
+ nd_label = (union nd_lsa_label *) label_ent->label;
if (!nd_label)
continue;
active++;
- if (!nsl_uuid_equal(ndd, nd_label, uuid))
- continue;
+
+ switch (ltype) {
+ case NS_LABEL_TYPE:
+ if (!nsl_uuid_equal(ndd, &nd_label->ns_label, uuid))
+ continue;
+
+ break;
+ case RG_LABEL_TYPE:
+ if (!region_label_uuid_equal(&nd_label->region_label,
+ uuid))
+ continue;
+
+ break;
+ }
+
active--;
- slot = to_slot(ndd, nd_label);
+ slot = to_slot(ndd, &nd_label->ns_label);
nd_label_free_slot(ndd, slot);
dev_dbg(ndd->dev, "free: %d\n", slot);
list_move_tail(&label_ent->list, &list);
@@ -1161,7 +1176,7 @@ static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
}
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");
}
@@ -1198,7 +1213,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;
@@ -1281,6 +1297,55 @@ 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 ns_region_cnt = 0;
+ 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))
+ ns_region_cnt++;
+ }
+ }
+
+ if (ns_region_cnt) {
+ 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 284e2a763b49..276dd822e142 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 559f822ef24f..564a73b1da41 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 f04c042dcfa9..046063ea08b6 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -331,6 +331,12 @@ static inline bool is_region_label(struct nvdimm_drvdata *ndd,
return uuid_equal(®ion_type, ns_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] 32+ messages in thread* [PATCH V3 08/20] nvdimm/label: Include region label in slot validation
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (5 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 07/20] nvdimm/region_label: Add region label delete support Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 09/20] nvdimm/namespace_label: Skip region label during ns label DPA reservation Neeraj Kumar
` (11 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar,
Jonathan Cameron
Slot validation routine validates label slot by calculating label
checksum. It was only validating namespace label. This changeset also
validates region label if present.
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
drivers/nvdimm/label.c | 72 ++++++++++++++++++++++++++++++++----------
drivers/nvdimm/nd.h | 5 +++
2 files changed, 60 insertions(+), 17 deletions(-)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index d33db96ba8ba..5e476154cf81 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -359,7 +359,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);
@@ -374,13 +374,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)
{
@@ -392,16 +404,30 @@ 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;
+ char *label_name;
bool valid;
/* 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)) {
+ label_name = "rg";
+ if (slot != region_label_get_slot(region_label))
+ return false;
+ valid = region_label_validate_checksum(ndd, region_label);
+ } else {
+ label_name = "ns";
+ 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, slot);
+
return valid;
}
@@ -424,7 +450,7 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
nd_label = to_label(ndd, slot);
- if (!slot_valid(ndd, nd_label, slot))
+ if (!slot_valid(ndd, (union nd_lsa_label *) nd_label, slot))
continue;
nsl_get_uuid(ndd, nd_label, &label_uuid);
@@ -575,18 +601,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 = (union nd_lsa_label *) to_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++;
@@ -607,7 +645,7 @@ struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
struct nd_namespace_label *nd_label;
nd_label = to_label(ndd, slot);
- if (!slot_valid(ndd, nd_label, slot))
+ if (!slot_valid(ndd, (union nd_lsa_label *) nd_label, slot))
continue;
if (n-- == 0)
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 046063ea08b6..c985f91728dd 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -344,6 +344,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] 32+ messages in thread* [PATCH V3 09/20] nvdimm/namespace_label: Skip region label during ns label DPA reservation
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (6 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 08/20] nvdimm/label: Include region label in slot validation Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 10/20] nvdimm/namespace_label: Skip region label during namespace creation Neeraj Kumar
` (10 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 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 | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index 5e476154cf81..935a0df5b47e 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -441,6 +441,7 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
return 0; /* no label, nothing to reserve */
for_each_clear_bit_le(slot, free, nslot) {
+ union nd_lsa_label *lsa_label;
struct nd_namespace_label *nd_label;
struct nd_region *nd_region = NULL;
struct nd_label_id label_id;
@@ -448,9 +449,14 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
uuid_t label_uuid;
u32 flags;
- nd_label = to_label(ndd, slot);
+ lsa_label = (union nd_lsa_label *) to_label(ndd, slot);
+ nd_label = &lsa_label->ns_label;
- if (!slot_valid(ndd, (union nd_lsa_label *) nd_label, slot))
+ /* 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;
nsl_get_uuid(ndd, nd_label, &label_uuid);
--
2.34.1
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH V3 10/20] nvdimm/namespace_label: Skip region label during namespace creation
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (7 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 09/20] nvdimm/namespace_label: Skip region label during ns label DPA reservation Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 11/20] nvdimm/region_label: Preserve cxl region information from region label Neeraj Kumar
` (9 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
During namespace creation, skip any region labels found. And Preserve
region label into labels list if present.
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/nvdimm/namespace_devs.c | 52 +++++++++++++++++++++++++++++----
1 file changed, 47 insertions(+), 5 deletions(-)
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index 564a73b1da41..735310e6fc11 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -1979,6 +1979,10 @@ static struct device **scan_labels(struct nd_region *nd_region)
if (!nd_label)
continue;
+ /* Skip region labels if present */
+ if (is_region_label(ndd, (union nd_lsa_label *) nd_label))
+ continue;
+
/* skip labels that describe extents outside of the region */
if (nsl_get_dpa(ndd, nd_label) < nd_mapping->start ||
nsl_get_dpa(ndd, nd_label) > map_end)
@@ -2017,9 +2021,31 @@ 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++) {
+ union nd_lsa_label *nd_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) {
+ nd_label = (union nd_lsa_label *) le->label;
+
+ /* Preserve region labels if present */
+ if (is_region_label(ndd, nd_label))
+ 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;
@@ -2031,7 +2057,8 @@ 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;
+ union nd_lsa_label *nd_label;
+ struct nd_label_ent *le, *e;
LIST_HEAD(list);
int j;
@@ -2042,10 +2069,25 @@ 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) {
+ nd_label = (union nd_lsa_label *) le->label;
+
+ /* Preserve region labels */
+ if (is_region_label(ndd, nd_label)) {
+ 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] 32+ messages in thread* [PATCH V3 11/20] nvdimm/region_label: Preserve cxl region information from region label
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (8 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 10/20] nvdimm/namespace_label: Skip region label during namespace creation Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 12/20] nvdimm/region_label: Export routine to fetch region information Neeraj Kumar
` (8 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 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 | 41 +++++++++++++++++++++++++++++++++++++++
drivers/nvdimm/nd-core.h | 2 ++
drivers/nvdimm/nd.h | 1 +
include/linux/libnvdimm.h | 14 +++++++++++++
5 files changed, 62 insertions(+)
diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c
index bda22cb94e5b..30fc90591093 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 935a0df5b47e..3250e3ecd973 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -473,6 +473,47 @@ 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 *nd_label;
+ struct cxl_region_label *region_label;
+ uuid_t rg_type, region_type;
+
+ nd_label = (union nd_lsa_label *) to_label(ndd, slot);
+ region_label = &nd_label->region_label;
+ uuid_parse(CXL_REGION_UUID, ®ion_type);
+ import_uuid(&rg_type, nd_label->region_label.type);
+
+ /* TODO: Currently preserving only one region */
+ if (uuid_equal(®ion_type, &rg_type)) {
+ 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 c985f91728dd..2d0f6dd64c52 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -593,6 +593,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_check_region_label_format(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] 32+ messages in thread* [PATCH V3 12/20] nvdimm/region_label: Export routine to fetch region information
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (9 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 11/20] nvdimm/region_label: Preserve cxl region information from region label Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 13/20] cxl/mem: Refactor cxl pmem region auto-assembling Neeraj Kumar
` (7 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 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 918c3db93195..619c8ce56dce 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 nvdimm->is_region_label;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(nvdimm_has_cxl_region);
+
+void *nvdimm_get_cxl_region_param(struct nvdimm *nvdimm)
+{
+ if (nvdimm)
+ return &nvdimm->cxl_region_params;
+
+ return NULL;
+}
+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] 32+ messages in thread* [PATCH V3 13/20] cxl/mem: Refactor cxl pmem region auto-assembling
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (10 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 12/20] nvdimm/region_label: Export routine to fetch region information Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 14/20] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation Neeraj Kumar
` (6 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 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. Moved cxl pmem region auto-assembly from cxl_endpoint_port_probe() to
cxl_mem_probe() after devm_cxl_add_nvdimm(). It gurantees both the
completion of endpoint probe and cxl_nvd presence before its call.
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/core/region.c | 33 +++++++++++++++++++++++++++++++++
drivers/cxl/cxl.h | 4 ++++
drivers/cxl/mem.c | 24 +++++++++++++++---------
drivers/cxl/port.c | 39 +--------------------------------------
4 files changed, 53 insertions(+), 47 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 7a0cead24490..c325aa827992 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3606,6 +3606,39 @@ 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;
+}
+
+void cxl_region_discovery(struct cxl_port *port)
+{
+ device_for_each_child(&port->dev, NULL, discover_region);
+}
+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 4fe3df06f57a..b57597e55f7e 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -873,6 +873,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);
+void cxl_region_discovery(struct cxl_port *port);
#else
static inline bool is_cxl_pmem_region(struct device *dev)
{
@@ -895,6 +896,9 @@ static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint,
{
return 0;
}
+static inline void cxl_region_discovery(struct cxl_port *port)
+{
+}
#endif
void cxl_endpoint_parse_cdat(struct cxl_port *port);
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 6e6777b7bafb..54501616ff09 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -152,15 +152,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
@@ -184,6 +175,21 @@ static int cxl_mem_probe(struct device *dev)
if (rc)
dev_dbg(dev, "CXL memdev EDAC registration failed rc=%d\n", 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;
+ }
+ }
+
+ /*
+ * Now that all endpoint decoders are successfully enumerated, try to
+ * assemble region autodiscovery from committed decoders.
+ */
+ cxl_region_discovery(cxlmd->endpoint);
+
/*
* The kernel may be operating out of CXL memory on this device,
* there is no spec defined way to determine whether this device
diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
index cf32dc50b7a6..07bb909b7d2e 100644
--- a/drivers/cxl/port.c
+++ b/drivers/cxl/port.c
@@ -30,33 +30,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)
{
struct cxl_hdm *cxlhdm;
@@ -121,17 +94,7 @@ static int cxl_endpoint_port_probe(struct cxl_port *port)
if (rc)
return rc;
- rc = devm_cxl_enumerate_decoders(cxlhdm, &info);
- 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_enumerate_decoders(cxlhdm, &info);
}
static int cxl_port_probe(struct device *dev)
--
2.34.1
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH V3 14/20] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (11 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 13/20] cxl/mem: Refactor cxl pmem region auto-assembling Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 15/20] cxl: Add a routine to find cxl root decoder on cxl bus using cxl port Neeraj Kumar
` (5 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 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
Renamed __create_region() to cxl_create_region() and make it an exported
routine. This 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/region.c | 127 ++++++++++++++++++++++++++++++++++++--
drivers/cxl/cxl.h | 12 ++++
2 files changed, 134 insertions(+), 5 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index c325aa827992..d5c227ce7b09 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2573,6 +2573,116 @@ 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);
+ rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem);
+ if (rc)
+ 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,
+ enum cxl_partition_mode mode,
+ enum cxl_decoder_type type,
+ struct cxl_pmem_region_params *params,
+ struct cxl_decoder *cxld)
+{
+ 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 = mode;
+ 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(-EINVAL);
+
+ 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));
@@ -2590,8 +2700,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;
@@ -2613,8 +2725,12 @@ 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, mode,
+ CXL_DECODER_HOSTONLYMEM, pmem_params, cxld);
return devm_cxl_add_region(cxlrd, id, mode, CXL_DECODER_HOSTONLYMEM);
}
+EXPORT_SYMBOL_NS_GPL(cxl_create_region, "CXL");
static ssize_t create_region_store(struct device *dev, const char *buf,
size_t len, enum cxl_partition_mode mode)
@@ -2627,7 +2743,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);
@@ -3523,8 +3639,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)) {
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index b57597e55f7e..3abadc3dc82e 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -874,6 +874,10 @@ 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);
void cxl_region_discovery(struct cxl_port *port);
+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 bool is_cxl_pmem_region(struct device *dev)
{
@@ -899,6 +903,14 @@ static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint,
static inline void cxl_region_discovery(struct cxl_port *port)
{
}
+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);
+}
#endif
void cxl_endpoint_parse_cdat(struct cxl_port *port);
--
2.34.1
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH V3 15/20] cxl: Add a routine to find cxl root decoder on cxl bus using cxl port
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (12 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 14/20] cxl/region: Add devm_cxl_pmem_add_region() for pmem region creation Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 16/20] cxl/mem: Preserve cxl root decoder during mem probe Neeraj Kumar
` (4 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Add cxl_find_root_decoder_by_port() to find root decoder on cxl bus.
It is used to find root decoder using cxl port.
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/core/port.c | 27 +++++++++++++++++++++++++++
drivers/cxl/cxl.h | 1 +
2 files changed, 28 insertions(+)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 8f36ff413f5d..647d9ce32b64 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -518,6 +518,33 @@ struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev)
}
EXPORT_SYMBOL_NS_GPL(to_cxl_switch_decoder, "CXL");
+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
+ */
+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;
+
+ /* Release device ref taken via device_find_child() */
+ put_device(dev);
+ return to_cxl_root_decoder(dev);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_find_root_decoder_by_port, "CXL");
+
static void cxl_ep_release(struct cxl_ep *ep)
{
put_device(ep->ep);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 3abadc3dc82e..1eb1aca7c69f 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -866,6 +866,7 @@ struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
bool is_cxl_nvdimm(struct device *dev);
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);
+struct cxl_root_decoder *cxl_find_root_decoder_by_port(struct cxl_port *port);
#ifdef CONFIG_CXL_REGION
bool is_cxl_pmem_region(struct device *dev);
--
2.34.1
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH V3 16/20] cxl/mem: Preserve cxl root decoder during mem probe
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (13 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 15/20] cxl: Add a routine to find cxl root decoder on cxl bus using cxl port Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 17/20] cxl/pmem: Preserve region information into nd_set Neeraj Kumar
` (3 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Saved root decoder info is required for cxl region persistency
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/cxlmem.h | 1 +
drivers/cxl/mem.c | 2 ++
2 files changed, 3 insertions(+)
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 434031a0c1f7..25cb115b72bd 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -59,6 +59,7 @@ struct cxl_memdev {
struct cxl_nvdimm_bridge *cxl_nvb;
struct cxl_nvdimm *cxl_nvd;
struct cxl_port *endpoint;
+ struct cxl_root_decoder *cxlrd;
int id;
int depth;
u8 scrub_cycle;
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 54501616ff09..1a0da7253a24 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -152,6 +152,8 @@ static int cxl_mem_probe(struct device *dev)
return -ENXIO;
}
+ cxlmd->cxlrd = cxl_find_root_decoder_by_port(parent_port);
+
if (dport->rch)
endpoint_parent = parent_port->uport_dev;
else
--
2.34.1
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH V3 17/20] cxl/pmem: Preserve region information into nd_set
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (14 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 16/20] cxl/mem: Preserve cxl root decoder during mem probe Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 18/20] cxl/pmem_region: Prep patch to accommodate pmem_region attributes Neeraj Kumar
` (2 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 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..38a5bcdc68ce 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] 32+ messages in thread* [PATCH V3 18/20] cxl/pmem_region: Prep patch to accommodate pmem_region attributes
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (15 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 17/20] cxl/pmem: Preserve region information into nd_set Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 19/20] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 20/20] cxl/pmem: Add CXL LSA 2.1 support in cxl pmem Neeraj Kumar
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 UTC (permalink / raw)
To: linux-cxl, nvdimm, linux-kernel, gost.dev
Cc: a.manzanares, vishak.g, neeraj.kernel, Neeraj Kumar
Created a separate file core/pmem_region.c along with CONFIG_PMEM_REGION
Moved pmem_region related code from core/region.c to core/pmem_region.c
For region label update, need to create device attribute, which calls
nvdimm exported function thus making pmem_region dependent on libnvdimm.
Because of this dependency of pmem region on libnvdimm, segregated pmem
region related code from core/region.c
Signed-off-by: Neeraj Kumar <s.neeraj@samsung.com>
---
drivers/cxl/Kconfig | 14 +++
drivers/cxl/core/Makefile | 1 +
drivers/cxl/core/core.h | 8 +-
drivers/cxl/core/pmem_region.c | 203 +++++++++++++++++++++++++++++++++
drivers/cxl/core/port.c | 2 +-
drivers/cxl/core/region.c | 191 +------------------------------
drivers/cxl/cxl.h | 30 +++--
tools/testing/cxl/Kbuild | 1 +
8 files changed, 250 insertions(+), 200 deletions(-)
create mode 100644 drivers/cxl/core/pmem_region.c
diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
index 48b7314afdb8..532eaa1bbdd6 100644
--- a/drivers/cxl/Kconfig
+++ b/drivers/cxl/Kconfig
@@ -211,6 +211,20 @@ 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 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
+ updation/deletion 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 5ad8fef210b5..399157beb917 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -17,6 +17,7 @@ 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_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 5707cd60a8eb..536636a752dc 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);
@@ -74,10 +73,15 @@ static inline void cxl_region_exit(void)
#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 (&cxl_pmem_region_type)
+#else
+#define CXL_PMEM_REGION_TYPE NULL
+#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/pmem_region.c b/drivers/cxl/core/pmem_region.c
new file mode 100644
index 000000000000..55b80d587403
--- /dev/null
+++ b/drivers/cxl/core/pmem_region.c
@@ -0,0 +1,203 @@
+// 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.
+ */
+
+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 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,
+};
+
+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;
+}
+EXPORT_SYMBOL_NS_GPL(devm_cxl_add_pmem_region, "CXL");
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 647d9ce32b64..717de1d3f70e 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -53,7 +53,7 @@ static int cxl_device_id(const struct device *dev)
return CXL_DEVICE_NVDIMM_BRIDGE;
if (dev->type == &cxl_nvdimm_type)
return CXL_DEVICE_NVDIMM;
- if (dev->type == CXL_PMEM_REGION_TYPE())
+ if (dev->type == CXL_PMEM_REGION_TYPE)
return CXL_DEVICE_PMEM_REGION;
if (dev->type == CXL_DAX_REGION_TYPE())
return CXL_DEVICE_DAX_REGION;
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index d5c227ce7b09..d2ef7fcc19fe 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, \
@@ -2382,7 +2380,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"))
@@ -2390,6 +2388,7 @@ static struct cxl_region *to_cxl_region(struct device *dev)
return container_of(dev, struct cxl_region, dev);
}
+EXPORT_SYMBOL_NS_GPL(to_cxl_region, "CXL");
static void unregister_region(void *_cxlr)
{
@@ -2814,46 +2813,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;
@@ -3213,64 +3172,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);
@@ -3334,92 +3235,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;
@@ -3985,6 +3800,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 1eb1aca7c69f..0d576b359de6 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -825,6 +825,7 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds,
struct cxl_endpoint_dvsec_info *info);
bool is_cxl_region(struct device *dev);
+struct cxl_region *to_cxl_region(struct device *dev);
extern const struct bus_type cxl_bus_type;
@@ -869,8 +870,6 @@ struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port);
struct cxl_root_decoder *cxl_find_root_decoder_by_port(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);
@@ -880,14 +879,6 @@ struct cxl_region *cxl_create_region(struct cxl_root_decoder *cxlrd,
struct cxl_pmem_region_params *params,
struct cxl_decoder *cxld);
#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;
@@ -914,6 +905,25 @@ cxl_create_region(struct cxl_root_decoder *cxlrd,
}
#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);
+int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
+#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 devm_cxl_add_pmem_region(struct cxl_region *cxlr)
+{
+ return 0;
+}
+#endif
+
void cxl_endpoint_parse_cdat(struct cxl_port *port);
void cxl_switch_parse_cdat(struct cxl_port *port);
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index d07f14cb7aa4..cf58ada337b7 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -64,6 +64,7 @@ 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_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] 32+ messages in thread* [PATCH V3 19/20] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (16 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 18/20] cxl/pmem_region: Prep patch to accommodate pmem_region attributes Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
2025-09-17 13:29 ` [PATCH V3 20/20] cxl/pmem: Add CXL LSA 2.1 support in cxl pmem Neeraj Kumar
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 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 | 91 ++++++++++++++++++++++++-
drivers/cxl/cxl.h | 1 +
3 files changed, 113 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl
index 6b4e8c7a963d..d6080fcf843a 100644
--- a/Documentation/ABI/testing/sysfs-bus-cxl
+++ b/Documentation/ABI/testing/sysfs-bus-cxl
@@ -615,3 +615,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: Sept, 2025
+KernelVersion: v6.17
+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: Sept, 2025
+KernelVersion: v6.17
+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 55b80d587403..665b603c907b 100644
--- a/drivers/cxl/core/pmem_region.c
+++ b/drivers/cxl/core/pmem_region.c
@@ -45,9 +45,98 @@ 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) {
+ rc = nd_region_label_update(cxlr->cxlr_pmem->nd_region);
+ if (!rc)
+ cxlr->params.region_label_state = 1;
+ }
+
+ if (rc)
+ return rc;
+
+ 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->region_label_state);
+}
+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) {
+ rc = nd_region_label_delete(cxlr->cxlr_pmem->nd_region);
+ if (rc)
+ return rc;
+ cxlr->params.region_label_state = 0;
+ }
+
+ 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 0d576b359de6..f01f8c942fdf 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -484,6 +484,7 @@ enum cxl_config_state {
*/
struct cxl_region_params {
enum cxl_config_state state;
+ int region_label_state;
uuid_t uuid;
int interleave_ways;
int interleave_granularity;
--
2.34.1
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH V3 20/20] cxl/pmem: Add CXL LSA 2.1 support in cxl pmem
2025-09-17 13:29 ` [PATCH V3 00/20] Add CXL LSA 2.1 format support in nvdimm and cxl pmem Neeraj Kumar
` (17 preceding siblings ...)
2025-09-17 13:29 ` [PATCH V3 19/20] cxl/pmem_region: Add sysfs attribute cxl region label updation/deletion Neeraj Kumar
@ 2025-09-17 13:29 ` Neeraj Kumar
18 siblings, 0 replies; 32+ messages in thread
From: Neeraj Kumar @ 2025-09-17 13:29 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/core/pmem_region.c | 53 ++++++++++++++++++++++++++++++++++
drivers/cxl/cxl.h | 4 +++
drivers/cxl/pmem.c | 2 ++
3 files changed, 59 insertions(+)
diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
index 665b603c907b..3ef9c7d15041 100644
--- a/drivers/cxl/core/pmem_region.c
+++ b/drivers/cxl/core/pmem_region.c
@@ -290,3 +290,56 @@ int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
return rc;
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_pmem_region, "CXL");
+
+static int match_free_ep_decoder(struct device *dev, const void *data)
+{
+ struct cxl_decoder *cxld = to_cxl_decoder(dev);
+
+ return !cxld->region;
+}
+
+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;
+
+ /* Release device ref taken via device_find_child() */
+ put_device(dev);
+ 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_root_decoder *cxlrd;
+ struct cxl_decoder *cxld;
+ 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;
+ cxlrd = cxlmd->cxlrd;
+
+ /* TODO: Region creation support only for interleave way == 1 */
+ if (!(params->nlabel == 1))
+ dev_info(&cxlmd->dev,
+ "Region Creation is not supported with iw > 1\n");
+ else {
+ cxld = cxl_find_free_ep_decoder(cxlmd->endpoint);
+ cxlr = cxl_create_region(cxlrd, CXL_PARTMODE_PMEM,
+ atomic_read(&cxlrd->region_id),
+ params, cxld);
+ if (IS_ERR(cxlr))
+ dev_info(&cxlmd->dev, "Region Creation failed\n");
+ }
+}
+EXPORT_SYMBOL_NS_GPL(create_pmem_region, "CXL");
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index f01f8c942fdf..0a87ea79742a 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -910,6 +910,7 @@ cxl_create_region(struct cxl_root_decoder *cxlrd,
bool is_cxl_pmem_region(struct device *dev);
struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
+void create_pmem_region(struct nvdimm *nvdimm);
#else
static inline bool is_cxl_pmem_region(struct device *dev)
{
@@ -923,6 +924,9 @@ static inline int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
{
return 0;
}
+static inline void create_pmem_region(struct nvdimm *nvdimm)
+{
+}
#endif
void cxl_endpoint_parse_cdat(struct cxl_port *port);
diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c
index 38a5bcdc68ce..0cdef01dbc68 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] 32+ messages in thread