diff options
author | Hou Pengyang <houpengyang@huawei.com> | 2016-01-26 07:56:26 -0500 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2016-02-22 19:07:23 -0500 |
commit | 201ef5e080c9b58d619bfd9e11a845bd330712ea (patch) | |
tree | bd8a5c5b13838a59cb68ce23db8846160bbc361b | |
parent | 429267442af1ddc2f8d9f5195fc5e919b5ced301 (diff) |
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>
-rw-r--r-- | fs/f2fs/extent_cache.c | 76 | ||||
-rw-r--r-- | fs/f2fs/f2fs.h | 1 |
2 files changed, 29 insertions, 48 deletions
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index aae99f2a4345..53f49f713323 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c | |||
@@ -33,6 +33,7 @@ static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi, | |||
33 | 33 | ||
34 | en->ei = *ei; | 34 | en->ei = *ei; |
35 | INIT_LIST_HEAD(&en->list); | 35 | INIT_LIST_HEAD(&en->list); |
36 | en->et = et; | ||
36 | 37 | ||
37 | rb_link_node(&en->rb_node, parent, p); | 38 | rb_link_node(&en->rb_node, parent, p); |
38 | rb_insert_color(&en->rb_node, &et->root); | 39 | rb_insert_color(&en->rb_node, &et->root); |
@@ -63,8 +64,8 @@ static void __release_extent_node(struct f2fs_sb_info *sbi, | |||
63 | struct extent_tree *et, struct extent_node *en) | 64 | struct extent_tree *et, struct extent_node *en) |
64 | { | 65 | { |
65 | spin_lock(&sbi->extent_lock); | 66 | spin_lock(&sbi->extent_lock); |
66 | if (!list_empty(&en->list)) | 67 | f2fs_bug_on(sbi, list_empty(&en->list)); |
67 | list_del_init(&en->list); | 68 | list_del_init(&en->list); |
68 | spin_unlock(&sbi->extent_lock); | 69 | spin_unlock(&sbi->extent_lock); |
69 | 70 | ||
70 | __detach_extent_node(sbi, et, en); | 71 | __detach_extent_node(sbi, et, en); |
@@ -147,7 +148,7 @@ static struct extent_node *__init_extent_tree(struct f2fs_sb_info *sbi, | |||
147 | } | 148 | } |
148 | 149 | ||
149 | static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, | 150 | static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, |
150 | struct extent_tree *et, bool free_all) | 151 | struct extent_tree *et) |
151 | { | 152 | { |
152 | struct rb_node *node, *next; | 153 | struct rb_node *node, *next; |
153 | struct extent_node *en; | 154 | struct extent_node *en; |
@@ -157,11 +158,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, | |||
157 | while (node) { | 158 | while (node) { |
158 | next = rb_next(node); | 159 | next = rb_next(node); |
159 | en = rb_entry(node, struct extent_node, rb_node); | 160 | en = rb_entry(node, struct extent_node, rb_node); |
160 | 161 | __release_extent_node(sbi, et, en); | |
161 | if (free_all) | ||
162 | __release_extent_node(sbi, et, en); | ||
163 | else if (list_empty(&en->list)) | ||
164 | __detach_extent_node(sbi, et, en); | ||
165 | node = next; | 162 | node = next; |
166 | } | 163 | } |
167 | 164 | ||
@@ -532,7 +529,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, | |||
532 | } | 529 | } |
533 | 530 | ||
534 | if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) | 531 | if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) |
535 | __free_extent_tree(sbi, et, true); | 532 | __free_extent_tree(sbi, et); |
536 | 533 | ||
537 | write_unlock(&et->lock); | 534 | write_unlock(&et->lock); |
538 | 535 | ||
@@ -541,14 +538,10 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, | |||
541 | 538 | ||
542 | unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) | 539 | unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) |
543 | { | 540 | { |
544 | struct extent_tree *treevec[EXT_TREE_VEC_SIZE]; | ||
545 | struct extent_tree *et, *next; | 541 | struct extent_tree *et, *next; |
546 | struct extent_node *en, *tmp; | 542 | struct extent_node *en; |
547 | unsigned long ino = F2FS_ROOT_INO(sbi); | ||
548 | unsigned int found; | ||
549 | unsigned int node_cnt = 0, tree_cnt = 0; | 543 | unsigned int node_cnt = 0, tree_cnt = 0; |
550 | int remained; | 544 | int remained; |
551 | bool do_free = false; | ||
552 | 545 | ||
553 | if (!test_opt(sbi, EXTENT_CACHE)) | 546 | if (!test_opt(sbi, EXTENT_CACHE)) |
554 | return 0; | 547 | return 0; |
@@ -563,10 +556,10 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) | |||
563 | list_for_each_entry_safe(et, next, &sbi->zombie_list, list) { | 556 | list_for_each_entry_safe(et, next, &sbi->zombie_list, list) { |
564 | if (atomic_read(&et->node_cnt)) { | 557 | if (atomic_read(&et->node_cnt)) { |
565 | write_lock(&et->lock); | 558 | write_lock(&et->lock); |
566 | node_cnt += __free_extent_tree(sbi, et, true); | 559 | node_cnt += __free_extent_tree(sbi, et); |
567 | write_unlock(&et->lock); | 560 | write_unlock(&et->lock); |
568 | } | 561 | } |
569 | 562 | f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); | |
570 | list_del_init(&et->list); | 563 | list_del_init(&et->list); |
571 | radix_tree_delete(&sbi->extent_tree_root, et->ino); | 564 | radix_tree_delete(&sbi->extent_tree_root, et->ino); |
572 | kmem_cache_free(extent_tree_slab, et); | 565 | kmem_cache_free(extent_tree_slab, et); |
@@ -587,42 +580,29 @@ free_node: | |||
587 | remained = nr_shrink - (node_cnt + tree_cnt); | 580 | remained = nr_shrink - (node_cnt + tree_cnt); |
588 | 581 | ||
589 | spin_lock(&sbi->extent_lock); | 582 | spin_lock(&sbi->extent_lock); |
590 | list_for_each_entry_safe(en, tmp, &sbi->extent_list, list) { | 583 | for (; remained > 0; remained--) { |
591 | if (!remained--) | 584 | if (list_empty(&sbi->extent_list)) |
592 | break; | 585 | break; |
593 | list_del_init(&en->list); | 586 | en = list_first_entry(&sbi->extent_list, |
594 | do_free = true; | 587 | struct extent_node, list); |
595 | } | 588 | et = en->et; |
596 | spin_unlock(&sbi->extent_lock); | 589 | if (!write_trylock(&et->lock)) { |
597 | 590 | /* refresh this extent node's position in extent list */ | |
598 | if (do_free == false) | 591 | list_move_tail(&en->list, &sbi->extent_list); |
599 | goto unlock_out; | 592 | continue; |
600 | 593 | } | |
601 | /* | ||
602 | * reset ino for searching victims from beginning of global extent tree. | ||
603 | */ | ||
604 | ino = F2FS_ROOT_INO(sbi); | ||
605 | |||
606 | while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root, | ||
607 | (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { | ||
608 | unsigned i; | ||
609 | |||
610 | ino = treevec[found - 1]->ino + 1; | ||
611 | for (i = 0; i < found; i++) { | ||
612 | struct extent_tree *et = treevec[i]; | ||
613 | 594 | ||
614 | if (!atomic_read(&et->node_cnt)) | 595 | list_del_init(&en->list); |
615 | continue; | 596 | spin_unlock(&sbi->extent_lock); |
616 | 597 | ||
617 | if (write_trylock(&et->lock)) { | 598 | __detach_extent_node(sbi, et, en); |
618 | node_cnt += __free_extent_tree(sbi, et, false); | ||
619 | write_unlock(&et->lock); | ||
620 | } | ||
621 | 599 | ||
622 | if (node_cnt + tree_cnt >= nr_shrink) | 600 | write_unlock(&et->lock); |
623 | goto unlock_out; | 601 | node_cnt++; |
624 | } | 602 | spin_lock(&sbi->extent_lock); |
625 | } | 603 | } |
604 | spin_unlock(&sbi->extent_lock); | ||
605 | |||
626 | unlock_out: | 606 | unlock_out: |
627 | up_write(&sbi->extent_tree_lock); | 607 | up_write(&sbi->extent_tree_lock); |
628 | out: | 608 | out: |
@@ -641,7 +621,7 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode) | |||
641 | return 0; | 621 | return 0; |
642 | 622 | ||
643 | write_lock(&et->lock); | 623 | write_lock(&et->lock); |
644 | node_cnt = __free_extent_tree(sbi, et, true); | 624 | node_cnt = __free_extent_tree(sbi, et); |
645 | write_unlock(&et->lock); | 625 | write_unlock(&et->lock); |
646 | 626 | ||
647 | return node_cnt; | 627 | return node_cnt; |
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c4e723b99930..4e7eab9f3f5a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -354,6 +354,7 @@ struct extent_node { | |||
354 | struct rb_node rb_node; /* rb node located in rb-tree */ | 354 | struct rb_node rb_node; /* rb node located in rb-tree */ |
355 | struct list_head list; /* node in global extent list of sbi */ | 355 | struct list_head list; /* node in global extent list of sbi */ |
356 | struct extent_info ei; /* extent info */ | 356 | struct extent_info ei; /* extent info */ |
357 | struct extent_tree *et; /* extent tree pointer */ | ||
357 | }; | 358 | }; |
358 | 359 | ||
359 | struct extent_tree { | 360 | struct extent_tree { |