OSDN Git Service

HACK: ext4: make bitmap corruption not fatal
authorWang Shilong <wshilong@ddn.com>
Sat, 11 Jul 2015 03:49:55 +0000 (11:49 +0800)
committerChih-Wei Huang <cwhuang@linux.org.tw>
Wed, 19 Apr 2017 15:35:25 +0000 (23:35 +0800)
There can be occasional reasons for bitmap problems, which are
detected by ext4_mb_check_ondisk_bitmap() and cause the
filesystem to be remounted read-only due to ext4_error():

 EXT4-fs error (device /dev/dm-6-8): ext4_mb_generate_buddy:755:
    group 294, block 0: block bitmap and bg descriptor inconsistent:
    20180 vs 20181 free clusters
 Aborting journal on device dm-6-8.
 EXT4-fs (dm-6): Remounting filesystem read-only

This might be caused by some ext4 internal bugs, which are addressed
separately.  This patch makes ext4 more robust by the following changes:

- ext4_read_block_bitmap() printed error, so do not call ext4_error() again
- mark all bits in bitmap used so that it will not be used for allocation
- mark block group corrupt, use ext4_warning() instead of ext4_error()

Tested by following script:

TEST_DEV="/dev/sdb"
TEST_MNT="/mnt/ext4"

mkdir -p $TEST_MNT
mkfs.ext4 -F $TEST_DEV

mount -t ext4 $TEST_DEV $TEST_MNT
dd if=/dev/zero of=$TEST_MNT/largefile oflag=direct bs=10485760 count=200
umount $TEST_MNT
dd if=/dev/zero of=$TEST_DEV oflag=direct bs=4096 seek=641 count=10
mount -t ext4 $TEST_DEV $TEST_MNT
rm -f $TEST_MNT/largefile
dd if=/dev/zero of=$TEST_MNT/largefile oflag=direct bs=10485760 count=200 &&
      echo "FILESYSTEM still usable after bitmaps corrupts happen"
umount $TEST_MNT
e2fsck $TEST_DEV -y

Signed-off-by: Wang Shilong <wshilong@ddn.com>
Intel-bug-id: https://jira.hpdd.intel.com/browse/LU-1026
Reviewed-on: http://review.whamcloud.com/16679
Reviewed-by: Bob Glossman <bob.glossman@intel.com>
Reviewed-by: Yang Sheng <yang.sheng@intel.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
fs/ext4/balloc.c
fs/ext4/ext4.h
fs/ext4/ialloc.c
fs/ext4/mballoc.c
fs/ext4/super.c

index f971104..f4c5aff 100644 (file)
@@ -184,25 +184,17 @@ static int ext4_init_block_bitmap(struct super_block *sb,
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        ext4_fsblk_t start, tmp;
        int flex_bg = 0;
-       struct ext4_group_info *grp;
 
        J_ASSERT_BH(bh, buffer_locked(bh));
 
        /* If checksum is bad mark all blocks used to prevent allocation
         * essentially implementing a per-group read-only flag. */
        if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
-               grp = ext4_get_group_info(sb, block_group);
-               if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-                       percpu_counter_sub(&sbi->s_freeclusters_counter,
-                                          grp->bb_free);
-               set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
-               if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
-                       int count;
-                       count = ext4_free_inodes_count(sb, gdp);
-                       percpu_counter_sub(&sbi->s_freeinodes_counter,
-                                          count);
-               }
-               set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state);
+               ext4_corrupted_block_group(sb, block_group,
+                               EXT4_GROUP_INFO_BBITMAP_CORRUPT |
+                               EXT4_GROUP_INFO_IBITMAP_CORRUPT,
+                               "Checksum bad for group %u",
+                               block_group);
                return -EFSBADCRC;
        }
        memset(bh->b_data, 0, sb->s_blocksize);
