ext4: handle errors on ext4_commit_super
PATCH 1/1 ext4: handle errors on ext4_commit_super
From: Jaegeuk Kim <[email protected]>
When remounting ext4 from ro to rw, currently it allows its transition, even if ext4_commit_super() returns EIO. Even worse thing is, after that, fs/buffer complains buffer dirty bits like:
Call trace: [<ffffff9750c259dc>] mark_buffer_dirty+0x184/0x1a4 [<ffffff9750cb398c>] __ext4_handle_dirty_super+0x4c/0xfc [<ffffff9750c7a9fc>] ext4_file_open+0x154/0x1c0 [<ffffff9750bea51c>] do_dentry_open+0x114/0x2d0 [<ffffff9750bea75c>] vfs_open+0x5c/0x94 [<ffffff9750bf879c>] path_openat+0x668/0xfe8 [<ffffff9750bf8088>] do_filp_open+0x74/0x120 [<ffffff9750beac98>] do_sys_open+0x148/0x254 [<ffffff9750beade0>] SyS_openat+0x10/0x18 [<ffffff9750a83ab0>] el0_svc_naked+0x24/0x28 EXT4-fs (dm-1): previous I/O error to superblock detected Buffer I/O error on dev dm-1, logical block 0, lost sync page write EXT4-fs (dm-1): re-mounted. Opts: (null) Buffer I/O error on dev dm-1, logical block 80, lost async page write
--- fs/ext4/super.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)
fs/ext4/super.c +13 -8
@@ -2151,7 +2151,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,if (sbi->s_journal)ext4_set_feature_journal_needs_recovery (sb); -ext4_commit_super (sb,1 ); + res =ext4_commit_super (sb,1 ); done:if (test_opt (sb,DEBUG )) printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
@@ -4739,11 +4739,10 @@ static int ext4_commit_super(struct super_block *sb, int sync)unlock_buffer (sbh); error =__sync_dirty_buffer (sbh,REQ_SYNC | (test_opt (sb,BARRIER ) ?REQ_FUA :0 )); -if (error) +if (error && !buffer_write_io_error (sbh))return error; - error =buffer_write_io_error (sbh); -if (error) { +if (buffer_write_io_error (sbh)) {ext4_msg (sb,KERN_ERR ,"I/O error while writing " "superblock" );clear_buffer_write_io_error (sbh);
@@ -5144,8 +5143,12 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)if (sbi->s_journal)ext4_clear_journal_err (sb, es); sbi->s_mount_state =le16_to_cpu (es->s_state); -if (!ext4_setup_super (sb, es,0 )) - sb->s_flags &= ~SB_RDONLY ; + + err =ext4_setup_super (sb, es,0 ); +if (err) + goto restore_opts; + + sb->s_flags &= ~SB_RDONLY ;if (ext4_has_feature_mmp (sb))if (ext4_multi_mount_protect (sb,le64_to_cpu (es->s_mmp_block))) {
@@ -5169,8 +5172,10 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) }ext4_setup_system_zone (sb); -if (sbi->s_journal ==NULL && !(old_sb_flags &SB_RDONLY )) -ext4_commit_super (sb,1 ); +if (sbi->s_journal ==NULL && !(old_sb_flags &SB_RDONLY )) { + err =ext4_commit_super (sb,1 ); + goto restore_opts; + }#ifdef CONFIG_QUOTA /* Release old quota file names */
Signature · 1 line
2.15.0.531.g2ccb3012c9-goog
Jan Kara
reviewerQuoted 22 lines · Jaegeuk Kim
From: Jaegeuk Kim <[email protected]>
When remounting ext4 from ro to rw, currently it allows its transition, even if
ext4_commit_super()returns EIO. Even worse thing is, after that, fs/buffer complains buffer dirty bits like:Call trace: [<ffffff9750c259dc>] mark_buffer_dirty+0x184/0x1a4 [<ffffff9750cb398c>] __ext4_handle_dirty_super+0x4c/0xfc [<ffffff9750c7a9fc>] ext4_file_open+0x154/0x1c0 [<ffffff9750bea51c>] do_dentry_open+0x114/0x2d0 [<ffffff9750bea75c>] vfs_open+0x5c/0x94 [<ffffff9750bf879c>] path_openat+0x668/0xfe8 [<ffffff9750bf8088>] do_filp_open+0x74/0x120 [<ffffff9750beac98>] do_sys_open+0x148/0x254 [<ffffff9750beade0>] SyS_openat+0x10/0x18 [<ffffff9750a83ab0>] el0_svc_naked+0x24/0x28 EXT4-fs (dm-1): previous I/O error to superblock detected Buffer I/O error on dev dm-1, logical block 0, lost sync page write EXT4-fs (dm-1): re-mounted. Opts: (null) Buffer I/O error on dev dm-1, logical block 80, lost async page write
Quoted 8 lines
Cc "Theodore Ts'o" <[email protected]>Cc Andreas Dilger <[email protected]>Cc Jaegeuk Kim <[email protected]>Signed-off-by Jaegeuk Kim <[email protected]>--- fs/ext4/super.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)
Thanks for the patch. Some comments below.
Quoted 6 lines
fs/ext4/super.c +0
-0
fs/ext4/super.c static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, · line 2157
Quoted 8 lines
fs/ext4/super.c +1
-1@@ -2151,7 +2151,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,if (sbi->s_journal)ext4_set_feature_journal_needs_recovery (sb); -ext4_commit_super (sb,1 ); + res =ext4_commit_super (sb,1 ); done:if (test_opt (sb,DEBUG )) printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
So return value of ext4_setup_super() looks a bit strange and it seems it currently returns 0 or SB_RDONLY. So I think you first need to clean this up before making ext4_setup_super() return error code (as your patch could make ext4_fill_super() return SB_RDONLY under some circumstances).
Quoted 4 lines
fs/ext4/super.c +0
-0@@ -4739,11 +4739,10 @@ static int ext4_commit_super(struct super_block *sb, int sync)unlock_buffer (sbh); error =__sync_dirty_buffer (sbh,REQ_SYNC | (test_opt (sb,BARRIER ) ?REQ_FUA :0 ));
fs/ext4/super.c static int ext4_commit_super(struct super_block *sb, int sync) · line 4748
Quoted 10 lines
fs/ext4/super.c +2
-3-if (error) +if (error && !buffer_write_io_error (sbh))return error; - error =buffer_write_io_error (sbh); -if (error) { +if (buffer_write_io_error (sbh)) {ext4_msg (sb,KERN_ERR ,"I/O error while writing " "superblock" );clear_buffer_write_io_error (sbh);
I'd rather rewrite this as:
if (buffer_write_io_error(sbh)) {
do stuff as now
}
if (error)
return error;
HonzaSignature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Hi Jan,
Thank you for the review.
Quoted 43 lines · Jan Kara
Quoted 22 lines · Jaegeuk Kim
From: Jaegeuk Kim <[email protected]>
When remounting ext4 from ro to rw, currently it allows its transition, even if
ext4_commit_super()returns EIO. Even worse thing is, after that, fs/buffer complains buffer dirty bits like:Call trace: [<ffffff9750c259dc>] mark_buffer_dirty+0x184/0x1a4 [<ffffff9750cb398c>] __ext4_handle_dirty_super+0x4c/0xfc [<ffffff9750c7a9fc>] ext4_file_open+0x154/0x1c0 [<ffffff9750bea51c>] do_dentry_open+0x114/0x2d0 [<ffffff9750bea75c>] vfs_open+0x5c/0x94 [<ffffff9750bf879c>] path_openat+0x668/0xfe8 [<ffffff9750bf8088>] do_filp_open+0x74/0x120 [<ffffff9750beac98>] do_sys_open+0x148/0x254 [<ffffff9750beade0>] SyS_openat+0x10/0x18 [<ffffff9750a83ab0>] el0_svc_naked+0x24/0x28 EXT4-fs (dm-1): previous I/O error to superblock detected Buffer I/O error on dev dm-1, logical block 0, lost sync page write EXT4-fs (dm-1): re-mounted. Opts: (null) Buffer I/O error on dev dm-1, logical block 80, lost async page writeQuoted 8 lines
Cc "Theodore Ts'o" <[email protected]>Cc Andreas Dilger <[email protected]>Cc Jaegeuk Kim <[email protected]>Signed-off-by Jaegeuk Kim <[email protected]>--- fs/ext4/super.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)Thanks for the patch. Some comments below.
Quoted 9 lines
fs/ext4/super.c +0
-0@@ -2151,7 +2151,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,if (sbi->s_journal)ext4_set_feature_journal_needs_recovery (sb);
Quoted 8 lines
fs/ext4/super.c static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, · line 2157
Quoted 5 lines
fs/ext4/super.c +1
-1-ext4_commit_super (sb,1 ); + res =ext4_commit_super (sb,1 ); done:if (test_opt (sb,DEBUG )) printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "So return value of
ext4_setup_super()looks a bit strange and it seems it currently returns 0 orSB_RDONLY. So I think you first need to clean this
Indeed. I'll update it in v2.
Quoted 2 lines
up before making
ext4_setup_super()return error code (as your patch could makeext4_fill_super()returnSB_RDONLYunder some circumstances).
Interestingly, it only affects ext4_remount() only. :P
Quoted 17 lines
Quoted 5 lines
fs/ext4/super.c +0
-0@@ -4739,11 +4739,10 @@ static int ext4_commit_super(struct super_block *sb, int sync)unlock_buffer (sbh); error =__sync_dirty_buffer (sbh,REQ_SYNC | (test_opt (sb,BARRIER ) ?REQ_FUA :0 ));fs/ext4/super.c static int ext4_commit_super(struct super_block *sb, int sync) · line 4748
Quoted 10 lines
fs/ext4/super.c +2
-3-if (error) +if (error && !buffer_write_io_error (sbh))return error; - error =buffer_write_io_error (sbh); -if (error) { +if (buffer_write_io_error (sbh)) {ext4_msg (sb,KERN_ERR ,"I/O error while writing " "superblock" );clear_buffer_write_io_error (sbh);I'd rather rewrite this as:
Quoted 5 lines
if (buffer_write_io_error(sbh)) {
do stuff as now
}
if (error)
return error;Got it.
Thanks,
Quoted 5 lines
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
When remounting ext4 from ro to rw, currently it allows its transition, even if ext4_commit_super() returns EIO. Even worse thing is, after that, fs/buffer complains buffer dirty bits like:
Call trace: [<ffffff9750c259dc>] mark_buffer_dirty+0x184/0x1a4 [<ffffff9750cb398c>] __ext4_handle_dirty_super+0x4c/0xfc [<ffffff9750c7a9fc>] ext4_file_open+0x154/0x1c0 [<ffffff9750bea51c>] do_dentry_open+0x114/0x2d0 [<ffffff9750bea75c>] vfs_open+0x5c/0x94 [<ffffff9750bf879c>] path_openat+0x668/0xfe8 [<ffffff9750bf8088>] do_filp_open+0x74/0x120 [<ffffff9750beac98>] do_sys_open+0x148/0x254 [<ffffff9750beade0>] SyS_openat+0x10/0x18 [<ffffff9750a83ab0>] el0_svc_naked+0x24/0x28 EXT4-fs (dm-1): previous I/O error to superblock detected Buffer I/O error on dev dm-1, logical block 0, lost sync page write EXT4-fs (dm-1): re-mounted. Opts: (null) Buffer I/O error on dev dm-1, logical block 80, lost async page write
---
Change log from v1:
- modify return value of ext4_setup_super
- change the error handling order in ext4_commit_super
fs/ext4/super.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-)
fs/ext4/super.c +15 -13
@@ -2116,12 +2116,12 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, int read_only) {struct ext4_sb_info *sbi =EXT4_SB (sb); -int res =0 ; +int err =0 ;if (le32_to_cpu (es->s_rev_level) >EXT4_MAX_SUPP_REV ) {ext4_msg (sb,KERN_ERR ,"revision level too high, " "forcing read-only mode" ); - res =SB_RDONLY ; + err = -EROFS ; }if (read_only) goto done;
@@ -2154,7 +2154,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,if (sbi->s_journal)ext4_set_feature_journal_needs_recovery (sb); -ext4_commit_super (sb,1 ); + err =ext4_commit_super (sb,1 ); done:if (test_opt (sb,DEBUG )) printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
@@ -2166,7 +2166,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, sbi->s_mount_opt, sbi->s_mount_opt2);cleancache_init_fs (sb); -return res; +return err; }int ext4_alloc_flex_bg_array (struct super_block *sb,ext4_group_t ngroup)
@@ -4760,11 +4760,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)unlock_buffer (sbh); error =__sync_dirty_buffer (sbh,REQ_SYNC | (test_opt (sb,BARRIER ) ?REQ_FUA :0 )); -if (error) -return error; - - error =buffer_write_io_error (sbh); -if (error) { +if (buffer_write_io_error (sbh)) {ext4_msg (sb,KERN_ERR ,"I/O error while writing " "superblock" );clear_buffer_write_io_error (sbh);
@@ -5165,8 +5161,12 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)if (sbi->s_journal)ext4_clear_journal_err (sb, es); sbi->s_mount_state =le16_to_cpu (es->s_state); -if (!ext4_setup_super (sb, es,0 )) - sb->s_flags &= ~SB_RDONLY ; + + err =ext4_setup_super (sb, es,0 ); +if (err) + goto restore_opts; + + sb->s_flags &= ~SB_RDONLY ;if (ext4_has_feature_mmp (sb))if (ext4_multi_mount_protect (sb,le64_to_cpu (es->s_mmp_block))) {
@@ -5190,8 +5190,10 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) }ext4_setup_system_zone (sb); -if (sbi->s_journal ==NULL && !(old_sb_flags &SB_RDONLY )) -ext4_commit_super (sb,1 ); +if (sbi->s_journal ==NULL && !(old_sb_flags &SB_RDONLY )) { + err =ext4_commit_super (sb,1 ); + goto restore_opts; + }#ifdef CONFIG_QUOTA /* Release old quota file names */
Signature · 1 line
2.17.0.484.g0c8726318c-goog
Jan Kara
reviewerQuoted 20 lines · Jaegeuk Kim
When remounting ext4 from ro to rw, currently it allows its transition, even if
ext4_commit_super()returns EIO. Even worse thing is, after that, fs/buffer complains buffer dirty bits like:Call trace: [<ffffff9750c259dc>] mark_buffer_dirty+0x184/0x1a4 [<ffffff9750cb398c>] __ext4_handle_dirty_super+0x4c/0xfc [<ffffff9750c7a9fc>] ext4_file_open+0x154/0x1c0 [<ffffff9750bea51c>] do_dentry_open+0x114/0x2d0 [<ffffff9750bea75c>] vfs_open+0x5c/0x94 [<ffffff9750bf879c>] path_openat+0x668/0xfe8 [<ffffff9750bf8088>] do_filp_open+0x74/0x120 [<ffffff9750beac98>] do_sys_open+0x148/0x254 [<ffffff9750beade0>] SyS_openat+0x10/0x18 [<ffffff9750a83ab0>] el0_svc_naked+0x24/0x28 EXT4-fs (dm-1): previous I/O error to superblock detected Buffer I/O error on dev dm-1, logical block 0, lost sync page write EXT4-fs (dm-1): re-mounted. Opts: (null) Buffer I/O error on dev dm-1, logical block 80, lost async page write
Quoted 5 lines
Cc "Theodore Ts'o" <[email protected]>Cc Andreas Dilger <[email protected]>Cc Jaegeuk Kim <[email protected]>Signed-off-by Jaegeuk Kim <[email protected]>
Looks mostly good. Just:
fs/ext4/super.c static int ext4_remount(struct super_block *sb, int *flags, char *data) · line 5196
Quoted 10 lines
fs/ext4/super.c +4
-2@@ -5190,8 +5190,10 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) }ext4_setup_system_zone (sb); -if (sbi->s_journal ==NULL && !(old_sb_flags &SB_RDONLY )) -ext4_commit_super (sb,1 ); +if (sbi->s_journal ==NULL && !(old_sb_flags &SB_RDONLY )) { + err =ext4_commit_super (sb,1 ); + goto restore_opts; + }
Why is goto restore_opts unconditional here?
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
When remounting ext4 from ro to rw, currently it allows its transition, even if ext4_commit_super() returns EIO. Even worse thing is, after that, fs/buffer complains buffer dirty bits like:
Call trace: [<ffffff9750c259dc>] mark_buffer_dirty+0x184/0x1a4 [<ffffff9750cb398c>] __ext4_handle_dirty_super+0x4c/0xfc [<ffffff9750c7a9fc>] ext4_file_open+0x154/0x1c0 [<ffffff9750bea51c>] do_dentry_open+0x114/0x2d0 [<ffffff9750bea75c>] vfs_open+0x5c/0x94 [<ffffff9750bf879c>] path_openat+0x668/0xfe8 [<ffffff9750bf8088>] do_filp_open+0x74/0x120 [<ffffff9750beac98>] do_sys_open+0x148/0x254 [<ffffff9750beade0>] SyS_openat+0x10/0x18 [<ffffff9750a83ab0>] el0_svc_naked+0x24/0x28 EXT4-fs (dm-1): previous I/O error to superblock detected Buffer I/O error on dev dm-1, logical block 0, lost sync page write EXT4-fs (dm-1): re-mounted. Opts: (null) Buffer I/O error on dev dm-1, logical block 80, lost async page write
---
Change log from v2:
- fix missing goto condition
fs/ext4/super.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-)
fs/ext4/super.c +16 -13
@@ -2116,12 +2116,12 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, int read_only) {struct ext4_sb_info *sbi =EXT4_SB (sb); -int res =0 ; +int err =0 ;if (le32_to_cpu (es->s_rev_level) >EXT4_MAX_SUPP_REV ) {ext4_msg (sb,KERN_ERR ,"revision level too high, " "forcing read-only mode" ); - res =SB_RDONLY ; + err = -EROFS ; }if (read_only) goto done;
@@ -2154,7 +2154,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,if (sbi->s_journal)ext4_set_feature_journal_needs_recovery (sb); -ext4_commit_super (sb,1 ); + err =ext4_commit_super (sb,1 ); done:if (test_opt (sb,DEBUG )) printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
@@ -2166,7 +2166,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, sbi->s_mount_opt, sbi->s_mount_opt2);cleancache_init_fs (sb); -return res; +return err; }int ext4_alloc_flex_bg_array (struct super_block *sb,ext4_group_t ngroup)
@@ -4760,11 +4760,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)unlock_buffer (sbh); error =__sync_dirty_buffer (sbh,REQ_SYNC | (test_opt (sb,BARRIER ) ?REQ_FUA :0 )); -if (error) -return error; - - error =buffer_write_io_error (sbh); -if (error) { +if (buffer_write_io_error (sbh)) {ext4_msg (sb,KERN_ERR ,"I/O error while writing " "superblock" );clear_buffer_write_io_error (sbh);
@@ -5165,8 +5161,12 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)if (sbi->s_journal)ext4_clear_journal_err (sb, es); sbi->s_mount_state =le16_to_cpu (es->s_state); -if (!ext4_setup_super (sb, es,0 )) - sb->s_flags &= ~SB_RDONLY ; + + err =ext4_setup_super (sb, es,0 ); +if (err) + goto restore_opts; + + sb->s_flags &= ~SB_RDONLY ;if (ext4_has_feature_mmp (sb))if (ext4_multi_mount_protect (sb,le64_to_cpu (es->s_mmp_block))) {
@@ -5190,8 +5190,11 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) }ext4_setup_system_zone (sb); -if (sbi->s_journal ==NULL && !(old_sb_flags &SB_RDONLY )) -ext4_commit_super (sb,1 ); +if (sbi->s_journal ==NULL && !(old_sb_flags &SB_RDONLY )) { + err =ext4_commit_super (sb,1 ); +if (err) + goto restore_opts; + }#ifdef CONFIG_QUOTA /* Release old quota file names */
Signature · 1 line
2.17.0.484.g0c8726318c-goog
Theodore Y. Ts'o
maintainerQuoted 20 lines · Jaegeuk Kim
When remounting ext4 from ro to rw, currently it allows its transition, even if
ext4_commit_super()returns EIO. Even worse thing is, after that, fs/buffer complains buffer dirty bits like:Call trace: [<ffffff9750c259dc>] mark_buffer_dirty+0x184/0x1a4 [<ffffff9750cb398c>] __ext4_handle_dirty_super+0x4c/0xfc [<ffffff9750c7a9fc>] ext4_file_open+0x154/0x1c0 [<ffffff9750bea51c>] do_dentry_open+0x114/0x2d0 [<ffffff9750bea75c>] vfs_open+0x5c/0x94 [<ffffff9750bf879c>] path_openat+0x668/0xfe8 [<ffffff9750bf8088>] do_filp_open+0x74/0x120 [<ffffff9750beac98>] do_sys_open+0x148/0x254 [<ffffff9750beade0>] SyS_openat+0x10/0x18 [<ffffff9750a83ab0>] el0_svc_naked+0x24/0x28 EXT4-fs (dm-1): previous I/O error to superblock detected Buffer I/O error on dev dm-1, logical block 0, lost sync page write EXT4-fs (dm-1): re-mounted. Opts: (null) Buffer I/O error on dev dm-1, logical block 80, lost async page write
Quoted 5 lines
Cc "Theodore Ts'o" <[email protected]>Cc Andreas Dilger <[email protected]>Cc Jaegeuk Kim <[email protected]>Signed-off-by Jaegeuk Kim <[email protected]>
Applied with a fix up to ext4_fill_super() when it calls ext4_setup_super() with the changed error return semantics.
- Ted
fs/ext4/super.c +5 -1
@@ -4254,8 +4254,12 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount4; } -if (ext4_setup_super (sb, es,sb_rdonly (sb))) + ret =ext4_setup_super (sb, es,sb_rdonly (sb)); +if (ret == -EROFS ) { sb->s_flags |=SB_RDONLY ; + ret =0 ; + }else if (ret) + goto failed_mount4a;/* determine the minimum size of new large inodes, if present */ if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE &&