aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2013-07-15 00:12:14 -0400
committerTheodore Ts'o <tytso@mit.edu>2013-07-15 00:12:14 -0400
commite15f742ce816076497549b955fbec3254820db85 (patch)
tree4666b07669926bb9bd93b594129eb12d2200f7db /fs
parentc8e15130e1636f68d5165aa2605b8e9cba0f644c (diff)
ext4: make the extent_status code more robust against ENOMEM failures
Some callers of ext4_es_remove_extent() and ext4_es_insert_extent() may not be completely robust against ENOMEM failures (or the consequences of reflecting ENOMEM back up to userspace may lead to xfstest or user application failure). To mitigate against this, when trying to insert an entry in the extent status tree, try to shrink the inode's extent status tree before returning ENOMEM. If there are entries which don't record information about extents under delayed allocations, freeing one of them is preferable to returning ENOMEM. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Reviewed-by: Zheng Liu <wenqing.lz@taobao.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/extents_status.c51
1 files changed, 39 insertions, 12 deletions
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index 4b8df7fbb10a..91cb110da1b4 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -148,6 +148,8 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
148 ext4_lblk_t end); 148 ext4_lblk_t end);
149static int __es_try_to_reclaim_extents(struct ext4_inode_info *ei, 149static int __es_try_to_reclaim_extents(struct ext4_inode_info *ei,
150 int nr_to_scan); 150 int nr_to_scan);
151static int __ext4_es_shrink(struct ext4_sb_info *sbi, int nr_to_scan,
152 struct ext4_inode_info *locked_ei);
151 153
152int __init ext4_init_es(void) 154int __init ext4_init_es(void)
153{ 155{
@@ -665,7 +667,13 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
665 err = __es_remove_extent(inode, lblk, end); 667 err = __es_remove_extent(inode, lblk, end);
666 if (err != 0) 668 if (err != 0)
667 goto error; 669 goto error;
670retry:
668 err = __es_insert_extent(inode, &newes); 671 err = __es_insert_extent(inode, &newes);
672 if (err == -ENOMEM && __ext4_es_shrink(EXT4_SB(inode->i_sb), 1,
673 EXT4_I(inode)))
674 goto retry;
675 if (err == -ENOMEM && !ext4_es_is_delayed(&newes))
676 err = 0;
669 677
670error: 678error:
671 write_unlock(&EXT4_I(inode)->i_es_lock); 679 write_unlock(&EXT4_I(inode)->i_es_lock);
@@ -744,8 +752,10 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
744 struct extent_status orig_es; 752 struct extent_status orig_es;
745 ext4_lblk_t len1, len2; 753 ext4_lblk_t len1, len2;
746 ext4_fsblk_t block; 754 ext4_fsblk_t block;
747 int err = 0; 755 int err;
748 756
757retry:
758 err = 0;
749 es = __es_tree_search(&tree->root, lblk); 759 es = __es_tree_search(&tree->root, lblk);
750 if (!es) 760 if (!es)
751 goto out; 761 goto out;
@@ -780,6 +790,10 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
780 if (err) { 790 if (err) {
781 es->es_lblk = orig_es.es_lblk; 791 es->es_lblk = orig_es.es_lblk;
782 es->es_len = orig_es.es_len; 792 es->es_len = orig_es.es_len;
793 if ((err == -ENOMEM) &&
794 __ext4_es_shrink(EXT4_SB(inode->i_sb), 1,
795 EXT4_I(inode)))
796 goto retry;
783 goto out; 797 goto out;
784 } 798 }
785 } else { 799 } else {
@@ -889,22 +903,14 @@ static int ext4_inode_touch_time_cmp(void *priv, struct list_head *a,
889 return -1; 903 return -1;
890} 904}
891 905
892static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) 906static int __ext4_es_shrink(struct ext4_sb_info *sbi, int nr_to_scan,
907 struct ext4_inode_info *locked_ei)
893{ 908{
894 struct ext4_sb_info *sbi = container_of(shrink,
895 struct ext4_sb_info, s_es_shrinker);
896 struct ext4_inode_info *ei; 909 struct ext4_inode_info *ei;
897 struct list_head *cur, *tmp; 910 struct list_head *cur, *tmp;
898 LIST_HEAD(skiped); 911 LIST_HEAD(skiped);
899 int nr_to_scan = sc->nr_to_scan;
900 int ret, nr_shrunk = 0; 912 int ret, nr_shrunk = 0;
901 913
902 ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt);
903 trace_ext4_es_shrink_enter(sbi->s_sb, nr_to_scan, ret);
904
905 if (!nr_to_scan)
906 return ret;
907
908 spin_lock(&sbi->s_es_lru_lock); 914 spin_lock(&sbi->s_es_lru_lock);
909 915
910 /* 916 /*
@@ -933,7 +939,7 @@ static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc)
933 continue; 939 continue;
934 } 940 }
935 941
936 if (ei->i_es_lru_nr == 0) 942 if (ei->i_es_lru_nr == 0 || ei == locked_ei)
937 continue; 943 continue;
938 944
939 write_lock(&ei->i_es_lock); 945 write_lock(&ei->i_es_lock);
@@ -952,6 +958,27 @@ static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc)
952 list_splice_tail(&skiped, &sbi->s_es_lru); 958 list_splice_tail(&skiped, &sbi->s_es_lru);
953 spin_unlock(&sbi->s_es_lru_lock); 959 spin_unlock(&sbi->s_es_lru_lock);
954 960
961 if (locked_ei && nr_shrunk == 0)
962 nr_shrunk = __es_try_to_reclaim_extents(ei, nr_to_scan);
963
964 return nr_shrunk;
965}
966
967static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc)
968{
969 struct ext4_sb_info *sbi = container_of(shrink,
970 struct ext4_sb_info, s_es_shrinker);
971 int nr_to_scan = sc->nr_to_scan;
972 int ret, nr_shrunk;
973
974 ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt);
975 trace_ext4_es_shrink_enter(sbi->s_sb, nr_to_scan, ret);
976
977 if (!nr_to_scan)
978 return ret;
979
980 nr_shrunk = __ext4_es_shrink(sbi, nr_to_scan, NULL);
981
955 ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt); 982 ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt);
956 trace_ext4_es_shrink_exit(sbi->s_sb, nr_shrunk, ret); 983 trace_ext4_es_shrink_exit(sbi->s_sb, nr_shrunk, ret);
957 return ret; 984 return ret;