On Thu, Oct 1, 2026 at 5:09 PM Miklos Szeredi <[email protected]> wrote:
>
> This is only possible with FUSE_PASSTHROUGH_V2 enabled.
>
> Mark the inode with S_DAX if FUSE_LOOKUP returns with FUSE_ATTR_DAX set.
>
> This patch does not yet provide a way actually use the dax dev backing:
> when such a backing ID is provided in reply to FUSE_OPEN with
> FOPEN_PASSTHROUGH flag set, an error will be returned.
>
> Signed-off-by: Miklos Szeredi <[email protected]>
> ---
>  fs/fuse/backing.c     | 121 ++++++++++++++++++++++++++++++------------
>  fs/fuse/file.c        |   2 +-
>  fs/fuse/fuse_i.h      |  28 ++++++++--
>  fs/fuse/inode.c       |  11 +++-
>  fs/fuse/passthrough.c |   6 ++-
>  5 files changed, 126 insertions(+), 42 deletions(-)
>
> diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
> index 3c879df7989c..c852f0498961 100644
> --- a/fs/fuse/backing.c
> +++ b/fs/fuse/backing.c
> @@ -9,6 +9,7 @@
>  #include "fuse_i.h"
>
>  #include <linux/file.h>
> +#include <linux/dax.h>
>  #include <linux/rhashtable.h>
>
>  static struct fuse_backing *fuse_backing_get(struct fuse_backing *fb)
> @@ -22,9 +23,16 @@ static void fuse_backing_free(struct fuse_backing *fb)
>  {
>         pr_debug("%s: fb=0x%p\n", __func__, fb);
>
> -       if (fb->file)
> -               fput(fb->file);
> -       put_cred(fb->cred);
> +       switch (fb->type) {
> +       case FUSE_BACKING_PATH:
> +               path_put(&fb->path);
> +               put_cred(fb->cred);
> +               break;
> +
> +       case FUSE_BACKING_DAXDEV:
> +               fs_put_dax(fb->dax_dev, fb);
> +               break;
> +       }
>         kfree_rcu(fb, rcu);
>  }
>
> @@ -103,39 +111,83 @@ int fuse_backing_close_64(struct fuse_conn *fc, u64 
> backing_id)
>         return 0;
>  }
>
> -static struct fuse_backing *fuse_backing_new(struct fuse_conn *fc, int fd)
> +static int fuse_dax_notify_failure(struct dax_device *daxdev, u64 offset, 
> u64 len, int mf_flags)
>  {
> -       struct fuse_backing *fb;
> -       struct super_block *backing_sb;
> -       struct file *file;
> +       struct fuse_backing *fb = dax_holder(daxdev);
>
> -       /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
> -       if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
> -               return ERR_PTR(-EPERM);
> +       fb->dax_error = true;
>
> -       CLASS(fd_raw, f)(fd);
> -       if (fd_empty(f))
> -               return ERR_PTR(-EBADF);
> +       return 0;
> +}
>
> -       file = fd_file(f);
> +static const struct dax_holder_operations fuse_dax_holder_ops = {
> +       .notify_failure         = fuse_dax_notify_failure,
> +};
> +
> +static int fuse_backing_open_file(struct fuse_conn *fc, struct fuse_backing 
> *fb, struct file *file)
> +{
> +       struct inode *inode = file_inode(file);
> +       struct dax_device *daxdev;
> +       int err;
>
> -       /* read/write/splice/mmap passthrough only relevant for regular files 
> */
> -       if (!d_is_reg(file->f_path.dentry))
> -               return d_is_dir(file->f_path.dentry) ? ERR_PTR(-EISDIR) : 
> ERR_PTR(-EINVAL);
> +       switch (inode->i_mode & S_IFMT) {
> +       case S_IFREG:
> +               /* TODO: relax CAP_SYS_ADMIN once backing files are visible 
> to lsof */
> +               if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
> +                       return -EPERM;
> +
> +               if (inode->i_sb->s_stack_depth >= fc->max_stack_depth)
> +                       return -ELOOP;
> +
> +               fb->type = FUSE_BACKING_PATH;
> +               fb->path = file->f_path;
> +               path_get(&fb->path);
> +               fb->cred = get_current_cred();
> +               return 0;
> +
> +       case S_IFCHR:
> +               daxdev = dax_dev_find(inode->i_rdev);
> +               if (!daxdev)
> +                       return -EINVAL;
> +
> +               err = -EPERM;
> +               if (capable(CAP_SYS_RAWIO)) {
> +                       err = fs_dax_get(daxdev, fb, &fuse_dax_holder_ops);
> +                       if (!err) {
> +                               fb->type = FUSE_BACKING_DAXDEV;
> +                               fb->dax_dev = daxdev;
> +                       }
> +               }
> +               put_dax(daxdev);
> +               return err;
>
> -       backing_sb = file_inode(file)->i_sb;
> -       if (backing_sb->s_stack_depth >= fc->max_stack_depth)
> -               return ERR_PTR(-ELOOP);
> +       case S_IFDIR:
> +               return -EISDIR;
> +
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static struct fuse_backing *fuse_backing_new(struct fuse_conn *fc, int fd)
> +{
> +       struct fuse_backing *fb __free(kfree) = kmalloc_obj(*fb);
> +       int err;
>
> -       fb = kmalloc_obj(struct fuse_backing);
>         if (!fb)
>                 return ERR_PTR(-ENOMEM);
>
> -       fb->file = get_file(file);
> -       fb->cred = get_current_cred();
> +       CLASS(fd_raw, f)(fd);
> +       if (fd_empty(f))
> +               return ERR_PTR(-EBADF);
> +
> +       err = fuse_backing_open_file(fc, fb, fd_file(f));
> +       if (err)
> +               return ERR_PTR(err);
> +
>         refcount_set(&fb->count, 1);
>
> -       return fb;
> +       return_ptr(fb);
>  }
>
>  int fuse_backing_open_64(struct fuse_conn *fc, struct fuse_backing_create_in 
> *map)
> @@ -143,22 +195,21 @@ int fuse_backing_open_64(struct fuse_conn *fc, struct 
> fuse_backing_create_in *ma
>         struct fuse_backing *fb;
>         int res;
>
> -       res = -EINVAL;
>         if (map->padding || map->spare[0] || map->spare[1])
> -               goto out;
> +               return -EINVAL;
>
>         if (!fc->backing_id_64)
> -               goto out;
> +               return -EINVAL;
>
>         fb = fuse_backing_new(fc, map->fd);
> -       res = PTR_ERR(fb);
> -       if (!IS_ERR(fb)) {
> -               fb->backing_id = map->backing_id;
> -               res = fuse_backing_add_64(fc, fb);
> -               if (res < 0)
> -                       fuse_backing_free(fb);
> -       }
> -out:
> +       if (IS_ERR(fb))
> +               return PTR_ERR(fb);
> +
> +       fb->backing_id = map->backing_id;
> +       res = fuse_backing_add_64(fc, fb);
> +       if (res < 0)
> +               fuse_backing_free(fb);
> +
>         return res;
>  }
>
> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
> index 1036c06fe35a..fb8e0a9fa4f6 100644
> --- a/fs/fuse/file.c
> +++ b/fs/fuse/file.c
> @@ -297,7 +297,7 @@ static int fuse_open(struct inode *inode, struct file 
> *file)
>         if (!err) {
>                 if (is_truncate)
>                         truncate_pagecache(inode, 0);
> -               else if (!(ff->open_flags & FOPEN_KEEP_CACHE))
> +               else if (!(ff->open_flags & FOPEN_KEEP_CACHE) && 
> !IS_DAX(inode))
>                         invalidate_inode_pages2(inode->i_mapping);
>         }
>  out_unlock:
> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
> index 59afebc3dd2b..3e7ffb9bb2c3 100644
> --- a/fs/fuse/fuse_i.h
> +++ b/fs/fuse/fuse_i.h
> @@ -89,10 +89,25 @@ struct fuse_submount_lookup {
>         struct fuse_forget_link *forget;
>  };
>
> +enum fuse_backing_type {
> +       FUSE_BACKING_PATH,
> +       FUSE_BACKING_DAXDEV,
> +};
> +
>  /* Container for data related to mapping to backing file */
>  struct fuse_backing {
> -       struct file *file;
> -       const struct cred *cred;
> +       enum fuse_backing_type type;
> +
> +       union {
> +               struct {
> +                       struct path path;
> +                       const struct cred *cred;
> +               };
> +               struct {
> +                       struct dax_device *dax_dev;
> +                       bool dax_error;
> +               };
> +       };
>         u64 backing_id;
>         struct rhash_head hash_node;
>         /* refcount */
> @@ -1239,7 +1254,14 @@ void fuse_free_conn(struct fuse_conn *fc);
>
>  /* dax.c */
>
> -#define FUSE_IS_VDAX(inode) (IS_ENABLED(CONFIG_FUSE_VDAX) && IS_DAX(inode))
> +static inline bool FUSE_IS_VDAX(struct inode *inode)
> +{
> +#ifdef CONFIG_FUSE_VDAX
> +       return get_fuse_inode(inode)->vdax && IS_DAX(inode);
> +#else
> +       return false;
> +#endif
> +}
>
>  ssize_t fuse_vdax_read_iter(struct kiocb *iocb, struct iov_iter *to);
>  ssize_t fuse_vdax_write_iter(struct kiocb *iocb, struct iov_iter *from);
> diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
> index bb76bddfc241..d42c824fd6af 100644
> --- a/fs/fuse/inode.c
> +++ b/fs/fuse/inode.c
> @@ -148,7 +148,7 @@ static void fuse_evict_inode(struct inode *inode)
>         /* Will write inode on close/munmap and in all other dirtiers */
>         WARN_ON(inode_state_read_once(inode) & I_DIRTY_INODE);
>
> -       if (FUSE_IS_VDAX(inode))
> +       if (IS_DAX(inode))
>                 dax_break_layout_final(inode);
>
>         truncate_inode_pages_final(&inode->i_data);
> @@ -403,6 +403,10 @@ static void fuse_init_submount_lookup(struct 
> fuse_submount_lookup *sl,
>         refcount_set(&sl->count, 1);
>  }
>
> +static const struct address_space_operations fuse_dax_aops = {
> +       .dirty_folio    = noop_dirty_folio,
> +};
> +
>  static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr,
>                             struct fuse_conn *fc)
>  {
> @@ -430,6 +434,11 @@ static void fuse_init_inode(struct inode *inode, struct 
> fuse_attr *attr,
>          */
>         if (!fc->posix_acl)
>                 inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE;
> +
> +       if ((attr->flags & FUSE_ATTR_DAX) && !fc->vdax) {

This (fc->vdax) fails build without CONFIG_FUSE_DAX

> +               inode->i_flags |= S_DAX;
> +               inode->i_data.a_ops = &fuse_dax_aops;
> +       }
>  }
>
>  static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
> diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
> index 4894842ad6d0..e9ab1aea34e2 100644
> --- a/fs/fuse/passthrough.c
> +++ b/fs/fuse/passthrough.c
> @@ -156,9 +156,11 @@ 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_PATH)
> +               return fuse_EIO("invalid backing type");
> +
>         /* Allocate backing file per fuse file to store fuse path */
> -       backing_file = backing_file_open(file, file->f_flags,
> -                                        &fb->file->f_path, fb->cred);
> +       backing_file = backing_file_open(file, file->f_flags, &fb->path, 
> fb->cred);
>         if (IS_ERR(backing_file))
>                 return fuse_EIO("failed to open backing file (%ld)", 
> PTR_ERR(backing_file));
>
> --
> 2.54.0
>

Reply via email to