diff options
Diffstat (limited to 'fs/ext4/extents_status.c')
-rw-r--r-- | fs/ext4/extents_status.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index cce152c3c8dc..9f1380e05474 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c | |||
@@ -145,6 +145,9 @@ static struct kmem_cache *ext4_es_cachep; | |||
145 | static int __es_insert_extent(struct inode *inode, struct extent_status *newes); | 145 | static int __es_insert_extent(struct inode *inode, struct extent_status *newes); |
146 | static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, | 146 | static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, |
147 | ext4_lblk_t end); | 147 | ext4_lblk_t end); |
148 | static int __es_try_to_reclaim_extents(struct ext4_inode_info *ei, | ||
149 | int nr_to_scan); | ||
150 | static int ext4_es_reclaim_extents_count(struct super_block *sb); | ||
148 | 151 | ||
149 | int __init ext4_init_es(void) | 152 | int __init ext4_init_es(void) |
150 | { | 153 | { |
@@ -280,6 +283,7 @@ out: | |||
280 | 283 | ||
281 | read_unlock(&EXT4_I(inode)->i_es_lock); | 284 | read_unlock(&EXT4_I(inode)->i_es_lock); |
282 | 285 | ||
286 | ext4_es_lru_add(inode); | ||
283 | trace_ext4_es_find_delayed_extent_exit(inode, es); | 287 | trace_ext4_es_find_delayed_extent_exit(inode, es); |
284 | } | 288 | } |
285 | 289 | ||
@@ -294,11 +298,24 @@ ext4_es_alloc_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, | |||
294 | es->es_lblk = lblk; | 298 | es->es_lblk = lblk; |
295 | es->es_len = len; | 299 | es->es_len = len; |
296 | es->es_pblk = pblk; | 300 | es->es_pblk = pblk; |
301 | |||
302 | /* | ||
303 | * We don't count delayed extent because we never try to reclaim them | ||
304 | */ | ||
305 | if (!ext4_es_is_delayed(es)) | ||
306 | EXT4_I(inode)->i_es_lru_nr++; | ||
307 | |||
297 | return es; | 308 | return es; |
298 | } | 309 | } |
299 | 310 | ||
300 | static void ext4_es_free_extent(struct inode *inode, struct extent_status *es) | 311 | static void ext4_es_free_extent(struct inode *inode, struct extent_status *es) |
301 | { | 312 | { |
313 | /* Decrease the lru counter when this es is not delayed */ | ||
314 | if (!ext4_es_is_delayed(es)) { | ||
315 | BUG_ON(EXT4_I(inode)->i_es_lru_nr == 0); | ||
316 | EXT4_I(inode)->i_es_lru_nr--; | ||
317 | } | ||
318 | |||
302 | kmem_cache_free(ext4_es_cachep, es); | 319 | kmem_cache_free(ext4_es_cachep, es); |
303 | } | 320 | } |
304 | 321 | ||
@@ -456,6 +473,7 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, | |||
456 | error: | 473 | error: |
457 | write_unlock(&EXT4_I(inode)->i_es_lock); | 474 | write_unlock(&EXT4_I(inode)->i_es_lock); |
458 | 475 | ||
476 | ext4_es_lru_add(inode); | ||
459 | ext4_es_print_tree(inode); | 477 | ext4_es_print_tree(inode); |
460 | 478 | ||
461 | return err; | 479 | return err; |
@@ -517,6 +535,7 @@ out: | |||
517 | 535 | ||
518 | read_unlock(&EXT4_I(inode)->i_es_lock); | 536 | read_unlock(&EXT4_I(inode)->i_es_lock); |
519 | 537 | ||
538 | ext4_es_lru_add(inode); | ||
520 | trace_ext4_es_lookup_extent_exit(inode, es, found); | 539 | trace_ext4_es_lookup_extent_exit(inode, es, found); |
521 | return found; | 540 | return found; |
522 | } | 541 | } |
@@ -639,3 +658,140 @@ int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, | |||
639 | ext4_es_print_tree(inode); | 658 | ext4_es_print_tree(inode); |
640 | return err; | 659 | return err; |
641 | } | 660 | } |
661 | |||
662 | static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) | ||
663 | { | ||
664 | struct ext4_sb_info *sbi = container_of(shrink, | ||
665 | struct ext4_sb_info, s_es_shrinker); | ||
666 | struct ext4_inode_info *ei; | ||
667 | struct list_head *cur, *tmp, scanned; | ||
668 | int nr_to_scan = sc->nr_to_scan; | ||
669 | int ret, nr_shrunk = 0; | ||
670 | |||
671 | trace_ext4_es_shrink_enter(sbi->s_sb, nr_to_scan); | ||
672 | |||
673 | if (!nr_to_scan) | ||
674 | return ext4_es_reclaim_extents_count(sbi->s_sb); | ||
675 | |||
676 | INIT_LIST_HEAD(&scanned); | ||
677 | |||
678 | spin_lock(&sbi->s_es_lru_lock); | ||
679 | list_for_each_safe(cur, tmp, &sbi->s_es_lru) { | ||
680 | list_move_tail(cur, &scanned); | ||
681 | |||
682 | ei = list_entry(cur, struct ext4_inode_info, i_es_lru); | ||
683 | |||
684 | read_lock(&ei->i_es_lock); | ||
685 | if (ei->i_es_lru_nr == 0) { | ||
686 | read_unlock(&ei->i_es_lock); | ||
687 | continue; | ||
688 | } | ||
689 | read_unlock(&ei->i_es_lock); | ||
690 | |||
691 | write_lock(&ei->i_es_lock); | ||
692 | ret = __es_try_to_reclaim_extents(ei, nr_to_scan); | ||
693 | write_unlock(&ei->i_es_lock); | ||
694 | |||
695 | nr_shrunk += ret; | ||
696 | nr_to_scan -= ret; | ||
697 | if (nr_to_scan == 0) | ||
698 | break; | ||
699 | } | ||
700 | list_splice_tail(&scanned, &sbi->s_es_lru); | ||
701 | spin_unlock(&sbi->s_es_lru_lock); | ||
702 | trace_ext4_es_shrink_exit(sbi->s_sb, nr_shrunk); | ||
703 | |||
704 | return ext4_es_reclaim_extents_count(sbi->s_sb); | ||
705 | } | ||
706 | |||
707 | void ext4_es_register_shrinker(struct super_block *sb) | ||
708 | { | ||
709 | struct ext4_sb_info *sbi; | ||
710 | |||
711 | sbi = EXT4_SB(sb); | ||
712 | INIT_LIST_HEAD(&sbi->s_es_lru); | ||
713 | spin_lock_init(&sbi->s_es_lru_lock); | ||
714 | sbi->s_es_shrinker.shrink = ext4_es_shrink; | ||
715 | sbi->s_es_shrinker.seeks = DEFAULT_SEEKS; | ||
716 | register_shrinker(&sbi->s_es_shrinker); | ||
717 | } | ||
718 | |||
719 | void ext4_es_unregister_shrinker(struct super_block *sb) | ||
720 | { | ||
721 | unregister_shrinker(&EXT4_SB(sb)->s_es_shrinker); | ||
722 | } | ||
723 | |||
724 | void ext4_es_lru_add(struct inode *inode) | ||
725 | { | ||
726 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
727 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); | ||
728 | |||
729 | spin_lock(&sbi->s_es_lru_lock); | ||
730 | if (list_empty(&ei->i_es_lru)) | ||
731 | list_add_tail(&ei->i_es_lru, &sbi->s_es_lru); | ||
732 | else | ||
733 | list_move_tail(&ei->i_es_lru, &sbi->s_es_lru); | ||
734 | spin_unlock(&sbi->s_es_lru_lock); | ||
735 | } | ||
736 | |||
737 | void ext4_es_lru_del(struct inode *inode) | ||
738 | { | ||
739 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
740 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); | ||
741 | |||
742 | spin_lock(&sbi->s_es_lru_lock); | ||
743 | if (!list_empty(&ei->i_es_lru)) | ||
744 | list_del_init(&ei->i_es_lru); | ||
745 | spin_unlock(&sbi->s_es_lru_lock); | ||
746 | } | ||
747 | |||
748 | static int ext4_es_reclaim_extents_count(struct super_block *sb) | ||
749 | { | ||
750 | struct ext4_sb_info *sbi = EXT4_SB(sb); | ||
751 | struct ext4_inode_info *ei; | ||
752 | struct list_head *cur; | ||
753 | int nr_cached = 0; | ||
754 | |||
755 | spin_lock(&sbi->s_es_lru_lock); | ||
756 | list_for_each(cur, &sbi->s_es_lru) { | ||
757 | ei = list_entry(cur, struct ext4_inode_info, i_es_lru); | ||
758 | read_lock(&ei->i_es_lock); | ||
759 | nr_cached += ei->i_es_lru_nr; | ||
760 | read_unlock(&ei->i_es_lock); | ||
761 | } | ||
762 | spin_unlock(&sbi->s_es_lru_lock); | ||
763 | trace_ext4_es_reclaim_extents_count(sb, nr_cached); | ||
764 | return nr_cached; | ||
765 | } | ||
766 | |||
767 | static int __es_try_to_reclaim_extents(struct ext4_inode_info *ei, | ||
768 | int nr_to_scan) | ||
769 | { | ||
770 | struct inode *inode = &ei->vfs_inode; | ||
771 | struct ext4_es_tree *tree = &ei->i_es_tree; | ||
772 | struct rb_node *node; | ||
773 | struct extent_status *es; | ||
774 | int nr_shrunk = 0; | ||
775 | |||
776 | if (ei->i_es_lru_nr == 0) | ||
777 | return 0; | ||
778 | |||
779 | node = rb_first(&tree->root); | ||
780 | while (node != NULL) { | ||
781 | es = rb_entry(node, struct extent_status, rb_node); | ||
782 | node = rb_next(&es->rb_node); | ||
783 | /* | ||
784 | * We can't reclaim delayed extent from status tree because | ||
785 | * fiemap, bigallic, and seek_data/hole need to use it. | ||
786 | */ | ||
787 | if (!ext4_es_is_delayed(es)) { | ||
788 | rb_erase(&es->rb_node, &tree->root); | ||
789 | ext4_es_free_extent(inode, es); | ||
790 | nr_shrunk++; | ||
791 | if (--nr_to_scan == 0) | ||
792 | break; | ||
793 | } | ||
794 | } | ||
795 | tree->cache_es = NULL; | ||
796 | return nr_shrunk; | ||
797 | } | ||