* [PATCH] Revert "f2fs: skip node_change lock for inline data writes"
@ 2026-10-07 10:17 Chao Yu
2026-10-07 13:50 ` Seongjae Jeong
0 siblings, 1 reply; 2+ messages in thread
From: Chao Yu @ 2026-10-07 10:17 UTC (permalink / raw)
To: jaegeuk; +Cc: linux-f2fs-devel, linux-kernel, Chao Yu, Seongjae Jeong
From: Chao Yu <chao@kernel.org>
This reverts commit 66eec36c2421383e5b32e9c1740f656474c80df8.
Fault injection + shutdown stress test reports inconsistent inline
data after "f2fs_io shutdown 2" (mode=lfs, nonzone, segs=1):
[__chk_dentries:2067] [ 6]-[0x26] name[0MVNBi,Cv33...] len[0x20] ino[0x4190] type[0x7]
[ASSERT] (fsck_chk_inode_blk:1142) --> [0x4190] junk inline data
[fsck_chk_inode_blk:1150] ino[0x4190] has inline data!
...
[FSCK] other corrupted bugs [Fail]
ino 0x4190 is an encrypted symlink, the inode block pointed by the
last checkpoint's NAT entry contains valid inline data, however
F2FS_DATA_EXIST is not set in i_inline (i_size is stale as well).
The node_change rwsem was taken in prepare_write_begin() for inline
inodes in order to serialize write_begin with block_operations():
checkpoint holds node_change for write from the last check of
F2FS_DIRTY_IMETA until all dirty node blocks are flushed, so either
1) FI_DATA_EXIST is set before the check, the inode is in DIRTY_META
list, and f2fs_sync_inode_meta() -> f2fs_update_inode() syncs
F2FS_DATA_EXIST and i_size into the inode block before it is
written by checkpoint, or
2) FI_DATA_EXIST is set after block_operations(), then the inode
block written by this checkpoint does not contain the new inline
data.
Commit 66eec36c2421 ("f2fs: skip node_change lock for inline data
writes") skips the lock for writes which fit in inline area, assuming
setting FI_DATA_EXIST only dirties inode metadata.
However, dirtying inode metadata after F2FS_DIRTY_IMETA was checked,
while f2fs_write_inline_data() (which never holds any checkpoint
lock) copies inline data into the inode block, breaks the above
ordering, then checkpoint persists an inode block which has inline
data but stale i_inline/i_size:
- f2fs_symlink - f2fs_write_checkpoint
- f2fs_add_link - block_operations
- f2fs_init_inode_metadata
: i_inline = F2FS_INLINE_DATA
- f2fs_update_parent_metadata
: clear FI_NEW_INODE
- f2fs_flush_inline_data
- f2fs_lock_all
- f2fs_down_write(node_change)
: F2FS_DIRTY_IMETA is zero, skip
f2fs_sync_inode_meta()
- page_symlink
- f2fs_write_begin
- prepare_write_begin
: missed f2fs_map_lock(), i.e.
f2fs_down_read(node_change), so
it isn't blocked by checkpoint
- set_inode_flag(FI_DATA_EXIST)
: inode becomes dirty, too late
- f2fs_write_end
- f2fs_i_size_write
- filemap_write_and_wait_range
- f2fs_write_single_data_page
- f2fs_write_inline_data
- memcpy_from_folio
- f2fs_mark_cache_dirty
: ientry has inline data, but
no F2FS_DATA_EXIST in i_inline
- f2fs_down_write(node_write)
- f2fs_writeback_node_caches
- __write_node_cache
: write ientry w/ stale i_inline
: F2FS_DIRTY_IMETA isn't rechecked
- f2fs_flush_nat_entries
- do_checkpoint
- f2fs_io shutdown 2
: dirty inode is lost, fsck finds "junk inline data"
Besides the fsck error, for regular inline file, stale i_size may
cause data loss after sudden power-off even if checkpoint is
committed.
Let's revert the commit to restore node_change serialization for
inline data writes, and add a comment above f2fs_map_lock().
Fixes: 66eec36c2421 ("f2fs: skip node_change lock for inline data writes")
Cc: Seongjae Jeong <jsjlee1020@gmail.com>
Signed-off-by: Chao Yu <chao@kernel.org>
---
Drop original one or apply this revert patch is both fine to me.
fs/f2fs/data.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 6d4ba5e77906..4bfa301a5df6 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -3915,11 +3915,15 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
/* f2fs_lock_op avoids race between write CP and convert_inline_page */
if (f2fs_has_inline_data(inode)) {
- if (pos + len > MAX_INLINE_DATA(inode)) {
+ if (pos + len > MAX_INLINE_DATA(inode))
flag = F2FS_GET_BLOCK_DEFAULT;
- f2fs_map_lock(sbi, &lc, flag);
- locked = true;
- }
+ /*
+ * it needs to cover FI_DATA_EXIST and inline data update w/
+ * node_change, otherwise concurrent checkpoint may persist
+ * inline data only w/o F2FS_DATA_EXIST.
+ */
+ f2fs_map_lock(sbi, &lc, flag);
+ locked = true;
} else if ((pos & PAGE_MASK) >= i_size_read(inode)) {
f2fs_map_lock(sbi, &lc, flag);
locked = true;
--
2.49.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] Revert "f2fs: skip node_change lock for inline data writes"
2026-10-07 10:17 [PATCH] Revert "f2fs: skip node_change lock for inline data writes" Chao Yu
@ 2026-10-07 13:50 ` Seongjae Jeong
0 siblings, 0 replies; 2+ messages in thread
From: Seongjae Jeong @ 2026-10-07 13:50 UTC (permalink / raw)
To: Chao Yu; +Cc: jaegeuk, linux-f2fs-devel, linux-kernel
Hi Chao,
Sorry I missed this case.
Thanks for catching this and for the detailed explanation.
I agree with the revert patch.
Thanks,
Seongjae
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-07 13:50 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-07 10:17 [PATCH] Revert "f2fs: skip node_change lock for inline data writes" Chao Yu
2026-10-07 13:50 ` Seongjae Jeong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®