| From 4327ba52afd03fc4b5afa0ee1d774c9c5b0e85c5 Mon Sep 17 00:00:00 2001 |
| From: Daeho Jeong <daeho.jeong@samsung.com> |
| Date: Sun, 18 Oct 2015 17:02:56 -0400 |
| Subject: ext4, jbd2: ensure entering into panic after recording an error in |
| superblock |
| |
| commit 4327ba52afd03fc4b5afa0ee1d774c9c5b0e85c5 upstream. |
| |
| If a EXT4 filesystem utilizes JBD2 journaling and an error occurs, the |
| journaling will be aborted first and the error number will be recorded |
| into JBD2 superblock and, finally, the system will enter into the |
| panic state in "errors=panic" option. But, in the rare case, this |
| sequence is little twisted like the below figure and it will happen |
| that the system enters into panic state, which means the system reset |
| in mobile environment, before completion of recording an error in the |
| journal superblock. In this case, e2fsck cannot recognize that the |
| filesystem failure occurred in the previous run and the corruption |
| wouldn't be fixed. |
| |
| Task A Task B |
| ext4_handle_error() |
| -> jbd2_journal_abort() |
| -> __journal_abort_soft() |
| -> __jbd2_journal_abort_hard() |
| | -> journal->j_flags |= JBD2_ABORT; |
| | |
| | __ext4_abort() |
| | -> jbd2_journal_abort() |
| | | -> __journal_abort_soft() |
| | | -> if (journal->j_flags & JBD2_ABORT) |
| | | return; |
| | -> panic() |
| | |
| -> jbd2_journal_update_sb_errno() |
| |
| Tested-by: Hobin Woo <hobin.woo@samsung.com> |
| Signed-off-by: Daeho Jeong <daeho.jeong@samsung.com> |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Signed-off-by: Zefan Li <lizefan@huawei.com> |
| --- |
| fs/ext4/super.c | 12 ++++++++++-- |
| fs/jbd2/journal.c | 6 +++++- |
| include/linux/jbd2.h | 1 + |
| 3 files changed, 16 insertions(+), 3 deletions(-) |
| |
| --- a/fs/ext4/super.c |
| +++ b/fs/ext4/super.c |
| @@ -483,9 +483,13 @@ static void ext4_handle_error(struct sup |
| ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only"); |
| sb->s_flags |= MS_RDONLY; |
| } |
| - if (test_opt(sb, ERRORS_PANIC)) |
| + if (test_opt(sb, ERRORS_PANIC)) { |
| + if (EXT4_SB(sb)->s_journal && |
| + !(EXT4_SB(sb)->s_journal->j_flags & JBD2_REC_ERR)) |
| + return; |
| panic("EXT4-fs (device %s): panic forced after error\n", |
| sb->s_id); |
| + } |
| } |
| |
| void __ext4_error(struct super_block *sb, const char *function, |
| @@ -659,8 +663,12 @@ void __ext4_abort(struct super_block *sb |
| jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO); |
| save_error_info(sb, function, line); |
| } |
| - if (test_opt(sb, ERRORS_PANIC)) |
| + if (test_opt(sb, ERRORS_PANIC)) { |
| + if (EXT4_SB(sb)->s_journal && |
| + !(EXT4_SB(sb)->s_journal->j_flags & JBD2_REC_ERR)) |
| + return; |
| panic("EXT4-fs panic from previous error\n"); |
| + } |
| } |
| |
| void ext4_msg(struct super_block *sb, const char *prefix, const char *fmt, ...) |
| --- a/fs/jbd2/journal.c |
| +++ b/fs/jbd2/journal.c |
| @@ -1921,8 +1921,12 @@ static void __journal_abort_soft (journa |
| |
| __jbd2_journal_abort_hard(journal); |
| |
| - if (errno) |
| + if (errno) { |
| jbd2_journal_update_sb_errno(journal); |
| + write_lock(&journal->j_state_lock); |
| + journal->j_flags |= JBD2_REC_ERR; |
| + write_unlock(&journal->j_state_lock); |
| + } |
| } |
| |
| /** |
| --- a/include/linux/jbd2.h |
| +++ b/include/linux/jbd2.h |
| @@ -954,6 +954,7 @@ struct journal_s |
| #define JBD2_ABORT_ON_SYNCDATA_ERR 0x040 /* Abort the journal on file |
| * data write error in ordered |
| * mode */ |
| +#define JBD2_REC_ERR 0x080 /* The errno in the sb has been recorded */ |
| |
| /* |
| * Function declarations for the journaling transaction and buffer |