On Mon, Jun 08, 2026 at 05:12:59PM -0700, Dave Jiang wrote:
>
>
> 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()
>
errno propagated. For snprintf, since it returns pos rc if output was
truncated:
rc = snprintf(path, len, "%s/uuid", dev->dev_path);
if (rc < 0)
return rc;
if (rc >= len) {
err(ctx, "%s: buffer too small!\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,
>