Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

f2fs: fix fsck inconsistency caused by incorrect nat_entry flag usage

f2fs_need_dentry_mark() reads nat_entry flags without mutual exclusion
with the checkpoint path, which can result in an incorrect inode block
marking state. The scenario is as follows:

create & write & fsync 'file A' write checkpoint
- f2fs_do_sync_file // inline inode
- f2fs_write_inode // inode folio is dirty
- f2fs_write_checkpoint
- f2fs_flush_merged_writes
- f2fs_sync_node_pages
- f2fs_fsync_node_pages // no dirty node
- f2fs_need_inode_block_update // return true
- f2fs_fsync_node_pages // inode dirtied
- f2fs_need_dentry_mark //return true
- f2fs_flush_nat_entries
- f2fs_write_checkpoint end
- __write_node_folio // inode with DENT_BIT_SHIFT set
SPO, "fsck --dry-run" find inode has already checkpointed but still
with DENT_BIT_SHIFT set

The state observed by f2fs_need_dentry_mark() can differ from the state
observed in __write_node_folio() after acquiring sbi->node_write. The
root cause is that the semantics of IS_CHECKPOINTED and
HAS_FSYNCED_INODE are only guaranteed after the checkpoint write has
fully completed.

This patch moves set_dentry_mark() into __write_node_folio() and
protects it with the sbi->node_write lock.

Cc: stable@kernel.org
Fixes: 88bd02c9472a ("f2fs: fix conditions to remain recovery information in f2fs_sync_file")
Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

authored by

Yongpeng Yang and committed by
Jaegeuk Kim
019f9dda 6af249c9

+5 -9
+5 -9
fs/f2fs/node.c
··· 1799 1799 goto redirty_out; 1800 1800 } 1801 1801 1802 - if (atomic) { 1803 - if (!test_opt(sbi, NOBARRIER)) 1804 - fio.op_flags |= REQ_PREFLUSH | REQ_FUA; 1805 - if (IS_INODE(folio)) 1806 - set_dentry_mark(folio, 1802 + if (atomic && !test_opt(sbi, NOBARRIER)) 1803 + fio.op_flags |= REQ_PREFLUSH | REQ_FUA; 1804 + 1805 + if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio))) 1806 + set_dentry_mark(folio, 1807 1807 f2fs_need_dentry_mark(sbi, ino_of_node(folio))); 1808 - } 1809 1808 1810 1809 /* should add to global list before clearing PAGECACHE status */ 1811 1810 if (f2fs_in_warm_node_list(folio)) { ··· 1955 1956 if (is_inode_flag_set(inode, 1956 1957 FI_DIRTY_INODE)) 1957 1958 f2fs_update_inode(inode, folio); 1958 - if (!atomic) 1959 - set_dentry_mark(folio, 1960 - f2fs_need_dentry_mark(sbi, ino)); 1961 1959 } 1962 1960 /* may be written by other thread */ 1963 1961 if (!folio_test_dirty(folio))