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", &param.check_config, \
 #define CREATE_OPTIONS() \
 OPT_STRING('s', "size", &param.size, "size", "size to switch the device to"), \
 OPT_STRING('a', "align", &param.align, "align", "alignment to switch the 
device to"), \
-OPT_STRING('\0', "input", &param.input, "input", "input device JSON file")
+OPT_STRING('\0', "input", &param.input, "input", "input device JSON file"), \
+OPT_STRING('\0', "uuid", &param.uuid, "uuid", \
+       "claim sparse dax_resource(s) matching this uuid (\"0\" for untagged)")
 
 #define DESTROY_OPTIONS() \
 OPT_BOOLEAN('f', "force", &param.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


Reply via email to