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>
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);
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);
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);
/* 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,
#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)))
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);
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);
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:
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);
*/
ret = ext4_mb_init_group(sb, group, gfp);
if (ret)
- return ret;
+ goto 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;
}
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;
}
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;
}
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;
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;
}
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;
goto do_more;
}
error_return:
- brelse(bitmap_bh);
ext4_std_error(sb, err);
+error_brelse:
+ brelse(bitmap_bh);
return;
}
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
err = ret;
error_return:
- brelse(bitmap_bh);
ext4_std_error(sb, err);
+error_brelse:
+ brelse(bitmap_bh);
return err;
}
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;
}
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,