* [PATCH 1/9] mm/memory_hotplug: pass online_type to online_memory_block() via arg
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
@ 2026-01-29 21:04 ` Gregory Price
2026-02-02 17:10 ` Jonathan Cameron
2026-01-29 21:04 ` [PATCH 2/9] mm/memory_hotplug: add __add_memory_driver_managed() with online_type arg Gregory Price
` (9 subsequent siblings)
10 siblings, 1 reply; 30+ messages in thread
From: Gregory Price @ 2026-01-29 21:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john, Oscar Salvador, Andrew Morton,
David Hildenbrand (Red Hat)
Modify online_memory_block() to accept the online type through its arg
parameter rather than calling mhp_get_default_online_type() internally.
This prepares for allowing callers to specify explicit online types.
Update the caller in add_memory_resource() to pass the default online
type via a local variable.
No functional change.
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Acked-by: David Hildenbrand (Red Hat) <david@kernel.org>
Signed-off-by: Gregory Price <gourry@gourry.net>
---
mm/memory_hotplug.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index bc805029da51..87796b617d9e 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1337,7 +1337,9 @@ static int check_hotplug_memory_range(u64 start, u64 size)
static int online_memory_block(struct memory_block *mem, void *arg)
{
- mem->online_type = mhp_get_default_online_type();
+ int *online_type = arg;
+
+ mem->online_type = *online_type;
return device_online(&mem->dev);
}
@@ -1578,8 +1580,12 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
merge_system_ram_resource(res);
/* online pages if requested */
- if (mhp_get_default_online_type() != MMOP_OFFLINE)
- walk_memory_blocks(start, size, NULL, online_memory_block);
+ if (mhp_get_default_online_type() != MMOP_OFFLINE) {
+ int online_type = mhp_get_default_online_type();
+
+ walk_memory_blocks(start, size, &online_type,
+ online_memory_block);
+ }
return ret;
error:
--
2.52.0
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 1/9] mm/memory_hotplug: pass online_type to online_memory_block() via arg
2026-01-29 21:04 ` [PATCH 1/9] mm/memory_hotplug: pass online_type to online_memory_block() via arg Gregory Price
@ 2026-02-02 17:10 ` Jonathan Cameron
2026-02-02 17:46 ` Gregory Price
0 siblings, 1 reply; 30+ messages in thread
From: Jonathan Cameron @ 2026-02-02 17:10 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john, Oscar Salvador, Andrew Morton,
David Hildenbrand (Red Hat)
On Thu, 29 Jan 2026 16:04:34 -0500
Gregory Price <gourry@gourry.net> wrote:
> Modify online_memory_block() to accept the online type through its arg
> parameter rather than calling mhp_get_default_online_type() internally.
> This prepares for allowing callers to specify explicit online types.
>
> Update the caller in add_memory_resource() to pass the default online
> type via a local variable.
>
> No functional change.
>
> Cc: Oscar Salvador <osalvador@suse.de>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Acked-by: David Hildenbrand (Red Hat) <david@kernel.org>
> Signed-off-by: Gregory Price <gourry@gourry.net>
Trivial comment inline. I don't really care either way.
Pushing the policy up to the caller and ensuring it's explicitly constant
for all the memory blocks (as opposed to relying on locks) seems sensible to me
even without anything else.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> ---
> mm/memory_hotplug.c | 12 +++++++++---
> 1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index bc805029da51..87796b617d9e 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -1337,7 +1337,9 @@ static int check_hotplug_memory_range(u64 start, u64 size)
>
> static int online_memory_block(struct memory_block *mem, void *arg)
> {
> - mem->online_type = mhp_get_default_online_type();
> + int *online_type = arg;
> +
> + mem->online_type = *online_type;
> return device_online(&mem->dev);
> }
>
> @@ -1578,8 +1580,12 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
> merge_system_ram_resource(res);
>
> /* online pages if requested */
> - if (mhp_get_default_online_type() != MMOP_OFFLINE)
> - walk_memory_blocks(start, size, NULL, online_memory_block);
> + if (mhp_get_default_online_type() != MMOP_OFFLINE) {
> + int online_type = mhp_get_default_online_type();
Maybe move the local variable outside the loop to avoid the double call.
> +
> + walk_memory_blocks(start, size, &online_type,
> + online_memory_block);
> + }
>
> return ret;
> error:
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 1/9] mm/memory_hotplug: pass online_type to online_memory_block() via arg
2026-02-02 17:10 ` Jonathan Cameron
@ 2026-02-02 17:46 ` Gregory Price
0 siblings, 0 replies; 30+ messages in thread
From: Gregory Price @ 2026-02-02 17:46 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john, Oscar Salvador, Andrew Morton,
David Hildenbrand (Red Hat)
On Mon, Feb 02, 2026 at 05:10:29PM +0000, Jonathan Cameron wrote:
> On Thu, 29 Jan 2026 16:04:34 -0500
> Gregory Price <gourry@gourry.net> wrote:
>
> > Modify online_memory_block() to accept the online type through its arg
> > parameter rather than calling mhp_get_default_online_type() internally.
> > This prepares for allowing callers to specify explicit online types.
> >
> > Update the caller in add_memory_resource() to pass the default online
> > type via a local variable.
> >
> > No functional change.
> >
> > Cc: Oscar Salvador <osalvador@suse.de>
> > Cc: Andrew Morton <akpm@linux-foundation.org>
> > Acked-by: David Hildenbrand (Red Hat) <david@kernel.org>
> > Signed-off-by: Gregory Price <gourry@gourry.net>
>
> Trivial comment inline. I don't really care either way.
> Pushing the policy up to the caller and ensuring it's explicitly constant
> for all the memory blocks (as opposed to relying on locks) seems sensible to me
> even without anything else.
>
> >
> > /* online pages if requested */
> > - if (mhp_get_default_online_type() != MMOP_OFFLINE)
> > - walk_memory_blocks(start, size, NULL, online_memory_block);
> > + if (mhp_get_default_online_type() != MMOP_OFFLINE) {
> > + int online_type = mhp_get_default_online_type();
>
> Maybe move the local variable outside the loop to avoid the double call.
>
ack. will update for next version w/ Ben's notes and the build fix.
Thanks!
~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 2/9] mm/memory_hotplug: add __add_memory_driver_managed() with online_type arg
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
2026-01-29 21:04 ` [PATCH 1/9] mm/memory_hotplug: pass online_type to online_memory_block() via arg Gregory Price
@ 2026-01-29 21:04 ` Gregory Price
2026-02-02 17:25 ` Jonathan Cameron
2026-01-29 21:04 ` [PATCH 3/9] dax: plumb online_type from dax_kmem creators to hotplug Gregory Price
` (8 subsequent siblings)
10 siblings, 1 reply; 30+ messages in thread
From: Gregory Price @ 2026-01-29 21:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john, David Hildenbrand,
Oscar Salvador, Andrew Morton
Enable dax kmem driver to select how to online the memory rather than
implicitly depending on the system default. This will allow users of
dax to plumb through a preferred auto-online policy for their region.
Refactor and new interface:
Add __add_memory_driver_managed() which accepts an explicit online_type
and export mhp_get_default_online_type() so callers can pass it when
they want the default behavior.
Refactor:
Extract __add_memory_resource() to take an explicit online_type parameter,
and update add_memory_resource() to pass the system default.
No functional change for existing users.
Cc: David Hildenbrand <david@kernel.org>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Gregory Price <gourry@gourry.net>
---
include/linux/memory_hotplug.h | 3 ++
mm/memory_hotplug.c | 91 ++++++++++++++++++++++++----------
2 files changed, 67 insertions(+), 27 deletions(-)
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index f2f16cdd73ee..1eb63d1a247d 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -293,6 +293,9 @@ extern int __add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags);
extern int add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags);
extern int add_memory_resource(int nid, struct resource *resource,
mhp_t mhp_flags);
+int __add_memory_driver_managed(int nid, u64 start, u64 size,
+ const char *resource_name, mhp_t mhp_flags,
+ int online_type);
extern int add_memory_driver_managed(int nid, u64 start, u64 size,
const char *resource_name,
mhp_t mhp_flags);
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 87796b617d9e..d3ca95b872bd 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -239,6 +239,7 @@ int mhp_get_default_online_type(void)
return mhp_default_online_type;
}
+EXPORT_SYMBOL_GPL(mhp_get_default_online_type);
void mhp_set_default_online_type(int online_type)
{
@@ -1490,7 +1491,8 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group,
*
* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG
*/
-int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
+static int __add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags,
+ int online_type)
{
struct mhp_params params = { .pgprot = pgprot_mhp(PAGE_KERNEL) };
enum memblock_flags memblock_flags = MEMBLOCK_NONE;
@@ -1580,12 +1582,9 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
merge_system_ram_resource(res);
/* online pages if requested */
- if (mhp_get_default_online_type() != MMOP_OFFLINE) {
- int online_type = mhp_get_default_online_type();
-
+ if (online_type != MMOP_OFFLINE)
walk_memory_blocks(start, size, &online_type,
online_memory_block);
- }
return ret;
error:
@@ -1601,7 +1600,13 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
return ret;
}
-/* requires device_hotplug_lock, see add_memory_resource() */
+int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
+{
+ return __add_memory_resource(nid, res, mhp_flags,
+ mhp_get_default_online_type());
+}
+
+/* requires device_hotplug_lock, see __add_memory_resource() */
int __add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags)
{
struct resource *res;
@@ -1629,29 +1634,24 @@ int add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags)
}
EXPORT_SYMBOL_GPL(add_memory);
-/*
- * Add special, driver-managed memory to the system as system RAM. Such
- * memory is not exposed via the raw firmware-provided memmap as system
- * RAM, instead, it is detected and added by a driver - during cold boot,
- * after a reboot, and after kexec.
- *
- * Reasons why this memory should not be used for the initial memmap of a
- * kexec kernel or for placing kexec images:
- * - The booting kernel is in charge of determining how this memory will be
- * used (e.g., use persistent memory as system RAM)
- * - Coordination with a hypervisor is required before this memory
- * can be used (e.g., inaccessible parts).
+/**
+ * __add_memory_driver_managed - add driver-managed memory with explicit online_type
+ * @nid: NUMA node ID where the memory will be added
+ * @start: Start physical address of the memory range
+ * @size: Size of the memory range in bytes
+ * @resource_name: Resource name in format "System RAM ($DRIVER)"
+ * @mhp_flags: Memory hotplug flags
+ * @online_type: Online behavior (MMOP_ONLINE, MMOP_ONLINE_KERNEL,
+ * MMOP_ONLINE_MOVABLE, or MMOP_OFFLINE)
*
- * For this memory, no entries in /sys/firmware/memmap ("raw firmware-provided
- * memory map") are created. Also, the created memory resource is flagged
- * with IORESOURCE_SYSRAM_DRIVER_MANAGED, so in-kernel users can special-case
- * this memory as well (esp., not place kexec images onto it).
+ * Add driver-managed memory with explicit online_type specification.
+ * The resource_name must have the format "System RAM ($DRIVER)".
*
- * The resource_name (visible via /proc/iomem) has to have the format
- * "System RAM ($DRIVER)".
+ * Return: 0 on success, negative error code on failure.
*/
-int add_memory_driver_managed(int nid, u64 start, u64 size,
- const char *resource_name, mhp_t mhp_flags)
+int __add_memory_driver_managed(int nid, u64 start, u64 size,
+ const char *resource_name, mhp_t mhp_flags,
+ int online_type)
{
struct resource *res;
int rc;
@@ -1661,6 +1661,9 @@ int add_memory_driver_managed(int nid, u64 start, u64 size,
resource_name[strlen(resource_name) - 1] != ')')
return -EINVAL;
+ if (online_type < 0 || online_type > MMOP_ONLINE_MOVABLE)
+ return -EINVAL;
+
lock_device_hotplug();
res = register_memory_resource(start, size, resource_name);
@@ -1669,7 +1672,7 @@ int add_memory_driver_managed(int nid, u64 start, u64 size,
goto out_unlock;
}
- rc = add_memory_resource(nid, res, mhp_flags);
+ rc = __add_memory_resource(nid, res, mhp_flags, online_type);
if (rc < 0)
release_memory_resource(res);
@@ -1677,6 +1680,40 @@ int add_memory_driver_managed(int nid, u64 start, u64 size,
unlock_device_hotplug();
return rc;
}
+EXPORT_SYMBOL_FOR_MODULES(__add_memory_driver_managed, "kmem");
+
+/*
+ * Add special, driver-managed memory to the system as system RAM. Such
+ * memory is not exposed via the raw firmware-provided memmap as system
+ * RAM, instead, it is detected and added by a driver - during cold boot,
+ * after a reboot, and after kexec.
+ *
+ * Reasons why this memory should not be used for the initial memmap of a
+ * kexec kernel or for placing kexec images:
+ * - The booting kernel is in charge of determining how this memory will be
+ * used (e.g., use persistent memory as system RAM)
+ * - Coordination with a hypervisor is required before this memory
+ * can be used (e.g., inaccessible parts).
+ *
+ * For this memory, no entries in /sys/firmware/memmap ("raw firmware-provided
+ * memory map") are created. Also, the created memory resource is flagged
+ * with IORESOURCE_SYSRAM_DRIVER_MANAGED, so in-kernel users can special-case
+ * this memory as well (esp., not place kexec images onto it).
+ *
+ * The resource_name (visible via /proc/iomem) has to have the format
+ * "System RAM ($DRIVER)".
+ *
+ * Memory will be onlined using the system default online type.
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+int add_memory_driver_managed(int nid, u64 start, u64 size,
+ const char *resource_name, mhp_t mhp_flags)
+{
+ return __add_memory_driver_managed(nid, start, size, resource_name,
+ mhp_flags,
+ mhp_get_default_online_type());
+}
EXPORT_SYMBOL_GPL(add_memory_driver_managed);
/*
--
2.52.0
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 2/9] mm/memory_hotplug: add __add_memory_driver_managed() with online_type arg
2026-01-29 21:04 ` [PATCH 2/9] mm/memory_hotplug: add __add_memory_driver_managed() with online_type arg Gregory Price
@ 2026-02-02 17:25 ` Jonathan Cameron
2026-02-02 18:02 ` Gregory Price
0 siblings, 1 reply; 30+ messages in thread
From: Jonathan Cameron @ 2026-02-02 17:25 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john, David Hildenbrand, Oscar Salvador,
Andrew Morton
On Thu, 29 Jan 2026 16:04:35 -0500
Gregory Price <gourry@gourry.net> wrote:
> Enable dax kmem driver to select how to online the memory rather than
> implicitly depending on the system default. This will allow users of
> dax to plumb through a preferred auto-online policy for their region.
>
> Refactor and new interface:
> Add __add_memory_driver_managed() which accepts an explicit online_type
> and export mhp_get_default_online_type() so callers can pass it when
> they want the default behavior.
Hi Gregory,
I think maybe I'd have left the export for the first user outside of
memory_hotplug.c. Not particularly important however.
Maybe talk about why a caller of __add_memory_driver_managed() might want
the default? Feels like that's for the people who don't...
Or is this all a dance to avoid an
if (special mode)
__add_memory_driver_managed();
else
add_memory_driver_managed();
?
Other comments are mostly about using a named enum. I'm not sure
if there is some existing reason why that doesn't work? -Errno pushed through
this variable or anything like that?
>
> Refactor:
> Extract __add_memory_resource() to take an explicit online_type parameter,
> and update add_memory_resource() to pass the system default.
>
> No functional change for existing users.
>
> Cc: David Hildenbrand <david@kernel.org>
> Cc: Oscar Salvador <osalvador@suse.de>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Signed-off-by: Gregory Price <gourry@gourry.net>
> ---
> include/linux/memory_hotplug.h | 3 ++
> mm/memory_hotplug.c | 91 ++++++++++++++++++++++++----------
> 2 files changed, 67 insertions(+), 27 deletions(-)
>
> diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
> index f2f16cdd73ee..1eb63d1a247d 100644
> --- a/include/linux/memory_hotplug.h
> +++ b/include/linux/memory_hotplug.h
> @@ -293,6 +293,9 @@ extern int __add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags);
> extern int add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags);
> extern int add_memory_resource(int nid, struct resource *resource,
> mhp_t mhp_flags);
> +int __add_memory_driver_managed(int nid, u64 start, u64 size,
> + const char *resource_name, mhp_t mhp_flags,
> + int online_type);
Given online_type values are from an enum anyway, maybe we can name that enum and use
it explicitly?
> extern int add_memory_driver_managed(int nid, u64 start, u64 size,
> const char *resource_name,
> mhp_t mhp_flags);
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index 87796b617d9e..d3ca95b872bd 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -239,6 +239,7 @@ int mhp_get_default_online_type(void)
>
> return mhp_default_online_type;
> }
> +EXPORT_SYMBOL_GPL(mhp_get_default_online_type);
>
> void mhp_set_default_online_type(int online_type)
> {
> @@ -1490,7 +1491,8 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group,
> *
> * we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG
> */
> -int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
> +static int __add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags,
> + int online_type)
> {
> struct mhp_params params = { .pgprot = pgprot_mhp(PAGE_KERNEL) };
> enum memblock_flags memblock_flags = MEMBLOCK_NONE;
> @@ -1580,12 +1582,9 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
> merge_system_ram_resource(res);
>
> /* online pages if requested */
> - if (mhp_get_default_online_type() != MMOP_OFFLINE) {
> - int online_type = mhp_get_default_online_type();
> -
> + if (online_type != MMOP_OFFLINE)
Ah. Fair enough, ignore comment in previous patch. I should have read on...
> walk_memory_blocks(start, size, &online_type,
> online_memory_block);
> - }
>
> return ret;
> error:
> @@ -1601,7 +1600,13 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
> return ret;
> }
>
> -/* requires device_hotplug_lock, see add_memory_resource() */
> +int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
> +{
> + return __add_memory_resource(nid, res, mhp_flags,
> + mhp_get_default_online_type());
> +}
> +
> +/* requires device_hotplug_lock, see __add_memory_resource() */
> int __add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags)
> {
> struct resource *res;
> @@ -1629,29 +1634,24 @@ int add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags)
> }
> EXPORT_SYMBOL_GPL(add_memory);
>
> -/*
> - * Add special, driver-managed memory to the system as system RAM. Such
> - * memory is not exposed via the raw firmware-provided memmap as system
> - * RAM, instead, it is detected and added by a driver - during cold boot,
> - * after a reboot, and after kexec.
> - *
> - * Reasons why this memory should not be used for the initial memmap of a
> - * kexec kernel or for placing kexec images:
> - * - The booting kernel is in charge of determining how this memory will be
> - * used (e.g., use persistent memory as system RAM)
> - * - Coordination with a hypervisor is required before this memory
> - * can be used (e.g., inaccessible parts).
> +/**
> + * __add_memory_driver_managed - add driver-managed memory with explicit online_type
It's a little odd to add nice kernel-doc formatted documentation
when the non __ variant has free form docs. Maybe tidy that up first
if we want to go kernel-doc in this file? (I'm in favor, but no idea
on general feelings...)
> + * @nid: NUMA node ID where the memory will be added
> + * @start: Start physical address of the memory range
> + * @size: Size of the memory range in bytes
> + * @resource_name: Resource name in format "System RAM ($DRIVER)"
> + * @mhp_flags: Memory hotplug flags
> + * @online_type: Online behavior (MMOP_ONLINE, MMOP_ONLINE_KERNEL,
> + * MMOP_ONLINE_MOVABLE, or MMOP_OFFLINE)
Given that's currently the full set, seems like enum wins out here over
an int.
> *
> - * For this memory, no entries in /sys/firmware/memmap ("raw firmware-provided
> - * memory map") are created. Also, the created memory resource is flagged
> - * with IORESOURCE_SYSRAM_DRIVER_MANAGED, so in-kernel users can special-case
> - * this memory as well (esp., not place kexec images onto it).
> + * Add driver-managed memory with explicit online_type specification.
> + * The resource_name must have the format "System RAM ($DRIVER)".
> *
> - * The resource_name (visible via /proc/iomem) has to have the format
> - * "System RAM ($DRIVER)".
> + * Return: 0 on success, negative error code on failure.
> */
> -int add_memory_driver_managed(int nid, u64 start, u64 size,
> - const char *resource_name, mhp_t mhp_flags)
> +int __add_memory_driver_managed(int nid, u64 start, u64 size,
> + const char *resource_name, mhp_t mhp_flags,
> + int online_type)
> {
> struct resource *res;
> int rc;
> @@ -1661,6 +1661,9 @@ int add_memory_driver_managed(int nid, u64 start, u64 size,
> resource_name[strlen(resource_name) - 1] != ')')
> return -EINVAL;
>
> + if (online_type < 0 || online_type > MMOP_ONLINE_MOVABLE)
This is where using an enum would help compiler know what is going on
and maybe warn if anyone writes something that isn't defined.
> + return -EINVAL;
> +
> lock_device_hotplug();
>
> res = register_memory_resource(start, size, resource_name);
> @@ -1669,7 +1672,7 @@ int add_memory_driver_managed(int nid, u64 start, u64 size,
> goto out_unlock;
> }
>
> - rc = add_memory_resource(nid, res, mhp_flags);
> + rc = __add_memory_resource(nid, res, mhp_flags, online_type);
> if (rc < 0)
> release_memory_resource(res);
>
> @@ -1677,6 +1680,40 @@ int add_memory_driver_managed(int nid, u64 start, u64 size,
> unlock_device_hotplug();
> return rc;
> }
> +EXPORT_SYMBOL_FOR_MODULES(__add_memory_driver_managed, "kmem");
> +
> +/*
> + * Add special, driver-managed memory to the system as system RAM. Such
> + * memory is not exposed via the raw firmware-provided memmap as system
> + * RAM, instead, it is detected and added by a driver - during cold boot,
> + * after a reboot, and after kexec.
> + *
> + * Reasons why this memory should not be used for the initial memmap of a
> + * kexec kernel or for placing kexec images:
> + * - The booting kernel is in charge of determining how this memory will be
> + * used (e.g., use persistent memory as system RAM)
> + * - Coordination with a hypervisor is required before this memory
> + * can be used (e.g., inaccessible parts).
> + *
> + * For this memory, no entries in /sys/firmware/memmap ("raw firmware-provided
> + * memory map") are created. Also, the created memory resource is flagged
> + * with IORESOURCE_SYSRAM_DRIVER_MANAGED, so in-kernel users can special-case
> + * this memory as well (esp., not place kexec images onto it).
> + *
> + * The resource_name (visible via /proc/iomem) has to have the format
> + * "System RAM ($DRIVER)".
> + *
> + * Memory will be onlined using the system default online type.
> + *
> + * Returns 0 on success, negative error code on failure.
> + */
> +int add_memory_driver_managed(int nid, u64 start, u64 size,
> + const char *resource_name, mhp_t mhp_flags)
> +{
> + return __add_memory_driver_managed(nid, start, size, resource_name,
> + mhp_flags,
> + mhp_get_default_online_type());
> +}
> EXPORT_SYMBOL_GPL(add_memory_driver_managed);
>
> /*
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 2/9] mm/memory_hotplug: add __add_memory_driver_managed() with online_type arg
2026-02-02 17:25 ` Jonathan Cameron
@ 2026-02-02 18:02 ` Gregory Price
2026-02-02 18:46 ` Jonathan Cameron
0 siblings, 1 reply; 30+ messages in thread
From: Gregory Price @ 2026-02-02 18:02 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john, David Hildenbrand, Oscar Salvador,
Andrew Morton
On Mon, Feb 02, 2026 at 05:25:24PM +0000, Jonathan Cameron wrote:
> On Thu, 29 Jan 2026 16:04:35 -0500
> Gregory Price <gourry@gourry.net> wrote:
>
> > Enable dax kmem driver to select how to online the memory rather than
> > implicitly depending on the system default. This will allow users of
> > dax to plumb through a preferred auto-online policy for their region.
> >
> > Refactor and new interface:
> > Add __add_memory_driver_managed() which accepts an explicit online_type
> > and export mhp_get_default_online_type() so callers can pass it when
> > they want the default behavior.
>
> Hi Gregory,
>
> I think maybe I'd have left the export for the first user outside of
> memory_hotplug.c. Not particularly important however.
>
> Maybe talk about why a caller of __add_memory_driver_managed() might want
> the default? Feels like that's for the people who don't...
>
Less about why they want the default, more about maintaining backward
compatibility.
In the cxl driver, Ben pointed out something that made me realize we can
change `region/bind()` to actually use the new `sysram/bind` path by
just adding a one line `sysram_regionN->online_type = default()`
I can add this detail to the changelog.
>
> Other comments are mostly about using a named enum. I'm not sure
> if there is some existing reason why that doesn't work? -Errno pushed through
> this variable or anything like that?
>
I can add a cleanup-patch prior to use the enum, but i don't think this
actually enables the compiler to do anything new at the moment?
An enum just resolves to an int, and setting `enum thing val = -1` when
the enum definition doesn't include -1 doesn't actually fire any errors
(at least IIRC - maybe i'm just wrong). Same with
function(enum) -> function(-1) wouldn't fire a compilation error
It might actually be worth adding `MMOP_NOT_CONFIGURED = -1` so that the
cxl-sysram driver can set this explicitly rather than just setting -1
as an implicit version of this - but then why would memory_hotplug.c
ever want to expose a NOT_CONFIGURED option lol.
So, yeah, the enum looks nicer, but not sure how much it buys us beyond
that.
> It's a little odd to add nice kernel-doc formatted documentation
> when the non __ variant has free form docs. Maybe tidy that up first
> if we want to go kernel-doc in this file? (I'm in favor, but no idea
> on general feelings...)
>
ack. Can add some more cleanups early in the series.
> > + if (online_type < 0 || online_type > MMOP_ONLINE_MOVABLE)
>
> This is where using an enum would help compiler know what is going on
> and maybe warn if anyone writes something that isn't defined.
>
I think you still have to sanity check this, but maybe the code looks
cleaner, so will do.
~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 2/9] mm/memory_hotplug: add __add_memory_driver_managed() with online_type arg
2026-02-02 18:02 ` Gregory Price
@ 2026-02-02 18:46 ` Jonathan Cameron
2026-02-02 21:37 ` Gregory Price
0 siblings, 1 reply; 30+ messages in thread
From: Jonathan Cameron @ 2026-02-02 18:46 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john, David Hildenbrand, Oscar Salvador,
Andrew Morton
On Mon, 2 Feb 2026 13:02:10 -0500
Gregory Price <gourry@gourry.net> wrote:
> On Mon, Feb 02, 2026 at 05:25:24PM +0000, Jonathan Cameron wrote:
> > On Thu, 29 Jan 2026 16:04:35 -0500
> > Gregory Price <gourry@gourry.net> wrote:
> >
> > > Enable dax kmem driver to select how to online the memory rather than
> > > implicitly depending on the system default. This will allow users of
> > > dax to plumb through a preferred auto-online policy for their region.
> > >
> > > Refactor and new interface:
> > > Add __add_memory_driver_managed() which accepts an explicit online_type
> > > and export mhp_get_default_online_type() so callers can pass it when
> > > they want the default behavior.
> >
> > Hi Gregory,
> >
> > I think maybe I'd have left the export for the first user outside of
> > memory_hotplug.c. Not particularly important however.
> >
> > Maybe talk about why a caller of __add_memory_driver_managed() might want
> > the default? Feels like that's for the people who don't...
> >
>
> Less about why they want the default, more about maintaining backward
> compatibility.
>
> In the cxl driver, Ben pointed out something that made me realize we can
> change `region/bind()` to actually use the new `sysram/bind` path by
> just adding a one line `sysram_regionN->online_type = default()`
>
> I can add this detail to the changelog.
>
> >
> > Other comments are mostly about using a named enum. I'm not sure
> > if there is some existing reason why that doesn't work? -Errno pushed through
> > this variable or anything like that?
> >
>
> I can add a cleanup-patch prior to use the enum, but i don't think this
> actually enables the compiler to do anything new at the moment?
Good point. More coffee needed (or sleep)
It lets sparse do some checking, but sadly only for wrong enum assignment.
(Gcc has -Wenum-conversion as well which I think is effectively the same)
I.e. you can't assign a value from a different enum without casting.
It can't do anything if people just pass in an out of range int.
>
> An enum just resolves to an int, and setting `enum thing val = -1` when
> the enum definition doesn't include -1 doesn't actually fire any errors
> (at least IIRC - maybe i'm just wrong). Same with
>
> function(enum) -> function(-1) wouldn't fire a compilation error
>
> It might actually be worth adding `MMOP_NOT_CONFIGURED = -1` so that the
> cxl-sysram driver can set this explicitly rather than just setting -1
> as an implicit version of this - but then why would memory_hotplug.c
> ever want to expose a NOT_CONFIGURED option lol.
>
> So, yeah, the enum looks nicer, but not sure how much it buys us beyond
> that.
>
> > It's a little odd to add nice kernel-doc formatted documentation
> > when the non __ variant has free form docs. Maybe tidy that up first
> > if we want to go kernel-doc in this file? (I'm in favor, but no idea
> > on general feelings...)
> >
>
> ack. Can add some more cleanups early in the series.
>
> > > + if (online_type < 0 || online_type > MMOP_ONLINE_MOVABLE)
> >
> > This is where using an enum would help compiler know what is going on
> > and maybe warn if anyone writes something that isn't defined.
> >
>
> I think you still have to sanity check this, but maybe the code looks
> cleaner, so will do.
I'm in two minds about this. If it's an enum and someone writes an int
I take take the view it's not our problem that they shot themselves in
the foot. Maybe we should be paranoid...
J
>
> ~Gregory
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 2/9] mm/memory_hotplug: add __add_memory_driver_managed() with online_type arg
2026-02-02 18:46 ` Jonathan Cameron
@ 2026-02-02 21:37 ` Gregory Price
2026-02-04 21:08 ` David Hildenbrand (arm)
0 siblings, 1 reply; 30+ messages in thread
From: Gregory Price @ 2026-02-02 21:37 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john, David Hildenbrand, Oscar Salvador,
Andrew Morton
On Mon, Feb 02, 2026 at 06:46:09PM +0000, Jonathan Cameron wrote:
> >
> > I can add a cleanup-patch prior to use the enum, but i don't think this
> > actually enables the compiler to do anything new at the moment?
>
> Good point. More coffee needed (or sleep)
>
> It lets sparse do some checking, but sadly only for wrong enum assignment.
> (Gcc has -Wenum-conversion as well which I think is effectively the same)
> I.e. you can't assign a value from a different enum without casting.
>
> It can't do anything if people just pass in an out of range int.
>
Which, after looking a bit... mm/memory_hotplug.c does this quite a bit
internally - except it uses a uint8_t
Example:
static int try_offline_memory_block(struct memory_block *mem, void *arg)
{
uint8_t online_type = MMOP_ONLINE_KERNEL;
uint8_t **online_types = arg;
... snip ...
}
int offline_and_remove_memory(u64 start, u64 size)
{
uint8_t *online_types, *tmp;
... snip ...
online_types = kmalloc_array(mb_count, sizeof(*online_types),
GFP_KERNEL);
}
So that's fun.
I'm not sure it's worth the churn here, but happy to do it if there are
strong opinions.
---
David do you have thoughts here?
~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 2/9] mm/memory_hotplug: add __add_memory_driver_managed() with online_type arg
2026-02-02 21:37 ` Gregory Price
@ 2026-02-04 21:08 ` David Hildenbrand (arm)
2026-02-05 4:23 ` Gregory Price
0 siblings, 1 reply; 30+ messages in thread
From: David Hildenbrand (arm) @ 2026-02-04 21:08 UTC (permalink / raw)
To: Gregory Price, Jonathan Cameron
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john, Oscar Salvador, Andrew Morton
On 2/2/26 22:37, Gregory Price wrote:
> On Mon, Feb 02, 2026 at 06:46:09PM +0000, Jonathan Cameron wrote:
>>>
>>> I can add a cleanup-patch prior to use the enum, but i don't think this
>>> actually enables the compiler to do anything new at the moment?
>>
>> Good point. More coffee needed (or sleep)
>>
>> It lets sparse do some checking, but sadly only for wrong enum assignment.
>> (Gcc has -Wenum-conversion as well which I think is effectively the same)
>> I.e. you can't assign a value from a different enum without casting.
>>
>> It can't do anything if people just pass in an out of range int.
>>
>
> Which, after looking a bit... mm/memory_hotplug.c does this quite a bit
> internally - except it uses a uint8_t
>
> Example:
>
> static int try_offline_memory_block(struct memory_block *mem, void *arg)
> {
> uint8_t online_type = MMOP_ONLINE_KERNEL;
> uint8_t **online_types = arg;
> ... snip ...
> }
>
> int offline_and_remove_memory(u64 start, u64 size)
> {
> uint8_t *online_types, *tmp;
> ... snip ...
> online_types = kmalloc_array(mb_count, sizeof(*online_types),
> GFP_KERNEL);
> }
>
> So that's fun.
>
> I'm not sure it's worth the churn here, but happy to do it if there are
> strong opinions.
>
> ---
>
> David do you have thoughts here?
I guess we should clean that all up where easily possible, but I don't
expect you to do that.
For online_types I used it, obviously, to save memory. So I'd expect it
to stay at least there, but cast it to the proper type once we take it
out the array.
--
Cheers,
David
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 2/9] mm/memory_hotplug: add __add_memory_driver_managed() with online_type arg
2026-02-04 21:08 ` David Hildenbrand (arm)
@ 2026-02-05 4:23 ` Gregory Price
0 siblings, 0 replies; 30+ messages in thread
From: Gregory Price @ 2026-02-05 4:23 UTC (permalink / raw)
To: David Hildenbrand (arm)
Cc: Jonathan Cameron, linux-mm, linux-cxl, nvdimm, linux-fsdevel,
linux-kernel, linux-doc, kernel-team, dave, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john, Oscar Salvador, Andrew Morton
On Wed, Feb 04, 2026 at 10:08:45PM +0100, David Hildenbrand (arm) wrote:
> >
> > David do you have thoughts here?
>
> I guess we should clean that all up where easily possible, but I don't
> expect you to do that.
>
> For online_types I used it, obviously, to save memory. So I'd expect it to
> stay at least there, but cast it to the proper type once we take it out the
> array.
>
I can do it pretty easily and pull it out ahead.
~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 3/9] dax: plumb online_type from dax_kmem creators to hotplug
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
2026-01-29 21:04 ` [PATCH 1/9] mm/memory_hotplug: pass online_type to online_memory_block() via arg Gregory Price
2026-01-29 21:04 ` [PATCH 2/9] mm/memory_hotplug: add __add_memory_driver_managed() with online_type arg Gregory Price
@ 2026-01-29 21:04 ` Gregory Price
2026-01-29 21:04 ` [PATCH 4/9] drivers/cxl,dax: add dax driver mode selection for dax regions Gregory Price
` (7 subsequent siblings)
10 siblings, 0 replies; 30+ messages in thread
From: Gregory Price @ 2026-01-29 21:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
There is no way for drivers leveraging dax_kmem to plumb through a
preferred auto-online policy - the system default policy is forced.
Add online_type field to DAX device creation path to allow drivers
to specify an auto-online policy when using the kmem driver.
Current callers initialize online_type to mhp_get_default_online_type()
which resolves to the system default (memhp_default_online_type).
No functional change to existing drivers.
Cc:David Hildenbrand <david@kernel.org>
Signed-off-by: Gregory Price <gourry@gourry.net>
---
drivers/cxl/core/region.c | 2 ++
drivers/cxl/cxl.h | 1 +
drivers/dax/bus.c | 3 +++
drivers/dax/bus.h | 1 +
drivers/dax/cxl.c | 1 +
drivers/dax/dax-private.h | 2 ++
drivers/dax/hmem/hmem.c | 2 ++
drivers/dax/kmem.c | 13 +++++++++++--
drivers/dax/pmem.c | 2 ++
9 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 5bd1213737fa..eef5d5fe3f95 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
#include <linux/memregion.h>
+#include <linux/memory_hotplug.h>
#include <linux/genalloc.h>
#include <linux/debugfs.h>
#include <linux/device.h>
@@ -3459,6 +3460,7 @@ static int devm_cxl_add_dax_region(struct cxl_region *cxlr)
if (IS_ERR(cxlr_dax))
return PTR_ERR(cxlr_dax);
+ cxlr_dax->online_type = mhp_get_default_online_type();
dev = &cxlr_dax->dev;
rc = dev_set_name(dev, "dax_region%d", cxlr->id);
if (rc)
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index ba17fa86d249..07d57d13f4c7 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -591,6 +591,7 @@ struct cxl_dax_region {
struct device dev;
struct cxl_region *cxlr;
struct range hpa_range;
+ int online_type; /* MMOP_ value for kmem driver */
};
/**
diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c
index fde29e0ad68b..121a6dd0afe7 100644
--- a/drivers/dax/bus.c
+++ b/drivers/dax/bus.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2017-2018 Intel Corporation. All rights reserved. */
#include <linux/memremap.h>
+#include <linux/memory_hotplug.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/list.h>
@@ -395,6 +396,7 @@ static ssize_t create_store(struct device *dev, struct device_attribute *attr,
.size = 0,
.id = -1,
.memmap_on_memory = false,
+ .online_type = mhp_get_default_online_type(),
};
struct dev_dax *dev_dax = __devm_create_dev_dax(&data);
@@ -1494,6 +1496,7 @@ static struct dev_dax *__devm_create_dev_dax(struct dev_dax_data *data)
ida_init(&dev_dax->ida);
dev_dax->memmap_on_memory = data->memmap_on_memory;
+ dev_dax->online_type = data->online_type;
inode = dax_inode(dax_dev);
dev->devt = inode->i_rdev;
diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h
index cbbf64443098..4ac92a4edfe7 100644
--- a/drivers/dax/bus.h
+++ b/drivers/dax/bus.h
@@ -24,6 +24,7 @@ struct dev_dax_data {
resource_size_t size;
int id;
bool memmap_on_memory;
+ int online_type; /* MMOP_ value for kmem driver */
};
struct dev_dax *devm_create_dev_dax(struct dev_dax_data *data);
diff --git a/drivers/dax/cxl.c b/drivers/dax/cxl.c
index 13cd94d32ff7..856a0cd24f3b 100644
--- a/drivers/dax/cxl.c
+++ b/drivers/dax/cxl.c
@@ -27,6 +27,7 @@ static int cxl_dax_region_probe(struct device *dev)
.id = -1,
.size = range_len(&cxlr_dax->hpa_range),
.memmap_on_memory = true,
+ .online_type = cxlr_dax->online_type,
};
return PTR_ERR_OR_ZERO(devm_create_dev_dax(&data));
diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h
index c6ae27c982f4..9559718cc988 100644
--- a/drivers/dax/dax-private.h
+++ b/drivers/dax/dax-private.h
@@ -77,6 +77,7 @@ struct dev_dax_range {
* @dev: device core
* @pgmap: pgmap for memmap setup / lifetime (driver owned)
* @memmap_on_memory: allow kmem to put the memmap in the memory
+ * @online_type: MMOP_* online type for memory hotplug
* @nr_range: size of @ranges
* @ranges: range tuples of memory used
*/
@@ -91,6 +92,7 @@ struct dev_dax {
struct device dev;
struct dev_pagemap *pgmap;
bool memmap_on_memory;
+ int online_type;
int nr_range;
struct dev_dax_range *ranges;
};
diff --git a/drivers/dax/hmem/hmem.c b/drivers/dax/hmem/hmem.c
index c18451a37e4f..119914b08fd9 100644
--- a/drivers/dax/hmem/hmem.c
+++ b/drivers/dax/hmem/hmem.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/platform_device.h>
+#include <linux/memory_hotplug.h>
#include <linux/memregion.h>
#include <linux/module.h>
#include <linux/dax.h>
@@ -36,6 +37,7 @@ static int dax_hmem_probe(struct platform_device *pdev)
.id = -1,
.size = region_idle ? 0 : range_len(&mri->range),
.memmap_on_memory = false,
+ .online_type = mhp_get_default_online_type(),
};
return PTR_ERR_OR_ZERO(devm_create_dev_dax(&data));
diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c
index c036e4d0b610..550dc605229e 100644
--- a/drivers/dax/kmem.c
+++ b/drivers/dax/kmem.c
@@ -16,6 +16,11 @@
#include "dax-private.h"
#include "bus.h"
+/* Internal function exported only to kmem module */
+extern int __add_memory_driver_managed(int nid, u64 start, u64 size,
+ const char *resource_name,
+ mhp_t mhp_flags, int online_type);
+
/*
* Default abstract distance assigned to the NUMA node onlined
* by DAX/kmem if the low level platform driver didn't initialize
@@ -72,6 +77,7 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax)
struct dax_kmem_data *data;
struct memory_dev_type *mtype;
int i, rc, mapped = 0;
+ int online_type;
mhp_t mhp_flags;
int numa_node;
int adist = MEMTIER_DEFAULT_DAX_ADISTANCE;
@@ -134,6 +140,8 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax)
goto err_reg_mgid;
data->mgid = rc;
+ online_type = dev_dax->online_type;
+
for (i = 0; i < dev_dax->nr_range; i++) {
struct resource *res;
struct range range;
@@ -174,8 +182,9 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax)
* Ensure that future kexec'd kernels will not treat
* this as RAM automatically.
*/
- rc = add_memory_driver_managed(data->mgid, range.start,
- range_len(&range), kmem_name, mhp_flags);
+ rc = __add_memory_driver_managed(data->mgid, range.start,
+ range_len(&range), kmem_name, mhp_flags,
+ online_type);
if (rc) {
dev_warn(dev, "mapping%d: %#llx-%#llx memory add failed\n",
diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c
index bee93066a849..a5925146b09f 100644
--- a/drivers/dax/pmem.c
+++ b/drivers/dax/pmem.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */
+#include <linux/memory_hotplug.h>
#include <linux/memremap.h>
#include <linux/module.h>
#include "../nvdimm/pfn.h"
@@ -63,6 +64,7 @@ static struct dev_dax *__dax_pmem_probe(struct device *dev)
.pgmap = &pgmap,
.size = range_len(&range),
.memmap_on_memory = false,
+ .online_type = mhp_get_default_online_type(),
};
return devm_create_dev_dax(&data);
--
2.52.0
^ permalink raw reply [flat|nested] 30+ messages in thread* [PATCH 4/9] drivers/cxl,dax: add dax driver mode selection for dax regions
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
` (2 preceding siblings ...)
2026-01-29 21:04 ` [PATCH 3/9] dax: plumb online_type from dax_kmem creators to hotplug Gregory Price
@ 2026-01-29 21:04 ` Gregory Price
2026-02-02 17:54 ` Jonathan Cameron
2026-01-29 21:04 ` [PATCH 5/9] cxl/core/region: move pmem region driver logic into pmem_region Gregory Price
` (6 subsequent siblings)
10 siblings, 1 reply; 30+ messages in thread
From: Gregory Price @ 2026-01-29 21:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
CXL regions may wish not to auto-configure their memory as dax kmem,
but the current plumbing defaults all cxl-created dax devices to the
kmem driver. This exposes them to hotplug policy, even if the user
intends to use the memory as a dax device.
Add plumbing to allow CXL drivers to select whether a DAX region should
default to kmem (DAXDRV_KMEM_TYPE) or device (DAXDRV_DEVICE_TYPE).
Add a 'dax_driver' field to struct cxl_dax_region and update
devm_cxl_add_dax_region() to take a dax_driver_type parameter.
In drivers/dax/cxl.c, the IORESOURCE_DAX_KMEM flag used by dax driver
matching code is now set conditionally based on dax_region->dax_driver.
Exports `enum dax_driver_type` to linux/dax.h for use in the cxl driver.
All current callers pass DAXDRV_KMEM_TYPE for backward compatibility.
Cc: John Groves <john@jagalactic.com>
Signed-off-by: Gregory Price <gourry@gourry.net>
---
drivers/cxl/core/core.h | 1 +
drivers/cxl/core/region.c | 6 ++++--
drivers/cxl/cxl.h | 2 ++
drivers/dax/bus.h | 6 +-----
drivers/dax/cxl.c | 6 +++++-
include/linux/dax.h | 5 +++++
6 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 1fb66132b777..dd987ef2def5 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -6,6 +6,7 @@
#include <cxl/mailbox.h>
#include <linux/rwsem.h>
+#include <linux/dax.h>
extern const struct device_type cxl_nvdimm_bridge_type;
extern const struct device_type cxl_nvdimm_type;
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index eef5d5fe3f95..e4097c464ed3 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3450,7 +3450,8 @@ static void cxlr_dax_unregister(void *_cxlr_dax)
device_unregister(&cxlr_dax->dev);
}
-static int devm_cxl_add_dax_region(struct cxl_region *cxlr)
+static int devm_cxl_add_dax_region(struct cxl_region *cxlr,
+ enum dax_driver_type dax_driver)
{
struct cxl_dax_region *cxlr_dax;
struct device *dev;
@@ -3461,6 +3462,7 @@ static int devm_cxl_add_dax_region(struct cxl_region *cxlr)
return PTR_ERR(cxlr_dax);
cxlr_dax->online_type = mhp_get_default_online_type();
+ cxlr_dax->dax_driver = dax_driver;
dev = &cxlr_dax->dev;
rc = dev_set_name(dev, "dax_region%d", cxlr->id);
if (rc)
@@ -3994,7 +3996,7 @@ static int cxl_region_probe(struct device *dev)
p->res->start, p->res->end, cxlr,
is_system_ram) > 0)
return 0;
- return devm_cxl_add_dax_region(cxlr);
+ return devm_cxl_add_dax_region(cxlr, DAXDRV_KMEM_TYPE);
default:
dev_dbg(&cxlr->dev, "unsupported region mode: %d\n",
cxlr->mode);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 07d57d13f4c7..c06a239c0008 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -12,6 +12,7 @@
#include <linux/node.h>
#include <linux/io.h>
#include <linux/range.h>
+#include <linux/dax.h>
extern const struct nvdimm_security_ops *cxl_security_ops;
@@ -592,6 +593,7 @@ struct cxl_dax_region {
struct cxl_region *cxlr;
struct range hpa_range;
int online_type; /* MMOP_ value for kmem driver */
+ enum dax_driver_type dax_driver;
};
/**
diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h
index 4ac92a4edfe7..9144593b4029 100644
--- a/drivers/dax/bus.h
+++ b/drivers/dax/bus.h
@@ -2,6 +2,7 @@
/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */
#ifndef __DAX_BUS_H__
#define __DAX_BUS_H__
+#include <linux/dax.h>
#include <linux/device.h>
#include <linux/range.h>
@@ -29,11 +30,6 @@ struct dev_dax_data {
struct dev_dax *devm_create_dev_dax(struct dev_dax_data *data);
-enum dax_driver_type {
- DAXDRV_KMEM_TYPE,
- DAXDRV_DEVICE_TYPE,
-};
-
struct dax_device_driver {
struct device_driver drv;
struct list_head ids;
diff --git a/drivers/dax/cxl.c b/drivers/dax/cxl.c
index 856a0cd24f3b..b13ecc2f9806 100644
--- a/drivers/dax/cxl.c
+++ b/drivers/dax/cxl.c
@@ -11,14 +11,18 @@ static int cxl_dax_region_probe(struct device *dev)
struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev);
int nid = phys_to_target_node(cxlr_dax->hpa_range.start);
struct cxl_region *cxlr = cxlr_dax->cxlr;
+ unsigned long flags = 0;
struct dax_region *dax_region;
struct dev_dax_data data;
+ if (cxlr_dax->dax_driver == DAXDRV_KMEM_TYPE)
+ flags |= IORESOURCE_DAX_KMEM;
+
if (nid == NUMA_NO_NODE)
nid = memory_add_physaddr_to_nid(cxlr_dax->hpa_range.start);
dax_region = alloc_dax_region(dev, cxlr->id, &cxlr_dax->hpa_range, nid,
- PMD_SIZE, IORESOURCE_DAX_KMEM);
+ PMD_SIZE, flags);
if (!dax_region)
return -ENOMEM;
diff --git a/include/linux/dax.h b/include/linux/dax.h
index bf103f317cac..e62f92d0ace1 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -19,6 +19,11 @@ enum dax_access_mode {
DAX_RECOVERY_WRITE,
};
+enum dax_driver_type {
+ DAXDRV_KMEM_TYPE,
+ DAXDRV_DEVICE_TYPE,
+};
+
struct dax_operations {
/*
* direct_access: translate a device-relative
--
2.52.0
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 4/9] drivers/cxl,dax: add dax driver mode selection for dax regions
2026-01-29 21:04 ` [PATCH 4/9] drivers/cxl,dax: add dax driver mode selection for dax regions Gregory Price
@ 2026-02-02 17:54 ` Jonathan Cameron
0 siblings, 0 replies; 30+ messages in thread
From: Jonathan Cameron @ 2026-02-02 17:54 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john
On Thu, 29 Jan 2026 16:04:37 -0500
Gregory Price <gourry@gourry.net> wrote:
> CXL regions may wish not to auto-configure their memory as dax kmem,
> but the current plumbing defaults all cxl-created dax devices to the
> kmem driver. This exposes them to hotplug policy, even if the user
> intends to use the memory as a dax device.
>
> Add plumbing to allow CXL drivers to select whether a DAX region should
> default to kmem (DAXDRV_KMEM_TYPE) or device (DAXDRV_DEVICE_TYPE).
>
> Add a 'dax_driver' field to struct cxl_dax_region and update
> devm_cxl_add_dax_region() to take a dax_driver_type parameter.
>
> In drivers/dax/cxl.c, the IORESOURCE_DAX_KMEM flag used by dax driver
> matching code is now set conditionally based on dax_region->dax_driver.
>
> Exports `enum dax_driver_type` to linux/dax.h for use in the cxl driver.
>
> All current callers pass DAXDRV_KMEM_TYPE for backward compatibility.
>
> Cc: John Groves <john@jagalactic.com>
> Signed-off-by: Gregory Price <gourry@gourry.net>
LGTM
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 5/9] cxl/core/region: move pmem region driver logic into pmem_region
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
` (3 preceding siblings ...)
2026-01-29 21:04 ` [PATCH 4/9] drivers/cxl,dax: add dax driver mode selection for dax regions Gregory Price
@ 2026-01-29 21:04 ` Gregory Price
2026-02-02 17:56 ` Jonathan Cameron
2026-01-29 21:04 ` [PATCH 6/9] cxl/core/region: move dax region device logic into dax_region.c Gregory Price
` (5 subsequent siblings)
10 siblings, 1 reply; 30+ messages in thread
From: Gregory Price @ 2026-01-29 21:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
Move the pmem region driver logic from region.c into pmem_region.c.
No functional changes.
Signed-off-by: Gregory Price <gourry@gourry.net>
---
drivers/cxl/core/Makefile | 1 +
drivers/cxl/core/core.h | 1 +
drivers/cxl/core/pmem_region.c | 191 +++++++++++++++++++++++++++++++++
drivers/cxl/core/region.c | 184 -------------------------------
4 files changed, 193 insertions(+), 184 deletions(-)
create mode 100644 drivers/cxl/core/pmem_region.c
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index 5ad8fef210b5..23269c81fd44 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_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 dd987ef2def5..26991de12d76 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -43,6 +43,7 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port);
struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
u64 dpa);
+int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
#else
static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
new file mode 100644
index 000000000000..81b66e548bb5
--- /dev/null
+++ b/drivers/cxl/core/pmem_region.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <cxlmem.h>
+#include <cxl.h>
+#include "core.h"
+
+static void cxl_pmem_region_release(struct device *dev)
+{
+ struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
+ int i;
+
+ for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
+ struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd;
+
+ put_device(&cxlmd->dev);
+ }
+
+ kfree(cxlr_pmem);
+}
+
+static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
+ &cxl_base_attribute_group,
+ NULL,
+};
+
+const struct device_type cxl_pmem_region_type = {
+ .name = "cxl_pmem_region",
+ .release = cxl_pmem_region_release,
+ .groups = cxl_pmem_region_attribute_groups,
+};
+bool is_cxl_pmem_region(struct device *dev)
+{
+ return dev->type == &cxl_pmem_region_type;
+}
+EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL");
+
+struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
+{
+ if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev),
+ "not a cxl_pmem_region device\n"))
+ return NULL;
+ return container_of(dev, struct cxl_pmem_region, dev);
+}
+EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL");
+static struct lock_class_key cxl_pmem_region_key;
+
+static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
+{
+ struct cxl_region_params *p = &cxlr->params;
+ struct cxl_nvdimm_bridge *cxl_nvb;
+ struct device *dev;
+ int i;
+
+ guard(rwsem_read)(&cxl_rwsem.region);
+ if (p->state != CXL_CONFIG_COMMIT)
+ return -ENXIO;
+
+ struct cxl_pmem_region *cxlr_pmem __free(kfree) =
+ kzalloc(struct_size(cxlr_pmem, mapping, p->nr_targets), GFP_KERNEL);
+ if (!cxlr_pmem)
+ return -ENOMEM;
+
+ cxlr_pmem->hpa_range.start = p->res->start;
+ cxlr_pmem->hpa_range.end = p->res->end;
+
+ /* Snapshot the region configuration underneath the cxl_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 cxlr_pmem_unregister(void *_cxlr_pmem)
+{
+ struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem;
+ struct cxl_region *cxlr = cxlr_pmem->cxlr;
+ struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
+
+ /*
+ * Either the bridge is in ->remove() context under the device_lock(),
+ * or cxlr_release_nvdimm() is cancelling the bridge's release action
+ * for @cxlr_pmem and doing it itself (while manually holding the bridge
+ * lock).
+ */
+ device_lock_assert(&cxl_nvb->dev);
+ cxlr->cxlr_pmem = NULL;
+ cxlr_pmem->cxlr = NULL;
+ device_unregister(&cxlr_pmem->dev);
+}
+
+static void cxlr_release_nvdimm(void *_cxlr)
+{
+ struct cxl_region *cxlr = _cxlr;
+ struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
+
+ scoped_guard(device, &cxl_nvb->dev) {
+ if (cxlr->cxlr_pmem)
+ devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister,
+ cxlr->cxlr_pmem);
+ }
+ cxlr->cxl_nvb = NULL;
+ put_device(&cxl_nvb->dev);
+}
+
+/**
+ * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge
+ * @cxlr: parent CXL region for this pmem region bridge device
+ *
+ * Return: 0 on success negative error code on failure.
+ */
+int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
+{
+ struct cxl_pmem_region *cxlr_pmem;
+ struct cxl_nvdimm_bridge *cxl_nvb;
+ struct device *dev;
+ int rc;
+
+ rc = cxl_pmem_region_alloc(cxlr);
+ if (rc)
+ return rc;
+ cxlr_pmem = cxlr->cxlr_pmem;
+ cxl_nvb = cxlr->cxl_nvb;
+
+ dev = &cxlr_pmem->dev;
+ rc = dev_set_name(dev, "pmem_region%d", cxlr->id);
+ if (rc)
+ goto err;
+
+ rc = device_add(dev);
+ if (rc)
+ goto err;
+
+ dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
+ dev_name(dev));
+
+ scoped_guard(device, &cxl_nvb->dev) {
+ if (cxl_nvb->dev.driver)
+ rc = devm_add_action_or_reset(&cxl_nvb->dev,
+ cxlr_pmem_unregister,
+ cxlr_pmem);
+ else
+ rc = -ENXIO;
+ }
+
+ if (rc)
+ goto err_bridge;
+
+ /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */
+ return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr);
+
+err:
+ put_device(dev);
+err_bridge:
+ put_device(&cxl_nvb->dev);
+ cxlr->cxl_nvb = NULL;
+ return rc;
+}
+
+
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index e4097c464ed3..fc56f8f03805 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2747,46 +2747,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;
@@ -3236,64 +3196,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);
@@ -3357,92 +3259,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;
--
2.52.0
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 5/9] cxl/core/region: move pmem region driver logic into pmem_region
2026-01-29 21:04 ` [PATCH 5/9] cxl/core/region: move pmem region driver logic into pmem_region Gregory Price
@ 2026-02-02 17:56 ` Jonathan Cameron
0 siblings, 0 replies; 30+ messages in thread
From: Jonathan Cameron @ 2026-02-02 17:56 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john
On Thu, 29 Jan 2026 16:04:38 -0500
Gregory Price <gourry@gourry.net> wrote:
> Move the pmem region driver logic from region.c into pmem_region.c.
Needs to answer the question: Why?
>
> No functional changes.
>
> Signed-off-by: Gregory Price <gourry@gourry.net>
> ---
Minor stuff inline.
> diff --git a/drivers/cxl/core/pmem_region.c b/drivers/cxl/core/pmem_region.c
> new file mode 100644
> index 000000000000..81b66e548bb5
> --- /dev/null
> +++ b/drivers/cxl/core/pmem_region.c
> @@ -0,0 +1,191 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +#include <cxlmem.h>
> +#include <cxl.h>
> +#include "core.h"
> +
> +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,
Maybe sneak in dropping that trailing comma whilst you are moving it.
> +};
> +/**
> + * 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)
> +{
...
> + /* @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;
> +}
> +
> +
Bonus line...
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 6/9] cxl/core/region: move dax region device logic into dax_region.c
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
` (4 preceding siblings ...)
2026-01-29 21:04 ` [PATCH 5/9] cxl/core/region: move pmem region driver logic into pmem_region Gregory Price
@ 2026-01-29 21:04 ` Gregory Price
2026-02-02 17:57 ` Jonathan Cameron
2026-01-29 21:04 ` [PATCH 7/9] cxl/core: add cxl_devdax_region driver for explicit userland region binding Gregory Price
` (4 subsequent siblings)
10 siblings, 1 reply; 30+ messages in thread
From: Gregory Price @ 2026-01-29 21:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
Move the CXL DAX region device infrastructure from region.c into a
new dax_region.c file.
No functional changes.
Signed-off-by: Gregory Price <gourry@gourry.net>
---
drivers/cxl/core/Makefile | 1 +
drivers/cxl/core/core.h | 1 +
drivers/cxl/core/dax_region.c | 113 ++++++++++++++++++++++++++++++++++
drivers/cxl/core/region.c | 102 ------------------------------
4 files changed, 115 insertions(+), 102 deletions(-)
create mode 100644 drivers/cxl/core/dax_region.c
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index 23269c81fd44..36f284d7c500 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_REGION) += dax_region.o
cxl_core-$(CONFIG_CXL_REGION) += pmem_region.o
cxl_core-$(CONFIG_CXL_MCE) += mce.o
cxl_core-$(CONFIG_CXL_FEATURES) += features.o
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 26991de12d76..217dd708a2a6 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -43,6 +43,7 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port);
struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
u64 dpa);
+int devm_cxl_add_dax_region(struct cxl_region *cxlr, enum dax_driver_type);
int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
#else
diff --git a/drivers/cxl/core/dax_region.c b/drivers/cxl/core/dax_region.c
new file mode 100644
index 000000000000..0602db5f7248
--- /dev/null
+++ b/drivers/cxl/core/dax_region.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2022 Intel Corporation. All rights reserved.
+ * Copyright(c) 2026 Meta Technologies Inc. All rights reserved.
+ */
+#include <linux/memory_hotplug.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <cxlmem.h>
+#include <cxl.h>
+#include "core.h"
+
+static void cxl_dax_region_release(struct device *dev)
+{
+ struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev);
+
+ kfree(cxlr_dax);
+}
+
+static const struct attribute_group *cxl_dax_region_attribute_groups[] = {
+ &cxl_base_attribute_group,
+ NULL,
+};
+
+const struct device_type cxl_dax_region_type = {
+ .name = "cxl_dax_region",
+ .release = cxl_dax_region_release,
+ .groups = cxl_dax_region_attribute_groups,
+};
+
+static bool is_cxl_dax_region(struct device *dev)
+{
+ return dev->type == &cxl_dax_region_type;
+}
+
+struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
+{
+ if (dev_WARN_ONCE(dev, !is_cxl_dax_region(dev),
+ "not a cxl_dax_region device\n"))
+ return NULL;
+ return container_of(dev, struct cxl_dax_region, dev);
+}
+EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, "CXL");
+
+static struct lock_class_key cxl_dax_region_key;
+
+static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
+{
+ struct cxl_region_params *p = &cxlr->params;
+ struct cxl_dax_region *cxlr_dax;
+ struct device *dev;
+
+ guard(rwsem_read)(&cxl_rwsem.region);
+ if (p->state != CXL_CONFIG_COMMIT)
+ return ERR_PTR(-ENXIO);
+
+ cxlr_dax = kzalloc(sizeof(*cxlr_dax), GFP_KERNEL);
+ if (!cxlr_dax)
+ return ERR_PTR(-ENOMEM);
+
+ cxlr_dax->hpa_range.start = p->res->start;
+ cxlr_dax->hpa_range.end = p->res->end;
+
+ dev = &cxlr_dax->dev;
+ cxlr_dax->cxlr = cxlr;
+ device_initialize(dev);
+ lockdep_set_class(&dev->mutex, &cxl_dax_region_key);
+ device_set_pm_not_required(dev);
+ dev->parent = &cxlr->dev;
+ dev->bus = &cxl_bus_type;
+ dev->type = &cxl_dax_region_type;
+
+ return cxlr_dax;
+}
+
+static void cxlr_dax_unregister(void *_cxlr_dax)
+{
+ struct cxl_dax_region *cxlr_dax = _cxlr_dax;
+
+ device_unregister(&cxlr_dax->dev);
+}
+
+int devm_cxl_add_dax_region(struct cxl_region *cxlr,
+ enum dax_driver_type dax_driver)
+{
+ struct cxl_dax_region *cxlr_dax;
+ struct device *dev;
+ int rc;
+
+ cxlr_dax = cxl_dax_region_alloc(cxlr);
+ if (IS_ERR(cxlr_dax))
+ return PTR_ERR(cxlr_dax);
+
+ cxlr_dax->online_type = mhp_get_default_online_type();
+ cxlr_dax->dax_driver = dax_driver;
+ dev = &cxlr_dax->dev;
+ rc = dev_set_name(dev, "dax_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));
+
+ return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister,
+ cxlr_dax);
+err:
+ put_device(dev);
+ return rc;
+}
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index fc56f8f03805..61ec939c1462 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3196,108 +3196,6 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
return -ENXIO;
}
-static void cxl_dax_region_release(struct device *dev)
-{
- struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev);
-
- kfree(cxlr_dax);
-}
-
-static const struct attribute_group *cxl_dax_region_attribute_groups[] = {
- &cxl_base_attribute_group,
- NULL,
-};
-
-const struct device_type cxl_dax_region_type = {
- .name = "cxl_dax_region",
- .release = cxl_dax_region_release,
- .groups = cxl_dax_region_attribute_groups,
-};
-
-static bool is_cxl_dax_region(struct device *dev)
-{
- return dev->type == &cxl_dax_region_type;
-}
-
-struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
-{
- if (dev_WARN_ONCE(dev, !is_cxl_dax_region(dev),
- "not a cxl_dax_region device\n"))
- return NULL;
- return container_of(dev, struct cxl_dax_region, dev);
-}
-EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, "CXL");
-
-static struct lock_class_key cxl_dax_region_key;
-
-static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
-{
- struct cxl_region_params *p = &cxlr->params;
- struct cxl_dax_region *cxlr_dax;
- struct device *dev;
-
- guard(rwsem_read)(&cxl_rwsem.region);
- if (p->state != CXL_CONFIG_COMMIT)
- return ERR_PTR(-ENXIO);
-
- cxlr_dax = kzalloc(sizeof(*cxlr_dax), GFP_KERNEL);
- if (!cxlr_dax)
- return ERR_PTR(-ENOMEM);
-
- cxlr_dax->hpa_range.start = p->res->start;
- cxlr_dax->hpa_range.end = p->res->end;
-
- dev = &cxlr_dax->dev;
- cxlr_dax->cxlr = cxlr;
- device_initialize(dev);
- lockdep_set_class(&dev->mutex, &cxl_dax_region_key);
- device_set_pm_not_required(dev);
- dev->parent = &cxlr->dev;
- dev->bus = &cxl_bus_type;
- dev->type = &cxl_dax_region_type;
-
- return cxlr_dax;
-}
-
-static void cxlr_dax_unregister(void *_cxlr_dax)
-{
- struct cxl_dax_region *cxlr_dax = _cxlr_dax;
-
- device_unregister(&cxlr_dax->dev);
-}
-
-static int devm_cxl_add_dax_region(struct cxl_region *cxlr,
- enum dax_driver_type dax_driver)
-{
- struct cxl_dax_region *cxlr_dax;
- struct device *dev;
- int rc;
-
- cxlr_dax = cxl_dax_region_alloc(cxlr);
- if (IS_ERR(cxlr_dax))
- return PTR_ERR(cxlr_dax);
-
- cxlr_dax->online_type = mhp_get_default_online_type();
- cxlr_dax->dax_driver = dax_driver;
- dev = &cxlr_dax->dev;
- rc = dev_set_name(dev, "dax_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));
-
- return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister,
- cxlr_dax);
-err:
- put_device(dev);
- return rc;
-}
-
static int match_decoder_by_range(struct device *dev, const void *data)
{
const struct range *r1, *r2 = data;
--
2.52.0
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 6/9] cxl/core/region: move dax region device logic into dax_region.c
2026-01-29 21:04 ` [PATCH 6/9] cxl/core/region: move dax region device logic into dax_region.c Gregory Price
@ 2026-02-02 17:57 ` Jonathan Cameron
0 siblings, 0 replies; 30+ messages in thread
From: Jonathan Cameron @ 2026-02-02 17:57 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john
On Thu, 29 Jan 2026 16:04:39 -0500
Gregory Price <gourry@gourry.net> wrote:
> Move the CXL DAX region device infrastructure from region.c into a
> new dax_region.c file.
Likewise. Why?
>
> No functional changes.
>
> Signed-off-by: Gregory Price <gourry@gourry.net>
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 7/9] cxl/core: add cxl_devdax_region driver for explicit userland region binding
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
` (5 preceding siblings ...)
2026-01-29 21:04 ` [PATCH 6/9] cxl/core/region: move dax region device logic into dax_region.c Gregory Price
@ 2026-01-29 21:04 ` Gregory Price
2026-01-29 21:04 ` [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers Gregory Price
` (3 subsequent siblings)
10 siblings, 0 replies; 30+ messages in thread
From: Gregory Price @ 2026-01-29 21:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
Add a new cxl_devdax_region driver that probes CXL regions in device
dax mode and creates dax_region devices. This allows explicit binding to
the device_dax dax driver instead of the kmem driver.
Exports to_cxl_region() to core.h so it can be used by the driver.
Signed-off-by: Gregory Price <gourry@gourry.net>
---
drivers/cxl/core/core.h | 2 ++
drivers/cxl/core/dax_region.c | 16 ++++++++++++++++
drivers/cxl/core/region.c | 21 +++++++++++++++++----
drivers/cxl/cxl.h | 1 +
4 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 217dd708a2a6..ea4df8abc2ad 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -46,6 +46,8 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
int devm_cxl_add_dax_region(struct cxl_region *cxlr, enum dax_driver_type);
int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
+extern struct cxl_driver cxl_devdax_region_driver;
+
#else
static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
const struct cxl_memdev *cxlmd, u64 dpa)
diff --git a/drivers/cxl/core/dax_region.c b/drivers/cxl/core/dax_region.c
index 0602db5f7248..391d51e5ec37 100644
--- a/drivers/cxl/core/dax_region.c
+++ b/drivers/cxl/core/dax_region.c
@@ -111,3 +111,19 @@ int devm_cxl_add_dax_region(struct cxl_region *cxlr,
put_device(dev);
return rc;
}
+
+static int cxl_devdax_region_driver_probe(struct device *dev)
+{
+ struct cxl_region *cxlr = to_cxl_region(dev);
+
+ if (cxlr->mode != CXL_PARTMODE_RAM)
+ return -ENODEV;
+
+ return devm_cxl_add_dax_region(cxlr, DAXDRV_DEVICE_TYPE);
+}
+
+struct cxl_driver cxl_devdax_region_driver = {
+ .name = "cxl_devdax_region",
+ .probe = cxl_devdax_region_driver_probe,
+ .id = CXL_DEVICE_REGION,
+};
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 61ec939c1462..6200ca1cc2dd 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -39,8 +39,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, \
@@ -2430,7 +2428,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"))
@@ -3726,11 +3724,26 @@ static struct cxl_driver cxl_region_driver = {
int cxl_region_init(void)
{
- return cxl_driver_register(&cxl_region_driver);
+ int rc;
+
+ rc = cxl_driver_register(&cxl_region_driver);
+ if (rc)
+ return rc;
+
+ rc = cxl_driver_register(&cxl_devdax_region_driver);
+ if (rc)
+ goto err_dax;
+
+ return 0;
+
+err_dax:
+ cxl_driver_unregister(&cxl_region_driver);
+ return rc;
}
void cxl_region_exit(void)
{
+ cxl_driver_unregister(&cxl_devdax_region_driver);
cxl_driver_unregister(&cxl_region_driver);
}
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index c06a239c0008..674d5f870c70 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -859,6 +859,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;
--
2.52.0
^ permalink raw reply [flat|nested] 30+ messages in thread* [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
` (6 preceding siblings ...)
2026-01-29 21:04 ` [PATCH 7/9] cxl/core: add cxl_devdax_region driver for explicit userland region binding Gregory Price
@ 2026-01-29 21:04 ` Gregory Price
2026-01-30 21:27 ` Cheatham, Benjamin
2026-02-02 18:20 ` Jonathan Cameron
2026-01-29 21:04 ` [PATCH 9/9] Documentation/driver-api/cxl: add dax and sysram driver documentation Gregory Price
` (2 subsequent siblings)
10 siblings, 2 replies; 30+ messages in thread
From: Gregory Price @ 2026-01-29 21:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
In the current kmem driver binding process, the only way for users
to define hotplug policy is via a build-time option, or by not
onlining memory by default and setting each individual memory block
online after hotplug occurs. We can solve this with a configuration
step between region-probe and dax-probe.
Add the infrastructure for a two-stage driver binding for kmem-mode
dax regions. The cxl_dax_kmem_region driver probes cxl_sysram_region
devices and creates cxl_dax_region with dax_driver=kmem.
This creates an interposition step where users can configure policy.
Device hierarchy:
region0 -> sysram_region0 -> dax_region0 -> dax0.0
The sysram_region device exposes a sysfs 'online_type' attribute
that allows users to configure the memory online type before the
underlying dax_region is created and memory is hotplugged.
sysram_region0/online_type:
invalid: not configured, blocks probe
offline: memory will not be onlined automatically
online: memory will be onlined in ZONE_NORMAL
online_movable: memory will be onlined in ZONE_MMOVABLE
The device initializes with online_type=invalid which prevents the
cxl_dax_kmem_region driver from binding until the user explicitly
configures a valid online_type.
This enables a two-step binding process:
echo region0 > cxl_sysram_region/bind
echo online_movable > sysram_region0/online_type
echo sysram_region0 > cxl_dax_kmem_region/bind
Signed-off-by: Gregory Price <gourry@gourry.net>
---
Documentation/ABI/testing/sysfs-bus-cxl | 21 +++
drivers/cxl/core/Makefile | 1 +
drivers/cxl/core/core.h | 6 +
drivers/cxl/core/dax_region.c | 50 +++++++
drivers/cxl/core/port.c | 2 +
drivers/cxl/core/region.c | 14 ++
drivers/cxl/core/sysram_region.c | 180 ++++++++++++++++++++++++
drivers/cxl/cxl.h | 25 ++++
8 files changed, 299 insertions(+)
create mode 100644 drivers/cxl/core/sysram_region.c
diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl
index c80a1b5a03db..a051cb86bdfc 100644
--- a/Documentation/ABI/testing/sysfs-bus-cxl
+++ b/Documentation/ABI/testing/sysfs-bus-cxl
@@ -624,3 +624,24 @@ 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/sysram_regionZ/online_type
+Date: January, 2026
+KernelVersion: v7.1
+Contact: linux-cxl@vger.kernel.org
+Description:
+ (RW) This attribute allows users to configure the memory online
+ type before the underlying dax_region engages in hotplug.
+
+ Valid values:
+ 'invalid': Not configured (default). Blocks probe.
+ 'offline': Memory will not be onlined automatically.
+ 'online' : Memory will be onlined in ZONE_NORMAL.
+ 'online_movable': Memory will be onlined in ZONE_MOVABLE.
+
+ The device initializes with online_type='invalid' which prevents
+ the cxl_dax_kmem_region driver from binding until the user
+ explicitly configures a valid online_type. This enables a
+ two-step binding process that gives users control over memory
+ hotplug policy before memory is added to the system.
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index 36f284d7c500..faf662c7d88b 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -18,6 +18,7 @@ cxl_core-y += ras.o
cxl_core-$(CONFIG_TRACING) += trace.o
cxl_core-$(CONFIG_CXL_REGION) += region.o
cxl_core-$(CONFIG_CXL_REGION) += dax_region.o
+cxl_core-$(CONFIG_CXL_REGION) += sysram_region.o
cxl_core-$(CONFIG_CXL_REGION) += pmem_region.o
cxl_core-$(CONFIG_CXL_MCE) += mce.o
cxl_core-$(CONFIG_CXL_FEATURES) += features.o
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index ea4df8abc2ad..04b32015e9b1 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -26,6 +26,7 @@ extern struct device_attribute dev_attr_delete_region;
extern struct device_attribute dev_attr_region;
extern const struct device_type cxl_pmem_region_type;
extern const struct device_type cxl_dax_region_type;
+extern const struct device_type cxl_sysram_region_type;
extern const struct device_type cxl_region_type;
int cxl_decoder_detach(struct cxl_region *cxlr,
@@ -37,6 +38,7 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
#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)
+#define CXL_SYSRAM_REGION_TYPE(x) (&cxl_sysram_region_type)
int cxl_region_init(void);
void cxl_region_exit(void);
int cxl_get_poison_by_endpoint(struct cxl_port *port);
@@ -44,9 +46,12 @@ struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
u64 dpa);
int devm_cxl_add_dax_region(struct cxl_region *cxlr, enum dax_driver_type);
+int devm_cxl_add_sysram_region(struct cxl_region *cxlr);
int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
extern struct cxl_driver cxl_devdax_region_driver;
+extern struct cxl_driver cxl_dax_kmem_region_driver;
+extern struct cxl_driver cxl_sysram_region_driver;
#else
static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
@@ -81,6 +86,7 @@ static inline void cxl_region_exit(void)
#define SET_CXL_REGION_ATTR(x)
#define CXL_PMEM_REGION_TYPE(x) NULL
#define CXL_DAX_REGION_TYPE(x) NULL
+#define CXL_SYSRAM_REGION_TYPE(x) NULL
#endif
struct cxl_send_command;
diff --git a/drivers/cxl/core/dax_region.c b/drivers/cxl/core/dax_region.c
index 391d51e5ec37..a379f5b85e3d 100644
--- a/drivers/cxl/core/dax_region.c
+++ b/drivers/cxl/core/dax_region.c
@@ -127,3 +127,53 @@ struct cxl_driver cxl_devdax_region_driver = {
.probe = cxl_devdax_region_driver_probe,
.id = CXL_DEVICE_REGION,
};
+
+static int cxl_dax_kmem_region_driver_probe(struct device *dev)
+{
+ struct cxl_sysram_region *cxlr_sysram = to_cxl_sysram_region(dev);
+ struct cxl_dax_region *cxlr_dax;
+ struct cxl_region *cxlr;
+ int rc;
+
+ if (!cxlr_sysram)
+ return -ENODEV;
+
+ /* Require explicit online_type configuration before binding */
+ if (cxlr_sysram->online_type == -1)
+ return -ENODEV;
+
+ cxlr = cxlr_sysram->cxlr;
+
+ cxlr_dax = cxl_dax_region_alloc(cxlr);
+ if (IS_ERR(cxlr_dax))
+ return PTR_ERR(cxlr_dax);
+
+ /* Inherit online_type from parent sysram_region */
+ cxlr_dax->online_type = cxlr_sysram->online_type;
+ cxlr_dax->dax_driver = DAXDRV_KMEM_TYPE;
+
+ /* Parent is the sysram_region device */
+ cxlr_dax->dev.parent = dev;
+
+ rc = dev_set_name(&cxlr_dax->dev, "dax_region%d", cxlr->id);
+ if (rc)
+ goto err;
+
+ rc = device_add(&cxlr_dax->dev);
+ if (rc)
+ goto err;
+
+ dev_dbg(dev, "%s: register %s\n", dev_name(dev),
+ dev_name(&cxlr_dax->dev));
+
+ return devm_add_action_or_reset(dev, cxlr_dax_unregister, cxlr_dax);
+err:
+ put_device(&cxlr_dax->dev);
+ return rc;
+}
+
+struct cxl_driver cxl_dax_kmem_region_driver = {
+ .name = "cxl_dax_kmem_region",
+ .probe = cxl_dax_kmem_region_driver_probe,
+ .id = CXL_DEVICE_SYSRAM_REGION,
+};
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 3310dbfae9d6..dc7262a5efd6 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -66,6 +66,8 @@ static int cxl_device_id(const struct device *dev)
return CXL_DEVICE_PMEM_REGION;
if (dev->type == CXL_DAX_REGION_TYPE())
return CXL_DEVICE_DAX_REGION;
+ if (dev->type == CXL_SYSRAM_REGION_TYPE())
+ return CXL_DEVICE_SYSRAM_REGION;
if (is_cxl_port(dev)) {
if (is_cxl_root(to_cxl_port(dev)))
return CXL_DEVICE_ROOT;
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 6200ca1cc2dd..8bef91dc726c 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3734,8 +3734,20 @@ int cxl_region_init(void)
if (rc)
goto err_dax;
+ rc = cxl_driver_register(&cxl_sysram_region_driver);
+ if (rc)
+ goto err_sysram;
+
+ rc = cxl_driver_register(&cxl_dax_kmem_region_driver);
+ if (rc)
+ goto err_dax_kmem;
+
return 0;
+err_dax_kmem:
+ cxl_driver_unregister(&cxl_sysram_region_driver);
+err_sysram:
+ cxl_driver_unregister(&cxl_devdax_region_driver);
err_dax:
cxl_driver_unregister(&cxl_region_driver);
return rc;
@@ -3743,6 +3755,8 @@ int cxl_region_init(void)
void cxl_region_exit(void)
{
+ cxl_driver_unregister(&cxl_dax_kmem_region_driver);
+ cxl_driver_unregister(&cxl_sysram_region_driver);
cxl_driver_unregister(&cxl_devdax_region_driver);
cxl_driver_unregister(&cxl_region_driver);
}
diff --git a/drivers/cxl/core/sysram_region.c b/drivers/cxl/core/sysram_region.c
new file mode 100644
index 000000000000..5665db238d0f
--- /dev/null
+++ b/drivers/cxl/core/sysram_region.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2026 Meta Platforms, Inc. All rights reserved. */
+/*
+ * CXL Sysram Region - Intermediate device for kmem hotplug configuration
+ *
+ * This provides an intermediate device between cxl_region and cxl_dax_region
+ * that allows users to configure memory hotplug parameters (like online_type)
+ * before the underlying dax_region is created and memory is hotplugged.
+ */
+
+#include <linux/memory_hotplug.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <cxlmem.h>
+#include <cxl.h>
+#include "core.h"
+
+static void cxl_sysram_region_release(struct device *dev)
+{
+ struct cxl_sysram_region *cxlr_sysram = to_cxl_sysram_region(dev);
+
+ kfree(cxlr_sysram);
+}
+
+static ssize_t online_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cxl_sysram_region *cxlr_sysram = to_cxl_sysram_region(dev);
+
+ switch (cxlr_sysram->online_type) {
+ case MMOP_OFFLINE:
+ return sysfs_emit(buf, "offline\n");
+ case MMOP_ONLINE:
+ return sysfs_emit(buf, "online\n");
+ case MMOP_ONLINE_MOVABLE:
+ return sysfs_emit(buf, "online_movable\n");
+ default:
+ return sysfs_emit(buf, "invalid\n");
+ }
+}
+
+static ssize_t online_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct cxl_sysram_region *cxlr_sysram = to_cxl_sysram_region(dev);
+
+ if (sysfs_streq(buf, "offline"))
+ cxlr_sysram->online_type = MMOP_OFFLINE;
+ else if (sysfs_streq(buf, "online"))
+ cxlr_sysram->online_type = MMOP_ONLINE;
+ else if (sysfs_streq(buf, "online_movable"))
+ cxlr_sysram->online_type = MMOP_ONLINE_MOVABLE;
+ else
+ return -EINVAL;
+
+ return len;
+}
+
+static DEVICE_ATTR_RW(online_type);
+
+static struct attribute *cxl_sysram_region_attrs[] = {
+ &dev_attr_online_type.attr,
+ NULL,
+};
+
+static const struct attribute_group cxl_sysram_region_attribute_group = {
+ .attrs = cxl_sysram_region_attrs,
+};
+
+static const struct attribute_group *cxl_sysram_region_attribute_groups[] = {
+ &cxl_base_attribute_group,
+ &cxl_sysram_region_attribute_group,
+ NULL,
+};
+
+const struct device_type cxl_sysram_region_type = {
+ .name = "cxl_sysram_region",
+ .release = cxl_sysram_region_release,
+ .groups = cxl_sysram_region_attribute_groups,
+};
+
+static bool is_cxl_sysram_region(struct device *dev)
+{
+ return dev->type == &cxl_sysram_region_type;
+}
+
+struct cxl_sysram_region *to_cxl_sysram_region(struct device *dev)
+{
+ if (dev_WARN_ONCE(dev, !is_cxl_sysram_region(dev),
+ "not a cxl_sysram_region device\n"))
+ return NULL;
+ return container_of(dev, struct cxl_sysram_region, dev);
+}
+EXPORT_SYMBOL_NS_GPL(to_cxl_sysram_region, "CXL");
+
+static struct lock_class_key cxl_sysram_region_key;
+
+static struct cxl_sysram_region *cxl_sysram_region_alloc(struct cxl_region *cxlr)
+{
+ struct cxl_region_params *p = &cxlr->params;
+ struct cxl_sysram_region *cxlr_sysram;
+ struct device *dev;
+
+ guard(rwsem_read)(&cxl_rwsem.region);
+ if (p->state != CXL_CONFIG_COMMIT)
+ return ERR_PTR(-ENXIO);
+
+ cxlr_sysram = kzalloc(sizeof(*cxlr_sysram), GFP_KERNEL);
+ if (!cxlr_sysram)
+ return ERR_PTR(-ENOMEM);
+
+ cxlr_sysram->hpa_range.start = p->res->start;
+ cxlr_sysram->hpa_range.end = p->res->end;
+ cxlr_sysram->online_type = -1; /* Require explicit configuration */
+
+ dev = &cxlr_sysram->dev;
+ cxlr_sysram->cxlr = cxlr;
+ device_initialize(dev);
+ lockdep_set_class(&dev->mutex, &cxl_sysram_region_key);
+ device_set_pm_not_required(dev);
+ dev->parent = &cxlr->dev;
+ dev->bus = &cxl_bus_type;
+ dev->type = &cxl_sysram_region_type;
+
+ return cxlr_sysram;
+}
+
+static void cxlr_sysram_unregister(void *_cxlr_sysram)
+{
+ struct cxl_sysram_region *cxlr_sysram = _cxlr_sysram;
+
+ device_unregister(&cxlr_sysram->dev);
+}
+
+int devm_cxl_add_sysram_region(struct cxl_region *cxlr)
+{
+ struct cxl_sysram_region *cxlr_sysram;
+ struct device *dev;
+ int rc;
+
+ cxlr_sysram = cxl_sysram_region_alloc(cxlr);
+ if (IS_ERR(cxlr_sysram))
+ return PTR_ERR(cxlr_sysram);
+
+ dev = &cxlr_sysram->dev;
+ rc = dev_set_name(dev, "sysram_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));
+
+ return devm_add_action_or_reset(&cxlr->dev, cxlr_sysram_unregister,
+ cxlr_sysram);
+err:
+ put_device(dev);
+ return rc;
+}
+
+static int cxl_sysram_region_driver_probe(struct device *dev)
+{
+ struct cxl_region *cxlr = to_cxl_region(dev);
+
+ /* Only handle RAM regions */
+ if (cxlr->mode != CXL_PARTMODE_RAM)
+ return -ENODEV;
+
+ return devm_cxl_add_sysram_region(cxlr);
+}
+
+struct cxl_driver cxl_sysram_region_driver = {
+ .name = "cxl_sysram_region",
+ .probe = cxl_sysram_region_driver_probe,
+ .id = CXL_DEVICE_REGION,
+};
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 674d5f870c70..1544c27e9c89 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -596,6 +596,25 @@ struct cxl_dax_region {
enum dax_driver_type dax_driver;
};
+/**
+ * struct cxl_sysram_region - CXL RAM region for system memory hotplug
+ * @dev: device for this sysram_region
+ * @cxlr: parent cxl_region
+ * @hpa_range: Host physical address range for the region
+ * @online_type: Memory online type (MMOP_* 0-3, or -1 if not configured)
+ *
+ * Intermediate device that allows configuration of memory hotplug
+ * parameters before the underlying dax_region is created. The device
+ * starts with online_type=-1 which prevents the cxl_dax_kmem_region
+ * driver from binding until the user explicitly sets online_type.
+ */
+struct cxl_sysram_region {
+ struct device dev;
+ struct cxl_region *cxlr;
+ struct range hpa_range;
+ int online_type;
+};
+
/**
* struct cxl_port - logical collection of upstream port devices and
* downstream port devices to construct a CXL memory
@@ -890,6 +909,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
#define CXL_DEVICE_PMEM_REGION 7
#define CXL_DEVICE_DAX_REGION 8
#define CXL_DEVICE_PMU 9
+#define CXL_DEVICE_SYSRAM_REGION 10
#define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
#define CXL_MODALIAS_FMT "cxl:t%d"
@@ -907,6 +927,7 @@ 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);
+struct cxl_sysram_region *to_cxl_sysram_region(struct device *dev);
u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa);
#else
static inline bool is_cxl_pmem_region(struct device *dev)
@@ -925,6 +946,10 @@ static inline struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
{
return NULL;
}
+static inline struct cxl_sysram_region *to_cxl_sysram_region(struct device *dev)
+{
+ return NULL;
+}
static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint,
u64 spa)
{
--
2.52.0
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers
2026-01-29 21:04 ` [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers Gregory Price
@ 2026-01-30 21:27 ` Cheatham, Benjamin
2026-01-30 22:12 ` Gregory Price
2026-02-02 18:20 ` Jonathan Cameron
1 sibling, 1 reply; 30+ messages in thread
From: Cheatham, Benjamin @ 2026-01-30 21:27 UTC (permalink / raw)
To: Gregory Price, linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
On 1/29/2026 3:04 PM, Gregory Price wrote:
> In the current kmem driver binding process, the only way for users
> to define hotplug policy is via a build-time option, or by not
> onlining memory by default and setting each individual memory block
> online after hotplug occurs. We can solve this with a configuration
> step between region-probe and dax-probe.
>
> Add the infrastructure for a two-stage driver binding for kmem-mode
> dax regions. The cxl_dax_kmem_region driver probes cxl_sysram_region
> devices and creates cxl_dax_region with dax_driver=kmem.
>
> This creates an interposition step where users can configure policy.
>
> Device hierarchy:
> region0 -> sysram_region0 -> dax_region0 -> dax0.0
This technically comes up in the devdax_region driver patch first, but I noticed it here
so this is where I'm putting it:
I like the idea here, but the implementation is all off. Firstly, devm_cxl_add_sysram_region()
is never called outside of sysram_region_driver::probe(), so I'm not sure how they ever get
added to the system (same with devdax regions).
Second, there's this weird pattern of adding sub-region (sysram, devdax, etc.) devices being added
inside of the sub-region driver probe. I would expect the devices are added then the probe function
is called. What I think should be going on here (and correct me if I'm wrong) is:
1. a cxl_region device is added to the system
2. cxl_region::probe() is called on said device (one in cxl/core/region.c)
3. Said probe function figures out the device is a dax_region or whatever else and creates that type of region device
(i.e. cxl_region::probe() -> device_add(&cxl_sysram_device))
4. if the device's dax driver type is DAXDRV_DEVICE_TYPE it gets sent to the daxdev_region driver
5a. if the device's dax driver type is DAXDRV_KMEM_TYPE it gets sent to the sysram_region driver which holds it until
the online_type is set
5b. Once the online_type is set, the device is forwarded to the dax_kmem_region driver? Not sure on this part
What seems to be happening is that the cxl_region is added, all of these region drivers try
to bind to it since they all use the same device id (CXL_DEVICE_REGION) and the correct one is
figured out by magic? I'm somewhat confused at this point :/.
>
> The sysram_region device exposes a sysfs 'online_type' attribute
> that allows users to configure the memory online type before the
> underlying dax_region is created and memory is hotplugged.
>
> sysram_region0/online_type:
> invalid: not configured, blocks probe
> offline: memory will not be onlined automatically
> online: memory will be onlined in ZONE_NORMAL
> online_movable: memory will be onlined in ZONE_MMOVABLE
>
> The device initializes with online_type=invalid which prevents the
> cxl_dax_kmem_region driver from binding until the user explicitly
> configures a valid online_type.
>
> This enables a two-step binding process:
> echo region0 > cxl_sysram_region/bind
> echo online_movable > sysram_region0/online_type
> echo sysram_region0 > cxl_dax_kmem_region/bind
>
> Signed-off-by: Gregory Price <gourry@gourry.net>
> ---
> Documentation/ABI/testing/sysfs-bus-cxl | 21 +++
> drivers/cxl/core/Makefile | 1 +
> drivers/cxl/core/core.h | 6 +
> drivers/cxl/core/dax_region.c | 50 +++++++
> drivers/cxl/core/port.c | 2 +
> drivers/cxl/core/region.c | 14 ++
> drivers/cxl/core/sysram_region.c | 180 ++++++++++++++++++++++++
> drivers/cxl/cxl.h | 25 ++++
> 8 files changed, 299 insertions(+)
> create mode 100644 drivers/cxl/core/sysram_region.c
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl
> index c80a1b5a03db..a051cb86bdfc 100644
> --- a/Documentation/ABI/testing/sysfs-bus-cxl
> +++ b/Documentation/ABI/testing/sysfs-bus-cxl
> @@ -624,3 +624,24 @@ 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/sysram_regionZ/online_type
> +Date: January, 2026
> +KernelVersion: v7.1
> +Contact: linux-cxl@vger.kernel.org
> +Description:
> + (RW) This attribute allows users to configure the memory online
> + type before the underlying dax_region engages in hotplug.
> +
> + Valid values:
> + 'invalid': Not configured (default). Blocks probe.
This should be removed from the valid values section since it's not a valid value
to write to the attribute. The mention of the default in the paragraph below should
be enough.
> + 'offline': Memory will not be onlined automatically.
> + 'online' : Memory will be onlined in ZONE_NORMAL.
> + 'online_movable': Memory will be onlined in ZONE_MOVABLE.
> +
> + The device initializes with online_type='invalid' which prevents
> + the cxl_dax_kmem_region driver from binding until the user
> + explicitly configures a valid online_type. This enables a
> + two-step binding process that gives users control over memory
> + hotplug policy before memory is added to the system.
> diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
> index 36f284d7c500..faf662c7d88b 100644
> --- a/drivers/cxl/core/Makefile
> +++ b/drivers/cxl/core/Makefile
> @@ -18,6 +18,7 @@ cxl_core-y += ras.o
> cxl_core-$(CONFIG_TRACING) += trace.o
> cxl_core-$(CONFIG_CXL_REGION) += region.o
> cxl_core-$(CONFIG_CXL_REGION) += dax_region.o
> +cxl_core-$(CONFIG_CXL_REGION) += sysram_region.o
> cxl_core-$(CONFIG_CXL_REGION) += pmem_region.o
> cxl_core-$(CONFIG_CXL_MCE) += mce.o
> cxl_core-$(CONFIG_CXL_FEATURES) += features.o
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index ea4df8abc2ad..04b32015e9b1 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -26,6 +26,7 @@ extern struct device_attribute dev_attr_delete_region;
> extern struct device_attribute dev_attr_region;
> extern const struct device_type cxl_pmem_region_type;
> extern const struct device_type cxl_dax_region_type;
> +extern const struct device_type cxl_sysram_region_type;
> extern const struct device_type cxl_region_type;
>
> int cxl_decoder_detach(struct cxl_region *cxlr,
> @@ -37,6 +38,7 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
> #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)
> +#define CXL_SYSRAM_REGION_TYPE(x) (&cxl_sysram_region_type)
> int cxl_region_init(void);
> void cxl_region_exit(void);
> int cxl_get_poison_by_endpoint(struct cxl_port *port);
> @@ -44,9 +46,12 @@ struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
> u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
> u64 dpa);
> int devm_cxl_add_dax_region(struct cxl_region *cxlr, enum dax_driver_type);
> +int devm_cxl_add_sysram_region(struct cxl_region *cxlr);
> int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
>
> extern struct cxl_driver cxl_devdax_region_driver;
> +extern struct cxl_driver cxl_dax_kmem_region_driver;
> +extern struct cxl_driver cxl_sysram_region_driver;
>
> #else
> static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
> @@ -81,6 +86,7 @@ static inline void cxl_region_exit(void)
> #define SET_CXL_REGION_ATTR(x)
> #define CXL_PMEM_REGION_TYPE(x) NULL
> #define CXL_DAX_REGION_TYPE(x) NULL
> +#define CXL_SYSRAM_REGION_TYPE(x) NULL
> #endif
>
> struct cxl_send_command;
> diff --git a/drivers/cxl/core/dax_region.c b/drivers/cxl/core/dax_region.c
> index 391d51e5ec37..a379f5b85e3d 100644
> --- a/drivers/cxl/core/dax_region.c
> +++ b/drivers/cxl/core/dax_region.c
> @@ -127,3 +127,53 @@ struct cxl_driver cxl_devdax_region_driver = {
> .probe = cxl_devdax_region_driver_probe,
> .id = CXL_DEVICE_REGION,
> };
> +
> +static int cxl_dax_kmem_region_driver_probe(struct device *dev)
> +{
> + struct cxl_sysram_region *cxlr_sysram = to_cxl_sysram_region(dev);
> + struct cxl_dax_region *cxlr_dax;
> + struct cxl_region *cxlr;
> + int rc;
> +
> + if (!cxlr_sysram)
> + return -ENODEV;
> +
> + /* Require explicit online_type configuration before binding */
> + if (cxlr_sysram->online_type == -1)
> + return -ENODEV;
> +
> + cxlr = cxlr_sysram->cxlr;
> +
> + cxlr_dax = cxl_dax_region_alloc(cxlr);
> + if (IS_ERR(cxlr_dax))
> + return PTR_ERR(cxlr_dax);
You can use cleanup.h here to remove the goto's (I think). Following should work:
#DEFINE_FREE(cxlr_dax_region_put, struct cxl_dax_region *, if (!IS_ERR_OR_NULL(_T)) put_device(&cxlr_dax->dev))
static int cxl_dax_kmem_region_driver_probe(struct device *dev)
{
...
struct cxl_dax_region *cxlr_dax __free(cxlr_dax_region_put) = cxl_dax_region_alloc(cxlr);
if (IS_ERR(cxlr_dax))
return PTR_ERR(cxlr_dax);
...
rc = dev_set_name(&cxlr_dax->dev, "dax_region%d", cxlr->id);
if (rc)
return rc;
rc = device_add(&cxlr_dax->dev);
if (rc)
return rc;
dev_dbg(dev, "%s: register %s\n", dev_name(dev), dev_name(&cxlr_dax->dev));
return devm_add_action_or_reset(dev, cxlr_dax_unregister, no_free_ptr(cxlr_dax));
}
> +
> + /* Inherit online_type from parent sysram_region */
> + cxlr_dax->online_type = cxlr_sysram->online_type;
> + cxlr_dax->dax_driver = DAXDRV_KMEM_TYPE;
> +
> + /* Parent is the sysram_region device */
> + cxlr_dax->dev.parent = dev;
> +
> + rc = dev_set_name(&cxlr_dax->dev, "dax_region%d", cxlr->id);
> + if (rc)
> + goto err;
> +
> + rc = device_add(&cxlr_dax->dev);
> + if (rc)
> + goto err;
> +
> + dev_dbg(dev, "%s: register %s\n", dev_name(dev),
> + dev_name(&cxlr_dax->dev));
> +
> + return devm_add_action_or_reset(dev, cxlr_dax_unregister, cxlr_dax);
> +err:
> + put_device(&cxlr_dax->dev);
> + return rc;
> +}
> +
> +struct cxl_driver cxl_dax_kmem_region_driver = {
> + .name = "cxl_dax_kmem_region",
> + .probe = cxl_dax_kmem_region_driver_probe,
> + .id = CXL_DEVICE_SYSRAM_REGION,
> +};
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 3310dbfae9d6..dc7262a5efd6 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -66,6 +66,8 @@ static int cxl_device_id(const struct device *dev)
> return CXL_DEVICE_PMEM_REGION;
> if (dev->type == CXL_DAX_REGION_TYPE())
> return CXL_DEVICE_DAX_REGION;
> + if (dev->type == CXL_SYSRAM_REGION_TYPE())
> + return CXL_DEVICE_SYSRAM_REGION;
> if (is_cxl_port(dev)) {
> if (is_cxl_root(to_cxl_port(dev)))
> return CXL_DEVICE_ROOT;
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 6200ca1cc2dd..8bef91dc726c 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3734,8 +3734,20 @@ int cxl_region_init(void)
> if (rc)
> goto err_dax;
>
> + rc = cxl_driver_register(&cxl_sysram_region_driver);
> + if (rc)
> + goto err_sysram;
> +
> + rc = cxl_driver_register(&cxl_dax_kmem_region_driver);
> + if (rc)
> + goto err_dax_kmem;
> +
> return 0;
>
> +err_dax_kmem:
> + cxl_driver_unregister(&cxl_sysram_region_driver);
> +err_sysram:
> + cxl_driver_unregister(&cxl_devdax_region_driver);
> err_dax:
> cxl_driver_unregister(&cxl_region_driver);
> return rc;
> @@ -3743,6 +3755,8 @@ int cxl_region_init(void)
>
> void cxl_region_exit(void)
> {
> + cxl_driver_unregister(&cxl_dax_kmem_region_driver);
> + cxl_driver_unregister(&cxl_sysram_region_driver);
> cxl_driver_unregister(&cxl_devdax_region_driver);
> cxl_driver_unregister(&cxl_region_driver);
> }
> diff --git a/drivers/cxl/core/sysram_region.c b/drivers/cxl/core/sysram_region.c
> new file mode 100644
> index 000000000000..5665db238d0f
> --- /dev/null
> +++ b/drivers/cxl/core/sysram_region.c
> @@ -0,0 +1,180 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2026 Meta Platforms, Inc. All rights reserved. */
> +/*
> + * CXL Sysram Region - Intermediate device for kmem hotplug configuration
> + *
> + * This provides an intermediate device between cxl_region and cxl_dax_region
> + * that allows users to configure memory hotplug parameters (like online_type)
> + * before the underlying dax_region is created and memory is hotplugged.
> + */
> +
> +#include <linux/memory_hotplug.h>
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +#include <cxlmem.h>
> +#include <cxl.h>
> +#include "core.h"
> +
> +static void cxl_sysram_region_release(struct device *dev)
> +{
> + struct cxl_sysram_region *cxlr_sysram = to_cxl_sysram_region(dev);
> +
> + kfree(cxlr_sysram);
> +}
> +
> +static ssize_t online_type_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct cxl_sysram_region *cxlr_sysram = to_cxl_sysram_region(dev);
> +
> + switch (cxlr_sysram->online_type) {
> + case MMOP_OFFLINE:
> + return sysfs_emit(buf, "offline\n");
> + case MMOP_ONLINE:
> + return sysfs_emit(buf, "online\n");
> + case MMOP_ONLINE_MOVABLE:
> + return sysfs_emit(buf, "online_movable\n");
> + default:
> + return sysfs_emit(buf, "invalid\n");
> + }
> +}
> +
> +static ssize_t online_type_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct cxl_sysram_region *cxlr_sysram = to_cxl_sysram_region(dev);
> +
> + if (sysfs_streq(buf, "offline"))
> + cxlr_sysram->online_type = MMOP_OFFLINE;
> + else if (sysfs_streq(buf, "online"))
> + cxlr_sysram->online_type = MMOP_ONLINE;
> + else if (sysfs_streq(buf, "online_movable"))
> + cxlr_sysram->online_type = MMOP_ONLINE_MOVABLE;
> + else
> + return -EINVAL;
> +
> + return len;
> +}
> +
> +static DEVICE_ATTR_RW(online_type);
> +
> +static struct attribute *cxl_sysram_region_attrs[] = {
> + &dev_attr_online_type.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group cxl_sysram_region_attribute_group = {
> + .attrs = cxl_sysram_region_attrs,
> +};
> +
> +static const struct attribute_group *cxl_sysram_region_attribute_groups[] = {
> + &cxl_base_attribute_group,
> + &cxl_sysram_region_attribute_group,
> + NULL,
> +};
> +
> +const struct device_type cxl_sysram_region_type = {
> + .name = "cxl_sysram_region",
> + .release = cxl_sysram_region_release,
> + .groups = cxl_sysram_region_attribute_groups,
> +};
> +
> +static bool is_cxl_sysram_region(struct device *dev)
> +{
> + return dev->type == &cxl_sysram_region_type;
> +}
> +
> +struct cxl_sysram_region *to_cxl_sysram_region(struct device *dev)
> +{
> + if (dev_WARN_ONCE(dev, !is_cxl_sysram_region(dev),
> + "not a cxl_sysram_region device\n"))
> + return NULL;
> + return container_of(dev, struct cxl_sysram_region, dev);
> +}
> +EXPORT_SYMBOL_NS_GPL(to_cxl_sysram_region, "CXL");
> +
> +static struct lock_class_key cxl_sysram_region_key;
> +
> +static struct cxl_sysram_region *cxl_sysram_region_alloc(struct cxl_region *cxlr)
> +{
> + struct cxl_region_params *p = &cxlr->params;
> + struct cxl_sysram_region *cxlr_sysram;
> + struct device *dev;
> +
> + guard(rwsem_read)(&cxl_rwsem.region);
> + if (p->state != CXL_CONFIG_COMMIT)
> + return ERR_PTR(-ENXIO);
> +
> + cxlr_sysram = kzalloc(sizeof(*cxlr_sysram), GFP_KERNEL);
> + if (!cxlr_sysram)
> + return ERR_PTR(-ENOMEM);
> +
> + cxlr_sysram->hpa_range.start = p->res->start;
> + cxlr_sysram->hpa_range.end = p->res->end;
> + cxlr_sysram->online_type = -1; /* Require explicit configuration */
> +
> + dev = &cxlr_sysram->dev;
> + cxlr_sysram->cxlr = cxlr;
> + device_initialize(dev);
> + lockdep_set_class(&dev->mutex, &cxl_sysram_region_key);
> + device_set_pm_not_required(dev);
> + dev->parent = &cxlr->dev;
> + dev->bus = &cxl_bus_type;
> + dev->type = &cxl_sysram_region_type;
> +
> + return cxlr_sysram;
> +}
> +
> +static void cxlr_sysram_unregister(void *_cxlr_sysram)
> +{
> + struct cxl_sysram_region *cxlr_sysram = _cxlr_sysram;
> +
> + device_unregister(&cxlr_sysram->dev);
> +}
> +
> +int devm_cxl_add_sysram_region(struct cxl_region *cxlr)
> +{
> + struct cxl_sysram_region *cxlr_sysram;
> + struct device *dev;
> + int rc;
> +
> + cxlr_sysram = cxl_sysram_region_alloc(cxlr);
> + if (IS_ERR(cxlr_sysram))
> + return PTR_ERR(cxlr_sysram);
Same thing as above
Thanks,
Ben
> +
> + dev = &cxlr_sysram->dev;
> + rc = dev_set_name(dev, "sysram_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));
> +
> + return devm_add_action_or_reset(&cxlr->dev, cxlr_sysram_unregister,
> + cxlr_sysram);
> +err:
> + put_device(dev);
> + return rc;
> +}
> +
> +static int cxl_sysram_region_driver_probe(struct device *dev)
> +{
> + struct cxl_region *cxlr = to_cxl_region(dev);
> +
> + /* Only handle RAM regions */
> + if (cxlr->mode != CXL_PARTMODE_RAM)
> + return -ENODEV;
> +
> + return devm_cxl_add_sysram_region(cxlr);
> +}
> +
> +struct cxl_driver cxl_sysram_region_driver = {
> + .name = "cxl_sysram_region",
> + .probe = cxl_sysram_region_driver_probe,
> + .id = CXL_DEVICE_REGION,
> +};
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 674d5f870c70..1544c27e9c89 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -596,6 +596,25 @@ struct cxl_dax_region {
> enum dax_driver_type dax_driver;
> };
>
> +/**
> + * struct cxl_sysram_region - CXL RAM region for system memory hotplug
> + * @dev: device for this sysram_region
> + * @cxlr: parent cxl_region
> + * @hpa_range: Host physical address range for the region
> + * @online_type: Memory online type (MMOP_* 0-3, or -1 if not configured)
> + *
> + * Intermediate device that allows configuration of memory hotplug
> + * parameters before the underlying dax_region is created. The device
> + * starts with online_type=-1 which prevents the cxl_dax_kmem_region
> + * driver from binding until the user explicitly sets online_type.
> + */
> +struct cxl_sysram_region {
> + struct device dev;
> + struct cxl_region *cxlr;
> + struct range hpa_range;
> + int online_type;
> +};
> +
> /**
> * struct cxl_port - logical collection of upstream port devices and
> * downstream port devices to construct a CXL memory
> @@ -890,6 +909,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
> #define CXL_DEVICE_PMEM_REGION 7
> #define CXL_DEVICE_DAX_REGION 8
> #define CXL_DEVICE_PMU 9
> +#define CXL_DEVICE_SYSRAM_REGION 10
>
> #define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
> #define CXL_MODALIAS_FMT "cxl:t%d"
> @@ -907,6 +927,7 @@ 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);
> +struct cxl_sysram_region *to_cxl_sysram_region(struct device *dev);
> u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa);
> #else
> static inline bool is_cxl_pmem_region(struct device *dev)
> @@ -925,6 +946,10 @@ static inline struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
> {
> return NULL;
> }
> +static inline struct cxl_sysram_region *to_cxl_sysram_region(struct device *dev)
> +{
> + return NULL;
> +}
> static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint,
> u64 spa)
> {
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers
2026-01-30 21:27 ` Cheatham, Benjamin
@ 2026-01-30 22:12 ` Gregory Price
2026-02-02 17:02 ` Cheatham, Benjamin
0 siblings, 1 reply; 30+ messages in thread
From: Gregory Price @ 2026-01-30 22:12 UTC (permalink / raw)
To: Cheatham, Benjamin
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
On Fri, Jan 30, 2026 at 03:27:12PM -0600, Cheatham, Benjamin wrote:
> On 1/29/2026 3:04 PM, Gregory Price wrote:
> > In the current kmem driver binding process, the only way for users
> > to define hotplug policy is via a build-time option, or by not
> > onlining memory by default and setting each individual memory block
> > online after hotplug occurs. We can solve this with a configuration
> > step between region-probe and dax-probe.
> >
> > Add the infrastructure for a two-stage driver binding for kmem-mode
> > dax regions. The cxl_dax_kmem_region driver probes cxl_sysram_region
> > devices and creates cxl_dax_region with dax_driver=kmem.
> >
> > This creates an interposition step where users can configure policy.
> >
> > Device hierarchy:
> > region0 -> sysram_region0 -> dax_region0 -> dax0.0
>
> This technically comes up in the devdax_region driver patch first, but I noticed it here
> so this is where I'm putting it:
>
> I like the idea here, but the implementation is all off. Firstly, devm_cxl_add_sysram_region()
> is never called outside of sysram_region_driver::probe(), so I'm not sure how they ever get
> added to the system (same with devdax regions).
>
> Second, there's this weird pattern of adding sub-region (sysram, devdax, etc.) devices being added
> inside of the sub-region driver probe. I would expect the devices are added then the probe function
> is called.
I originally tried doing with region0/region_driver, but that design
pattern is also confusing - and it creates differently bad patterns.
echo region0 > decoder0.0/create_ram_region -> creates region0
# Current pattern
echo region > driver/region/probe /* auto-region behavior */
# region_driver attribute pattern
echo "sysram" > region0/region_driver
echo region0 > driver/region/probe /* uses sysram region driver */
https://lore.kernel.org/linux-cxl/20260113202138.3021093-1-gourry@gourry.net/
Ira pointed out that this design makes the "implicit" design of the
driver worse. The user doesn't actually know what driver is being used
under the hood - it just knows something is being used.
This at least makes it explicit which driver is being used - and splits
the uses-case logic up into discrete drivers (dax users don't have to
worry about sysram users breaking their stuff).
If it makes more sense, you could swap the ordering of the names
echo region0 > region/bind
echo region0 > region_sysram/bind
echo region0 > region_daxdev/bind
echo region0 > region_dax_kmem/bind
echo region0 > region_pony/bind
---
The underlying issue is that region::probe() is trying to be a
god-function for every possible use case, and hiding the use case
behind an attribute vs a driver is not good.
(also the default behavior for region::probe() in an otherwise
unconfigured region is required for backwards compatibility)
> What I think should be going on here (and correct me if I'm wrong) is:
> 1. a cxl_region device is added to the system
> 2. cxl_region::probe() is called on said device (one in cxl/core/region.c)
> 3. Said probe function figures out the device is a dax_region or whatever else and creates that type of region device
> (i.e. cxl_region::probe() -> device_add(&cxl_sysram_device))
> 4. if the device's dax driver type is DAXDRV_DEVICE_TYPE it gets sent to the daxdev_region driver
> 5a. if the device's dax driver type is DAXDRV_KMEM_TYPE it gets sent to the sysram_region driver which holds it until
> the online_type is set
> 5b. Once the online_type is set, the device is forwarded to the dax_kmem_region driver? Not sure on this part
>
> What seems to be happening is that the cxl_region is added, all of these region drivers try
> to bind to it since they all use the same device id (CXL_DEVICE_REGION) and the correct one is
> figured out by magic? I'm somewhat confused at this point :/.
>
For auto-regions:
region_probe() eats it and you get the default behavior.
For non-auto regions:
create_x_region generates an un-configured region and fails to probe
until the user commits it and probes it.
auto-regions are evil and should be discouraged.
~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers
2026-01-30 22:12 ` Gregory Price
@ 2026-02-02 17:02 ` Cheatham, Benjamin
2026-02-02 17:41 ` Gregory Price
2026-02-02 19:19 ` Gregory Price
0 siblings, 2 replies; 30+ messages in thread
From: Cheatham, Benjamin @ 2026-02-02 17:02 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
On 1/30/2026 4:12 PM, Gregory Price wrote:
> On Fri, Jan 30, 2026 at 03:27:12PM -0600, Cheatham, Benjamin wrote:
>> On 1/29/2026 3:04 PM, Gregory Price wrote:
>>> In the current kmem driver binding process, the only way for users
>>> to define hotplug policy is via a build-time option, or by not
>>> onlining memory by default and setting each individual memory block
>>> online after hotplug occurs. We can solve this with a configuration
>>> step between region-probe and dax-probe.
>>>
>>> Add the infrastructure for a two-stage driver binding for kmem-mode
>>> dax regions. The cxl_dax_kmem_region driver probes cxl_sysram_region
>>> devices and creates cxl_dax_region with dax_driver=kmem.
>>>
>>> This creates an interposition step where users can configure policy.
>>>
>>> Device hierarchy:
>>> region0 -> sysram_region0 -> dax_region0 -> dax0.0
>>
>> This technically comes up in the devdax_region driver patch first, but I noticed it here
>> so this is where I'm putting it:
>>
>> I like the idea here, but the implementation is all off. Firstly, devm_cxl_add_sysram_region()
>> is never called outside of sysram_region_driver::probe(), so I'm not sure how they ever get
>> added to the system (same with devdax regions).
>>
>> Second, there's this weird pattern of adding sub-region (sysram, devdax, etc.) devices being added
>> inside of the sub-region driver probe. I would expect the devices are added then the probe function
>> is called.
>
> I originally tried doing with region0/region_driver, but that design
> pattern is also confusing - and it creates differently bad patterns.
>
> echo region0 > decoder0.0/create_ram_region -> creates region0
>
> # Current pattern
> echo region > driver/region/probe /* auto-region behavior */
>
> # region_driver attribute pattern
> echo "sysram" > region0/region_driver
> echo region0 > driver/region/probe /* uses sysram region driver */
>
> https://lore.kernel.org/linux-cxl/20260113202138.3021093-1-gourry@gourry.net/
>
> Ira pointed out that this design makes the "implicit" design of the
> driver worse. The user doesn't actually know what driver is being used
> under the hood - it just knows something is being used.
>
> This at least makes it explicit which driver is being used - and splits
> the uses-case logic up into discrete drivers (dax users don't have to
> worry about sysram users breaking their stuff).
>
> If it makes more sense, you could swap the ordering of the names
>
> echo region0 > region/bind
> echo region0 > region_sysram/bind
> echo region0 > region_daxdev/bind
> echo region0 > region_dax_kmem/bind
> echo region0 > region_pony/bind
>
> ---
>
> The underlying issue is that region::probe() is trying to be a
> god-function for every possible use case, and hiding the use case
> behind an attribute vs a driver is not good.
>
> (also the default behavior for region::probe() in an otherwise
> unconfigured region is required for backwards compatibility)
Ok, that makes sense. I think I just got lost in the sauce while looking at this last
week and this explanation helped a lot.>
>> What I think should be going on here (and correct me if I'm wrong) is:
>> 1. a cxl_region device is added to the system
>> 2. cxl_region::probe() is called on said device (one in cxl/core/region.c)
>> 3. Said probe function figures out the device is a dax_region or whatever else and creates that type of region device
>> (i.e. cxl_region::probe() -> device_add(&cxl_sysram_device))
>> 4. if the device's dax driver type is DAXDRV_DEVICE_TYPE it gets sent to the daxdev_region driver
>> 5a. if the device's dax driver type is DAXDRV_KMEM_TYPE it gets sent to the sysram_region driver which holds it until
>> the online_type is set
>> 5b. Once the online_type is set, the device is forwarded to the dax_kmem_region driver? Not sure on this part
>>
>> What seems to be happening is that the cxl_region is added, all of these region drivers try
>> to bind to it since they all use the same device id (CXL_DEVICE_REGION) and the correct one is
>> figured out by magic? I'm somewhat confused at this point :/.
>>
>
> For auto-regions:
> region_probe() eats it and you get the default behavior.
>
> For non-auto regions:
> create_x_region generates an un-configured region and fails to probe
> until the user commits it and probes it.
I think this was the source of my misunderstanding. I was trying to understand how it
works for auto regions when it's never meant to apply to them.
Sorry if this is a stupid question, but what stops auto regions from binding to the
sysram/dax region drivers? They all bind to region devices, so I assume there's something
keeping them from binding before the core region driver gets a chance.
Thanks,
Ben
>
> auto-regions are evil and should be discouraged.
>
> ~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers
2026-02-02 17:02 ` Cheatham, Benjamin
@ 2026-02-02 17:41 ` Gregory Price
2026-02-02 19:19 ` Gregory Price
1 sibling, 0 replies; 30+ messages in thread
From: Gregory Price @ 2026-02-02 17:41 UTC (permalink / raw)
To: Cheatham, Benjamin
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
On Mon, Feb 02, 2026 at 11:02:37AM -0600, Cheatham, Benjamin wrote:
> >
> > For auto-regions:
> > region_probe() eats it and you get the default behavior.
> >
> > For non-auto regions:
> > create_x_region generates an un-configured region and fails to probe
> > until the user commits it and probes it.
>
> I think this was the source of my misunderstanding. I was trying to understand how it
> works for auto regions when it's never meant to apply to them.
>
> Sorry if this is a stupid question, but what stops auto regions from binding to the
> sysram/dax region drivers? They all bind to region devices, so I assume there's something
> keeping them from binding before the core region driver gets a chance.
>
Auto regions explicitly use the dax_kmem path (all existing code,
unchanged)- which auto-plugs into dax/hotplug.
I do get what you're saying that everything binds on a region type,
I will look a little closer at this and see if there's something more
reasonable we can do.
I think i can update `region/bind` to use the sysram driver with
online_type=mhp_default_online_type
so you'd end up with effective the auto-region logic:
cxlcli create-region -m ram ... existing argument set
------
echo region0 > create_ram_region
/* program decoders */
echo region0 > region/bind
/*
* region_bind():
* 1) alloc sysram_region object
* 2) sysram_regionN->online_type=mhp_default_online_type()
* 3) add device to bus
* 4) device auto-probes all the way down to dax
* 5) dax auto-onlines with system default setting
*/
------
and Non-auto-region logic (approximation)
cxlcli creation-region -m ram --type sysram --online-type=movable
-----
echo region0 > create_ram_region
/* program decoders */
echo region0 > sysram/bind
echo online_movable > sysram_region0/online_type
echo sysram_region0 > dax_kmem/bind
-----
I want to retain the dax_kmem driver because there may be multiple users
other than sysram. For example, a compressed memory region wants to
utilize dax_kmem, but has its own complex policy (via N_MEMORY_PRIVATE)
so it doesn't want to abstract through sysram_region, but it does want
to abstract through dax_kmem.
weeeee "software defined memory" weeeee
~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers
2026-02-02 17:02 ` Cheatham, Benjamin
2026-02-02 17:41 ` Gregory Price
@ 2026-02-02 19:19 ` Gregory Price
1 sibling, 0 replies; 30+ messages in thread
From: Gregory Price @ 2026-02-02 19:19 UTC (permalink / raw)
To: Cheatham, Benjamin
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
On Mon, Feb 02, 2026 at 11:02:37AM -0600, Cheatham, Benjamin wrote:
> Sorry if this is a stupid question, but what stops auto regions from binding to the
> sysram/dax region drivers? They all bind to region devices, so I assume there's something
> keeping them from binding before the core region driver gets a chance.
>
I just grok'd the implications of this question.
I *think* the answer is "probe order" - which is bad.
I need to think about this a bit more and get a more definitive answer.
~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers
2026-01-29 21:04 ` [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers Gregory Price
2026-01-30 21:27 ` Cheatham, Benjamin
@ 2026-02-02 18:20 ` Jonathan Cameron
2026-02-02 18:23 ` Gregory Price
1 sibling, 1 reply; 30+ messages in thread
From: Jonathan Cameron @ 2026-02-02 18:20 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john
On Thu, 29 Jan 2026 16:04:41 -0500
Gregory Price <gourry@gourry.net> wrote:
> In the current kmem driver binding process, the only way for users
> to define hotplug policy is via a build-time option, or by not
> onlining memory by default and setting each individual memory block
> online after hotplug occurs. We can solve this with a configuration
> step between region-probe and dax-probe.
>
> Add the infrastructure for a two-stage driver binding for kmem-mode
> dax regions. The cxl_dax_kmem_region driver probes cxl_sysram_region
> devices and creates cxl_dax_region with dax_driver=kmem.
>
> This creates an interposition step where users can configure policy.
>
> Device hierarchy:
> region0 -> sysram_region0 -> dax_region0 -> dax0.0
>
> The sysram_region device exposes a sysfs 'online_type' attribute
> that allows users to configure the memory online type before the
> underlying dax_region is created and memory is hotplugged.
>
> sysram_region0/online_type:
> invalid: not configured, blocks probe
> offline: memory will not be onlined automatically
> online: memory will be onlined in ZONE_NORMAL
> online_movable: memory will be onlined in ZONE_MMOVABLE
ZONE_MOVABLE
>
> The device initializes with online_type=invalid which prevents the
> cxl_dax_kmem_region driver from binding until the user explicitly
> configures a valid online_type.
>
> This enables a two-step binding process:
> echo region0 > cxl_sysram_region/bind
> echo online_movable > sysram_region0/online_type
> echo sysram_region0 > cxl_dax_kmem_region/bind
>
> Signed-off-by: Gregory Price <gourry@gourry.net>
Trivial stuff. Will mull over this series as a whole...
My first instinctive reaction is positive - I'm just wondering
where additional drivers fit into this and whether it has the
right degree of flexibility.
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 6200ca1cc2dd..8bef91dc726c 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3734,8 +3734,20 @@ int cxl_region_init(void)
> if (rc)
> goto err_dax;
>
> + rc = cxl_driver_register(&cxl_sysram_region_driver);
This smells like a loop over an array of drivers is becoming sensible.
> + if (rc)
> + goto err_sysram;
> +
> + rc = cxl_driver_register(&cxl_dax_kmem_region_driver);
> + if (rc)
> + goto err_dax_kmem;
> +
> return 0;
>
> +err_dax_kmem:
> + cxl_driver_unregister(&cxl_sysram_region_driver);
> +err_sysram:
> + cxl_driver_unregister(&cxl_devdax_region_driver);
> err_dax:
> cxl_driver_unregister(&cxl_region_driver);
> return rc;
> @@ -3743,6 +3755,8 @@ int cxl_region_init(void)
>
> void cxl_region_exit(void)
> {
> + cxl_driver_unregister(&cxl_dax_kmem_region_driver);
> + cxl_driver_unregister(&cxl_sysram_region_driver);
> cxl_driver_unregister(&cxl_devdax_region_driver);
> cxl_driver_unregister(&cxl_region_driver);
> }
> diff --git a/drivers/cxl/core/sysram_region.c b/drivers/cxl/core/sysram_region.c
> new file mode 100644
> index 000000000000..5665db238d0f
> --- /dev/null
> +++ b/drivers/cxl/core/sysram_region.c
> @@ -0,0 +1,180 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2026 Meta Platforms, Inc. All rights reserved. */
> +/*
> + * CXL Sysram Region - Intermediate device for kmem hotplug configuration
> + *
> + * This provides an intermediate device between cxl_region and cxl_dax_region
> + * that allows users to configure memory hotplug parameters (like online_type)
> + * before the underlying dax_region is created and memory is hotplugged.
> + */
> +
> +#include <linux/memory_hotplug.h>
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +#include <cxlmem.h>
> +#include <cxl.h>
> +#include "core.h"
> +
> +static DEVICE_ATTR_RW(online_type);
> +
> +static struct attribute *cxl_sysram_region_attrs[] = {
> + &dev_attr_online_type.attr,
> + NULL,
As below.
> +};
> +
> +static const struct attribute_group cxl_sysram_region_attribute_group = {
> + .attrs = cxl_sysram_region_attrs,
> +};
> +
> +static const struct attribute_group *cxl_sysram_region_attribute_groups[] = {
> + &cxl_base_attribute_group,
> + &cxl_sysram_region_attribute_group,
> + NULL,
Trivial, but don't want a comma on that NULL.
> +};
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 674d5f870c70..1544c27e9c89 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -596,6 +596,25 @@ struct cxl_dax_region {
> enum dax_driver_type dax_driver;
> };
>
> +/**
> + * struct cxl_sysram_region - CXL RAM region for system memory hotplug
> + * @dev: device for this sysram_region
> + * @cxlr: parent cxl_region
> + * @hpa_range: Host physical address range for the region
> + * @online_type: Memory online type (MMOP_* 0-3, or -1 if not configured)
Ah. An there's our reason for an int. Can we just add a MMOP enum value
for not configured yet and so let us use it as an enum?
Or have a separate bool for that and ignore the online_type until it's set.
> + *
> + * Intermediate device that allows configuration of memory hotplug
> + * parameters before the underlying dax_region is created. The device
> + * starts with online_type=-1 which prevents the cxl_dax_kmem_region
> + * driver from binding until the user explicitly sets online_type.
> + */
> +struct cxl_sysram_region {
> + struct device dev;
> + struct cxl_region *cxlr;
> + struct range hpa_range;
> + int online_type;
> +};
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers
2026-02-02 18:20 ` Jonathan Cameron
@ 2026-02-02 18:23 ` Gregory Price
0 siblings, 0 replies; 30+ messages in thread
From: Gregory Price @ 2026-02-02 18:23 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-mm, linux-cxl, nvdimm, linux-fsdevel, linux-kernel,
linux-doc, kernel-team, dave, dave.jiang, alison.schofield,
vishal.l.verma, ira.weiny, dan.j.williams, willy, jack,
terry.bowman, john
On Mon, Feb 02, 2026 at 06:20:15PM +0000, Jonathan Cameron wrote:
> >
> > +/**
> > + * struct cxl_sysram_region - CXL RAM region for system memory hotplug
> > + * @dev: device for this sysram_region
> > + * @cxlr: parent cxl_region
> > + * @hpa_range: Host physical address range for the region
> > + * @online_type: Memory online type (MMOP_* 0-3, or -1 if not configured)
>
> Ah. An there's our reason for an int. Can we just add a MMOP enum value
> for not configured yet and so let us use it as an enum?
> Or have a separate bool for that and ignore the online_type until it's set.
>
I think the latter is more reasonably, MMOP_UNCONFIGURED doesn't much
make sense for memory_hotplug.c
ack.
~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 9/9] Documentation/driver-api/cxl: add dax and sysram driver documentation
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
` (7 preceding siblings ...)
2026-01-29 21:04 ` [PATCH 8/9] cxl/core: Add dax_kmem_region and sysram_region drivers Gregory Price
@ 2026-01-29 21:04 ` Gregory Price
2026-01-29 21:17 ` [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
2026-01-30 17:34 ` Gregory Price
10 siblings, 0 replies; 30+ messages in thread
From: Gregory Price @ 2026-01-29 21:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
Explain the binding process for sysram and daxdev regions which are
explicit about which dax driver to use during region creation.
Jonathan Corbet <corbet@lwn.net>
Signed-off-by: Gregory Price <gourry@gourry.net>
---
.../driver-api/cxl/linux/cxl-driver.rst | 43 +++++++++++++++++++
.../driver-api/cxl/linux/dax-driver.rst | 29 +++++++++++++
2 files changed, 72 insertions(+)
diff --git a/Documentation/driver-api/cxl/linux/cxl-driver.rst b/Documentation/driver-api/cxl/linux/cxl-driver.rst
index dd6dd17dc536..1f857345e896 100644
--- a/Documentation/driver-api/cxl/linux/cxl-driver.rst
+++ b/Documentation/driver-api/cxl/linux/cxl-driver.rst
@@ -445,6 +445,49 @@ for more details. ::
dax0.0 devtype modalias uevent
dax_region driver subsystem
+DAX regions are created when a CXL RAM region is bound to one of the
+following drivers:
+
+* :code:`cxl_devdax_region` - Creates a dax_region for device_dax mode.
+ The resulting DAX device provides direct userspace access via
+ :code:`/dev/daxN.Y`.
+
+* :code:`cxl_dax_kmem_region` - Creates a dax_region for kmem mode via a
+ sysram_region intermediate device. See `Sysram Region`_ below.
+
+Sysram Region
+~~~~~~~~~~~~~
+A `Sysram Region` is an intermediate device between a CXL `Memory Region`
+and a `DAX Region` for kmem mode. It is created when a CXL RAM region is
+bound to the :code:`cxl_sysram_region` driver.
+
+The sysram_region device provides an interposition point where users can
+configure memory hotplug policy before the underlying dax_region is created
+and memory is hotplugged to the system.
+
+The device hierarchy for kmem mode is::
+
+ regionX -> sysram_regionX -> dax_regionX -> daxX.Y
+
+The sysram_region exposes an :code:`online_type` attribute that controls
+how memory will be onlined when the dax_kmem driver binds:
+
+* :code:`invalid` - Not configured (default). Blocks driver binding.
+* :code:`offline` - Memory will not be onlined automatically.
+* :code:`online` - Memory will be onlined in ZONE_NORMAL.
+* :code:`online_movable` - Memory will be onlined in ZONE_MOVABLE.
+
+Example two-stage binding process::
+
+ # Bind region to sysram_region driver
+ echo region0 > /sys/bus/cxl/drivers/cxl_sysram_region/bind
+
+ # Configure memory online type
+ echo online_movable > /sys/bus/cxl/devices/sysram_region0/online_type
+
+ # Bind sysram_region to dax_kmem_region driver
+ echo sysram_region0 > /sys/bus/cxl/drivers/cxl_dax_kmem_region/bind
+
Mailbox Interfaces
------------------
A mailbox command interface for each device is exposed in ::
diff --git a/Documentation/driver-api/cxl/linux/dax-driver.rst b/Documentation/driver-api/cxl/linux/dax-driver.rst
index 10d953a2167b..2b8e21736292 100644
--- a/Documentation/driver-api/cxl/linux/dax-driver.rst
+++ b/Documentation/driver-api/cxl/linux/dax-driver.rst
@@ -17,6 +17,35 @@ The DAX subsystem exposes this ability through the `cxl_dax_region` driver.
A `dax_region` provides the translation between a CXL `memory_region` and
a `DAX Device`.
+CXL DAX Region Drivers
+======================
+CXL provides multiple drivers for creating DAX regions, each suited for
+different use cases:
+
+cxl_devdax_region
+-----------------
+The :code:`cxl_devdax_region` driver creates a dax_region configured for
+device_dax mode. When a CXL RAM region is bound to this driver, the
+resulting DAX device provides direct userspace access via :code:`/dev/daxN.Y`.
+
+Device hierarchy::
+
+ regionX -> dax_regionX -> daxX.Y
+
+This is the simplest path for applications that want to manage CXL memory
+directly from userspace.
+
+cxl_dax_kmem_region
+-------------------
+For kmem mode, CXL provides a two-stage binding process that allows users
+to configure memory hotplug policy before memory is added to the system.
+
+The :code:`cxl_dax_kmem_region` driver then binds a sysram_region
+device and creates a dax_region configured for kmem mode.
+
+The :code:`online_type` policy will be passed from sysram_region to
+the dax kmem driver for use when hotplugging the memory.
+
DAX Device
==========
A `DAX Device` is a file-like interface exposed in :code:`/dev/daxN.Y`. A
--
2.52.0
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 0/9] cxl: explicit DAX driver selection and hotplug
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
` (8 preceding siblings ...)
2026-01-29 21:04 ` [PATCH 9/9] Documentation/driver-api/cxl: add dax and sysram driver documentation Gregory Price
@ 2026-01-29 21:17 ` Gregory Price
2026-01-30 17:34 ` Gregory Price
10 siblings, 0 replies; 30+ messages in thread
From: Gregory Price @ 2026-01-29 21:17 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
Annoyingly, my email client has been truncating my titles:
cxl: explicit DAX driver selection and hotplug policy for CXL regions
~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 0/9] cxl: explicit DAX driver selection and hotplug
2026-01-29 21:04 [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
` (9 preceding siblings ...)
2026-01-29 21:17 ` [PATCH 0/9] cxl: explicit DAX driver selection and hotplug Gregory Price
@ 2026-01-30 17:34 ` Gregory Price
10 siblings, 0 replies; 30+ messages in thread
From: Gregory Price @ 2026-01-30 17:34 UTC (permalink / raw)
To: linux-mm
Cc: linux-cxl, nvdimm, linux-fsdevel, linux-kernel, linux-doc,
kernel-team, dave, jonathan.cameron, dave.jiang,
alison.schofield, vishal.l.verma, ira.weiny, dan.j.williams,
willy, jack, terry.bowman, john
On Thu, Jan 29, 2026 at 04:04:33PM -0500, Gregory Price wrote:
> Currently, CXL regions that create DAX devices have no mechanism to
> control select the hotplug online policy for kmem regions at region
> creation time. Users must either rely on a build-time default or
> manually configure each memory block after hotplug occurs.
>
Looks like build regression on configs without hotplug
MMOP_ defines and mhp_get_default_online_type() undefined
Will let this version sit for a bit before spinning a v2
~Gregory
^ permalink raw reply [flat|nested] 30+ messages in thread