Skip to content

Commit 7b97d86

Browse files
zhangyi089tytso
authored andcommitted
ext4, jbd2: ensure panic by fix a race between jbd2 abort and ext4 error handlers
In the ext4 filesystem with errors=panic, if one process is recording errno in the superblock when invoking jbd2_journal_abort() due to some error cases, it could be raced by another __ext4_abort() which is setting the SB_RDONLY flag but missing panic because errno has not been recorded. jbd2_journal_commit_transaction() jbd2_journal_abort() journal->j_flags |= JBD2_ABORT; jbd2_journal_update_sb_errno() | ext4_journal_check_start() | __ext4_abort() | sb->s_flags |= SB_RDONLY; | if (!JBD2_REC_ERR) | return; journal->j_flags |= JBD2_REC_ERR; Finally, it will no longer trigger panic because the filesystem has already been set read-only. Fix this by introduce j_abort_mutex to make sure journal abort is completed before panic, and remove JBD2_REC_ERR flag. Fixes: 4327ba5 ("ext4, jbd2: ensure entering into panic after recording an error in superblock") Signed-off-by: zhangyi (F) <[email protected]> Reviewed-by: Jan Kara <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 88ee9d5 commit 7b97d86

File tree

3 files changed

+22
-17
lines changed

3 files changed

+22
-17
lines changed

fs/ext4/super.c

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -522,9 +522,6 @@ static void ext4_handle_error(struct super_block *sb)
522522
smp_wmb();
523523
sb->s_flags |= SB_RDONLY;
524524
} else if (test_opt(sb, ERRORS_PANIC)) {
525-
if (EXT4_SB(sb)->s_journal &&
526-
!(EXT4_SB(sb)->s_journal->j_flags & JBD2_REC_ERR))
527-
return;
528525
panic("EXT4-fs (device %s): panic forced after error\n",
529526
sb->s_id);
530527
}
@@ -725,23 +722,20 @@ void __ext4_abort(struct super_block *sb, const char *function,
725722
va_end(args);
726723

727724
if (sb_rdonly(sb) == 0) {
728-
ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
729725
EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
726+
if (EXT4_SB(sb)->s_journal)
727+
jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
728+
729+
ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
730730
/*
731731
* Make sure updated value of ->s_mount_flags will be visible
732732
* before ->s_flags update
733733
*/
734734
smp_wmb();
735735
sb->s_flags |= SB_RDONLY;
736-
if (EXT4_SB(sb)->s_journal)
737-
jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
738736
}
739-
if (test_opt(sb, ERRORS_PANIC) && !system_going_down()) {
740-
if (EXT4_SB(sb)->s_journal &&
741-
!(EXT4_SB(sb)->s_journal->j_flags & JBD2_REC_ERR))
742-
return;
737+
if (test_opt(sb, ERRORS_PANIC) && !system_going_down())
743738
panic("EXT4-fs panic from previous error\n");
744-
}
745739
}
746740

747741
void __ext4_msg(struct super_block *sb,

fs/jbd2/journal.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,7 @@ static journal_t *journal_init_common(struct block_device *bdev,
11401140
init_waitqueue_head(&journal->j_wait_commit);
11411141
init_waitqueue_head(&journal->j_wait_updates);
11421142
init_waitqueue_head(&journal->j_wait_reserved);
1143+
mutex_init(&journal->j_abort_mutex);
11431144
mutex_init(&journal->j_barrier);
11441145
mutex_init(&journal->j_checkpoint_mutex);
11451146
spin_lock_init(&journal->j_revoke_lock);
@@ -1402,7 +1403,8 @@ static int jbd2_write_superblock(journal_t *journal, int write_flags)
14021403
printk(KERN_ERR "JBD2: Error %d detected when updating "
14031404
"journal superblock for %s.\n", ret,
14041405
journal->j_devname);
1405-
jbd2_journal_abort(journal, ret);
1406+
if (!is_journal_aborted(journal))
1407+
jbd2_journal_abort(journal, ret);
14061408
}
14071409

14081410
return ret;
@@ -2153,6 +2155,13 @@ void jbd2_journal_abort(journal_t *journal, int errno)
21532155
{
21542156
transaction_t *transaction;
21552157

2158+
/*
2159+
* Lock the aborting procedure until everything is done, this avoid
2160+
* races between filesystem's error handling flow (e.g. ext4_abort()),
2161+
* ensure panic after the error info is written into journal's
2162+
* superblock.
2163+
*/
2164+
mutex_lock(&journal->j_abort_mutex);
21562165
/*
21572166
* ESHUTDOWN always takes precedence because a file system check
21582167
* caused by any other journal abort error is not required after
@@ -2167,6 +2176,7 @@ void jbd2_journal_abort(journal_t *journal, int errno)
21672176
journal->j_errno = errno;
21682177
jbd2_journal_update_sb_errno(journal);
21692178
}
2179+
mutex_unlock(&journal->j_abort_mutex);
21702180
return;
21712181
}
21722182

@@ -2188,10 +2198,7 @@ void jbd2_journal_abort(journal_t *journal, int errno)
21882198
* layer could realise that a filesystem check is needed.
21892199
*/
21902200
jbd2_journal_update_sb_errno(journal);
2191-
2192-
write_lock(&journal->j_state_lock);
2193-
journal->j_flags |= JBD2_REC_ERR;
2194-
write_unlock(&journal->j_state_lock);
2201+
mutex_unlock(&journal->j_abort_mutex);
21952202
}
21962203

21972204
/**

include/linux/jbd2.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,11 @@ struct journal_s
765765
*/
766766
int j_errno;
767767

768+
/**
769+
* @j_abort_mutex: Lock the whole aborting procedure.
770+
*/
771+
struct mutex j_abort_mutex;
772+
768773
/**
769774
* @j_sb_buffer: The first part of the superblock buffer.
770775
*/
@@ -1247,7 +1252,6 @@ JBD2_FEATURE_INCOMPAT_FUNCS(csum3, CSUM_V3)
12471252
#define JBD2_ABORT_ON_SYNCDATA_ERR 0x040 /* Abort the journal on file
12481253
* data write error in ordered
12491254
* mode */
1250-
#define JBD2_REC_ERR 0x080 /* The errno in the sb has been recorded */
12511255

12521256
/*
12531257
* Function declarations for the journaling transaction and buffer

0 commit comments

Comments
 (0)