Skip to content

Commit 3fc5d5a

Browse files
chaseyuJaegeuk Kim
authored andcommitted
f2fs: fix to shrink read extent node in batches
We use rwlock to protect core structure data of extent tree during its shrink, however, if there is a huge number of extent nodes in extent tree, during shrink of extent tree, it may hold rwlock for a very long time, which may trigger kernel hang issue. This patch fixes to shrink read extent node in batches, so that, critical region of the rwlock can be shrunk to avoid its extreme long time hold. Reported-by: Xiuhong Wang <xiuhong.wang@unisoc.com> Closes: https://lore.kernel.org/linux-f2fs-devel/20241112110627.1314632-1-xiuhong.wang@unisoc.com/ Signed-off-by: Xiuhong Wang <xiuhong.wang@unisoc.com> Signed-off-by: Zhiguo Niu <zhiguo.niu@unisoc.com> Signed-off-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
1 parent 81520c6 commit 3fc5d5a

File tree

1 file changed

+41
-28
lines changed

1 file changed

+41
-28
lines changed

fs/f2fs/extent_cache.c

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -379,21 +379,22 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode,
379379
}
380380

381381
static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
382-
struct extent_tree *et)
382+
struct extent_tree *et, unsigned int nr_shrink)
383383
{
384384
struct rb_node *node, *next;
385385
struct extent_node *en;
386-
unsigned int count = atomic_read(&et->node_cnt);
386+
unsigned int count;
387387

388388
node = rb_first_cached(&et->root);
389-
while (node) {
389+
390+
for (count = 0; node && count < nr_shrink; count++) {
390391
next = rb_next(node);
391392
en = rb_entry(node, struct extent_node, rb_node);
392393
__release_extent_node(sbi, et, en);
393394
node = next;
394395
}
395396

396-
return count - atomic_read(&et->node_cnt);
397+
return count;
397398
}
398399

399400
static void __drop_largest_extent(struct extent_tree *et,
@@ -622,6 +623,30 @@ static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi,
622623
return en;
623624
}
624625

626+
static unsigned int __destroy_extent_node(struct inode *inode,
627+
enum extent_type type)
628+
{
629+
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
630+
struct extent_tree *et = F2FS_I(inode)->extent_tree[type];
631+
unsigned int nr_shrink = type == EX_READ ?
632+
READ_EXTENT_CACHE_SHRINK_NUMBER :
633+
AGE_EXTENT_CACHE_SHRINK_NUMBER;
634+
unsigned int node_cnt = 0;
635+
636+
if (!et || !atomic_read(&et->node_cnt))
637+
return 0;
638+
639+
while (atomic_read(&et->node_cnt)) {
640+
write_lock(&et->lock);
641+
node_cnt += __free_extent_tree(sbi, et, nr_shrink);
642+
write_unlock(&et->lock);
643+
}
644+
645+
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
646+
647+
return node_cnt;
648+
}
649+
625650
static void __update_extent_tree_range(struct inode *inode,
626651
struct extent_info *tei, enum extent_type type)
627652
{
@@ -760,9 +785,6 @@ static void __update_extent_tree_range(struct inode *inode,
760785
}
761786
}
762787

763-
if (is_inode_flag_set(inode, FI_NO_EXTENT))
764-
__free_extent_tree(sbi, et);
765-
766788
if (et->largest_updated) {
767789
et->largest_updated = false;
768790
updated = true;
@@ -780,6 +802,9 @@ static void __update_extent_tree_range(struct inode *inode,
780802
out_read_extent_cache:
781803
write_unlock(&et->lock);
782804

805+
if (is_inode_flag_set(inode, FI_NO_EXTENT))
806+
__destroy_extent_node(inode, EX_READ);
807+
783808
if (updated)
784809
f2fs_mark_inode_dirty_sync(inode, true);
785810
}
@@ -942,10 +967,14 @@ static unsigned int __shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink
942967
list_for_each_entry_safe(et, next, &eti->zombie_list, list) {
943968
if (atomic_read(&et->node_cnt)) {
944969
write_lock(&et->lock);
945-
node_cnt += __free_extent_tree(sbi, et);
970+
node_cnt += __free_extent_tree(sbi, et,
971+
nr_shrink - node_cnt - tree_cnt);
946972
write_unlock(&et->lock);
947973
}
948-
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
974+
975+
if (atomic_read(&et->node_cnt))
976+
goto unlock_out;
977+
949978
list_del_init(&et->list);
950979
radix_tree_delete(&eti->extent_tree_root, et->ino);
951980
kmem_cache_free(extent_tree_slab, et);
@@ -1084,23 +1113,6 @@ unsigned int f2fs_shrink_age_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink
10841113
return __shrink_extent_tree(sbi, nr_shrink, EX_BLOCK_AGE);
10851114
}
10861115

1087-
static unsigned int __destroy_extent_node(struct inode *inode,
1088-
enum extent_type type)
1089-
{
1090-
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
1091-
struct extent_tree *et = F2FS_I(inode)->extent_tree[type];
1092-
unsigned int node_cnt = 0;
1093-
1094-
if (!et || !atomic_read(&et->node_cnt))
1095-
return 0;
1096-
1097-
write_lock(&et->lock);
1098-
node_cnt = __free_extent_tree(sbi, et);
1099-
write_unlock(&et->lock);
1100-
1101-
return node_cnt;
1102-
}
1103-
11041116
void f2fs_destroy_extent_node(struct inode *inode)
11051117
{
11061118
__destroy_extent_node(inode, EX_READ);
@@ -1109,15 +1121,13 @@ void f2fs_destroy_extent_node(struct inode *inode)
11091121

11101122
static void __drop_extent_tree(struct inode *inode, enum extent_type type)
11111123
{
1112-
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
11131124
struct extent_tree *et = F2FS_I(inode)->extent_tree[type];
11141125
bool updated = false;
11151126

11161127
if (!__may_extent_tree(inode, type))
11171128
return;
11181129

11191130
write_lock(&et->lock);
1120-
__free_extent_tree(sbi, et);
11211131
if (type == EX_READ) {
11221132
set_inode_flag(inode, FI_NO_EXTENT);
11231133
if (et->largest.len) {
@@ -1126,6 +1136,9 @@ static void __drop_extent_tree(struct inode *inode, enum extent_type type)
11261136
}
11271137
}
11281138
write_unlock(&et->lock);
1139+
1140+
__destroy_extent_node(inode, type);
1141+
11291142
if (updated)
11301143
f2fs_mark_inode_dirty_sync(inode, true);
11311144
}

0 commit comments

Comments
 (0)