Add the FUSE_BACKING_MAP_CYCLIC flag for FUSE_NOTIFY_BACKING_MAP
notifications to enable creating striped maps.

This is just a set of regular extents that repeats after the end of the
last extent using a striping pattern (i.e. on the target backings the
stripes have a contiguous footprint).

Two additional limits apply compared to non-cyclic extent map population:

 - the size of the extents (chunk size) must be equal

 - the extents must start from zero offset and follow each other without
   gaps.

Originally-by: John Groves <[email protected]>
Reviewed-by: Amir Goldstein <[email protected]>
Signed-off-by: Miklos Szeredi <[email protected]>
---
 fs/fuse/ext_map.c         | 30 +++++++++++++++++++++++++++---
 fs/fuse/fuse_i.h          |  1 +
 fs/fuse/notify.c          |  2 +-
 include/uapi/linux/fuse.h |  5 ++++-
 4 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/fs/fuse/ext_map.c b/fs/fuse/ext_map.c
index 4f418185c0b7..049e0b4514bc 100644
--- a/fs/fuse/ext_map.c
+++ b/fs/fuse/ext_map.c
@@ -109,12 +109,18 @@ static int fuse_ext_map_iomap_begin(struct inode *inode, 
loff_t offset, loff_t l
 {
        struct fuse_backing *fb = fuse_inode_backing(get_fuse_inode(inode));
        struct fuse_iext *fie;
+       u64 ncycle = 0, seq_off = 0, addr, tmp;
        loff_t ext_len;
 
        if (!fb || fb->type != FUSE_BACKING_EXTMAP)
                return fuse_EIO("missing or wrong type backing");
 
-       fie = fuse_find_extent(&fb->extents, offset);
+       if (fb->cycle_length) {
+               ncycle = div64_u64(offset, fb->cycle_length);
+               seq_off = ncycle * fb->cycle_length;
+       }
+
+       fie = fuse_find_extent(&fb->extents, offset - seq_off);
        if (!fie)
                return fuse_EIO("missing mapping");
 
@@ -127,9 +133,15 @@ static int fuse_ext_map_iomap_begin(struct inode *inode, 
loff_t offset, loff_t l
        }
 
        ext_len = fie->end - fie->start;
+       addr = fie->backing_offset;
+       if (check_mul_overflow(ncycle, ext_len, &tmp) || 
check_add_overflow(addr, tmp, &addr))
+               return fuse_EIO("extent address overflow");
 
-       iomap->offset = fie->start;
-       iomap->addr = fie->backing_offset;
+       if (check_add_overflow(addr, ext_len, &tmp))
+               return fuse_EIO("extent end overflow");
+
+       iomap->offset = fie->start + seq_off;
+       iomap->addr = addr;
        iomap->length = ext_len;
        iomap->dax_dev = fie->backing->dax_dev;
        iomap->type = IOMAP_MAPPED;
@@ -256,6 +268,7 @@ int fuse_ext_map_populate(struct fuse_conn *fc, struct 
fuse_notify_backing_map_o
        struct fuse_backing *fb;
        unsigned int i;
        int err;
+       u64 chunk_size = 0;
 
        if (!arg->num_extents)
                return fuse_EIO("no extents");
@@ -273,7 +286,18 @@ int fuse_ext_map_populate(struct fuse_conn *fc, struct 
fuse_notify_backing_map_o
                return -EINVAL;
        }
 
+       if (arg->flags & FUSE_BACKING_MAP_CYCLIC) {
+               chunk_size = ext[0].length;
+               err = -EINVAL;
+               if (check_mul_overflow(chunk_size, arg->num_extents, 
&fb->cycle_length))
+                       goto err_put;
+       }
+
        for (i = 0; i < arg->num_extents; i++) {
+               err = -EINVAL;
+               if (chunk_size && (ext[i].offset != chunk_size * i || 
ext[i].length != chunk_size))
+                       goto err_put;
+
                err = fuse_add_extent(fc, &fb->extents, &ext[i]);
                if (err)
                        goto err_put;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 5d38dc1b6f0c..cd10156e09d4 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -110,6 +110,7 @@ struct fuse_backing {
                };
                struct {
                        struct rb_root extents;
+                       u64 cycle_length;
                };
        };
        u64 backing_id;
diff --git a/fs/fuse/notify.c b/fs/fuse/notify.c
index 3bfedd0cdbdd..31172de72477 100644
--- a/fs/fuse/notify.c
+++ b/fs/fuse/notify.c
@@ -461,7 +461,7 @@ static int fuse_notify_map(struct fuse_conn *fc, unsigned 
int size,
        if (outarg.reserved[0] != 0 || outarg.reserved[1] != 0)
                return -EINVAL;
 
-       if (outarg.flags & ~FUSE_BACKING_MAP_CREATE)
+       if (outarg.flags & ~(FUSE_BACKING_MAP_CREATE | FUSE_BACKING_MAP_CYCLIC))
                return -EINVAL;
 
        if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 60060da98e8d..6b2fe68b4ab5 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -255,7 +255,8 @@
  *  - add FUSE_DEV_IOC_BACKING_CREATE, struct fuse_backing_create_in
  *  - add FUSE_NOTIFY_BACKING_REMOVE, struct fuse_notify_backing_remove_out
  *  - add backing_id_64 to fuse_open_out
- *  - add FUSE_NOTIFY_BACKING_MAP, fuse_notify_backing_map_out, fuse_extent, 
FUSE_BACKING_MAP_CREATE
+ *  - add FUSE_NOTIFY_BACKING_MAP, fuse_notify_backing_map_out, fuse_extent
+ *  - add FUSE_BACKING_MAP_CREATE, FUSE_BACKING_MAP_CYCLIC
  */
 
 #ifndef _LINUX_FUSE_H
@@ -1219,8 +1220,10 @@ struct fuse_notify_backing_remove_out {
  * notify_map flags
  *
  * FUSE_BACKING_MAP_CREATE:    create backing with the supplied ID
+ * FUSE_BACKING_MAP_CYCLIC:    map repeats after last extent
  */
 #define FUSE_BACKING_MAP_CREATE        (1 << 0)
+#define FUSE_BACKING_MAP_CYCLIC        (1 << 1)
 
 struct fuse_notify_backing_map_out {
        uint64_t        backing_id;
-- 
2.54.0


Reply via email to