summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2026-07-27 12:49:21 +0200
committerChristian Brauner <brauner@kernel.org>2026-07-27 16:25:31 +0200
commit5a499dad2c794c19bf8ad51429dce1d53d9d3e12 (patch)
tree5da3f723279a93c1496b6de68d4ab2bb185e740e
parentb0bca4e95b03438cd2c20bb7be3e6e109d1a01fa (diff)
downloadlinux-next-5a499dad2c794c19bf8ad51429dce1d53d9d3e12.tar.gz
linux-next-5a499dad2c794c19bf8ad51429dce1d53d9d3e12.zip
fs: Fix missed inode writeback when racing with __writeback_single_inode
When mmb_fsync_noflush() or simple_fsync_noflush() race with another writeback of the same inode, they can see inode dirty bits are already clear and skip inode writeback although the racing __writeback_single_inode() didn't yet get to writing anything. This can result in fsync(2) returning without properly persisting the inode. We already have I_SYNC bit for this synchronization and writeback_single_inode() properly uses it so just fix mmb_fsync_noflush() and simple_fsync_noflush() to take it into account as well. Signed-off-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260727104923.3828017-23-jack@suse.cz Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
-rw-r--r--fs/buffer.c5
-rw-r--r--fs/libfs.c5
2 files changed, 6 insertions, 4 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index daaa6614a6d6..7e5ad9f4754d 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -655,9 +655,10 @@ int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb,
if (mmb)
ret = mmb_sync(mmb);
- if (!(inode_state_read_once(inode) & I_DIRTY_ALL))
+ if (!(inode_state_read_once(inode) & (I_DIRTY_ALL | I_SYNC)))
goto out;
- if (datasync && !(inode_state_read_once(inode) & I_DIRTY_DATASYNC))
+ if (datasync &&
+ !(inode_state_read_once(inode) & (I_DIRTY_DATASYNC | I_SYNC)))
goto out;
err = sync_inode_metadata(inode, 1);
diff --git a/fs/libfs.c b/fs/libfs.c
index 5a0d276379d1..57e5971b6331 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1559,9 +1559,10 @@ int simple_fsync_noflush(struct file *file, loff_t start, loff_t end,
if (err)
return err;
- if (!(inode_state_read_once(inode) & I_DIRTY_ALL))
+ if (!(inode_state_read_once(inode) & (I_DIRTY_ALL | I_SYNC)))
goto out;
- if (datasync && !(inode_state_read_once(inode) & I_DIRTY_DATASYNC))
+ if (datasync &&
+ !(inode_state_read_once(inode) & (I_DIRTY_DATASYNC | I_SYNC)))
goto out;
ret = sync_inode_metadata(inode, 1);