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 3/9] fuse: add helpers for EIO return value with kernel message
Date: Tue, 6 Oct 2026 20:01:08 +0200 [thread overview]
Message-ID: <20261006180115.1425232-4-mszeredi@redhat.com> (raw)
In-Reply-To: <20261006180115.1425232-1-mszeredi@redhat.com>
Conditions are triggered by a buggy fuse server will result in -EIO which
is hard to interpret without any context.
Add helpers that print a short message to the kernel log as well as
returning the error value. This serves a dual purpose:
- documents the error condition inside the code
- allows the implementor of the fuse server to get the context for the
error
Some debug messages are removed in favor of this.
This also changes the mmap return value from -ETXTBSY to -ENODEV if a
passthrough open raced with the prior check. This makes both cases return
-ENODEV if the file is in passthrough mode.
Additionally change the return value of fuse_file_cached_io_open() from an
error value to a bool (true on success) as the error value is translated
anyway.
Currently these use the pr_notice_once() variant. Possibly should be
changed to a ratelimit of e.g. once per minute.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
---
fs/fuse/file.c | 6 ++--
fs/fuse/fuse_i.h | 4 ++-
fs/fuse/iomode.c | 75 ++++++++++++++++++-------------------------
fs/fuse/passthrough.c | 19 ++++-------
4 files changed, 43 insertions(+), 61 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 6d707f2b3bff..bde07948d1f0 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -2415,7 +2415,6 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = ff->fm->fc;
struct inode *inode = file_inode(file);
- int rc;
/* DAX mmap is superior to direct_io mmap */
if (FUSE_IS_VDAX(inode))
@@ -2457,9 +2456,8 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
* After first mmap, the inode stays in caching io mode until
* the direct_io file release.
*/
- rc = fuse_file_cached_io_open(inode, ff);
- if (rc)
- return rc;
+ if (!fuse_file_cached_io_open(inode, ff))
+ return -ENODEV;
}
if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 8546855386b5..d9ec88028d7b 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -11,6 +11,8 @@
# define pr_fmt(fmt) "fuse: " fmt
#endif
+#define fuse_EIO(fmt, ...) (pr_notice_once("%s: " fmt "\n", __func__, ##__VA_ARGS__), -EIO)
+
#include "args.h"
#include <linux/fuse.h>
#include <linux/fs.h>
@@ -1251,7 +1253,7 @@ int fuse_fileattr_set(struct mnt_idmap *idmap,
struct dentry *dentry, struct file_kattr *fa);
/* iomode.c */
-int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff);
+bool fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff);
int fuse_inode_uncached_io_start(struct fuse_inode *fi,
struct fuse_backing *fb);
void fuse_inode_uncached_io_end(struct fuse_inode *fi);
diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c
index 79637c09e883..1b8b267d0c0d 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -27,15 +27,15 @@ static inline bool fuse_is_io_cache_wait(struct fuse_inode *fi)
* Blocks new parallel dio writes and waits for the in-progress parallel dio
* writes to complete.
*/
-int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff)
+bool fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff)
{
struct fuse_inode *fi = get_fuse_inode(inode);
/* There are no io modes if server does not implement open */
if (!ff->args)
- return 0;
+ return true;
- spin_lock(&fi->lock);
+ guard(spinlock)(&fi->lock);
/*
* Setting the bit advises new direct-io writes to use an exclusive
* lock - without it the wait below might be forever.
@@ -53,8 +53,7 @@ int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff)
*/
if (fuse_inode_backing(fi)) {
clear_bit(FUSE_I_CACHE_IO_MODE, &fi->state);
- spin_unlock(&fi->lock);
- return -ETXTBSY;
+ return false;
}
WARN_ON(ff->iomode == IOM_UNCACHED);
@@ -64,8 +63,7 @@ int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff)
set_bit(FUSE_I_CACHE_IO_MODE, &fi->state);
fi->iocachectr++;
}
- spin_unlock(&fi->lock);
- return 0;
+ return true;
}
static void fuse_file_cached_io_release(struct fuse_file *ff,
@@ -85,19 +83,16 @@ static void fuse_file_cached_io_release(struct fuse_file *ff,
int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb)
{
struct fuse_backing *oldfb;
- int err = 0;
- spin_lock(&fi->lock);
+ guard(spinlock)(&fi->lock);
/* deny conflicting backing files on same fuse inode */
oldfb = fuse_inode_backing(fi);
- if (fb && oldfb && oldfb != fb) {
- err = -EBUSY;
- goto unlock;
- }
- if (fi->iocachectr > 0) {
- err = -ETXTBSY;
- goto unlock;
- }
+ if (fb && oldfb && oldfb != fb)
+ return -EBUSY;
+
+ if (fi->iocachectr > 0)
+ return -ETXTBSY;
+
fi->iocachectr--;
/* fuse inode holds a single refcount of backing file */
@@ -107,9 +102,7 @@ int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb)
} else {
fuse_backing_put(fb);
}
-unlock:
- spin_unlock(&fi->lock);
- return err;
+ return 0;
}
/* Takes uncached_io inode mode reference to be dropped on file release */
@@ -121,8 +114,11 @@ static int fuse_file_uncached_io_open(struct inode *inode,
int err;
err = fuse_inode_uncached_io_start(fi, fb);
- if (err)
- return err;
+ if (err) {
+ if (err == -EBUSY)
+ return fuse_EIO("mismatched backing");
+ return fuse_EIO("conflicting caching mode");
+ }
WARN_ON(ff->iomode != IOM_NONE);
ff->iomode = IOM_UNCACHED;
@@ -173,9 +169,11 @@ static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
int err;
/* Check allowed conditions for file open in passthrough mode */
- if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough ||
- (ff->open_flags & ~FOPEN_PASSTHROUGH_MASK))
- return -EINVAL;
+ if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough)
+ return fuse_EIO("passthrough not enabled");
+
+ if (ff->open_flags & ~FOPEN_PASSTHROUGH_MASK)
+ return fuse_EIO("conflicting open flags");
fb = fuse_passthrough_open(file, ff->args->open_outarg.backing_id);
if (IS_ERR(fb))
@@ -197,7 +195,6 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
{
struct fuse_file *ff = file->private_data;
struct fuse_inode *fi = get_fuse_inode(inode);
- int err;
/*
* io modes are not relevant with virtiofs DAX and with server that does not
@@ -208,11 +205,12 @@ 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.
+ * 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.
*/
- err = -EINVAL;
if (fuse_inode_backing(fi) && !(ff->open_flags & FOPEN_PASSTHROUGH))
- goto fail;
+ return fuse_EIO("FOPEN_PASSTHROUGH expected");
/*
* FOPEN_PARALLEL_DIRECT_WRITES requires FOPEN_DIRECT_IO.
@@ -233,23 +231,12 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
return 0;
if (ff->open_flags & FOPEN_PASSTHROUGH)
- err = fuse_file_passthrough_open(inode, file);
- else
- err = fuse_file_cached_io_open(inode, ff);
- if (err)
- goto fail;
+ return fuse_file_passthrough_open(inode, file);
- return 0;
+ if (!fuse_file_cached_io_open(inode, ff))
+ return fuse_EIO("conflicting passthrough open");
-fail:
- pr_debug("failed to open file in requested io mode (open_flags=0x%x, err=%i).\n",
- ff->open_flags, err);
- /*
- * The file open mode determines the inode io mode.
- * Using incorrect open mode is a server mistake, which results in
- * user visible failure of open() with EIO error.
- */
- return -EIO;
+ return 0;
}
/* No more pending io and no new io possible to inode via open/mmapped file */
diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
index b43d3e0f7081..489838462d1e 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -159,34 +159,29 @@ struct fuse_backing *fuse_passthrough_open(struct file *file, int backing_id)
struct fuse_conn *fc = ff->fm->fc;
struct fuse_backing *fb = NULL;
struct file *backing_file;
- int err;
- err = -EINVAL;
if (backing_id <= 0)
- goto out;
+ return ERR_PTR(fuse_EIO("invalid backing_id"));
- err = -ENOENT;
fb = fuse_backing_lookup(fc, backing_id);
if (!fb)
- goto out;
+ return ERR_PTR(fuse_EIO("backing not found"));
/* 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);
- err = PTR_ERR(backing_file);
if (IS_ERR(backing_file)) {
fuse_backing_put(fb);
- goto out;
+ return ERR_PTR(fuse_EIO("failed to open backing file (%ld)", PTR_ERR(backing_file)));
}
- err = 0;
ff->passthrough = backing_file;
ff->cred = get_cred(fb->cred);
-out:
- pr_debug("%s: backing_id=%d, fb=0x%p, backing_file=0x%p, err=%i\n", __func__,
- backing_id, fb, ff->passthrough, err);
- return err ? ERR_PTR(err) : fb;
+ pr_debug("%s: backing_id=%d, fb=0x%p, backing_file=0x%p\n", __func__,
+ backing_id, fb, ff->passthrough);
+
+ return fb;
}
void fuse_passthrough_release(struct fuse_file *ff, struct fuse_backing *fb)
--
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 ` Miklos Szeredi [this message]
2026-10-06 21:03 ` [PATCH v3 3/9] fuse: add helpers for EIO return value with kernel message 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 ` [PATCH v3 6/9] fuse: add support for opening dax device as backing Miklos Szeredi
2026-10-06 18:18 ` 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-4-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