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

f2fs: improve shrink performance of extent nodes

On the worst case, we need to scan the whole radix tree and every rb-tree to
free the victimed extent_nodes when shrinking.

Pengyang initially introduced a victim_list to record the victimed extent_nodes,
and free these extent_nodes by just scanning a list.

Later, Chao Yu enhances the original patch to improve memory footprint by
removing victim list.

The policy of lru list shrinking becomes:
1) lock lru list's lock
2) trylock extent tree's lock
3) remove extent node from lru list
4) unlock lru list's lock
5) do shrink
6) repeat 1) to 5)

Signed-off-by: Hou Pengyang <houpengyang@huawei.com>
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

authored by

Hou Pengyang and committed by
Jaegeuk Kim
201ef5e0 42926744

+29 -48
+28 -48
fs/f2fs/extent_cache.c
··· 33 33 34 34 en->ei = *ei; 35 35 INIT_LIST_HEAD(&en->list); 36 + en->et = et; 36 37 37 38 rb_link_node(&en->rb_node, parent, p); 38 39 rb_insert_color(&en->rb_node, &et->root); ··· 64 63 struct extent_tree *et, struct extent_node *en) 65 64 { 66 65 spin_lock(&sbi->extent_lock); 67 - if (!list_empty(&en->list)) 68 - list_del_init(&en->list); 66 + f2fs_bug_on(sbi, list_empty(&en->list)); 67 + list_del_init(&en->list); 69 68 spin_unlock(&sbi->extent_lock); 70 69 71 70 __detach_extent_node(sbi, et, en); ··· 148 147 } 149 148 150 149 static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, 151 - struct extent_tree *et, bool free_all) 150 + struct extent_tree *et) 152 151 { 153 152 struct rb_node *node, *next; 154 153 struct extent_node *en; ··· 158 157 while (node) { 159 158 next = rb_next(node); 160 159 en = rb_entry(node, struct extent_node, rb_node); 161 - 162 - if (free_all) 163 - __release_extent_node(sbi, et, en); 164 - else if (list_empty(&en->list)) 165 - __detach_extent_node(sbi, et, en); 160 + __release_extent_node(sbi, et, en); 166 161 node = next; 167 162 } 168 163 ··· 529 532 } 530 533 531 534 if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) 532 - __free_extent_tree(sbi, et, true); 535 + __free_extent_tree(sbi, et); 533 536 534 537 write_unlock(&et->lock); 535 538 ··· 538 541 539 542 unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) 540 543 { 541 - struct extent_tree *treevec[EXT_TREE_VEC_SIZE]; 542 544 struct extent_tree *et, *next; 543 - struct extent_node *en, *tmp; 544 - unsigned long ino = F2FS_ROOT_INO(sbi); 545 - unsigned int found; 545 + struct extent_node *en; 546 546 unsigned int node_cnt = 0, tree_cnt = 0; 547 547 int remained; 548 - bool do_free = false; 549 548 550 549 if (!test_opt(sbi, EXTENT_CACHE)) 551 550 return 0; ··· 556 563 list_for_each_entry_safe(et, next, &sbi->zombie_list, list) { 557 564 if (atomic_read(&et->node_cnt)) { 558 565 write_lock(&et->lock); 559 - node_cnt += __free_extent_tree(sbi, et, true); 566 + node_cnt += __free_extent_tree(sbi, et); 560 567 write_unlock(&et->lock); 561 568 } 562 - 569 + f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); 563 570 list_del_init(&et->list); 564 571 radix_tree_delete(&sbi->extent_tree_root, et->ino); 565 572 kmem_cache_free(extent_tree_slab, et); ··· 580 587 remained = nr_shrink - (node_cnt + tree_cnt); 581 588 582 589 spin_lock(&sbi->extent_lock); 583 - list_for_each_entry_safe(en, tmp, &sbi->extent_list, list) { 584 - if (!remained--) 590 + for (; remained > 0; remained--) { 591 + if (list_empty(&sbi->extent_list)) 585 592 break; 593 + en = list_first_entry(&sbi->extent_list, 594 + struct extent_node, list); 595 + et = en->et; 596 + if (!write_trylock(&et->lock)) { 597 + /* refresh this extent node's position in extent list */ 598 + list_move_tail(&en->list, &sbi->extent_list); 599 + continue; 600 + } 601 + 586 602 list_del_init(&en->list); 587 - do_free = true; 603 + spin_unlock(&sbi->extent_lock); 604 + 605 + __detach_extent_node(sbi, et, en); 606 + 607 + write_unlock(&et->lock); 608 + node_cnt++; 609 + spin_lock(&sbi->extent_lock); 588 610 } 589 611 spin_unlock(&sbi->extent_lock); 590 612 591 - if (do_free == false) 592 - goto unlock_out; 593 - 594 - /* 595 - * reset ino for searching victims from beginning of global extent tree. 596 - */ 597 - ino = F2FS_ROOT_INO(sbi); 598 - 599 - while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root, 600 - (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { 601 - unsigned i; 602 - 603 - ino = treevec[found - 1]->ino + 1; 604 - for (i = 0; i < found; i++) { 605 - struct extent_tree *et = treevec[i]; 606 - 607 - if (!atomic_read(&et->node_cnt)) 608 - continue; 609 - 610 - if (write_trylock(&et->lock)) { 611 - node_cnt += __free_extent_tree(sbi, et, false); 612 - write_unlock(&et->lock); 613 - } 614 - 615 - if (node_cnt + tree_cnt >= nr_shrink) 616 - goto unlock_out; 617 - } 618 - } 619 613 unlock_out: 620 614 up_write(&sbi->extent_tree_lock); 621 615 out: ··· 621 641 return 0; 622 642 623 643 write_lock(&et->lock); 624 - node_cnt = __free_extent_tree(sbi, et, true); 644 + node_cnt = __free_extent_tree(sbi, et); 625 645 write_unlock(&et->lock); 626 646 627 647 return node_cnt;
+1
fs/f2fs/f2fs.h
··· 354 354 struct rb_node rb_node; /* rb node located in rb-tree */ 355 355 struct list_head list; /* node in global extent list of sbi */ 356 356 struct extent_info ei; /* extent info */ 357 + struct extent_tree *et; /* extent tree pointer */ 357 358 }; 358 359 359 360 struct extent_tree {