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 v3 6/9] fuse: add support for opening dax device as backing
Date: Tue, 6 Oct 2026 20:01:11 +0200 [thread overview]
Message-ID: <20261006180115.1425232-7-mszeredi@redhat.com> (raw)
In-Reply-To: <20261006180115.1425232-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.
Originally-by: John Groves <john@groves.net>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
---
fs/fuse/backing.c | 105 +++++++++++++++++++++++++++++++++---------
fs/fuse/file.c | 2 +-
fs/fuse/fuse_i.h | 33 +++++++++++--
fs/fuse/inode.c | 11 ++++-
fs/fuse/iomode.c | 10 ++--
fs/fuse/passthrough.c | 6 ++-
6 files changed, 134 insertions(+), 33 deletions(-)
diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
index bc40818778df..0ed850ddf8cd 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,90 @@ 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;
- /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
- if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
- return ERR_PTR(-EPERM);
+ guard(rcu)();
- CLASS(fd_raw, f)(fd);
- if (fd_empty(f))
- return ERR_PTR(-EBADF);
+ fb = dax_holder(daxdev);
+ if (fb)
+ fb->dax_error = true;
+
+ return 0;
+}
+
+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;
+
+ 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:
+ if (!fc->passthrough || !fc->backing_id_64)
+ return -EINVAL;
+
+ 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;
- file = fd_file(f);
+ case S_IFDIR:
+ return -EISDIR;
- /* 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);
+ default:
+ return -EINVAL;
+ }
+}
- backing_sb = file_inode(file)->i_sb;
- if (backing_sb->s_stack_depth >= fc->max_stack_depth)
- return ERR_PTR(-ELOOP);
+static struct fuse_backing *fuse_backing_new(struct fuse_conn *fc, int fd)
+{
+ struct fuse_backing *fb __free(kfree) = kzalloc_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)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 5273957f0399..e0d72b9d3c4a 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 f34607eda2c0..7acf860865de 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 */
@@ -1242,7 +1257,19 @@ 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_inode_vdax(struct inode *inode)
+{
+#ifdef CONFIG_FUSE_VDAX
+ return get_fuse_inode(inode)->vdax;
+#else
+ return false;
+#endif
+}
+
+static inline bool FUSE_IS_VDAX(struct inode *inode)
+{
+ return fuse_inode_vdax(inode) && IS_DAX(inode);
+}
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 b5b51865d59f..750e092c3971 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) && !fuse_inode_vdax(inode)) {
+ 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/iomode.c b/fs/fuse/iomode.c
index 8b4774c80b11..38afe1f238ef 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -230,10 +230,14 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
* Server is expected to use FOPEN_PASSTHROUGH for all opens of an inode
* which is already open for passthrough. Using incorrect open mode is
* a server mistake, which results in user visible failure of open()
- * with EIO error.
+ * with EIO error. Same with DAX inodes.
*/
- if (fuse_inode_backing(fi) && !(ff->open_flags & FOPEN_PASSTHROUGH))
- return fuse_EIO("FOPEN_PASSTHROUGH expected");
+ if (!(ff->open_flags & FOPEN_PASSTHROUGH)) {
+ if (fuse_inode_backing(fi))
+ return fuse_EIO("FOPEN_PASSTHROUGH expected");
+ if (IS_DAX(inode))
+ return fuse_EIO("DAX inode without FOPEN_PASSTHROUGH");
+ }
/*
* FOPEN_PARALLEL_DIRECT_WRITES requires FOPEN_DIRECT_IO.
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-06 18:01 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-06 18:01 [PATCH v3 0/9] fuse: DAX device based extent maps (famfs) Miklos Szeredi
2026-10-06 18:01 ` [PATCH v3 1/9] dax: replace exported dax_dev_get() with non-allocating dax_dev_find() Miklos Szeredi
2026-10-06 18:10 ` sashiko-bot
2026-10-06 18:01 ` [PATCH v3 2/9] dax: use READ_ONCE() in dax_holder() Miklos Szeredi
2026-10-06 18:13 ` sashiko-bot
2026-10-08 1:02 ` Alison Schofield
2026-10-08 6:58 ` Miklos Szeredi
2026-10-06 18:01 ` [PATCH v3 3/9] fuse: add helpers for EIO return value with kernel message Miklos Szeredi
2026-10-06 21:03 ` Amir Goldstein
2026-10-06 18:01 ` [PATCH v3 4/9] fuse: support 64 bit, server allocated backing ID Miklos Szeredi
2026-10-06 18:20 ` sashiko-bot
2026-10-06 20:10 ` John Groves
2026-10-07 12:49 ` Miklos Szeredi
2026-10-06 22:08 ` Amir Goldstein
2026-10-07 12:54 ` Miklos Szeredi
2026-10-06 18:01 ` [PATCH v3 5/9] fuse: support opening 64 bit " Miklos Szeredi
2026-10-06 18:01 ` Miklos Szeredi [this message]
2026-10-06 18:18 ` [PATCH v3 6/9] fuse: add support for opening dax device as backing sashiko-bot
2026-10-06 18:01 ` [PATCH v3 7/9] fuse: add extent map data structure Miklos Szeredi
2026-10-06 18:17 ` sashiko-bot
2026-10-06 18:01 ` [PATCH v3 8/9] fuse: add extent map I/O support Miklos Szeredi
2026-10-06 18:22 ` sashiko-bot
2026-10-06 18:01 ` [PATCH v3 9/9] fuse: add support for striped backing 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=20261006180115.1425232-7-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox