Jan Kara 7e8f58
From 7b97d868b7ab2448859668de9222b8af43f76e78 Mon Sep 17 00:00:00 2001
Jan Kara 7e8f58
From: "zhangyi (F)" <yi.zhang@huawei.com>
Jan Kara 7e8f58
Date: Tue, 9 Jun 2020 15:35:40 +0800
Jan Kara 7e8f58
Subject: [PATCH] ext4, jbd2: ensure panic by fix a race between jbd2 abort and
Jan Kara 7e8f58
 ext4 error handlers
Jan Kara 7e8f58
Git-commit: 7b97d868b7ab2448859668de9222b8af43f76e78
Jan Kara 7e8f58
Patch-mainline: v5.8-rc2
Jan Kara 7e8f58
References: bsc#1173833
Jan Kara 7e8f58
Jan Kara 7e8f58
In the ext4 filesystem with errors=panic, if one process is recording
Jan Kara 7e8f58
errno in the superblock when invoking jbd2_journal_abort() due to some
Jan Kara 7e8f58
error cases, it could be raced by another __ext4_abort() which is
Jan Kara 7e8f58
setting the SB_RDONLY flag but missing panic because errno has not been
Jan Kara 7e8f58
recorded.
Jan Kara 7e8f58
Jan Kara 7e8f58
jbd2_journal_commit_transaction()
Jan Kara 7e8f58
 jbd2_journal_abort()
Jan Kara 7e8f58
  journal->j_flags |= JBD2_ABORT;
Jan Kara 7e8f58
  jbd2_journal_update_sb_errno()
Jan Kara 7e8f58
                                    | ext4_journal_check_start()
Jan Kara 7e8f58
                                    |  __ext4_abort()
Jan Kara 7e8f58
                                    |   sb->s_flags |= SB_RDONLY;
Jan Kara 7e8f58
                                    |   if (!JBD2_REC_ERR)
Jan Kara 7e8f58
                                    |        return;
Jan Kara 7e8f58
  journal->j_flags |= JBD2_REC_ERR;
Jan Kara 7e8f58
Jan Kara 7e8f58
Finally, it will no longer trigger panic because the filesystem has
Jan Kara 7e8f58
already been set read-only. Fix this by introduce j_abort_mutex to make
Jan Kara 7e8f58
sure journal abort is completed before panic, and remove JBD2_REC_ERR
Jan Kara 7e8f58
flag.
Jan Kara 7e8f58
Jan Kara 7e8f58
Fixes: 4327ba52afd03 ("ext4, jbd2: ensure entering into panic after recording an error in superblock")
Jan Kara 7e8f58
Signed-off-by: zhangyi (F) <yi.zhang@huawei.com>
Jan Kara 7e8f58
Reviewed-by: Jan Kara <jack@suse.cz>
Jan Kara 7e8f58
Cc: stable@vger.kernel.org
Jan Kara 7e8f58
Link: https://lore.kernel.org/r/20200609073540.3810702-1-yi.zhang@huawei.com
Jan Kara 7e8f58
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Jan Kara 7e8f58
Acked-by: Jan Kara <jack@suse.cz>
Jan Kara 7e8f58
Jan Kara 7e8f58
---
Jan Kara 7e8f58
 fs/ext4/super.c      |   16 +++++-----------
Jan Kara 7e8f58
 fs/jbd2/journal.c    |   16 ++++++++++++----
Jan Kara 7e8f58
 include/linux/jbd2.h |    6 +++++-
Jan Kara 7e8f58
 3 files changed, 22 insertions(+), 16 deletions(-)
Jan Kara 7e8f58
Jan Kara 7e8f58
--- a/fs/ext4/super.c
Jan Kara 7e8f58
+++ b/fs/ext4/super.c
Jan Kara 7e8f58
@@ -433,9 +433,6 @@ static void ext4_handle_error(struct sup
Jan Kara 7e8f58
 		smp_wmb();
Jan Kara 7e8f58
 		sb->s_flags |= MS_RDONLY;
Jan Kara 7e8f58
 	} else if (test_opt(sb, ERRORS_PANIC)) {
Jan Kara 7e8f58
-		if (EXT4_SB(sb)->s_journal &&
Jan Kara 7e8f58
-		  !(EXT4_SB(sb)->s_journal->j_flags & JBD2_REC_ERR))
Jan Kara 7e8f58
-			return;
Jan Kara 7e8f58
 		panic("EXT4-fs (device %s): panic forced after error\n",
Jan Kara 7e8f58
 			sb->s_id);
Jan Kara 7e8f58
 	}
Jan Kara 7e8f58
@@ -637,24 +634,21 @@ void __ext4_abort(struct super_block *sb
Jan Kara 7e8f58
 	va_end(args);
Jan Kara 7e8f58
 
Jan Kara 7e8f58
 	if ((sb->s_flags & MS_RDONLY) == 0) {
Jan Kara 7e8f58
-		ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
Jan Kara 7e8f58
 		EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
Jan Kara 7e8f58
+		if (EXT4_SB(sb)->s_journal)
Jan Kara 7e8f58
+			jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
Jan Kara 7e8f58
+
Jan Kara 7e8f58
+		ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
Jan Kara 7e8f58
 		/*
Jan Kara 7e8f58
 		 * Make sure updated value of ->s_mount_flags will be visible
Jan Kara 7e8f58
 		 * before ->s_flags update
Jan Kara 7e8f58
 		 */
Jan Kara 7e8f58
 		smp_wmb();
Jan Kara 7e8f58
 		sb->s_flags |= MS_RDONLY;
Jan Kara 7e8f58
-		if (EXT4_SB(sb)->s_journal)
Jan Kara 7e8f58
-			jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
Jan Kara 7e8f58
 		save_error_info(sb, function, line);
Jan Kara 7e8f58
 	}
Jan Kara 7e8f58
-	if (test_opt(sb, ERRORS_PANIC) && !system_going_down()) {
Jan Kara 7e8f58
-		if (EXT4_SB(sb)->s_journal &&
Jan Kara 7e8f58
-		  !(EXT4_SB(sb)->s_journal->j_flags & JBD2_REC_ERR))
Jan Kara 7e8f58
-			return;
Jan Kara 7e8f58
+	if (test_opt(sb, ERRORS_PANIC) && !system_going_down())
Jan Kara 7e8f58
 		panic("EXT4-fs panic from previous error\n");
Jan Kara 7e8f58
-	}
Jan Kara 7e8f58
 }
Jan Kara 7e8f58
 
Jan Kara 7e8f58
 void __ext4_msg(struct super_block *sb,
Jan Kara 7e8f58
--- a/fs/jbd2/journal.c
Jan Kara 7e8f58
+++ b/fs/jbd2/journal.c
Jan Kara 7e8f58
@@ -1151,6 +1151,7 @@ static journal_t *journal_init_common(st
Jan Kara 7e8f58
 	init_waitqueue_head(&journal->j_wait_commit);
Jan Kara 7e8f58
 	init_waitqueue_head(&journal->j_wait_updates);
Jan Kara 7e8f58
 	init_waitqueue_head(&journal->j_wait_reserved);
Jan Kara 7e8f58
+	mutex_init(&journal->j_abort_mutex);
Jan Kara 7e8f58
 	mutex_init(&journal->j_barrier);
Jan Kara 7e8f58
 	mutex_init(&journal->j_checkpoint_mutex);
Jan Kara 7e8f58
 	spin_lock_init(&journal->j_revoke_lock);
Jan Kara 7e8f58
@@ -1405,7 +1406,8 @@ static int jbd2_write_superblock(journal
Jan Kara 7e8f58
 		printk(KERN_ERR "JBD2: Error %d detected when updating "
Jan Kara 7e8f58
 		       "journal superblock for %s.\n", ret,
Jan Kara 7e8f58
 		       journal->j_devname);
Jan Kara 7e8f58
-		jbd2_journal_abort(journal, ret);
Jan Kara 7e8f58
+		if (!is_journal_aborted(journal))
Jan Kara 7e8f58
+			jbd2_journal_abort(journal, ret);
Jan Kara 7e8f58
 	}
Jan Kara 7e8f58
 
Jan Kara 7e8f58
 	return ret;
Jan Kara 7e8f58
@@ -2127,6 +2129,13 @@ static void __journal_abort_soft (journa
Jan Kara 7e8f58
 {
Jan Kara 7e8f58
 	int old_errno;
Jan Kara 7e8f58
 
Jan Kara 7e8f58
+	/*
Jan Kara 7e8f58
+	 * Lock the aborting procedure until everything is done, this avoid
Jan Kara 7e8f58
+	 * races between filesystem's error handling flow (e.g. ext4_abort()),
Jan Kara 7e8f58
+	 * ensure panic after the error info is written into journal's
Jan Kara 7e8f58
+	 * superblock.
Jan Kara 7e8f58
+	 */
Jan Kara 7e8f58
+	mutex_lock(&journal->j_abort_mutex);
Jan Kara 7e8f58
 	write_lock(&journal->j_state_lock);
Jan Kara 7e8f58
 	old_errno = journal->j_errno;
Jan Kara 7e8f58
 	if (!journal->j_errno || errno == -ESHUTDOWN)
Jan Kara 7e8f58
@@ -2136,6 +2145,7 @@ static void __journal_abort_soft (journa
Jan Kara 7e8f58
 		write_unlock(&journal->j_state_lock);
Jan Kara 7e8f58
 		if (old_errno != -ESHUTDOWN && errno == -ESHUTDOWN)
Jan Kara 7e8f58
 			jbd2_journal_update_sb_errno(journal);
Jan Kara 7e8f58
+		mutex_unlock(&journal->j_abort_mutex);
Jan Kara 7e8f58
 		return;
Jan Kara 7e8f58
 	}
Jan Kara 7e8f58
 	write_unlock(&journal->j_state_lock);
Jan Kara 7e8f58
@@ -2143,9 +2153,7 @@ static void __journal_abort_soft (journa
Jan Kara 7e8f58
 	__jbd2_journal_abort_hard(journal);
Jan Kara 7e8f58
 
Jan Kara 7e8f58
 	jbd2_journal_update_sb_errno(journal);
Jan Kara 7e8f58
-	write_lock(&journal->j_state_lock);
Jan Kara 7e8f58
-	journal->j_flags |= JBD2_REC_ERR;
Jan Kara 7e8f58
-	write_unlock(&journal->j_state_lock);
Jan Kara 7e8f58
+	mutex_unlock(&journal->j_abort_mutex);
Jan Kara 7e8f58
 }
Jan Kara 7e8f58
 
Jan Kara 7e8f58
 /**
Jan Kara 7e8f58
--- a/include/linux/jbd2.h
Jan Kara 7e8f58
+++ b/include/linux/jbd2.h
Jan Kara 7e8f58
@@ -779,6 +779,11 @@ struct journal_s
Jan Kara 7e8f58
 	int			j_errno;
Jan Kara 7e8f58
 
Jan Kara 7e8f58
 	/**
Jan Kara 7e8f58
+	 * @j_abort_mutex: Lock the whole aborting procedure.
Jan Kara 7e8f58
+	 */
Jan Kara 7e8f58
+	struct mutex		j_abort_mutex;
Jan Kara 7e8f58
+
Jan Kara 7e8f58
+	/**
Jan Kara 7e8f58
 	 * @j_sb_buffer: The first part of the superblock buffer.
Jan Kara 7e8f58
 	 */
Jan Kara 7e8f58
 	struct buffer_head	*j_sb_buffer;
Jan Kara 7e8f58
@@ -1253,7 +1258,6 @@ JBD2_FEATURE_INCOMPAT_FUNCS(csum3,		CSUM
Jan Kara 7e8f58
 #define JBD2_ABORT_ON_SYNCDATA_ERR	0x040	/* Abort the journal on file
Jan Kara 7e8f58
 						 * data write error in ordered
Jan Kara 7e8f58
 						 * mode */
Jan Kara 7e8f58
-#define JBD2_REC_ERR	0x080	/* The errno in the sb has been recorded */
Jan Kara 7e8f58
 
Jan Kara 7e8f58
 /*
Jan Kara 7e8f58
  * Function declarations for the journaling transaction and buffer