@@ -381,23 +373,20 @@ static int ext4_validate_block_bitmap(struct super_block *sb,
        if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group,
                        desc, bh))) {
                ext4_unlock_group(sb, block_group);
-               ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);
-               if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-                       percpu_counter_sub(&sbi->s_freeclusters_counter,
-                                          grp->bb_free);
-               set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
-               return -EFSBADCRC;
+               ext4_corrupted_block_group(sb, block_group,
+                               EXT4_GROUP_INFO_BBITMAP_CORRUPT,
+                               "bg %u: bad block bitmap checksum",
+                               block_group);
+               return 0;
        }
        blk = ext4_valid_block_bitmap(sb, desc, block_group, bh);
        if (unlikely(blk != 0)) {
                ext4_unlock_group(sb, block_group);
-               ext4_error(sb, "bg %u: block %llu: invalid block bitmap",
-                          block_group, blk);
-               if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-                       percpu_counter_sub(&sbi->s_freeclusters_counter,
-                                          grp->bb_free);
-               set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
-               return -EFSCORRUPTED;
+               ext4_corrupted_block_group(sb, block_group,
+                               EXT4_GROUP_INFO_BBITMAP_CORRUPT,
+                               "bg %u: block %llu: invalid block bitmap",
+                               block_group, blk);
+               return 0;
        }
        set_buffer_verified(bh);
        ext4_unlock_group(sb, block_group);
@@ -449,11 +438,6 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
                set_buffer_uptodate(bh);
                ext4_unlock_group(sb, block_group);
                unlock_buffer(bh);
-               if (err) {
-                       ext4_error(sb, "Failed to init block bitmap for group "
-                                  "%u: %d", block_group, err);
-                       goto out;
-               }
                goto verify;
        }
        ext4_unlock_group(sb, block_group);
index 8be0e4d..db07990 100644 (file)
@@ -82,6 +82,18 @@ typedef __u32 ext4_lblk_t;
 /* data type for block group number */
 typedef unsigned int ext4_group_t;
 
+void __ext4_corrupted_block_group(struct super_block *sb,
+                                 ext4_group_t group, unsigned int flags,
+                                 const char *function, unsigned int line);
+
+#define ext4_corrupted_block_group(sb, group, flags, fmt, ...)         \
+       do {                                                            \
+               __ext4_warning(sb, __func__, __LINE__, fmt,             \
+                               ##__VA_ARGS__);                         \
+               __ext4_corrupted_block_group(sb, group, flags,          \
+                                       __func__, __LINE__);            \
+       } while (0)
+
 enum SHIFT_DIRECTION {
        SHIFT_LEFT = 0,
        SHIFT_RIGHT,
@@ -2908,7 +2920,11 @@ struct ext4_group_info {
 #define EXT4_GROUP_INFO_NEED_INIT_BIT          0
 #define EXT4_GROUP_INFO_WAS_TRIMMED_BIT                1
 #define EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT    2
+#define EXT4_GROUP_INFO_BBITMAP_CORRUPT                \
+       (1 << EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT)
 #define EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT    3
+#define EXT4_GROUP_INFO_IBITMAP_CORRUPT                \
+       (1 << EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT)
 
 #define EXT4_MB_GRP_NEED_INIT(grp)     \
        (test_bit(EXT4_GROUP_INFO_NEED_INIT_BIT, &((grp)->bb_state)))
index 5388207..48bdf0b 100644 (file)
@@ -69,26 +69,16 @@ static int ext4_init_inode_bitmap(struct super_block *sb,
                                       ext4_group_t block_group,
                                       struct ext4_group_desc *gdp)
 {
-       struct ext4_group_info *grp;
-       struct ext4_sb_info *sbi = EXT4_SB(sb);
        J_ASSERT_BH(bh, buffer_locked(bh));
 
        /* If checksum is bad mark all blocks and inodes use to prevent
         * allocation, essentially implementing a per-group read-only flag. */
        if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
-               grp = ext4_get_group_info(sb, block_group);
-               if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-                       percpu_counter_sub(&sbi->s_freeclusters_counter,
-                                          grp->bb_free);
-               set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
-               if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
-                       int count;
-                       count = ext4_free_inodes_count(sb, gdp);
-                       percpu_counter_sub(&sbi->s_freeinodes_counter,
-                                          count);
-               }
-               set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state);
-               return -EFSBADCRC;
+               ext4_corrupted_block_group(sb, block_group,
+                               EXT4_GROUP_INFO_BBITMAP_CORRUPT |
+                               EXT4_GROUP_INFO_IBITMAP_CORRUPT,
+                               "Checksum bad for group %u", block_group);
+               return 0;
        }
 
        memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8);
