diff options
Diffstat (limited to 'fs/ext4/extents_status.c')
| -rw-r--r-- | fs/ext4/extents_status.c | 73 |
1 files changed, 49 insertions, 24 deletions
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index ee018d5f397e..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); |
| 149 | static int __es_try_to_reclaim_extents(struct ext4_inode_info *ei, | 149 | static int __es_try_to_reclaim_extents(struct ext4_inode_info *ei, |
| 150 | int nr_to_scan); | 150 | int nr_to_scan); |
| 151 | static int __ext4_es_shrink(struct ext4_sb_info *sbi, int nr_to_scan, | ||
| 152 | struct ext4_inode_info *locked_ei); | ||
| 151 | 153 | ||
| 152 | int __init ext4_init_es(void) | 154 | int __init ext4_init_es(void) |
| 153 | { | 155 | { |
| @@ -439,7 +441,7 @@ static void ext4_es_insert_extent_ext_check(struct inode *inode, | |||
| 439 | */ | 441 | */ |
| 440 | if (!ext4_es_is_written(es) && !ext4_es_is_unwritten(es)) { | 442 | if (!ext4_es_is_written(es) && !ext4_es_is_unwritten(es)) { |
| 441 | if (in_range(es->es_lblk, ee_block, ee_len)) { | 443 | if (in_range(es->es_lblk, ee_block, ee_len)) { |
| 442 | pr_warn("ES insert assertation failed for " | 444 | pr_warn("ES insert assertion failed for " |
| 443 | "inode: %lu we can find an extent " | 445 | "inode: %lu we can find an extent " |
| 444 | "at block [%d/%d/%llu/%c], but we " | 446 | "at block [%d/%d/%llu/%c], but we " |
| 445 | "want to add an delayed/hole extent " | 447 | "want to add an delayed/hole extent " |
| @@ -458,7 +460,7 @@ static void ext4_es_insert_extent_ext_check(struct inode *inode, | |||
| 458 | */ | 460 | */ |
| 459 | if (es->es_lblk < ee_block || | 461 | if (es->es_lblk < ee_block || |
| 460 | ext4_es_pblock(es) != ee_start + es->es_lblk - ee_block) { | 462 | ext4_es_pblock(es) != ee_start + es->es_lblk - ee_block) { |
| 461 | pr_warn("ES insert assertation failed for inode: %lu " | 463 | pr_warn("ES insert assertion failed for inode: %lu " |
| 462 | "ex_status [%d/%d/%llu/%c] != " | 464 | "ex_status [%d/%d/%llu/%c] != " |
| 463 | "es_status [%d/%d/%llu/%c]\n", inode->i_ino, | 465 | "es_status [%d/%d/%llu/%c]\n", inode->i_ino, |
| 464 | ee_block, ee_len, ee_start, | 466 | ee_block, ee_len, ee_start, |
| @@ -468,7 +470,7 @@ static void ext4_es_insert_extent_ext_check(struct inode *inode, | |||
| 468 | } | 470 | } |
| 469 | 471 | ||
| 470 | if (ee_status ^ es_status) { | 472 | if (ee_status ^ es_status) { |
| 471 | pr_warn("ES insert assertation failed for inode: %lu " | 473 | pr_warn("ES insert assertion failed for inode: %lu " |
| 472 | "ex_status [%d/%d/%llu/%c] != " | 474 | "ex_status [%d/%d/%llu/%c] != " |
| 473 | "es_status [%d/%d/%llu/%c]\n", inode->i_ino, | 475 | "es_status [%d/%d/%llu/%c]\n", inode->i_ino, |
| 474 | ee_block, ee_len, ee_start, | 476 | ee_block, ee_len, ee_start, |
| @@ -481,7 +483,7 @@ static void ext4_es_insert_extent_ext_check(struct inode *inode, | |||
| 481 | * that we don't want to add an written/unwritten extent. | 483 | * that we don't want to add an written/unwritten extent. |
| 482 | */ | 484 | */ |
| 483 | if (!ext4_es_is_delayed(es) && !ext4_es_is_hole(es)) { | 485 | if (!ext4_es_is_delayed(es) && !ext4_es_is_hole(es)) { |
| 484 | pr_warn("ES insert assertation failed for inode: %lu " | 486 | pr_warn("ES insert assertion failed for inode: %lu " |
| 485 | "can't find an extent at block %d but we want " | 487 | "can't find an extent at block %d but we want " |
| 486 | "to add an written/unwritten extent " | 488 | "to add an written/unwritten extent " |
| 487 | "[%d/%d/%llu/%llx]\n", inode->i_ino, | 489 | "[%d/%d/%llu/%llx]\n", inode->i_ino, |
| @@ -519,7 +521,7 @@ static void ext4_es_insert_extent_ind_check(struct inode *inode, | |||
| 519 | * We want to add a delayed/hole extent but this | 521 | * We want to add a delayed/hole extent but this |
| 520 | * block has been allocated. | 522 | * block has been allocated. |
| 521 | */ | 523 | */ |
| 522 | pr_warn("ES insert assertation failed for inode: %lu " | 524 | pr_warn("ES insert assertion failed for inode: %lu " |
| 523 | "We can find blocks but we want to add a " | 525 | "We can find blocks but we want to add a " |
| 524 | "delayed/hole extent [%d/%d/%llu/%llx]\n", | 526 | "delayed/hole extent [%d/%d/%llu/%llx]\n", |
| 525 | inode->i_ino, es->es_lblk, es->es_len, | 527 | inode->i_ino, es->es_lblk, es->es_len, |
| @@ -527,13 +529,13 @@ static void ext4_es_insert_extent_ind_check(struct inode *inode, | |||
| 527 | return; | 529 | return; |
| 528 | } else if (ext4_es_is_written(es)) { | 530 | } else if (ext4_es_is_written(es)) { |
| 529 | if (retval != es->es_len) { | 531 | if (retval != es->es_len) { |
| 530 | pr_warn("ES insert assertation failed for " | 532 | pr_warn("ES insert assertion failed for " |
| 531 | "inode: %lu retval %d != es_len %d\n", | 533 | "inode: %lu retval %d != es_len %d\n", |
| 532 | inode->i_ino, retval, es->es_len); | 534 | inode->i_ino, retval, es->es_len); |
| 533 | return; | 535 | return; |
| 534 | } | 536 | } |
| 535 | if (map.m_pblk != ext4_es_pblock(es)) { | 537 | if (map.m_pblk != ext4_es_pblock(es)) { |
| 536 | pr_warn("ES insert assertation failed for " | 538 | pr_warn("ES insert assertion failed for " |
| 537 | "inode: %lu m_pblk %llu != " | 539 | "inode: %lu m_pblk %llu != " |
| 538 | "es_pblk %llu\n", | 540 | "es_pblk %llu\n", |
| 539 | inode->i_ino, map.m_pblk, | 541 | inode->i_ino, map.m_pblk, |
| @@ -549,7 +551,7 @@ static void ext4_es_insert_extent_ind_check(struct inode *inode, | |||
| 549 | } | 551 | } |
| 550 | } else if (retval == 0) { | 552 | } else if (retval == 0) { |
| 551 | if (ext4_es_is_written(es)) { | 553 | if (ext4_es_is_written(es)) { |
| 552 | pr_warn("ES insert assertation failed for inode: %lu " | 554 | pr_warn("ES insert assertion failed for inode: %lu " |
| 553 | "We can't find the block but we want to add " | 555 | "We can't find the block but we want to add " |
| 554 | "an written extent [%d/%d/%llu/%llx]\n", | 556 | "an written extent [%d/%d/%llu/%llx]\n", |
| 555 | inode->i_ino, es->es_lblk, es->es_len, | 557 | inode->i_ino, es->es_lblk, es->es_len, |
| @@ -632,10 +634,8 @@ out: | |||
| 632 | } | 634 | } |
| 633 | 635 | ||
| 634 | /* | 636 | /* |
| 635 | * ext4_es_insert_extent() adds a space to a extent status tree. | 637 | * ext4_es_insert_extent() adds information to an inode's extent |
| 636 | * | 638 | * status tree. |
| 637 | * ext4_es_insert_extent is called by ext4_da_write_begin and | ||
| 638 | * ext4_es_remove_extent. | ||
| 639 | * | 639 | * |
| 640 | * Return 0 on success, error code on failure. | 640 | * Return 0 on success, error code on failure. |
| 641 | */ | 641 | */ |
| @@ -667,7 +667,13 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, | |||
| 667 | err = __es_remove_extent(inode, lblk, end); | 667 | err = __es_remove_extent(inode, lblk, end); |
| 668 | if (err != 0) | 668 | if (err != 0) |
| 669 | goto error; | 669 | goto error; |
| 670 | retry: | ||
| 670 | 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; | ||
| 671 | 677 | ||
| 672 | error: | 678 | error: |
| 673 | write_unlock(&EXT4_I(inode)->i_es_lock); | 679 | write_unlock(&EXT4_I(inode)->i_es_lock); |
| @@ -746,8 +752,10 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, | |||
| 746 | struct extent_status orig_es; | 752 | struct extent_status orig_es; |
| 747 | ext4_lblk_t len1, len2; | 753 | ext4_lblk_t len1, len2; |
| 748 | ext4_fsblk_t block; | 754 | ext4_fsblk_t block; |
| 749 | int err = 0; | 755 | int err; |
| 750 | 756 | ||
| 757 | retry: | ||
| 758 | err = 0; | ||
| 751 | es = __es_tree_search(&tree->root, lblk); | 759 | es = __es_tree_search(&tree->root, lblk); |
| 752 | if (!es) | 760 | if (!es) |
| 753 | goto out; | 761 | goto out; |
| @@ -782,6 +790,10 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, | |||
| 782 | if (err) { | 790 | if (err) { |
| 783 | es->es_lblk = orig_es.es_lblk; | 791 | es->es_lblk = orig_es.es_lblk; |
| 784 | 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; | ||
| 785 | goto out; | 797 | goto out; |
| 786 | } | 798 | } |
| 787 | } else { | 799 | } else { |
| @@ -891,22 +903,14 @@ static int ext4_inode_touch_time_cmp(void *priv, struct list_head *a, | |||
| 891 | return -1; | 903 | return -1; |
| 892 | } | 904 | } |
| 893 | 905 | ||
| 894 | static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) | 906 | static int __ext4_es_shrink(struct ext4_sb_info *sbi, int nr_to_scan, |
| 907 | struct ext4_inode_info *locked_ei) | ||
| 895 | { | 908 | { |
| 896 | struct ext4_sb_info *sbi = container_of(shrink, | ||
| 897 | struct ext4_sb_info, s_es_shrinker); | ||
| 898 | struct ext4_inode_info *ei; | 909 | struct ext4_inode_info *ei; |
| 899 | struct list_head *cur, *tmp; | 910 | struct list_head *cur, *tmp; |
| 900 | LIST_HEAD(skiped); | 911 | LIST_HEAD(skiped); |
| 901 | int nr_to_scan = sc->nr_to_scan; | ||
| 902 | int ret, nr_shrunk = 0; | 912 | int ret, nr_shrunk = 0; |
| 903 | 913 | ||
| 904 | ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt); | ||
| 905 | trace_ext4_es_shrink_enter(sbi->s_sb, nr_to_scan, ret); | ||
| 906 | |||
| 907 | if (!nr_to_scan) | ||
| 908 | return ret; | ||
| 909 | |||
| 910 | spin_lock(&sbi->s_es_lru_lock); | 914 | spin_lock(&sbi->s_es_lru_lock); |
| 911 | 915 | ||
| 912 | /* | 916 | /* |
| @@ -935,7 +939,7 @@ static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) | |||
| 935 | continue; | 939 | continue; |
| 936 | } | 940 | } |
| 937 | 941 | ||
| 938 | if (ei->i_es_lru_nr == 0) | 942 | if (ei->i_es_lru_nr == 0 || ei == locked_ei) |
| 939 | continue; | 943 | continue; |
| 940 | 944 | ||
| 941 | write_lock(&ei->i_es_lock); | 945 | write_lock(&ei->i_es_lock); |
| @@ -954,6 +958,27 @@ static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) | |||
| 954 | list_splice_tail(&skiped, &sbi->s_es_lru); | 958 | list_splice_tail(&skiped, &sbi->s_es_lru); |
| 955 | spin_unlock(&sbi->s_es_lru_lock); | 959 | spin_unlock(&sbi->s_es_lru_lock); |
| 956 | 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 | |||
| 967 | static 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 | |||
| 957 | ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt); | 982 | ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt); |
| 958 | trace_ext4_es_shrink_exit(sbi->s_sb, nr_shrunk, ret); | 983 | trace_ext4_es_shrink_exit(sbi->s_sb, nr_shrunk, ret); |
| 959 | return ret; | 984 | return ret; |
