On 5/23/26 2:50 AM, Anisa Su wrote: > 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 <[email protected]> > --- > 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; snprintf() returns negative errno, propogate > + } > + > + 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; propogate the errno from sysfs_write_attr() > + } > + > + /* > + * 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; propogate negative return value from snprintf() > + } > + if (sysfs_read_attr(ctx, path, buf) < 0) { > + err(ctx, "%s: failed to read back size\n", > + daxctl_dev_get_devname(dev)); > + return -ENXIO; propgate negative errno from sysfs_read_attr() > + } > + 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,

