From: Miklos Szeredi <mszeredi@redhat.com>
To: fuse-devel@lists.linux.dev
Cc: John Groves <john@groves.net>,
Amir Goldstein <amir73il@gmail.com>,
"Darrick J . Wong" <djwong@kernel.org>,
Vishal Verma <vishal.l.verma@intel.com>,
Dave Jiang <dave.jiang@intel.com>,
Alison Schofield <alison.schofield@intel.com>,
nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org
Subject: [PATCH v2 5/8] fuse: add support for opening dax device as backing
Date: Thu, 1 Oct 2026 17:07:23 +0200 [thread overview]
Message-ID: <20261001150935.655979-6-mszeredi@redhat.com> (raw)
In-Reply-To: <20261001150935.655979-1-mszeredi@redhat.com>
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 <mszeredi@redhat.com>
---
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) {
+ 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
next prev parent reply other threads:[~2026-10-01 15:09 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-01 15:07 [PATCH v2 0/8] fuse: DAX device based extent maps (famfs) Miklos Szeredi
2026-10-01 15:07 ` [PATCH v2 1/8] dax: replace exported dax_dev_get() with non-allocating dax_dev_find() Miklos Szeredi
2026-10-01 15:07 ` [PATCH v2 2/8] fuse: add helpers for EIO return value with kernel message Miklos Szeredi
2026-10-01 15:18 ` sashiko-bot
2026-10-01 16:32 ` Amir Goldstein
2026-10-05 9:46 ` Miklos Szeredi
2026-10-01 15:07 ` [PATCH v2 3/8] fuse: support 64 bit, server allocated backing ID Miklos Szeredi
2026-10-01 15:26 ` sashiko-bot
2026-10-01 17:07 ` Amir Goldstein
2026-10-01 18:58 ` Amir Goldstein
2026-10-05 13:33 ` Miklos Szeredi
2026-10-06 21:24 ` Amir Goldstein
2026-10-07 12:46 ` Miklos Szeredi
2026-10-01 15:07 ` [PATCH v2 4/8] fuse: support opening 64 bit " Miklos Szeredi
2026-10-01 15:22 ` sashiko-bot
2026-10-01 17:09 ` Amir Goldstein
2026-10-01 15:07 ` Miklos Szeredi [this message]
2026-10-01 15:30 ` [PATCH v2 5/8] fuse: add support for opening dax device as backing sashiko-bot
2026-10-01 16:07 ` Amir Goldstein
2026-10-01 15:07 ` [PATCH v2 6/8] fuse: add extent map data structure Miklos Szeredi
2026-10-01 15:24 ` sashiko-bot
2026-10-01 15:07 ` [PATCH v2 7/8] fuse: add extent map I/O support Miklos Szeredi
2026-10-01 15:27 ` sashiko-bot
2026-10-01 16:11 ` Amir Goldstein
2026-10-01 15:07 ` [PATCH v2 8/8] fuse: add support for striped backing Miklos Szeredi
2026-10-05 23:27 ` [PATCH v2 0/8] fuse: DAX device based extent maps (famfs) John Groves
2026-10-06 9:48 ` Miklos Szeredi
2026-10-08 23:00 ` John Groves
2026-10-09 10:39 ` Miklos Szeredi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261001150935.655979-6-mszeredi@redhat.com \
--to=mszeredi@redhat.com \
--cc=alison.schofield@intel.com \
--cc=amir73il@gmail.com \
--cc=dave.jiang@intel.com \
--cc=djwong@kernel.org \
--cc=fuse-devel@lists.linux.dev \
--cc=john@groves.net \
--cc=linux-cxl@vger.kernel.org \
--cc=nvdimm@lists.linux.dev \
--cc=vishal.l.verma@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.