[PATCH v6 6/7] daxctl: Add --uuid option to create-device for sparse regions
From: Anisa Su
Date: Sat May 23 2026 - 05:57:29 EST
Add a --uuid option to 'daxctl create-device' that writes the given
uuid to the new dax device's sysfs 'uuid' attribute. On sparse (DCD)
regions this claims dax_resources whose tag matches and populates the
seed device with their capacity; size is determined by the claim, so
--uuid is mutually exclusive with --size.
Pass "0" to claim a single untagged dax_resource. A claim that
matches no dax_resource leaves the device at size 0; the kernel
returns ENOENT.
Plumb the write through a new daxctl_dev_set_uuid() libdaxctl helper
(LIBDAXCTL_11) and document the option in the man page.
Signed-off-by: Anisa Su <anisa.su887@xxxxxxxxx>
---
Documentation/daxctl/daxctl-create-device.txt | 12 ++++
daxctl/device.c | 72 +++++++++++++------
daxctl/lib/libdaxctl.c | 44 ++++++++++++
daxctl/lib/libdaxctl.sym | 5 ++
daxctl/libdaxctl.h | 1 +
5 files changed, 114 insertions(+), 20 deletions(-)
diff --git a/Documentation/daxctl/daxctl-create-device.txt b/Documentation/daxctl/daxctl-create-device.txt
index b774b86..27b87d0 100644
--- a/Documentation/daxctl/daxctl-create-device.txt
+++ b/Documentation/daxctl/daxctl-create-device.txt
@@ -82,6 +82,18 @@ include::region-option.txt[]
The size must be a multiple of the region alignment.
+ Mutually exclusive with --uuid.
+
+--uuid=::
+ For dax devices on sparse (DCD) regions, claim dax_resource(s) whose
+ tag matches the given UUID. The device's size is determined by the
+ claimed capacity, so --uuid cannot be combined with --size.
+
+ A non-null UUID claims every matching dax_resource in the parent
+ region. The value "0" is shorthand for the null UUID and claims a
+ single untagged dax_resource. A write that matches no dax_resource
+ fails with ENOENT and the device is left at size 0.
+
-a::
--align::
Applications that want to establish dax memory mappings with
diff --git a/daxctl/device.c b/daxctl/device.c
index a4e36b1..21a941e 100644
--- a/daxctl/device.c
+++ b/daxctl/device.c
@@ -30,6 +30,7 @@ static struct {
const char *size;
const char *align;
const char *input;
+ const char *uuid;
bool check_config;
bool no_online;
bool no_movable;
@@ -85,7 +86,9 @@ OPT_BOOLEAN('C', "check-config", ¶m.check_config, \
#define CREATE_OPTIONS() \
OPT_STRING('s', "size", ¶m.size, "size", "size to switch the device to"), \
OPT_STRING('a', "align", ¶m.align, "align", "alignment to switch the device to"), \
-OPT_STRING('\0', "input", ¶m.input, "input", "input device JSON file")
+OPT_STRING('\0', "input", ¶m.input, "input", "input device JSON file"), \
+OPT_STRING('\0', "uuid", ¶m.uuid, "uuid", \
+ "claim sparse dax_resource(s) matching this uuid (\"0\" for untagged)")
#define DESTROY_OPTIONS() \
OPT_BOOLEAN('f', "force", ¶m.force, \
@@ -808,6 +811,22 @@ static int do_create(struct daxctl_region *region, long long val,
struct daxctl_dev *dev;
int i, rc = 0;
long long alloc = 0;
+ uuid_t uuid;
+
+ if (param.uuid) {
+ if (param.size) {
+ fprintf(stderr,
+ "--uuid and --size are mutually exclusive\n");
+ return -EINVAL;
+ }
+ if (strcmp(param.uuid, "0") == 0) {
+ uuid_clear(uuid);
+ } else if (uuid_parse(param.uuid, uuid) < 0) {
+ fprintf(stderr, "failed to parse uuid '%s'\n",
+ param.uuid);
+ return -EINVAL;
+ }
+ }
if (daxctl_region_create_dev(region))
return -ENOSPC;
@@ -816,33 +835,46 @@ static int do_create(struct daxctl_region *region, long long val,
if (!dev)
return -ENOSPC;
- if (val == -1)
- val = daxctl_region_get_available_size(region);
-
- if (val <= 0)
- return -ENOSPC;
-
if (align > 0) {
rc = daxctl_dev_set_align(dev, align);
if (rc < 0)
return rc;
}
- /* @maps is ordered by page_offset */
- for (i = 0; i < nmaps; i++) {
- rc = daxctl_dev_set_mapping(dev, maps[i].start, maps[i].end);
- if (rc < 0)
+ if (param.uuid) {
+ rc = daxctl_dev_set_uuid(dev, uuid);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s: failed to claim uuid '%s': %s\n",
+ daxctl_dev_get_devname(dev), param.uuid,
+ strerror(-rc));
return rc;
- alloc += (maps[i].end - maps[i].start + 1);
- }
-
- if (nmaps > 0 && val > 0 && alloc != val) {
- fprintf(stderr, "%s: allocated %lld but specified size %lld\n",
- daxctl_dev_get_devname(dev), alloc, val);
+ }
} else {
- rc = daxctl_dev_set_size(dev, val);
- if (rc < 0)
- return rc;
+ if (val == -1)
+ val = daxctl_region_get_available_size(region);
+
+ if (val <= 0)
+ return -ENOSPC;
+
+ /* @maps is ordered by page_offset */
+ for (i = 0; i < nmaps; i++) {
+ rc = daxctl_dev_set_mapping(dev, maps[i].start,
+ maps[i].end);
+ if (rc < 0)
+ return rc;
+ alloc += (maps[i].end - maps[i].start + 1);
+ }
+
+ if (nmaps > 0 && val > 0 && alloc != val) {
+ fprintf(stderr,
+ "%s: allocated %lld but specified size %lld\n",
+ daxctl_dev_get_devname(dev), alloc, val);
+ } else {
+ rc = daxctl_dev_set_size(dev, val);
+ if (rc < 0)
+ return rc;
+ }
}
rc = daxctl_dev_enable_devdax(dev);
diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c
index 02ae7e5..fe07939 100644
--- a/daxctl/lib/libdaxctl.c
+++ b/daxctl/lib/libdaxctl.c
@@ -1107,6 +1107,50 @@ DAXCTL_EXPORT int daxctl_dev_set_size(struct daxctl_dev *dev, unsigned long long
return 0;
}
+DAXCTL_EXPORT int daxctl_dev_set_uuid(struct daxctl_dev *dev, uuid_t uuid)
+{
+ struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev);
+ char buf[SYSFS_ATTR_SIZE];
+ char *path = dev->dev_buf;
+ int len = dev->buf_len;
+
+ if (snprintf(path, len, "%s/uuid", dev->dev_path) >= len) {
+ err(ctx, "%s: buffer too small!\n",
+ daxctl_dev_get_devname(dev));
+ return -ENXIO;
+ }
+
+ if (uuid_is_null(uuid))
+ sprintf(buf, "0\n");
+ else
+ uuid_unparse(uuid, buf);
+
+ if (sysfs_write_attr(ctx, path, buf) < 0) {
+ err(ctx, "%s: failed to set uuid\n",
+ daxctl_dev_get_devname(dev));
+ return -ENXIO;
+ }
+
+ /*
+ * On a sparse region the kernel populates the device size as a
+ * side effect of claiming the matching dax_resource(s); refresh
+ * the cached size so callers see the post-claim value.
+ */
+ if (snprintf(path, len, "%s/size", dev->dev_path) >= len) {
+ err(ctx, "%s: buffer too small!\n",
+ daxctl_dev_get_devname(dev));
+ return -ENXIO;
+ }
+ if (sysfs_read_attr(ctx, path, buf) < 0) {
+ err(ctx, "%s: failed to read back size\n",
+ daxctl_dev_get_devname(dev));
+ return -ENXIO;
+ }
+ dev->size = strtoull(buf, NULL, 0);
+
+ return 0;
+}
+
DAXCTL_EXPORT unsigned long daxctl_dev_get_align(struct daxctl_dev *dev)
{
return dev->align;
diff --git a/daxctl/lib/libdaxctl.sym b/daxctl/lib/libdaxctl.sym
index 3098811..16792eb 100644
--- a/daxctl/lib/libdaxctl.sym
+++ b/daxctl/lib/libdaxctl.sym
@@ -104,3 +104,8 @@ LIBDAXCTL_10 {
global:
daxctl_dev_is_system_ram_capable;
} LIBDAXCTL_9;
+
+LIBDAXCTL_11 {
+global:
+ daxctl_dev_set_uuid;
+} LIBDAXCTL_10;
diff --git a/daxctl/libdaxctl.h b/daxctl/libdaxctl.h
index 53c6bbd..cdd5995 100644
--- a/daxctl/libdaxctl.h
+++ b/daxctl/libdaxctl.h
@@ -63,6 +63,7 @@ int daxctl_dev_get_minor(struct daxctl_dev *dev);
unsigned long long daxctl_dev_get_resource(struct daxctl_dev *dev);
unsigned long long daxctl_dev_get_size(struct daxctl_dev *dev);
int daxctl_dev_set_size(struct daxctl_dev *dev, unsigned long long size);
+int daxctl_dev_set_uuid(struct daxctl_dev *dev, uuid_t uuid);
unsigned long daxctl_dev_get_align(struct daxctl_dev *dev);
int daxctl_dev_set_align(struct daxctl_dev *dev, unsigned long align);
int daxctl_dev_set_mapping(struct daxctl_dev *dev, unsigned long long start,
--
2.43.0