diff options
Diffstat (limited to 'fs/f2fs/extent_cache.c')
-rw-r--r-- | fs/f2fs/extent_cache.c | 122 |
1 files changed, 68 insertions, 54 deletions
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 7ddba812e11b..ccd5c636d3fe 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c | |||
@@ -36,7 +36,7 @@ static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi, | |||
36 | 36 | ||
37 | rb_link_node(&en->rb_node, parent, p); | 37 | rb_link_node(&en->rb_node, parent, p); |
38 | rb_insert_color(&en->rb_node, &et->root); | 38 | rb_insert_color(&en->rb_node, &et->root); |
39 | et->count++; | 39 | atomic_inc(&et->node_cnt); |
40 | atomic_inc(&sbi->total_ext_node); | 40 | atomic_inc(&sbi->total_ext_node); |
41 | return en; | 41 | return en; |
42 | } | 42 | } |
@@ -45,7 +45,7 @@ static void __detach_extent_node(struct f2fs_sb_info *sbi, | |||
45 | struct extent_tree *et, struct extent_node *en) | 45 | struct extent_tree *et, struct extent_node *en) |
46 | { | 46 | { |
47 | rb_erase(&en->rb_node, &et->root); | 47 | rb_erase(&en->rb_node, &et->root); |
48 | et->count--; | 48 | atomic_dec(&et->node_cnt); |
49 | atomic_dec(&sbi->total_ext_node); | 49 | atomic_dec(&sbi->total_ext_node); |
50 | 50 | ||
51 | if (et->cached_en == en) | 51 | if (et->cached_en == en) |
@@ -68,11 +68,13 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode) | |||
68 | et->root = RB_ROOT; | 68 | et->root = RB_ROOT; |
69 | et->cached_en = NULL; | 69 | et->cached_en = NULL; |
70 | rwlock_init(&et->lock); | 70 | rwlock_init(&et->lock); |
71 | atomic_set(&et->refcount, 0); | 71 | INIT_LIST_HEAD(&et->list); |
72 | et->count = 0; | 72 | atomic_set(&et->node_cnt, 0); |
73 | sbi->total_ext_tree++; | 73 | atomic_inc(&sbi->total_ext_tree); |
74 | } else { | ||
75 | atomic_dec(&sbi->total_zombie_tree); | ||
76 | list_del_init(&et->list); | ||
74 | } | 77 | } |
75 | atomic_inc(&et->refcount); | ||
76 | up_write(&sbi->extent_tree_lock); | 78 | up_write(&sbi->extent_tree_lock); |
77 | 79 | ||
78 | /* never died until evict_inode */ | 80 | /* never died until evict_inode */ |
@@ -131,7 +133,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, | |||
131 | { | 133 | { |
132 | struct rb_node *node, *next; | 134 | struct rb_node *node, *next; |
133 | struct extent_node *en; | 135 | struct extent_node *en; |
134 | unsigned int count = et->count; | 136 | unsigned int count = atomic_read(&et->node_cnt); |
135 | 137 | ||
136 | node = rb_first(&et->root); | 138 | node = rb_first(&et->root); |
137 | while (node) { | 139 | while (node) { |
@@ -152,7 +154,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, | |||
152 | node = next; | 154 | node = next; |
153 | } | 155 | } |
154 | 156 | ||
155 | return count - et->count; | 157 | return count - atomic_read(&et->node_cnt); |
156 | } | 158 | } |
157 | 159 | ||
158 | static void __drop_largest_extent(struct inode *inode, | 160 | static void __drop_largest_extent(struct inode *inode, |
@@ -164,34 +166,33 @@ static void __drop_largest_extent(struct inode *inode, | |||
164 | largest->len = 0; | 166 | largest->len = 0; |
165 | } | 167 | } |
166 | 168 | ||
167 | void f2fs_drop_largest_extent(struct inode *inode, pgoff_t fofs) | 169 | /* return true, if inode page is changed */ |
168 | { | 170 | bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) |
169 | if (!f2fs_may_extent_tree(inode)) | ||
170 | return; | ||
171 | |||
172 | __drop_largest_extent(inode, fofs, 1); | ||
173 | } | ||
174 | |||
175 | void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) | ||
176 | { | 171 | { |
177 | struct f2fs_sb_info *sbi = F2FS_I_SB(inode); | 172 | struct f2fs_sb_info *sbi = F2FS_I_SB(inode); |
178 | struct extent_tree *et; | 173 | struct extent_tree *et; |
179 | struct extent_node *en; | 174 | struct extent_node *en; |
180 | struct extent_info ei; | 175 | struct extent_info ei; |
181 | 176 | ||
182 | if (!f2fs_may_extent_tree(inode)) | 177 | if (!f2fs_may_extent_tree(inode)) { |
183 | return; | 178 | /* drop largest extent */ |
179 | if (i_ext && i_ext->len) { | ||
180 | i_ext->len = 0; | ||
181 | return true; | ||
182 | } | ||
183 | return false; | ||
184 | } | ||
184 | 185 | ||
185 | et = __grab_extent_tree(inode); | 186 | et = __grab_extent_tree(inode); |
186 | 187 | ||
187 | if (!i_ext || le32_to_cpu(i_ext->len) < F2FS_MIN_EXTENT_LEN) | 188 | if (!i_ext || !i_ext->len) |
188 | return; | 189 | return false; |
189 | 190 | ||
190 | set_extent_info(&ei, le32_to_cpu(i_ext->fofs), | 191 | set_extent_info(&ei, le32_to_cpu(i_ext->fofs), |
191 | le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len)); | 192 | le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len)); |
192 | 193 | ||
193 | write_lock(&et->lock); | 194 | write_lock(&et->lock); |
194 | if (et->count) | 195 | if (atomic_read(&et->node_cnt)) |
195 | goto out; | 196 | goto out; |
196 | 197 | ||
197 | en = __init_extent_tree(sbi, et, &ei); | 198 | en = __init_extent_tree(sbi, et, &ei); |
@@ -202,6 +203,7 @@ void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) | |||
202 | } | 203 | } |
203 | out: | 204 | out: |
204 | write_unlock(&et->lock); | 205 | write_unlock(&et->lock); |
206 | return false; | ||
205 | } | 207 | } |
206 | 208 | ||
207 | static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, | 209 | static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, |
@@ -549,45 +551,44 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, | |||
549 | unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) | 551 | unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) |
550 | { | 552 | { |
551 | struct extent_tree *treevec[EXT_TREE_VEC_SIZE]; | 553 | struct extent_tree *treevec[EXT_TREE_VEC_SIZE]; |
554 | struct extent_tree *et, *next; | ||
552 | struct extent_node *en, *tmp; | 555 | struct extent_node *en, *tmp; |
553 | unsigned long ino = F2FS_ROOT_INO(sbi); | 556 | unsigned long ino = F2FS_ROOT_INO(sbi); |
554 | struct radix_tree_root *root = &sbi->extent_tree_root; | ||
555 | unsigned int found; | 557 | unsigned int found; |
556 | unsigned int node_cnt = 0, tree_cnt = 0; | 558 | unsigned int node_cnt = 0, tree_cnt = 0; |
557 | int remained; | 559 | int remained; |
560 | bool do_free = false; | ||
558 | 561 | ||
559 | if (!test_opt(sbi, EXTENT_CACHE)) | 562 | if (!test_opt(sbi, EXTENT_CACHE)) |
560 | return 0; | 563 | return 0; |
561 | 564 | ||
565 | if (!atomic_read(&sbi->total_zombie_tree)) | ||
566 | goto free_node; | ||
567 | |||
562 | if (!down_write_trylock(&sbi->extent_tree_lock)) | 568 | if (!down_write_trylock(&sbi->extent_tree_lock)) |
563 | goto out; | 569 | goto out; |
564 | 570 | ||
565 | /* 1. remove unreferenced extent tree */ | 571 | /* 1. remove unreferenced extent tree */ |
566 | while ((found = radix_tree_gang_lookup(root, | 572 | list_for_each_entry_safe(et, next, &sbi->zombie_list, list) { |
567 | (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { | 573 | if (atomic_read(&et->node_cnt)) { |
568 | unsigned i; | 574 | write_lock(&et->lock); |
569 | 575 | node_cnt += __free_extent_tree(sbi, et, true); | |
570 | ino = treevec[found - 1]->ino + 1; | 576 | write_unlock(&et->lock); |
571 | for (i = 0; i < found; i++) { | 577 | } |
572 | struct extent_tree *et = treevec[i]; | ||
573 | |||
574 | if (!atomic_read(&et->refcount)) { | ||
575 | write_lock(&et->lock); | ||
576 | node_cnt += __free_extent_tree(sbi, et, true); | ||
577 | write_unlock(&et->lock); | ||
578 | 578 | ||
579 | radix_tree_delete(root, et->ino); | 579 | list_del_init(&et->list); |
580 | kmem_cache_free(extent_tree_slab, et); | 580 | radix_tree_delete(&sbi->extent_tree_root, et->ino); |
581 | sbi->total_ext_tree--; | 581 | kmem_cache_free(extent_tree_slab, et); |
582 | tree_cnt++; | 582 | atomic_dec(&sbi->total_ext_tree); |
583 | atomic_dec(&sbi->total_zombie_tree); | ||
584 | tree_cnt++; | ||
583 | 585 | ||
584 | if (node_cnt + tree_cnt >= nr_shrink) | 586 | if (node_cnt + tree_cnt >= nr_shrink) |
585 | goto unlock_out; | 587 | goto unlock_out; |
586 | } | ||
587 | } | ||
588 | } | 588 | } |
589 | up_write(&sbi->extent_tree_lock); | 589 | up_write(&sbi->extent_tree_lock); |
590 | 590 | ||
591 | free_node: | ||
591 | /* 2. remove LRU extent entries */ | 592 | /* 2. remove LRU extent entries */ |
592 | if (!down_write_trylock(&sbi->extent_tree_lock)) | 593 | if (!down_write_trylock(&sbi->extent_tree_lock)) |
593 | goto out; | 594 | goto out; |
@@ -599,15 +600,19 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) | |||
599 | if (!remained--) | 600 | if (!remained--) |
600 | break; | 601 | break; |
601 | list_del_init(&en->list); | 602 | list_del_init(&en->list); |
603 | do_free = true; | ||
602 | } | 604 | } |
603 | spin_unlock(&sbi->extent_lock); | 605 | spin_unlock(&sbi->extent_lock); |
604 | 606 | ||
607 | if (do_free == false) | ||
608 | goto unlock_out; | ||
609 | |||
605 | /* | 610 | /* |
606 | * reset ino for searching victims from beginning of global extent tree. | 611 | * reset ino for searching victims from beginning of global extent tree. |
607 | */ | 612 | */ |
608 | ino = F2FS_ROOT_INO(sbi); | 613 | ino = F2FS_ROOT_INO(sbi); |
609 | 614 | ||
610 | while ((found = radix_tree_gang_lookup(root, | 615 | while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root, |
611 | (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { | 616 | (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { |
612 | unsigned i; | 617 | unsigned i; |
613 | 618 | ||
@@ -615,9 +620,13 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) | |||
615 | for (i = 0; i < found; i++) { | 620 | for (i = 0; i < found; i++) { |
616 | struct extent_tree *et = treevec[i]; | 621 | struct extent_tree *et = treevec[i]; |
617 | 622 | ||
618 | write_lock(&et->lock); | 623 | if (!atomic_read(&et->node_cnt)) |
619 | node_cnt += __free_extent_tree(sbi, et, false); | 624 | continue; |
620 | write_unlock(&et->lock); | 625 | |
626 | if (write_trylock(&et->lock)) { | ||
627 | node_cnt += __free_extent_tree(sbi, et, false); | ||
628 | write_unlock(&et->lock); | ||
629 | } | ||
621 | 630 | ||
622 | if (node_cnt + tree_cnt >= nr_shrink) | 631 | if (node_cnt + tree_cnt >= nr_shrink) |
623 | goto unlock_out; | 632 | goto unlock_out; |
@@ -637,7 +646,7 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode) | |||
637 | struct extent_tree *et = F2FS_I(inode)->extent_tree; | 646 | struct extent_tree *et = F2FS_I(inode)->extent_tree; |
638 | unsigned int node_cnt = 0; | 647 | unsigned int node_cnt = 0; |
639 | 648 | ||
640 | if (!et) | 649 | if (!et || !atomic_read(&et->node_cnt)) |
641 | return 0; | 650 | return 0; |
642 | 651 | ||
643 | write_lock(&et->lock); | 652 | write_lock(&et->lock); |
@@ -656,8 +665,12 @@ void f2fs_destroy_extent_tree(struct inode *inode) | |||
656 | if (!et) | 665 | if (!et) |
657 | return; | 666 | return; |
658 | 667 | ||
659 | if (inode->i_nlink && !is_bad_inode(inode) && et->count) { | 668 | if (inode->i_nlink && !is_bad_inode(inode) && |
660 | atomic_dec(&et->refcount); | 669 | atomic_read(&et->node_cnt)) { |
670 | down_write(&sbi->extent_tree_lock); | ||
671 | list_add_tail(&et->list, &sbi->zombie_list); | ||
672 | atomic_inc(&sbi->total_zombie_tree); | ||
673 | up_write(&sbi->extent_tree_lock); | ||
661 | return; | 674 | return; |
662 | } | 675 | } |
663 | 676 | ||
@@ -666,11 +679,10 @@ void f2fs_destroy_extent_tree(struct inode *inode) | |||
666 | 679 | ||
667 | /* delete extent tree entry in radix tree */ | 680 | /* delete extent tree entry in radix tree */ |
668 | down_write(&sbi->extent_tree_lock); | 681 | down_write(&sbi->extent_tree_lock); |
669 | atomic_dec(&et->refcount); | 682 | f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); |
670 | f2fs_bug_on(sbi, atomic_read(&et->refcount) || et->count); | ||
671 | radix_tree_delete(&sbi->extent_tree_root, inode->i_ino); | 683 | radix_tree_delete(&sbi->extent_tree_root, inode->i_ino); |
672 | kmem_cache_free(extent_tree_slab, et); | 684 | kmem_cache_free(extent_tree_slab, et); |
673 | sbi->total_ext_tree--; | 685 | atomic_dec(&sbi->total_ext_tree); |
674 | up_write(&sbi->extent_tree_lock); | 686 | up_write(&sbi->extent_tree_lock); |
675 | 687 | ||
676 | F2FS_I(inode)->extent_tree = NULL; | 688 | F2FS_I(inode)->extent_tree = NULL; |
@@ -722,7 +734,9 @@ void init_extent_cache_info(struct f2fs_sb_info *sbi) | |||
722 | init_rwsem(&sbi->extent_tree_lock); | 734 | init_rwsem(&sbi->extent_tree_lock); |
723 | INIT_LIST_HEAD(&sbi->extent_list); | 735 | INIT_LIST_HEAD(&sbi->extent_list); |
724 | spin_lock_init(&sbi->extent_lock); | 736 | spin_lock_init(&sbi->extent_lock); |
725 | sbi->total_ext_tree = 0; | 737 | atomic_set(&sbi->total_ext_tree, 0); |
738 | INIT_LIST_HEAD(&sbi->zombie_list); | ||
739 | atomic_set(&sbi->total_zombie_tree, 0); | ||
726 | atomic_set(&sbi->total_ext_node, 0); | 740 | atomic_set(&sbi->total_ext_node, 0); |
727 | } | 741 | } |
728 | 742 | ||