linux/fs/f2fs
Yongpeng Yang 962c167b0f f2fs: fix IS_CHECKPOINTED flag inconsistency issue caused by concurrent atomic commit and checkpoint writes
[ Upstream commit 7633a7387e ]

During SPO tests, when mounting F2FS, an -EINVAL error was returned from
f2fs_recover_inode_page. The issue occurred under the following scenario

Thread A                                     Thread B
f2fs_ioc_commit_atomic_write
 - f2fs_do_sync_file // atomic = true
  - f2fs_fsync_node_pages
    : last_folio = inode folio
    : schedule before folio_lock(last_folio) f2fs_write_checkpoint
                                              - block_operations// writeback last_folio
                                              - schedule before f2fs_flush_nat_entries
    : set_fsync_mark(last_folio, 1)
    : set_dentry_mark(last_folio, 1)
    : folio_mark_dirty(last_folio)
    - __write_node_folio(last_folio)
      : f2fs_down_read(&sbi->node_write)//block
                                              - f2fs_flush_nat_entries
                                                : {struct nat_entry}->flag |= BIT(IS_CHECKPOINTED)
                                              - unblock_operations
                                                : f2fs_up_write(&sbi->node_write)
                                             f2fs_write_checkpoint//return
      : f2fs_do_write_node_page()
f2fs_ioc_commit_atomic_write//return
                                             SPO

Thread A calls f2fs_need_dentry_mark(sbi, ino), and the last_folio has
already been written once. However, the {struct nat_entry}->flag did not
have the IS_CHECKPOINTED set, causing set_dentry_mark(last_folio, 1) and
write last_folio again after Thread B finishes f2fs_write_checkpoint.

After SPO and reboot, it was detected that {struct node_info}->blk_addr
was not NULL_ADDR because Thread B successfully write the checkpoint.

This issue only occurs in atomic write scenarios. For regular file
fsync operations, the folio must be dirty. If
block_operations->f2fs_sync_node_pages successfully submit the folio
write, this path will not be executed. Otherwise, the
f2fs_write_checkpoint will need to wait for the folio write submission
to complete, as sbi->nr_pages[F2FS_DIRTY_NODES] > 0. Therefore, the
situation where f2fs_need_dentry_mark checks that the {struct
nat_entry}->flag /wo the IS_CHECKPOINTED flag, but the folio write has
already been submitted, will not occur.

Therefore, for atomic file fsync, sbi->node_write should be acquired
through __write_node_folio to ensure that the IS_CHECKPOINTED flag
correctly indicates that the checkpoint write has been completed.

Fixes: 608514deba ("f2fs: set fsync mark only for the last dnode")
Cc: stable@kernel.org
Signed-off-by: Sheng Yong <shengyong1@xiaomi.com>
Signed-off-by: Jinbao Liu <liujinbao1@xiaomi.com>
Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
[ folio => page ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
History 2026-02-19 16:29:56 +01:00
..
acl.c f2fs: Use in_group_or_capable() helper 2024-06-25 11:15:48 +02:00
acl.h fs: port ->set_acl() to pass mnt_idmap 2023-01-19 09:24:27 +01:00
checkpoint.c Revert "f2fs: rebuild nat_bits during umount" 2025-04-20 10:15:20 +02:00
compress.c f2fs: fix to avoid potential deadlock 2026-01-08 10:14:20 +01:00
data.c f2fs: fix to avoid UAF in f2fs_write_end_io() 2026-02-19 16:29:56 +01:00
debug.c f2fs: keep POSIX_FADV_NOREUSE ranges 2025-12-18 13:55:15 +01:00
dir.c f2fs: Introduce linear search for dentries 2025-02-08 09:58:11 +01:00
extent_cache.c f2fs: fix age extent cache insertion skip on counter overflow 2026-01-08 10:14:20 +01:00
f2fs.h f2fs: add timeout in f2fs_enable_checkpoint() 2026-01-08 10:14:59 +01:00
file.c f2fs: fix to avoid updating compression context during writeback 2026-01-08 10:14:57 +01:00
gc.c f2fs: fix to add gc count stat in f2fs_gc_range 2026-02-19 16:29:55 +01:00
gc.h f2fs: add gc_boost_gc_greedy sysfs node 2025-12-18 13:55:16 +01:00
hash.c f2fs: don't use casefolded comparison for "." and ".." 2022-05-17 11:19:23 -07:00
inline.c f2fs: Introduce linear search for dentries 2025-02-08 09:58:11 +01:00
inode.c f2fs: drop inode from the donation list when the last file is closed 2026-01-08 10:14:57 +01:00
iostat.c f2fs: add async reset zone command support 2023-06-12 13:04:09 -07:00
iostat.h f2fs: use iostat_lat_type directly as a parameter in the iostat_update_and_unbind_ctx() 2023-02-07 10:39:28 -08:00
Kconfig fs: add CONFIG_BUFFER_HEAD 2023-08-02 09:13:09 -06:00
Makefile f2fs: separate out iostat feature 2021-08-23 10:25:51 -07:00
namei.c f2fs: invalidate dentry cache on failed whiteout creation 2026-01-08 10:14:20 +01:00
node.c f2fs: fix IS_CHECKPOINTED flag inconsistency issue caused by concurrent atomic commit and checkpoint writes 2026-02-19 16:29:56 +01:00
node.h f2fs: use BLKS_PER_SEG, BLKS_PER_SEC, and SEGS_PER_SEC 2024-02-27 09:41:12 -08:00
recovery.c f2fs: fix to detect recoverable inode during dryrun of find_fsync_dnodes() 2026-01-08 10:14:57 +01:00
segment.c f2fs: fix age extent cache insertion skip on counter overflow 2026-01-08 10:14:20 +01:00
segment.h f2fs: fix to calculate dirty data during has_not_enough_free_secs() 2025-08-15 12:14:01 +02:00
shrinker.c f2fs: add a sysfs entry to reclaim POSIX_FADV_NOREUSE pages 2025-12-18 13:55:15 +01:00
super.c f2fs: fix to propagate error from f2fs_enable_checkpoint() 2026-01-08 10:14:59 +01:00
sysfs.c f2fs: fix out-of-bounds access in sysfs attribute read/write 2026-02-19 16:29:56 +01:00
verity.c f2fs-6.12-rc1 2024-09-24 15:12:38 -07:00
xattr.c f2fs: use global inline_xattr_slab instead of per-sb slab cache 2026-01-08 10:14:57 +01:00
xattr.h f2fs: use global inline_xattr_slab instead of per-sb slab cache 2026-01-08 10:14:57 +01:00