diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/ext4.h | 2 | ||||
-rw-r--r-- | fs/ext4/extents_status.c | 75 | ||||
-rw-r--r-- | fs/ext4/extents_status.h | 5 | ||||
-rw-r--r-- | fs/ext4/inode.c | 4 | ||||
-rw-r--r-- | fs/ext4/super.c | 7 |
5 files changed, 68 insertions, 25 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index f85f1fb49df8..f5f3b6c58240 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -864,6 +864,7 @@ struct ext4_inode_info { | |||
864 | rwlock_t i_es_lock; | 864 | rwlock_t i_es_lock; |
865 | struct list_head i_es_lru; | 865 | struct list_head i_es_lru; |
866 | unsigned int i_es_lru_nr; /* protected by i_es_lock */ | 866 | unsigned int i_es_lru_nr; /* protected by i_es_lock */ |
867 | unsigned long i_touch_when; /* jiffies of last accessing */ | ||
867 | 868 | ||
868 | /* ialloc */ | 869 | /* ialloc */ |
869 | ext4_group_t i_last_alloc_group; | 870 | ext4_group_t i_last_alloc_group; |
@@ -1303,6 +1304,7 @@ struct ext4_sb_info { | |||
1303 | /* Reclaim extents from extent status tree */ | 1304 | /* Reclaim extents from extent status tree */ |
1304 | struct shrinker s_es_shrinker; | 1305 | struct shrinker s_es_shrinker; |
1305 | struct list_head s_es_lru; | 1306 | struct list_head s_es_lru; |
1307 | unsigned long s_es_last_sorted; | ||
1306 | struct percpu_counter s_extent_cache_cnt; | 1308 | struct percpu_counter s_extent_cache_cnt; |
1307 | spinlock_t s_es_lru_lock ____cacheline_aligned_in_smp; | 1309 | spinlock_t s_es_lru_lock ____cacheline_aligned_in_smp; |
1308 | }; | 1310 | }; |
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index e6941e622d31..ee018d5f397e 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c | |||
@@ -10,6 +10,7 @@ | |||
10 | * Ext4 extents status tree core functions. | 10 | * Ext4 extents status tree core functions. |
11 | */ | 11 | */ |
12 | #include <linux/rbtree.h> | 12 | #include <linux/rbtree.h> |
13 | #include <linux/list_sort.h> | ||
13 | #include "ext4.h" | 14 | #include "ext4.h" |
14 | #include "extents_status.h" | 15 | #include "extents_status.h" |
15 | #include "ext4_extents.h" | 16 | #include "ext4_extents.h" |
@@ -291,7 +292,6 @@ out: | |||
291 | 292 | ||
292 | read_unlock(&EXT4_I(inode)->i_es_lock); | 293 | read_unlock(&EXT4_I(inode)->i_es_lock); |
293 | 294 | ||
294 | ext4_es_lru_add(inode); | ||
295 | trace_ext4_es_find_delayed_extent_range_exit(inode, es); | 295 | trace_ext4_es_find_delayed_extent_range_exit(inode, es); |
296 | } | 296 | } |
297 | 297 | ||
@@ -672,7 +672,6 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, | |||
672 | error: | 672 | error: |
673 | write_unlock(&EXT4_I(inode)->i_es_lock); | 673 | write_unlock(&EXT4_I(inode)->i_es_lock); |
674 | 674 | ||
675 | ext4_es_lru_add(inode); | ||
676 | ext4_es_print_tree(inode); | 675 | ext4_es_print_tree(inode); |
677 | 676 | ||
678 | return err; | 677 | return err; |
@@ -734,7 +733,6 @@ out: | |||
734 | 733 | ||
735 | read_unlock(&EXT4_I(inode)->i_es_lock); | 734 | read_unlock(&EXT4_I(inode)->i_es_lock); |
736 | 735 | ||
737 | ext4_es_lru_add(inode); | ||
738 | trace_ext4_es_lookup_extent_exit(inode, es, found); | 736 | trace_ext4_es_lookup_extent_exit(inode, es, found); |
739 | return found; | 737 | return found; |
740 | } | 738 | } |
@@ -878,12 +876,28 @@ int ext4_es_zeroout(struct inode *inode, struct ext4_extent *ex) | |||
878 | EXTENT_STATUS_WRITTEN); | 876 | EXTENT_STATUS_WRITTEN); |
879 | } | 877 | } |
880 | 878 | ||
879 | static int ext4_inode_touch_time_cmp(void *priv, struct list_head *a, | ||
880 | struct list_head *b) | ||
881 | { | ||
882 | struct ext4_inode_info *eia, *eib; | ||
883 | eia = list_entry(a, struct ext4_inode_info, i_es_lru); | ||
884 | eib = list_entry(b, struct ext4_inode_info, i_es_lru); | ||
885 | |||
886 | if (eia->i_touch_when == eib->i_touch_when) | ||
887 | return 0; | ||
888 | if (time_after(eia->i_touch_when, eib->i_touch_when)) | ||
889 | return 1; | ||
890 | else | ||
891 | return -1; | ||
892 | } | ||
893 | |||
881 | static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) | 894 | static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) |
882 | { | 895 | { |
883 | struct ext4_sb_info *sbi = container_of(shrink, | 896 | struct ext4_sb_info *sbi = container_of(shrink, |
884 | struct ext4_sb_info, s_es_shrinker); | 897 | struct ext4_sb_info, s_es_shrinker); |
885 | struct ext4_inode_info *ei; | 898 | struct ext4_inode_info *ei; |
886 | struct list_head *cur, *tmp, scanned; | 899 | struct list_head *cur, *tmp; |
900 | LIST_HEAD(skiped); | ||
887 | int nr_to_scan = sc->nr_to_scan; | 901 | int nr_to_scan = sc->nr_to_scan; |
888 | int ret, nr_shrunk = 0; | 902 | int ret, nr_shrunk = 0; |
889 | 903 | ||
@@ -893,23 +907,41 @@ static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) | |||
893 | if (!nr_to_scan) | 907 | if (!nr_to_scan) |
894 | return ret; | 908 | return ret; |
895 | 909 | ||
896 | INIT_LIST_HEAD(&scanned); | ||
897 | |||
898 | spin_lock(&sbi->s_es_lru_lock); | 910 | spin_lock(&sbi->s_es_lru_lock); |
911 | |||
912 | /* | ||
913 | * If the inode that is at the head of LRU list is newer than | ||
914 | * last_sorted time, that means that we need to sort this list. | ||
915 | */ | ||
916 | ei = list_first_entry(&sbi->s_es_lru, struct ext4_inode_info, i_es_lru); | ||
917 | if (sbi->s_es_last_sorted < ei->i_touch_when) { | ||
918 | list_sort(NULL, &sbi->s_es_lru, ext4_inode_touch_time_cmp); | ||
919 | sbi->s_es_last_sorted = jiffies; | ||
920 | } | ||
921 | |||
899 | list_for_each_safe(cur, tmp, &sbi->s_es_lru) { | 922 | list_for_each_safe(cur, tmp, &sbi->s_es_lru) { |
900 | list_move_tail(cur, &scanned); | 923 | /* |
924 | * If we have already reclaimed all extents from extent | ||
925 | * status tree, just stop the loop immediately. | ||
926 | */ | ||
927 | if (percpu_counter_read_positive(&sbi->s_extent_cache_cnt) == 0) | ||
928 | break; | ||
901 | 929 | ||
902 | ei = list_entry(cur, struct ext4_inode_info, i_es_lru); | 930 | ei = list_entry(cur, struct ext4_inode_info, i_es_lru); |
903 | 931 | ||
904 | read_lock(&ei->i_es_lock); | 932 | /* Skip the inode that is newer than the last_sorted time */ |
905 | if (ei->i_es_lru_nr == 0) { | 933 | if (sbi->s_es_last_sorted < ei->i_touch_when) { |
906 | read_unlock(&ei->i_es_lock); | 934 | list_move_tail(cur, &skiped); |
907 | continue; | 935 | continue; |
908 | } | 936 | } |
909 | read_unlock(&ei->i_es_lock); | 937 | |
938 | if (ei->i_es_lru_nr == 0) | ||
939 | continue; | ||
910 | 940 | ||
911 | write_lock(&ei->i_es_lock); | 941 | write_lock(&ei->i_es_lock); |
912 | ret = __es_try_to_reclaim_extents(ei, nr_to_scan); | 942 | ret = __es_try_to_reclaim_extents(ei, nr_to_scan); |
943 | if (ei->i_es_lru_nr == 0) | ||
944 | list_del_init(&ei->i_es_lru); | ||
913 | write_unlock(&ei->i_es_lock); | 945 | write_unlock(&ei->i_es_lock); |
914 | 946 | ||
915 | nr_shrunk += ret; | 947 | nr_shrunk += ret; |
@@ -917,7 +949,9 @@ static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) | |||
917 | if (nr_to_scan == 0) | 949 | if (nr_to_scan == 0) |
918 | break; | 950 | break; |
919 | } | 951 | } |
920 | list_splice_tail(&scanned, &sbi->s_es_lru); | 952 | |
953 | /* Move the newer inodes into the tail of the LRU list. */ | ||
954 | list_splice_tail(&skiped, &sbi->s_es_lru); | ||
921 | spin_unlock(&sbi->s_es_lru_lock); | 955 | spin_unlock(&sbi->s_es_lru_lock); |
922 | 956 | ||
923 | ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt); | 957 | ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt); |
@@ -925,21 +959,19 @@ static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) | |||
925 | return ret; | 959 | return ret; |
926 | } | 960 | } |
927 | 961 | ||
928 | void ext4_es_register_shrinker(struct super_block *sb) | 962 | void ext4_es_register_shrinker(struct ext4_sb_info *sbi) |
929 | { | 963 | { |
930 | struct ext4_sb_info *sbi; | ||
931 | |||
932 | sbi = EXT4_SB(sb); | ||
933 | INIT_LIST_HEAD(&sbi->s_es_lru); | 964 | INIT_LIST_HEAD(&sbi->s_es_lru); |
934 | spin_lock_init(&sbi->s_es_lru_lock); | 965 | spin_lock_init(&sbi->s_es_lru_lock); |
966 | sbi->s_es_last_sorted = 0; | ||
935 | sbi->s_es_shrinker.shrink = ext4_es_shrink; | 967 | sbi->s_es_shrinker.shrink = ext4_es_shrink; |
936 | sbi->s_es_shrinker.seeks = DEFAULT_SEEKS; | 968 | sbi->s_es_shrinker.seeks = DEFAULT_SEEKS; |
937 | register_shrinker(&sbi->s_es_shrinker); | 969 | register_shrinker(&sbi->s_es_shrinker); |
938 | } | 970 | } |
939 | 971 | ||
940 | void ext4_es_unregister_shrinker(struct super_block *sb) | 972 | void ext4_es_unregister_shrinker(struct ext4_sb_info *sbi) |
941 | { | 973 | { |
942 | unregister_shrinker(&EXT4_SB(sb)->s_es_shrinker); | 974 | unregister_shrinker(&sbi->s_es_shrinker); |
943 | } | 975 | } |
944 | 976 | ||
945 | void ext4_es_lru_add(struct inode *inode) | 977 | void ext4_es_lru_add(struct inode *inode) |
@@ -947,11 +979,14 @@ void ext4_es_lru_add(struct inode *inode) | |||
947 | struct ext4_inode_info *ei = EXT4_I(inode); | 979 | struct ext4_inode_info *ei = EXT4_I(inode); |
948 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); | 980 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); |
949 | 981 | ||
982 | ei->i_touch_when = jiffies; | ||
983 | |||
984 | if (!list_empty(&ei->i_es_lru)) | ||
985 | return; | ||
986 | |||
950 | spin_lock(&sbi->s_es_lru_lock); | 987 | spin_lock(&sbi->s_es_lru_lock); |
951 | if (list_empty(&ei->i_es_lru)) | 988 | if (list_empty(&ei->i_es_lru)) |
952 | list_add_tail(&ei->i_es_lru, &sbi->s_es_lru); | 989 | list_add_tail(&ei->i_es_lru, &sbi->s_es_lru); |
953 | else | ||
954 | list_move_tail(&ei->i_es_lru, &sbi->s_es_lru); | ||
955 | spin_unlock(&sbi->s_es_lru_lock); | 990 | spin_unlock(&sbi->s_es_lru_lock); |
956 | } | 991 | } |
957 | 992 | ||
diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index f740eb03b707..e936730cc5b0 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h | |||
@@ -39,6 +39,7 @@ | |||
39 | EXTENT_STATUS_DELAYED | \ | 39 | EXTENT_STATUS_DELAYED | \ |
40 | EXTENT_STATUS_HOLE) | 40 | EXTENT_STATUS_HOLE) |
41 | 41 | ||
42 | struct ext4_sb_info; | ||
42 | struct ext4_extent; | 43 | struct ext4_extent; |
43 | 44 | ||
44 | struct extent_status { | 45 | struct extent_status { |
@@ -119,8 +120,8 @@ static inline void ext4_es_store_status(struct extent_status *es, | |||
119 | es->es_pblk = block; | 120 | es->es_pblk = block; |
120 | } | 121 | } |
121 | 122 | ||
122 | extern void ext4_es_register_shrinker(struct super_block *sb); | 123 | extern void ext4_es_register_shrinker(struct ext4_sb_info *sbi); |
123 | extern void ext4_es_unregister_shrinker(struct super_block *sb); | 124 | extern void ext4_es_unregister_shrinker(struct ext4_sb_info *sbi); |
124 | extern void ext4_es_lru_add(struct inode *inode); | 125 | extern void ext4_es_lru_add(struct inode *inode); |
125 | extern void ext4_es_lru_del(struct inode *inode); | 126 | extern void ext4_es_lru_del(struct inode *inode); |
126 | 127 | ||
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 0db830d541ec..f9ba51f68777 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -514,6 +514,8 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, | |||
514 | "logical block %lu\n", inode->i_ino, flags, map->m_len, | 514 | "logical block %lu\n", inode->i_ino, flags, map->m_len, |
515 | (unsigned long) map->m_lblk); | 515 | (unsigned long) map->m_lblk); |
516 | 516 | ||
517 | ext4_es_lru_add(inode); | ||
518 | |||
517 | /* Lookup extent status tree firstly */ | 519 | /* Lookup extent status tree firstly */ |
518 | if (ext4_es_lookup_extent(inode, map->m_lblk, &es)) { | 520 | if (ext4_es_lookup_extent(inode, map->m_lblk, &es)) { |
519 | if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) { | 521 | if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) { |
@@ -1526,6 +1528,8 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, | |||
1526 | "logical block %lu\n", inode->i_ino, map->m_len, | 1528 | "logical block %lu\n", inode->i_ino, map->m_len, |
1527 | (unsigned long) map->m_lblk); | 1529 | (unsigned long) map->m_lblk); |
1528 | 1530 | ||
1531 | ext4_es_lru_add(inode); | ||
1532 | |||
1529 | /* Lookup extent status tree firstly */ | 1533 | /* Lookup extent status tree firstly */ |
1530 | if (ext4_es_lookup_extent(inode, iblock, &es)) { | 1534 | if (ext4_es_lookup_extent(inode, iblock, &es)) { |
1531 | 1535 | ||
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 54701fca4515..cc8201180b30 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
@@ -773,7 +773,7 @@ static void ext4_put_super(struct super_block *sb) | |||
773 | ext4_abort(sb, "Couldn't clean up the journal"); | 773 | ext4_abort(sb, "Couldn't clean up the journal"); |
774 | } | 774 | } |
775 | 775 | ||
776 | ext4_es_unregister_shrinker(sb); | 776 | ext4_es_unregister_shrinker(sbi); |
777 | del_timer(&sbi->s_err_report); | 777 | del_timer(&sbi->s_err_report); |
778 | ext4_release_system_zone(sb); | 778 | ext4_release_system_zone(sb); |
779 | ext4_mb_release(sb); | 779 | ext4_mb_release(sb); |
@@ -862,6 +862,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) | |||
862 | rwlock_init(&ei->i_es_lock); | 862 | rwlock_init(&ei->i_es_lock); |
863 | INIT_LIST_HEAD(&ei->i_es_lru); | 863 | INIT_LIST_HEAD(&ei->i_es_lru); |
864 | ei->i_es_lru_nr = 0; | 864 | ei->i_es_lru_nr = 0; |
865 | ei->i_touch_when = 0; | ||
865 | ei->i_reserved_data_blocks = 0; | 866 | ei->i_reserved_data_blocks = 0; |
866 | ei->i_reserved_meta_blocks = 0; | 867 | ei->i_reserved_meta_blocks = 0; |
867 | ei->i_allocated_meta_blocks = 0; | 868 | ei->i_allocated_meta_blocks = 0; |
@@ -3799,7 +3800,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) | |||
3799 | sbi->s_err_report.data = (unsigned long) sb; | 3800 | sbi->s_err_report.data = (unsigned long) sb; |
3800 | 3801 | ||
3801 | /* Register extent status tree shrinker */ | 3802 | /* Register extent status tree shrinker */ |
3802 | ext4_es_register_shrinker(sb); | 3803 | ext4_es_register_shrinker(sbi); |
3803 | 3804 | ||
3804 | err = percpu_counter_init(&sbi->s_freeclusters_counter, | 3805 | err = percpu_counter_init(&sbi->s_freeclusters_counter, |
3805 | ext4_count_free_clusters(sb)); | 3806 | ext4_count_free_clusters(sb)); |
@@ -4127,7 +4128,7 @@ failed_mount_wq: | |||
4127 | sbi->s_journal = NULL; | 4128 | sbi->s_journal = NULL; |
4128 | } | 4129 | } |
4129 | failed_mount3: | 4130 | failed_mount3: |
4130 | ext4_es_unregister_shrinker(sb); | 4131 | ext4_es_unregister_shrinker(sbi); |
4131 | del_timer(&sbi->s_err_report); | 4132 | del_timer(&sbi->s_err_report); |
4132 | if (sbi->s_flex_groups) | 4133 | if (sbi->s_flex_groups) |
4133 | ext4_kvfree(sbi->s_flex_groups); | 4134 | ext4_kvfree(sbi->s_flex_groups); |