summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2026-09-21 13:12:23 +0200
committerMiklos Szeredi <mszeredi@redhat.com>2026-10-19 14:56:24 +0200
commitdfc68b6c99f094e06f720b57cf4ee3d0ae5dbe8f (patch)
treeec94e94d3b83417611bd9a3b7ae03f61c61f0b4d
parente8bc74b6ed1b107f67ee7c362e9f6c1b035e2fe9 (diff)
downloadlinux-next-dfc68b6c99f094e06f720b57cf4ee3d0ae5dbe8f.tar.gz
linux-next-dfc68b6c99f094e06f720b57cf4ee3d0ae5dbe8f.zip
fuse: add support for striped backing
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 <john@groves.net> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/fuse/ext_map.c30
-rw-r--r--fs/fuse/fuse_i.h1
-rw-r--r--fs/fuse/notify.c2
-rw-r--r--include/uapi/linux/fuse.h5
4 files changed, 33 insertions, 5 deletions
diff --git a/fs/fuse/ext_map.c b/fs/fuse/ext_map.c
index 744940cbf36e..7adb78cf552d 100644
--- a/fs/fuse/ext_map.c
+++ b/fs/fuse/ext_map.c
@@ -103,12 +103,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");
@@ -121,9 +127,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;
@@ -259,6 +271,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");
@@ -276,7 +289,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 8419a4d3451b..4a63268c4b25 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 90d1f5c053e6..984e4945e3e3 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 43a92d834cd3..616430a2973c 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;