@@ -130,17 +120,11 @@ static int ext4_validate_inode_bitmap(struct super_block *sb,
        if (!ext4_inode_bitmap_csum_verify(sb, block_group, desc, bh,
                                           EXT4_INODES_PER_GROUP(sb) / 8)) {
                ext4_unlock_group(sb, block_group);
-               ext4_error(sb, "Corrupt inode bitmap - block_group = %u, "
-                          "inode_bitmap = %llu", block_group, blk);
-               grp = ext4_get_group_info(sb, block_group);
-               if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
-                       int count;
-                       count = ext4_free_inodes_count(sb, desc);
-                       percpu_counter_sub(&sbi->s_freeinodes_counter,
-                                          count);
-               }
-               set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state);
-               return -EFSBADCRC;
+               ext4_corrupted_block_group(sb, block_group,
+                               EXT4_GROUP_INFO_IBITMAP_CORRUPT,
+                               "Corrupt inode bitmap - block_group = %u, inode_bitmap = %llu",
+                               block_group, blk);
+               return 0;
        }
        set_buffer_verified(bh);
        ext4_unlock_group(sb, block_group);
@@ -368,14 +352,9 @@ out:
                if (!fatal)
                        fatal = err;
        } else {
-               ext4_error(sb, "bit already cleared for inode %lu", ino);
-               if (gdp && !EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
-                       int count;
-                       count = ext4_free_inodes_count(sb, gdp);
-                       percpu_counter_sub(&sbi->s_freeinodes_counter,
-                                          count);
-               }
-               set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state);
+               ext4_corrupted_block_group(sb, block_group,
+                               EXT4_GROUP_INFO_IBITMAP_CORRUPT,
+                               "bit already cleared for inode %lu", ino);
        }
 
 error_return:
index c281050..3acf68f 100644 (file)
@@ -1116,7 +1116,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
        int block;
        int pnum;
        int poff;
-       struct page *page;
+       struct page *page = NULL;
        int ret;
        struct ext4_group_info *grp;
        struct ext4_sb_info *sbi = EXT4_SB(sb);
@@ -1142,7 +1142,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
                 */
                ret = ext4_mb_init_group(sb, group, gfp);
                if (ret)
