[PATCH] f2fs: skip clean inode update during fsync

From: Wenjie Qi

Date: Thu May 28 2026 - 10:23:19 EST


f2fs_do_sync_file() calls f2fs_skip_inode_update() before deciding
whether it has to write an inode block and continue into the recovery
info/flush path.

For a full fsync, f2fs_skip_inode_update() currently returns false when
FI_AUTO_RECOVER is not set. That makes fsync on an already clean file
call f2fs_write_inode(). f2fs_write_inode() then returns immediately if
the in-memory timestamps match the inode block and FI_DIRTY_INODE is not
set, but f2fs_do_sync_file() still continues through go_write and may end
at f2fs_issue_flush().

Avoid that unnecessary path for clean, time-consistent inodes without
FI_AUTO_RECOVER. Keep the existing conservative checks for keep-size
files and non-block-aligned i_size before allowing the skip, and leave the
FI_AUTO_RECOVER path unchanged.

On a QEMU/KASAN test VM, repeated fsync() on an existing clean F2FS file
improved from about 35.7 us/fsync to about 1.13 us/fsync. The baseline
issued one flush per fsync, while the patched kernel kept the F2FS flush
count unchanged over 140000 clean fsync calls.

Signed-off-by: Wenjie Qi <qiwenjie@xxxxxxxxxx>
---
fs/f2fs/f2fs.h | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9f24287de4c3..ebd485abecb4 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3668,11 +3668,13 @@ static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync)
spin_unlock(&sbi->inode_lock[DIRTY_META]);
return ret;
}
- if (!is_inode_flag_set(inode, FI_AUTO_RECOVER) ||
- file_keep_isize(inode) ||
- i_size_read(inode) & ~PAGE_MASK)
+ if (file_keep_isize(inode) || i_size_read(inode) & ~PAGE_MASK)
return false;

+ if (!is_inode_flag_set(inode, FI_AUTO_RECOVER))
+ return f2fs_is_time_consistent(inode) &&
+ !is_inode_flag_set(inode, FI_DIRTY_INODE);
+
if (!f2fs_is_time_consistent(inode))
return false;

--
2.43.0