[v2,5/5] ext4: add suppport to make bitmaps corruptions nonfatal

Message ID 1527594317-9214-5-git-send-email-wshilong1991@gmail.com
State New
Headers show
Series
  • [1/5] ext4: fix race with setting free_inode/clusters_counter
Related show

Commit Message

Wang Shilong May 29, 2018, 11:45 a.m.
From: Wang Shilong <wshilong@ddn.com>

Originally, we saw bitmaps corruptions on Lustre server,
which could be ext4 bugs or disk corruptions, but currently
ext4 use ext4_error() for this which make filesystem RO
in default.

Ext4 have handled bitmaps corruptions very well, use corrupted
bit to isolate erros and prevent us any further allocations
to this block group, add an mount option 'bitmaps=warn' for
this which will record error state to superblock, and next
offline e2fsck will fix inconsitency problems, this gurantee
our Clusters usable with minimal risk.

Suggested-by: Andreas Dilger <adilger.kernel@dilger.ca>
Signed-off-by: Wang Shilong <wshilong@ddn.com>
---
v1->v2:
add mount option to control,
don't leak fatal error that is not related to bitmap corruptions
---
 fs/ext4/balloc.c  |  6 ++++--
 fs/ext4/ext4.h    | 27 +++++++++++++++++++++------
 fs/ext4/mballoc.c | 45 +++++++++++++++++++++++----------------------
 fs/ext4/super.c   | 20 ++++++++++++++------
 4 files changed, 62 insertions(+), 36 deletions(-)

Comments

Andreas Dilger June 4, 2018, 6:19 p.m. | #1
On May 29, 2018, at 5:45 AM, Wang Shilong <wangshilong1991@gmail.com> wrote:
> 
> From: Wang Shilong <wshilong@ddn.com>
> 
> Originally, we saw bitmaps corruptions on Lustre server,
> which could be ext4 bugs or disk corruptions, but currently
> ext4 use ext4_error() for this which make filesystem RO
> in default.
> 
> Ext4 have handled bitmaps corruptions very well, use corrupted
> bit to isolate erros and prevent us any further allocations
> to this block group, add an mount option 'bitmaps=warn' for
> this which will record error state to superblock, and next
> offline e2fsck will fix inconsitency problems, this gurantee
> our Clusters usable with minimal risk.
> 
> Suggested-by: Andreas Dilger <adilger.kernel@dilger.ca>
> Signed-off-by: Wang Shilong <wshilong@ddn.com>

Reviewed-by: Andreas Dilger <adilger@dilger.ca>

> ---
> v1->v2:
> add mount option to control,
> don't leak fatal error that is not related to bitmap corruptions
> ---
> fs/ext4/balloc.c  |  6 ++++--
> fs/ext4/ext4.h    | 27 +++++++++++++++++++++------
> fs/ext4/mballoc.c | 45 +++++++++++++++++++++++----------------------
> fs/ext4/super.c   | 20 ++++++++++++++------
> 4 files changed, 62 insertions(+), 36 deletions(-)
> 
> diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
> index 5fe63ff..bef6ae8 100644
> --- a/fs/ext4/balloc.c
> +++ b/fs/ext4/balloc.c
> @@ -451,8 +451,10 @@ struct buffer_head *
> 		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);
> +			if (!EXT4_MB_GRP_BBITMAP_CORRUPT(
> +			     ext4_get_group_info(sb, block_group)))
> +				ext4_error(sb, "Failed to init block bitmap for group "
> +					       "%u: %d", block_group, err);
> 			goto out;
> 		}
> 		goto verify;
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 26f8c12..96c5c1c 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -1113,6 +1113,7 @@ struct ext4_inode_info {
> #define EXT4_MOUNT_BLOCK_VALIDITY	0x20000000 /* Block validity checking */
> #define EXT4_MOUNT_DISCARD		0x40000000 /* Issue DISCARD requests */
> #define EXT4_MOUNT_INIT_INODE_TABLE	0x80000000 /* Initialize uninitialized itables */
> +#define EXT4_MOUNT_BITMAPS_WARN		0x100000000 /* warn on bitmaps corruptions */
> 
> /*
>  * Mount flags set either automatically (could not be set by mount option)
> @@ -1338,7 +1339,7 @@ struct ext4_sb_info {
> 	struct buffer_head * s_sbh;	/* Buffer containing the super block */
> 	struct ext4_super_block *s_es;	/* Pointer to the super block in the buffer */
> 	struct buffer_head **s_group_desc;
> -	unsigned int s_mount_opt;
> +	unsigned long long s_mount_opt;
> 	unsigned int s_mount_opt2;
> 	unsigned int s_mount_flags;
> 	unsigned int s_def_mount_opt;
> @@ -2563,6 +2564,8 @@ void __ext4_grp_locked_error(const char *, unsigned int,
> 			     struct super_block *, ext4_group_t,
> 			     unsigned long, ext4_fsblk_t, unsigned int,
> 			     const char *, ...);
> +void save_error_info(struct super_block *sb, const char *func,
> +		     unsigned int line);
> 
> #define EXT4_ERROR_INODE(inode, fmt, a...) \
> 	ext4_error_inode((inode), __func__, __LINE__, 0, (fmt), ## a)
> @@ -2598,10 +2601,17 @@ void __ext4_grp_locked_error(const char *, unsigned int,
> 	do {								\
> 		int ret;						\
> 		ret = __ext4_mark_group_bitmap_corrupted(sb, group,	\
> -							flags);		\
> -		if (ret)						\
> -			__ext4_error(sb, __func__, __LINE__,		\
> -				     fmt, ##__VA_ARGS__);		\
> +							 flags);	\
> +		if (ret) {						\
> +			if (test_opt(sb, BITMAPS_WARN)) {		\
> +				__ext4_warning(sb, __func__, __LINE__,	\
> +					fmt, ##__VA_ARGS__);		\
> +				save_error_info(sb, __func__, __LINE__);\
> +			} else {					\
> +				__ext4_error(sb, __func__, __LINE__,	\
> +					fmt, ##__VA_ARGS__);		\
> +			}						\
> +		}							\
> 	} while (0)
> 
> 
> @@ -2656,7 +2666,12 @@ void __ext4_grp_locked_error(const char *, unsigned int,
> 							 flags);	\
> 		if (ret) {						\
> 			no_printk(fmt, ##__VA_ARGS__);			\
> -			__ext4_error(sb, "", 0, " ");			\
> +			if (test_opt(sb, BITMAPS_WARN) {		\
> +				__ext4_warning(sb, "", 0, " ");		\
> +				save_error_info(sb, __func__, __LINE__);\
> +			} else {					\
> +				__ext4_error(sb, "", 0, " ");		\
> +			}						\
> 		}							\
> 	} while (0)
> 
> diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
> index 3680623..1ee3138 100644
> --- a/fs/ext4/mballoc.c
> +++ b/fs/ext4/mballoc.c
> @@ -3907,15 +3907,16 @@ static int ext4_mb_new_preallocation(struct ext4_allocation_context *ac)
> 	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",
> -			   err, group);
> 		return 0;
> 	}
> 
> 	err = ext4_mb_load_buddy(sb, group, &e4b);
> 	if (err) {
> -		ext4_warning(sb, "Error %d loading buddy information for %u",
> -			     err, group);
> +		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(
> +		     ext4_get_group_info(sb, group)))
> +			ext4_warning(sb,
> +				"Error %d loading buddy information for %u",
> +				err, group);
> 		put_bh(bitmap_bh);
> 		return 0;
> 	}
> @@ -4075,16 +4076,17 @@ void ext4_discard_preallocations(struct inode *inode)
> 		err = ext4_mb_load_buddy_gfp(sb, group, &e4b,
> 					     GFP_NOFS|__GFP_NOFAIL);
> 		if (err) {
> -			ext4_error(sb, "Error %d loading buddy information for %u",
> -				   err, group);
> +			if (!EXT4_MB_GRP_BBITMAP_CORRUPT(
> +					ext4_get_group_info(sb, group)))
> +				ext4_error(sb,
> +					"Error %d loading buddy information for %u",
> +					err, 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",
> -					err, group);
> 			ext4_mb_unload_buddy(&e4b);
> 			continue;
> 		}
> @@ -4338,8 +4340,10 @@ static void ext4_mb_group_or_file(struct ext4_allocation_context *ac)
> 		err = ext4_mb_load_buddy_gfp(sb, group, &e4b,
> 					     GFP_NOFS|__GFP_NOFAIL);
> 		if (err) {
> -			ext4_error(sb, "Error %d loading buddy information for %u",
> -				   err, group);
> +			if (!EXT4_MB_GRP_BBITMAP_CORRUPT(
> +					ext4_get_group_info(sb, group)))
> +				ext4_error(sb, "Error %d loading buddy information for %u",
> +					       err, group);
> 			continue;
> 		}
> 		ext4_lock_group(sb, group);
> @@ -4819,11 +4823,8 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
> 	}
> 	count_clusters = EXT4_NUM_B2C(sbi, count);
> 	bitmap_bh = ext4_read_block_bitmap(sb, block_group);
> -	if (IS_ERR(bitmap_bh)) {
> -		err = PTR_ERR(bitmap_bh);
> -		bitmap_bh = NULL;
> -		goto error_return;
> -	}
> +	if (IS_ERR(bitmap_bh))
> +		return;
> 	gdp = ext4_get_group_desc(sb, block_group, &gd_bh);
> 	if (!gdp) {
> 		err = -EIO;
> @@ -5001,11 +5002,8 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
> 	}
> 
> 	bitmap_bh = ext4_read_block_bitmap(sb, block_group);
> -	if (IS_ERR(bitmap_bh)) {
> -		err = PTR_ERR(bitmap_bh);
> -		bitmap_bh = NULL;
> -		goto error_return;
> -	}
> +	if (IS_ERR(bitmap_bh))
> +		return PTR_ERR(bitmap_bh);
> 
> 	desc = ext4_get_group_desc(sb, block_group, &gd_bh);
> 	if (!desc) {
> @@ -5168,8 +5166,11 @@ static int ext4_trim_extent(struct super_block *sb, int start, int count,
> 
> 	ret = ext4_mb_load_buddy(sb, group, &e4b);
> 	if (ret) {
> -		ext4_warning(sb, "Error %d loading buddy information for %u",
> -			     ret, group);
> +		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(
> +				ext4_get_group_info(sb, group)))
> +			ext4_warning(sb,
> +				"Error %d loading buddy information for %u",
> +				ret, group);
> 		return ret;
> 	}
> 	bitmap = e4b.bd_bitmap;
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 9f88991..63f40ae 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -342,8 +342,8 @@ static void __save_error_info(struct super_block *sb, const char *func,
> 	le32_add_cpu(&es->s_error_count, 1);
> }
> 
> -static void save_error_info(struct super_block *sb, const char *func,
> -			    unsigned int line)
> +void save_error_info(struct super_block *sb, const char *func,
> +		     unsigned int line)
> {
> 	__save_error_info(sb, func, line);
> 	ext4_commit_super(sb, 1);
> @@ -743,7 +743,8 @@ void __ext4_grp_locked_error(const char *function, unsigned int line,
> 	if (flags)
> 		__ext4_mark_group_bitmap_corrupted(sb, grp, flags);
> 
> -	if (test_opt(sb, ERRORS_CONT)) {
> +	if (test_opt(sb, ERRORS_CONT) ||
> +	    (flags && test_opt(sb, BITMAPS_WARN))) {
> 		ext4_commit_super(sb, 0);
> 		return;
> 	}
> @@ -1373,7 +1374,8 @@ static struct dquot **ext4_get_dquots(struct inode *inode)
> };
> 
> enum {
> -	Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
> +	Opt_bsd_df, Opt_minix_df, Opt_bitmaps_warn,
> +	Opt_bitmaps_err, Opt_grpid, Opt_nogrpid,
> 	Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro,
> 	Opt_nouid32, Opt_debug, Opt_removed,
> 	Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl,
> @@ -1398,6 +1400,8 @@ enum {
> static const match_table_t tokens = {
> 	{Opt_bsd_df, "bsddf"},
> 	{Opt_minix_df, "minixdf"},
> +	{Opt_bitmaps_warn, "bitmaps=warn"},
> +	{Opt_bitmaps_err, "bitmaps=error"},
> 	{Opt_grpid, "grpid"},
> 	{Opt_grpid, "bsdgroups"},
> 	{Opt_nogrpid, "nogrpid"},
> @@ -1598,11 +1602,13 @@ static int clear_qf_name(struct super_block *sb, int qtype)
> 
> static const struct mount_opts {
> 	int	token;
> -	int	mount_opt;
> +	unsigned long long mount_opt;
> 	int	flags;
> } ext4_mount_opts[] = {
> 	{Opt_minix_df, EXT4_MOUNT_MINIX_DF, MOPT_SET},
> 	{Opt_bsd_df, EXT4_MOUNT_MINIX_DF, MOPT_CLEAR},
> +	{Opt_bitmaps_warn, EXT4_MOUNT_BITMAPS_WARN, MOPT_SET},
> +	{Opt_bitmaps_err, EXT4_MOUNT_BITMAPS_WARN, MOPT_CLEAR},
> 	{Opt_grpid, EXT4_MOUNT_GRPID, MOPT_SET},
> 	{Opt_nogrpid, EXT4_MOUNT_GRPID, MOPT_CLEAR},
> 	{Opt_block_validity, EXT4_MOUNT_BLOCK_VALIDITY, MOPT_SET},
> @@ -2099,6 +2105,8 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
> 		SEQ_OPTS_PUTS("errors=continue");
> 	if (test_opt(sb, ERRORS_PANIC) && def_errors != EXT4_ERRORS_PANIC)
> 		SEQ_OPTS_PUTS("errors=panic");
> +	if (test_opt(sb, BITMAPS_WARN))
> +		SEQ_OPTS_PUTS("bitmaps=warn");
> 	if (nodefs || sbi->s_commit_interval != JBD2_DEFAULT_MAX_COMMIT_AGE*HZ)
> 		SEQ_OPTS_PRINT("commit=%lu", sbi->s_commit_interval / HZ);
> 	if (nodefs || sbi->s_min_batch_time != EXT4_DEF_MIN_BATCH_TIME)
> @@ -2197,7 +2205,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
> done:
> 	if (test_opt(sb, DEBUG))
> 		printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
> -				"bpg=%lu, ipg=%lu, mo=%04x, mo2=%04x]\n",
> +				"bpg=%lu, ipg=%lu, mo=%04llx, mo2=%04x]\n",
> 			sb->s_blocksize,
> 			sbi->s_groups_count,
> 			EXT4_BLOCKS_PER_GROUP(sb),
> --
> 1.8.3.1
> 


Cheers, Andreas
Theodore Y. Ts'o July 30, 2018, 2:54 a.m. | #2
On Tue, May 29, 2018 at 08:45:17PM +0900, Wang Shilong wrote:
> +#define EXT4_MOUNT_BITMAPS_WARN		0x100000000 /* warn on bitmaps corruptions */
>  
>  /*
>   * Mount flags set either automatically (could not be set by mount option)
> @@ -1338,7 +1339,7 @@ struct ext4_sb_info {
>  	struct buffer_head * s_sbh;	/* Buffer containing the super block */
>  	struct ext4_super_block *s_es;	/* Pointer to the super block in the buffer */
>  	struct buffer_head **s_group_desc;
> -	unsigned int s_mount_opt;
> +	unsigned long long s_mount_opt;

I'd really rather *not* make s_mount_opt a long long.  64-bit
operations are slower (especially on 32-bit systems).  It would
probably be better to make it be more covenient to use s_mount_opt2
--- by adding a new mount options flag MOPT_2 which causes MOPT_SET
and MOPT_CLEAR to operate on s_mount_opt2, and then teaching
_ext4_show_options() to support mount options that are set in
s_mount_opt2.   And please do this in a separate commit and explain why
--- because we're running out of bits in s_mount_opt)


> +void save_error_info(struct super_block *sb, const char *func,
> +		     unsigned int line);

Please don't make a formerly static function visible in the global
namespace without adding an ext4_ prefix().

I don't think this is actually the right way to do things, actually.
If you're going to set the error_info, then it should be logged as an
EXT4-fs error.  What I would do instead is add an argument to
__ext4_error() that requests it to skip calling ext4_handle_error().

I think you will find that this will significantly reduce the size of
the patch and the number of lines added.

					- Ted

Patch

diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index 5fe63ff..bef6ae8 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -451,8 +451,10 @@  struct buffer_head *
 		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);
+			if (!EXT4_MB_GRP_BBITMAP_CORRUPT(
+			     ext4_get_group_info(sb, block_group)))
+				ext4_error(sb, "Failed to init block bitmap for group "
+					       "%u: %d", block_group, err);
 			goto out;
 		}
 		goto verify;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 26f8c12..96c5c1c 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1113,6 +1113,7 @@  struct ext4_inode_info {
 #define EXT4_MOUNT_BLOCK_VALIDITY	0x20000000 /* Block validity checking */
 #define EXT4_MOUNT_DISCARD		0x40000000 /* Issue DISCARD requests */
 #define EXT4_MOUNT_INIT_INODE_TABLE	0x80000000 /* Initialize uninitialized itables */
+#define EXT4_MOUNT_BITMAPS_WARN		0x100000000 /* warn on bitmaps corruptions */
 
 /*
  * Mount flags set either automatically (could not be set by mount option)
@@ -1338,7 +1339,7 @@  struct ext4_sb_info {
 	struct buffer_head * s_sbh;	/* Buffer containing the super block */
 	struct ext4_super_block *s_es;	/* Pointer to the super block in the buffer */
 	struct buffer_head **s_group_desc;
-	unsigned int s_mount_opt;
+	unsigned long long s_mount_opt;
 	unsigned int s_mount_opt2;
 	unsigned int s_mount_flags;
 	unsigned int s_def_mount_opt;
@@ -2563,6 +2564,8 @@  void __ext4_grp_locked_error(const char *, unsigned int,
 			     struct super_block *, ext4_group_t,
 			     unsigned long, ext4_fsblk_t, unsigned int,
 			     const char *, ...);
+void save_error_info(struct super_block *sb, const char *func,
+		     unsigned int line);
 
 #define EXT4_ERROR_INODE(inode, fmt, a...) \
 	ext4_error_inode((inode), __func__, __LINE__, 0, (fmt), ## a)
@@ -2598,10 +2601,17 @@  void __ext4_grp_locked_error(const char *, unsigned int,
 	do {								\
 		int ret;						\
 		ret = __ext4_mark_group_bitmap_corrupted(sb, group,	\
-							flags);		\
-		if (ret)						\
-			__ext4_error(sb, __func__, __LINE__,		\
-				     fmt, ##__VA_ARGS__);		\
+							 flags);	\
+		if (ret) {						\
+			if (test_opt(sb, BITMAPS_WARN)) {		\
+				__ext4_warning(sb, __func__, __LINE__,	\
+					fmt, ##__VA_ARGS__);		\
+				save_error_info(sb, __func__, __LINE__);\
+			} else {					\
+				__ext4_error(sb, __func__, __LINE__,	\
+					fmt, ##__VA_ARGS__);		\
+			}						\
+		}							\
 	} while (0)
 
 
@@ -2656,7 +2666,12 @@  void __ext4_grp_locked_error(const char *, unsigned int,
 							 flags);	\
 		if (ret) {						\
 			no_printk(fmt, ##__VA_ARGS__);			\
-			__ext4_error(sb, "", 0, " ");			\
+			if (test_opt(sb, BITMAPS_WARN) {		\
+				__ext4_warning(sb, "", 0, " ");		\
+				save_error_info(sb, __func__, __LINE__);\
+			} else {					\
+				__ext4_error(sb, "", 0, " ");		\
+			}						\
 		}							\
 	} while (0)
 
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 3680623..1ee3138 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -3907,15 +3907,16 @@  static int ext4_mb_new_preallocation(struct ext4_allocation_context *ac)
 	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",
-			   err, group);
 		return 0;
 	}
 
 	err = ext4_mb_load_buddy(sb, group, &e4b);
 	if (err) {
-		ext4_warning(sb, "Error %d loading buddy information for %u",
-			     err, group);
+		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(
+		     ext4_get_group_info(sb, group)))
+			ext4_warning(sb,
+				"Error %d loading buddy information for %u",
+				err, group);
 		put_bh(bitmap_bh);
 		return 0;
 	}
@@ -4075,16 +4076,17 @@  void ext4_discard_preallocations(struct inode *inode)
 		err = ext4_mb_load_buddy_gfp(sb, group, &e4b,
 					     GFP_NOFS|__GFP_NOFAIL);
 		if (err) {
-			ext4_error(sb, "Error %d loading buddy information for %u",
-				   err, group);
+			if (!EXT4_MB_GRP_BBITMAP_CORRUPT(
+					ext4_get_group_info(sb, group)))
+				ext4_error(sb,
+					"Error %d loading buddy information for %u",
+					err, 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",
-					err, group);
 			ext4_mb_unload_buddy(&e4b);
 			continue;
 		}
@@ -4338,8 +4340,10 @@  static void ext4_mb_group_or_file(struct ext4_allocation_context *ac)
 		err = ext4_mb_load_buddy_gfp(sb, group, &e4b,
 					     GFP_NOFS|__GFP_NOFAIL);
 		if (err) {
-			ext4_error(sb, "Error %d loading buddy information for %u",
-				   err, group);
+			if (!EXT4_MB_GRP_BBITMAP_CORRUPT(
+					ext4_get_group_info(sb, group)))
+				ext4_error(sb, "Error %d loading buddy information for %u",
+					       err, group);
 			continue;
 		}
 		ext4_lock_group(sb, group);
@@ -4819,11 +4823,8 @@  void ext4_free_blocks(handle_t *handle, struct inode *inode,
 	}
 	count_clusters = EXT4_NUM_B2C(sbi, count);
 	bitmap_bh = ext4_read_block_bitmap(sb, block_group);
-	if (IS_ERR(bitmap_bh)) {
-		err = PTR_ERR(bitmap_bh);
-		bitmap_bh = NULL;
-		goto error_return;
-	}
+	if (IS_ERR(bitmap_bh))
+		return;
 	gdp = ext4_get_group_desc(sb, block_group, &gd_bh);
 	if (!gdp) {
 		err = -EIO;
@@ -5001,11 +5002,8 @@  int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
 	}
 
 	bitmap_bh = ext4_read_block_bitmap(sb, block_group);
-	if (IS_ERR(bitmap_bh)) {
-		err = PTR_ERR(bitmap_bh);
-		bitmap_bh = NULL;
-		goto error_return;
-	}
+	if (IS_ERR(bitmap_bh))
+		return PTR_ERR(bitmap_bh);
 
 	desc = ext4_get_group_desc(sb, block_group, &gd_bh);
 	if (!desc) {
@@ -5168,8 +5166,11 @@  static int ext4_trim_extent(struct super_block *sb, int start, int count,
 
 	ret = ext4_mb_load_buddy(sb, group, &e4b);
 	if (ret) {
-		ext4_warning(sb, "Error %d loading buddy information for %u",
-			     ret, group);
+		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(
+				ext4_get_group_info(sb, group)))
+			ext4_warning(sb,
+				"Error %d loading buddy information for %u",
+				ret, group);
 		return ret;
 	}
 	bitmap = e4b.bd_bitmap;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 9f88991..63f40ae 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -342,8 +342,8 @@  static void __save_error_info(struct super_block *sb, const char *func,
 	le32_add_cpu(&es->s_error_count, 1);
 }
 
-static void save_error_info(struct super_block *sb, const char *func,
-			    unsigned int line)
+void save_error_info(struct super_block *sb, const char *func,
+		     unsigned int line)
 {
 	__save_error_info(sb, func, line);
 	ext4_commit_super(sb, 1);
@@ -743,7 +743,8 @@  void __ext4_grp_locked_error(const char *function, unsigned int line,
 	if (flags)
 		__ext4_mark_group_bitmap_corrupted(sb, grp, flags);
 
-	if (test_opt(sb, ERRORS_CONT)) {
+	if (test_opt(sb, ERRORS_CONT) ||
+	    (flags && test_opt(sb, BITMAPS_WARN))) {
 		ext4_commit_super(sb, 0);
 		return;
 	}
@@ -1373,7 +1374,8 @@  static struct dquot **ext4_get_dquots(struct inode *inode)
 };
 
 enum {
-	Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
+	Opt_bsd_df, Opt_minix_df, Opt_bitmaps_warn,
+	Opt_bitmaps_err, Opt_grpid, Opt_nogrpid,
 	Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro,
 	Opt_nouid32, Opt_debug, Opt_removed,
 	Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl,
@@ -1398,6 +1400,8 @@  enum {
 static const match_table_t tokens = {
 	{Opt_bsd_df, "bsddf"},
 	{Opt_minix_df, "minixdf"},
+	{Opt_bitmaps_warn, "bitmaps=warn"},
+	{Opt_bitmaps_err, "bitmaps=error"},
 	{Opt_grpid, "grpid"},
 	{Opt_grpid, "bsdgroups"},
 	{Opt_nogrpid, "nogrpid"},
@@ -1598,11 +1602,13 @@  static int clear_qf_name(struct super_block *sb, int qtype)
 
 static const struct mount_opts {
 	int	token;
-	int	mount_opt;
+	unsigned long long mount_opt;
 	int	flags;
 } ext4_mount_opts[] = {
 	{Opt_minix_df, EXT4_MOUNT_MINIX_DF, MOPT_SET},
 	{Opt_bsd_df, EXT4_MOUNT_MINIX_DF, MOPT_CLEAR},
+	{Opt_bitmaps_warn, EXT4_MOUNT_BITMAPS_WARN, MOPT_SET},
+	{Opt_bitmaps_err, EXT4_MOUNT_BITMAPS_WARN, MOPT_CLEAR},
 	{Opt_grpid, EXT4_MOUNT_GRPID, MOPT_SET},
 	{Opt_nogrpid, EXT4_MOUNT_GRPID, MOPT_CLEAR},
 	{Opt_block_validity, EXT4_MOUNT_BLOCK_VALIDITY, MOPT_SET},
@@ -2099,6 +2105,8 @@  static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
 		SEQ_OPTS_PUTS("errors=continue");
 	if (test_opt(sb, ERRORS_PANIC) && def_errors != EXT4_ERRORS_PANIC)
 		SEQ_OPTS_PUTS("errors=panic");
+	if (test_opt(sb, BITMAPS_WARN))
+		SEQ_OPTS_PUTS("bitmaps=warn");
 	if (nodefs || sbi->s_commit_interval != JBD2_DEFAULT_MAX_COMMIT_AGE*HZ)
 		SEQ_OPTS_PRINT("commit=%lu", sbi->s_commit_interval / HZ);
 	if (nodefs || sbi->s_min_batch_time != EXT4_DEF_MIN_BATCH_TIME)
@@ -2197,7 +2205,7 @@  static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
 done:
 	if (test_opt(sb, DEBUG))
 		printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
-				"bpg=%lu, ipg=%lu, mo=%04x, mo2=%04x]\n",
+				"bpg=%lu, ipg=%lu, mo=%04llx, mo2=%04x]\n",
 			sb->s_blocksize,
 			sbi->s_groups_count,
 			EXT4_BLOCKS_PER_GROUP(sb),