-                       return ret;
+                       goto err;
        }
 
        /*
@@ -1245,6 +1245,7 @@ err:
                page_cache_release(e4b->bd_buddy_page);
        e4b->bd_buddy = NULL;
        e4b->bd_bitmap = NULL;
+       ext4_warning(sb, "Error loading buddy information for %u", group);
        return ret;
 }
 
@@ -3866,14 +3867,14 @@ ext4_mb_discard_group_preallocations(struct super_block *sb,
        bitmap_bh = ext4_read_block_bitmap(sb, group);
        if (IS_ERR(bitmap_bh)) {
                err = PTR_ERR(bitmap_bh);
-               ext4_error(sb, "Error %d reading block bitmap for %u",
+               ext4_warning(sb, "Error %d reading block bitmap for %u",
                           err, group);
                return 0;
        }
 
        err = ext4_mb_load_buddy(sb, group, &e4b);
        if (err) {
-               ext4_error(sb, "Error loading buddy information for %u", group);
+               ext4_warning(sb, "Error loading buddy information for %u", group);
                put_bh(bitmap_bh);
                return 0;
        }
@@ -4032,7 +4033,7 @@ repeat:
 
                err = ext4_mb_load_buddy(sb, group, &e4b);
                if (err) {
-                       ext4_error(sb, "Error loading buddy information for %u",
+                       ext4_warning(sb, "Error loading buddy information for %u",
                                        group);
                        continue;
                }
@@ -4040,7 +4041,7 @@ repeat:
                bitmap_bh = ext4_read_block_bitmap(sb, group);
                if (IS_ERR(bitmap_bh)) {
                        err = PTR_ERR(bitmap_bh);
-                       ext4_error(sb, "Error %d reading block bitmap for %u",
+                       ext4_warning(sb, "Error %d reading block bitmap for %u",
                                        err, group);
                        ext4_mb_unload_buddy(&e4b);
                        continue;
@@ -4292,7 +4293,7 @@ ext4_mb_discard_lg_preallocations(struct super_block *sb,
 
                group = ext4_get_group_number(sb, pa->pa_pstart);
                if (ext4_mb_load_buddy(sb, group, &e4b)) {
-                       ext4_error(sb, "Error loading buddy information for %u",
+                       ext4_warning(sb, "Error loading buddy information for %u",
                                        group);
                        continue;
                }
@@ -4827,7 +4828,7 @@ do_more:
        err = ext4_mb_load_buddy_gfp(sb, block_group, &e4b,
                                     GFP_NOFS|__GFP_NOFAIL);
        if (err)
-               goto error_return;
+               goto error_brelse;
 
        if ((flags & EXT4_FREE_BLOCKS_METADATA) && ext4_handle_valid(handle)) {
                struct ext4_free_data *new_entry;
@@ -4904,8 +4905,9 @@ do_more:
                goto do_more;
        }
 error_return:
-       brelse(bitmap_bh);
        ext4_std_error(sb, err);
+error_brelse:
+       brelse(bitmap_bh);
        return;
 }
 
@@ -5002,7 +5004,7 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
 
        err = ext4_mb_load_buddy(sb, block_group, &e4b);
        if (err)
-               goto error_return;
+               goto error_brelse;
 
        /*
         * need to update group_info->bb_free and bitmap
@@ -5039,8 +5041,9 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
                err = ret;
 
 error_return:
-       brelse(bitmap_bh);
        ext4_std_error(sb, err);
+error_brelse:
+       brelse(bitmap_bh);
        return err;
 }
 
@@ -5119,7 +5122,7 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
 
        ret = ext4_mb_load_buddy(sb, group, &e4b);
        if (ret) {
-               ext4_error(sb, "Error in loading buddy "
+               ext4_warning(sb, "Error in loading buddy "
                                "information for %u", group);
                return ret;
        }
index 6fe8e30..f1c33ef 100644 (file)
@@ -652,6 +652,37 @@ void __ext4_warning_inode(const struct inode *inode, const char *function,
        va_end(args);
 }
 
+void __ext4_corrupted_block_group(struct super_block *sb, ext4_group_t group,
+                                 unsigned int flags, const char *function,
+                                 unsigned int line)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       struct ext4_group_info *grp = ext4_get_group_info(sb, group);
+       struct ext4_group_desc *gdp = ext4_get_group_desc(sb, group, NULL);
+
+       if (flags & EXT4_GROUP_INFO_BBITMAP_CORRUPT &&
+           !EXT4_MB_GRP_BBITMAP_CORRUPT(grp)) {
+               percpu_counter_sub(&sbi->s_freeclusters_counter,
+                                       grp->bb_free);
+               set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT,
+                       &grp->bb_state);
+       }
+
+       if (flags & EXT4_GROUP_INFO_IBITMAP_CORRUPT &&
+           !EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
+               if (gdp) {
+                       int count;
+
+                       count = ext4_free_inodes_count(sb, gdp);
+                       percpu_counter_sub(&sbi->s_freeinodes_counter,
+                                          count);
+               }
+               set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT,
+                       &grp->bb_state);
+       }
+       save_error_info(sb, function, line);
+}
+
 void __ext4_grp_locked_error(const char *function, unsigned int line,
                             struct super_block *sb, ext4_group_t grp,
                             unsigned long ino, ext4_fsblk_t block,