Skip to content

Commit 42dd1c9

Browse files
Yongpeng Yanggregkh
authored andcommitted
f2fs: fix node_cnt race between extent node destroy and writeback
commit ed78aee upstream. f2fs_destroy_extent_node() does not set FI_NO_EXTENT before clearing extent nodes. When called from f2fs_drop_inode() with I_SYNC set, concurrent kworker writeback can insert new extent nodes into the same extent tree, racing with the destroy and triggering f2fs_bug_on() in __destroy_extent_node(). The scenario is as follows: drop inode writeback - iput - f2fs_drop_inode // I_SYNC set - f2fs_destroy_extent_node - __destroy_extent_node - while (node_cnt) { write_lock(&et->lock) __free_extent_tree write_unlock(&et->lock) - __writeback_single_inode - f2fs_outplace_write_data - f2fs_update_read_extent_cache - __update_extent_tree_range // FI_NO_EXTENT not set, // insert new extent node } // node_cnt == 0, exit while - f2fs_bug_on(node_cnt) // node_cnt > 0 Additionally, __update_extent_tree_range() only checks FI_NO_EXTENT for EX_READ type, leaving EX_BLOCK_AGE updates completely unprotected. This patch set FI_NO_EXTENT under et->lock in __destroy_extent_node(), consistent with other callers (__update_extent_tree_range and __drop_extent_tree) and check FI_NO_EXTENT for both EX_READ and EX_BLOCK_AGE tree. Fixes: 3fc5d5a ("f2fs: fix to shrink read extent node in batches") Cc: stable@vger.kernel.org Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com> Reviewed-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 88b98e3 commit 42dd1c9

1 file changed

Lines changed: 10 additions & 7 deletions

File tree

fs/f2fs/extent_cache.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ static bool __may_extent_tree(struct inode *inode, enum extent_type type)
8787
if (!__init_may_extent_tree(inode, type))
8888
return false;
8989

90+
if (is_inode_flag_set(inode, FI_NO_EXTENT))
91+
return false;
92+
9093
if (type == EX_READ) {
91-
if (is_inode_flag_set(inode, FI_NO_EXTENT))
92-
return false;
9394
if (is_inode_flag_set(inode, FI_COMPRESSED_FILE) &&
9495
!f2fs_sb_has_readonly(F2FS_I_SB(inode)))
9596
return false;
@@ -602,6 +603,8 @@ static unsigned int __destroy_extent_node(struct inode *inode,
602603

603604
while (atomic_read(&et->node_cnt)) {
604605
write_lock(&et->lock);
606+
if (!is_inode_flag_set(inode, FI_NO_EXTENT))
607+
set_inode_flag(inode, FI_NO_EXTENT);
605608
node_cnt += __free_extent_tree(sbi, et, nr_shrink);
606609
write_unlock(&et->lock);
607610
}
@@ -637,12 +640,12 @@ static void __update_extent_tree_range(struct inode *inode,
637640

638641
write_lock(&et->lock);
639642

640-
if (type == EX_READ) {
641-
if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
642-
write_unlock(&et->lock);
643-
return;
644-
}
643+
if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
644+
write_unlock(&et->lock);
645+
return;
646+
}
645647

648+
if (type == EX_READ) {
646649
prev = et->largest;
647650
dei.len = 0;
648651

0 commit comments

Comments
 (0)