[PATCH 09/28] fuse: isolate the other regular file IO paths from iomap

Darrick J. Wong
2025-09-16T00:30:19Z

iomap completely takes over all regular file IO, so we don't need to
access any of the other mechanisms at all. Gate them off so that we can
eventually overlay them with a union to save space in struct fuse_inode.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
fs/fuse/dir.c | 14 +++++++++-----
fs/fuse/file.c | 18 +++++++++++++-----
fs/fuse/inode.c | 3 ++-
fs/fuse/iomode.c | 2 +-
4 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index b116e42431ee12..6dbce975dc96b7 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1998,6 +1998,7 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
FUSE_ARGS(args);
struct fuse_setattr_in inarg;
struct fuse_attr_out outarg;
+ const bool is_iomap = fuse_inode_has_iomap(inode);
bool is_truncate = false;
bool is_wb = fc->writeback_cache && S_ISREG(inode->i_mode);
loff_t oldsize;
@@ -2055,12 +2056,15 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (err)
return err;

- fuse_set_nowrite(inode);
- fuse_release_nowrite(inode);
+ if (!is_iomap) {
+ fuse_set_nowrite(inode);
+ fuse_release_nowrite(inode);
+ }
}

if (is_truncate) {
- fuse_set_nowrite(inode);
+ if (!is_iomap)
+ fuse_set_nowrite(inode);
set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
if (trust_local_cmtime && attr->ia_size != inode->i_size)
attr->ia_valid |= ATTR_MTIME | ATTR_CTIME;
@@ -2132,7 +2136,7 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (!is_wb || is_truncate)
i_size_write(inode, outarg.attr.size);

- if (is_truncate) {
+ if (is_truncate && !is_iomap) {
/* NOTE: this may release/reacquire fi->lock */
__fuse_release_nowrite(inode);
}
@@ -2156,7 +2160,7 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
return 0;

error:
- if (is_truncate)
+ if (is_truncate && !is_iomap)
fuse_release_nowrite(inode);

clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 8982e0b9661bb1..0f253837b57fdc 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -238,6 +238,7 @@ static int fuse_open(struct inode *inode, struct file *file)
struct fuse_conn *fc = fm->fc;
struct fuse_file *ff;
int err;
+ const bool is_iomap = fuse_inode_has_iomap(inode);
bool is_truncate = (file->f_flags & O_TRUNC) && fc->atomic_o_trunc;
bool is_wb_truncate = is_truncate && fc->writeback_cache;
bool dax_truncate = is_truncate && FUSE_IS_DAX(inode);
@@ -259,7 +260,7 @@ static int fuse_open(struct inode *inode, struct file *file)
goto out_inode_unlock;
}

- if (is_wb_truncate || dax_truncate)
+ if ((is_wb_truncate || dax_truncate) && !is_iomap)
fuse_set_nowrite(inode);

err = fuse_do_open(fm, get_node_id(inode), file, false);
@@ -272,7 +273,7 @@ static int fuse_open(struct inode *inode, struct file *file)
fuse_truncate_update_attr(inode, file);
}

- if (is_wb_truncate || dax_truncate)
+ if ((is_wb_truncate || dax_truncate) && !is_iomap)
fuse_release_nowrite(inode);
if (!err) {
if (is_truncate)
@@ -520,12 +521,14 @@ static int fuse_fsync(struct file *file, loff_t start, loff_t end,
{
struct inode *inode = file->f_mapping->host;
struct fuse_conn *fc = get_fuse_conn(inode);
+ const bool need_sync_writes = !fuse_inode_has_iomap(inode);
int err;

if (fuse_is_bad(inode))
return -EIO;

- inode_lock(inode);
+ if (need_sync_writes)
+ inode_lock(inode);

/*
* Start writeback against all dirty pages of the inode, then
@@ -536,7 +539,8 @@ static int fuse_fsync(struct file *file, loff_t start, loff_t end,
if (err)
goto out;

- fuse_sync_writes(inode);
+ if (need_sync_writes)
+ fuse_sync_writes(inode);

/*
* Due to implementation of fuse writeback
@@ -560,7 +564,8 @@ static int fuse_fsync(struct file *file, loff_t start, loff_t end,
err = 0;
}
out:
- inode_unlock(inode);
+ if (need_sync_writes)
+ inode_unlock(inode);

return err;
}
@@ -1949,6 +1954,9 @@ static struct fuse_file *__fuse_write_file_get(struct fuse_inode *fi)
{
struct fuse_file *ff;

+ if (fuse_inode_has_iomap(&fi->inode))
+ return NULL;
+
spin_lock(&fi->lock);
ff = list_first_entry_or_null(&fi->write_files, struct fuse_file,
write_entry);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index b209db07e60e33..4f348fc575a5c3 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -194,7 +194,8 @@ static void fuse_evict_inode(struct inode *inode)
if (inode->i_nlink > 0)
atomic64_inc(&fc->evict_ctr);
}
- if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) {
+ if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode) &&
+ !fuse_inode_has_iomap(inode)) {
WARN_ON(fi->iocachectr != 0);
WARN_ON(!list_empty(&fi->write_files));
WARN_ON(!list_empty(&fi->queued_writes));
diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c
index c99e285f3183ef..92225dfa6e7ad9 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -204,7 +204,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
* io modes are not relevant with DAX and with server that does not
* implement open.
*/
- if (FUSE_IS_DAX(inode) || !ff->args)
+ if (fuse_inode_has_iomap(inode) || FUSE_IS_DAX(inode) || !ff->args)
return 0;

/*