[PATCH 5/9] fuse: cache atime when in iomap mode

Darrick J. Wong
2025-09-16T00:37:22Z

When we're running in iomap mode, allow the kernel to cache the access
timestamp to further reduce the number of roundtrips to the fuse server.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
fs/fuse/dir.c | 5 +++++
fs/fuse/inode.c | 19 ++++++++++++++++---
2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 30c914ba4bb23f..8247e5196fd0b2 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -2033,6 +2033,11 @@ int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
inarg.ctime = inode_get_ctime_sec(inode);
inarg.ctimensec = inode_get_ctime_nsec(inode);
}
+ if (fuse_inode_has_iomap(inode)) {
+ inarg.valid |= FATTR_ATIME;
+ inarg.atime = inode_get_atime_sec(inode);
+ inarg.atimensec = inode_get_atime_nsec(inode);
+ }
if (ff) {
inarg.valid |= FATTR_FH;
inarg.fh = ff->fh;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index f845864bf50dee..c29a8cbc55fa27 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -266,7 +266,8 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
attr->mtimensec = min_t(u32, attr->mtimensec, NSEC_PER_SEC - 1);
attr->ctimensec = min_t(u32, attr->ctimensec, NSEC_PER_SEC - 1);

- inode_set_atime(inode, attr->atime, attr->atimensec);
+ if (!(cache_mask & STATX_ATIME))
+ inode_set_atime(inode, attr->atime, attr->atimensec);
/* mtime from server may be stale due to local buffered write */
if (!(cache_mask & STATX_MTIME)) {
inode_set_mtime(inode, attr->mtime, attr->mtimensec);
@@ -328,8 +329,12 @@ u32 fuse_get_cache_mask(struct inode *inode)
{
struct fuse_conn *fc = get_fuse_conn(inode);

- if (S_ISREG(inode->i_mode) &&
- (fuse_inode_has_iomap(inode) || fc->writeback_cache))
+ if (!S_ISREG(inode->i_mode))
+ return 0;
+
+ if (fuse_inode_has_iomap(inode))
+ return STATX_MTIME | STATX_CTIME | STATX_ATIME | STATX_SIZE;
+ if (fc->writeback_cache)
return STATX_MTIME | STATX_CTIME | STATX_SIZE;

return 0;
@@ -448,6 +453,14 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr,
new_decode_dev(attr->rdev));
} else
BUG();
+
+ /*
+ * iomap caches atime too, so we must load it from the fuse server
+ * at instantiation time.
+ */
+ if (fuse_inode_has_iomap(inode))
+ inode_set_atime(inode, attr->atime, attr->atimensec);
+
/*
* Ensure that we don't cache acls for daemons without FUSE_POSIX_ACL
* so they see the exact same behavior as before.