On Thu, Oct 1, 2026 at 5:09 PM Miklos Szeredi <[email protected]> wrote:
>
> Wire up read, write, splice and mmap operations for extent-mapped
> files through the iomap/DAX infrastructure.
>
> When a passthrough file is opened with an EXTMAP backing, I/O is
> dispatched to the dax devices referenced by the extent map.
>
> If an address is not mapped, EIO is returned on the I/O operation.
>
> Reviewed-by: Amir Goldstein <[email protected]>
> Signed-off-by: Miklos Szeredi <[email protected]>
> ---
>  fs/fuse/backing.c     |  15 +++++
>  fs/fuse/ext_map.c     | 149 ++++++++++++++++++++++++++++++++++++++++++
>  fs/fuse/fuse_i.h      |   4 ++
>  fs/fuse/notify.c      |   3 +
>  fs/fuse/passthrough.c |  29 ++++++--
>  5 files changed, 194 insertions(+), 6 deletions(-)
>
> diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
> index e954391d696d..2708f140b94f 100644
> --- a/fs/fuse/backing.c
> +++ b/fs/fuse/backing.c
> @@ -278,6 +278,21 @@ int fuse_backing_close(struct fuse_conn *fc, int 
> backing_id)
>         return err;
>  }
>
> +bool fuse_backing_is_dax(struct fuse_backing *fb)
> +{
> +       switch (fb->type) {
> +       case FUSE_BACKING_PATH:
> +               return false;
> +       case FUSE_BACKING_DAXDEV:
> +               return true;
> +       case FUSE_BACKING_EXTMAP:
> +               return fuse_ext_map_is_dax(fb);
> +       default:
> +               WARN_ON(1);
> +               return false;
> +       }
> +}
> +
>  struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, u64 
> backing_id)
>  {
>         struct fuse_backing *fb;
> diff --git a/fs/fuse/ext_map.c b/fs/fuse/ext_map.c
> index ab01d678d08e..61cb22e821b6 100644
> --- a/fs/fuse/ext_map.c
> +++ b/fs/fuse/ext_map.c
> @@ -3,6 +3,8 @@
>  #include "fuse_i.h"
>  #include <linux/rbtree.h>
>  #include <linux/pagemap.h>
> +#include <linux/iomap.h>
> +#include <linux/dax.h>
>
>  struct fuse_iext {
>         struct rb_node rb;
> @@ -22,6 +24,24 @@ void fuse_ext_map_destroy(struct rb_root *extents)
>         }
>  }
>
> +static struct fuse_iext *fuse_find_extent(struct rb_root *extents, u64 
> offset)
> +{
> +       struct rb_node *node = extents->rb_node;
> +
> +       while (node) {
> +               struct fuse_iext *fie = rb_entry(node, typeof(*fie), rb);
> +
> +               if (offset < fie->start)
> +                       node = node->rb_left;
> +               else if (offset >= fie->end)
> +                       node = node->rb_right;
> +               else
> +                       return fie;
> +       }
> +
> +       return NULL;
> +}
> +
>  static int fuse_add_extent(struct fuse_conn *fc, struct rb_root *extents,
>                            struct fuse_extent *ext)
>  {
> @@ -76,6 +96,135 @@ static int fuse_add_extent(struct fuse_conn *fc, struct 
> rb_root *extents,
>         return 0;
>  }
>
> +static int fuse_ext_map_iomap_begin(struct inode *inode, loff_t offset, 
> loff_t length,
> +                                   unsigned int flags, struct iomap *iomap, 
> struct iomap *srcmap)
> +{
> +       struct fuse_backing *fb = fuse_inode_backing(get_fuse_inode(inode));
> +       struct fuse_iext *fie;
> +       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 (!fie)
> +               return fuse_EIO("missing mapping");
> +
> +       if (WARN_ON(fie->backing->type != FUSE_BACKING_DAXDEV))
> +               return fuse_EIO("wrong type backing for extent");
> +
> +       if (fie->backing->dax_error) {
> +               fuse_make_bad(inode);
> +               return fuse_EIO("dax error");
> +       }
> +
> +       ext_len = fie->end - fie->start;
> +
> +       iomap->offset = fie->start;
> +       iomap->addr = fie->backing_offset;
> +       iomap->length = ext_len;
> +       iomap->dax_dev = fie->backing->dax_dev;
> +       iomap->type = IOMAP_MAPPED;
> +       iomap->flags = 0;
> +
> +       return 0;
> +}
> +
> +static const struct iomap_ops fuse_ext_map_iomap_ops = {
> +       .iomap_begin            = fuse_ext_map_iomap_begin,
> +};
> +
> +static vm_fault_t fuse_ext_map_huge_fault(struct vm_fault *vmf, unsigned int 
> order)
> +{
> +       struct inode *inode = file_inode(vmf->vma->vm_file);
> +       bool write_fault = (vmf->flags & FAULT_FLAG_WRITE) && 
> (vmf->vma->vm_flags & VM_SHARED);
> +       vm_fault_t ret;
> +       unsigned long pfn;
> +
> +       if (WARN_ON_ONCE(!IS_DAX(inode)))
> +               return VM_FAULT_SIGBUS;
> +
> +       if (write_fault) {
> +               sb_start_pagefault(inode->i_sb);
> +               file_update_time(vmf->vma->vm_file);
> +       }
> +
> +       filemap_invalidate_lock_shared(inode->i_mapping);
> +
> +       ret = dax_iomap_fault(vmf, order, &pfn, NULL, 
> &fuse_ext_map_iomap_ops);
> +       if (ret & VM_FAULT_NEEDDSYNC)
> +               ret = dax_finish_sync_fault(vmf, order, pfn);
> +
> +       filemap_invalidate_unlock_shared(inode->i_mapping);
> +
> +       if (write_fault)
> +               sb_end_pagefault(inode->i_sb);
> +
> +       return ret;
> +}
> +
> +static vm_fault_t fuse_ext_map_fault(struct vm_fault *vmf)
> +{
> +       return fuse_ext_map_huge_fault(vmf, 0);
> +}
> +
> +static const struct vm_operations_struct fuse_ext_map_vm_ops = {
> +       .fault          = fuse_ext_map_fault,
> +       .huge_fault     = fuse_ext_map_huge_fault,
> +       .page_mkwrite   = fuse_ext_map_fault,
> +       .pfn_mkwrite    = fuse_ext_map_fault,
> +};
> +
> +static void fuse_rw_clamp(struct kiocb *iocb, struct iov_iter *ubuf)
> +{
> +       struct inode *inode = iocb->ki_filp->f_mapping->host;
> +       loff_t i_size = i_size_read(inode);
> +       loff_t max_count = iocb->ki_pos >= i_size ? 0 : i_size - iocb->ki_pos;
> +
> +       if (iov_iter_count(ubuf) > max_count)
> +               iov_iter_truncate(ubuf, max_count);
> +}
> +
> +ssize_t fuse_ext_map_read_iter(struct kiocb *iocb, struct iov_iter *to)
> +{
> +       ssize_t res;
> +
> +       fuse_rw_clamp(iocb, to);
> +
> +       if (!iov_iter_count(to))
> +               return 0;
> +
> +       res = dax_iomap_rw(iocb, to, &fuse_ext_map_iomap_ops);

These fail link without CONFIG_FS_DAX
Because this file build does not depend on CONFIG_FUSE_DAX
Probably simplest to #define dax_iomap_rw() (-EIO)
locally in this case

> +
> +       file_accessed(iocb->ki_filp);
> +       return res;
> +}
> +
> +ssize_t fuse_ext_map_write_iter(struct kiocb *iocb, struct iov_iter *from)
> +{
> +       ssize_t res;
> +
> +       fuse_rw_clamp(iocb, from);
> +
> +       res = generic_write_checks(iocb, from);
> +       if (res <= 0)
> +               return res;
> +
> +       res = kiocb_modified(iocb);
> +       if (res)
> +               return res;
> +
> +       return dax_iomap_rw(iocb, from, &fuse_ext_map_iomap_ops);
> +}
> +
> +int fuse_ext_map_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +       file_accessed(file);
> +       vma->vm_ops = &fuse_ext_map_vm_ops;
> +       vm_flags_set(vma, VM_HUGEPAGE);
> +       return 0;
> +}
> +
>  bool fuse_ext_map_is_dax(struct fuse_backing *fb)
>  {
>         struct fuse_iext *fie;
> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
> index 5a20f3863ee2..7248588b45c4 100644
> --- a/fs/fuse/fuse_i.h
> +++ b/fs/fuse/fuse_i.h
> @@ -1316,6 +1316,7 @@ static inline void fuse_backing_put(struct fuse_backing 
> *fb)
>
>  struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, u64 
> backing_id);
>  int fuse_backing_add_64(struct fuse_conn *fc, struct fuse_backing *fb);
> +bool fuse_backing_is_dax(struct fuse_backing *fb);
>  void fuse_backing_files_init(struct fuse_conn *fc);
>  void fuse_backing_files_init_64(struct fuse_conn *fc);
>  void fuse_backing_files_free(struct fuse_conn *fc);
> @@ -1369,6 +1370,9 @@ extern void fuse_sysctl_unregister(void);
>  /* ext_map.c */
>
>  void fuse_ext_map_destroy(struct rb_root *extents);
> +ssize_t fuse_ext_map_write_iter(struct kiocb *iocb, struct iov_iter *from);
> +ssize_t fuse_ext_map_read_iter(struct kiocb *iocb, struct iov_iter *to);
> +int fuse_ext_map_mmap(struct file *file, struct vm_area_struct *vma);
>  bool fuse_ext_map_is_dax(struct fuse_backing *fb);
>  int fuse_ext_map_populate(struct fuse_conn *fc, struct 
> fuse_notify_backing_map_out *arg,
>                           struct fuse_extent *ext);
> diff --git a/fs/fuse/notify.c b/fs/fuse/notify.c
> index 7c427f88bbc5..3bfedd0cdbdd 100644
> --- a/fs/fuse/notify.c
> +++ b/fs/fuse/notify.c
> @@ -448,6 +448,9 @@ static int fuse_notify_map(struct fuse_conn *fc, unsigned 
> int size,
>         if (err)
>                 return err;
>
> +       if (!fc->backing_id_64)
> +               return -EINVAL;
> +
>         if (outarg.num_extents > FUSE_MAX_EXTENTS)
>                 return -EINVAL;
>
> diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
> index e9ab1aea34e2..040817ad76e9 100644
> --- a/fs/fuse/passthrough.c
> +++ b/fs/fuse/passthrough.c
> @@ -35,7 +35,6 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, 
> struct iov_iter *iter)
>         struct fuse_file *ff = file->private_data;
>         struct file *backing_file = fuse_file_passthrough(ff);
>         size_t count = iov_iter_count(iter);
> -       ssize_t ret;
>         struct backing_file_ctx ctx = {
>                 .cred = ff->cred,
>                 .accessed = fuse_file_accessed,
> @@ -48,10 +47,10 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, 
> struct iov_iter *iter)
>         if (!count)
>                 return 0;
>
> -       ret = backing_file_read_iter(backing_file, iter, iocb, iocb->ki_flags,
> -                                    &ctx);
> +       if (!backing_file)
> +               return fuse_ext_map_read_iter(iocb, iter);
>
> -       return ret;
> +       return backing_file_read_iter(backing_file, iter, iocb, 
> iocb->ki_flags, &ctx);
>  }
>
>  ssize_t fuse_passthrough_write_iter(struct kiocb *iocb,
> @@ -74,10 +73,13 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb,
>         if (!count)
>                 return 0;
>
> -       inode_lock(inode);
> +       guard(rwsem_write)(&inode->i_rwsem);
> +
> +       if (!backing_file)
> +               return fuse_ext_map_write_iter(iocb, iter);
> +
>         ret = backing_file_write_iter(backing_file, iter, iocb, 
> iocb->ki_flags,
>                                       &ctx);
> -       inode_unlock(inode);
>
>         return ret;
>  }
> @@ -98,6 +100,9 @@ ssize_t fuse_passthrough_splice_read(struct file *in, 
> loff_t *ppos,
>         pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", 
> __func__,
>                  backing_file, *ppos, len, flags);
>
> +       if (!backing_file)
> +               return copy_splice_read(in, ppos, pipe, len, flags);
> +
>         init_sync_kiocb(&iocb, in);
>         iocb.ki_pos = *ppos;
>         ret = backing_file_splice_read(backing_file, &iocb, pipe, len, flags, 
> &ctx);
> @@ -123,6 +128,9 @@ ssize_t fuse_passthrough_splice_write(struct 
> pipe_inode_info *pipe,
>         pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", 
> __func__,
>                  backing_file, *ppos, len, flags);
>
> +       if (!backing_file)
> +               return iter_file_splice_write(pipe, out, ppos, len, flags);
> +
>         inode_lock(inode);
>         init_sync_kiocb(&iocb, out);
>         iocb.ki_pos = *ppos;
> @@ -145,6 +153,9 @@ ssize_t fuse_passthrough_mmap(struct file *file, struct 
> vm_area_struct *vma)
>         pr_debug("%s: backing_file=0x%p, start=%lu, end=%lu\n", __func__,
>                  backing_file, vma->vm_start, vma->vm_end);
>
> +       if (!backing_file)
> +               return fuse_ext_map_mmap(file, vma);
> +
>         return backing_file_mmap(backing_file, vma, &ctx);
>  }
>
> @@ -156,6 +167,12 @@ int fuse_passthrough_open(struct file *file, struct 
> fuse_backing *fb)
>         struct fuse_file *ff = file->private_data;
>         struct file *backing_file;
>
> +       if (fb->type == FUSE_BACKING_EXTMAP) {
> +               if (fuse_backing_is_dax(fb) != !!IS_DAX(file_inode(file)))
> +                       return fuse_EIO("dax mode mismatch");
> +               return 0;
> +       }
> +
>         if (fb->type != FUSE_BACKING_PATH)
>                 return fuse_EIO("invalid backing type");
>
> --
> 2.54.0
>

Reply via email to