summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/0007-jbd2-Free-journal-head-outside-of-locked-region.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches-rt/0007-jbd2-Free-journal-head-outside-of-locked-region.patch')
-rw-r--r--debian/patches-rt/0007-jbd2-Free-journal-head-outside-of-locked-region.patch89
1 files changed, 89 insertions, 0 deletions
diff --git a/debian/patches-rt/0007-jbd2-Free-journal-head-outside-of-locked-region.patch b/debian/patches-rt/0007-jbd2-Free-journal-head-outside-of-locked-region.patch
new file mode 100644
index 000000000..17a2978c9
--- /dev/null
+++ b/debian/patches-rt/0007-jbd2-Free-journal-head-outside-of-locked-region.patch
@@ -0,0 +1,89 @@
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Fri, 9 Aug 2019 14:42:33 +0200
+Subject: [PATCH 7/7] jbd2: Free journal head outside of locked region
+Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/5.4/older/patches-5.4.3-rt1.tar.xz
+
+On PREEMPT_RT bit-spinlocks have the same semantics as on PREEMPT_RT=n,
+i.e. they disable preemption. That means functions which are not safe to be
+called in preempt disabled context on RT trigger a might_sleep() assert.
+
+The journal head bit spinlock is mostly held for short code sequences with
+trivial RT safe functionality, except for one place:
+
+jbd2_journal_put_journal_head() invokes __journal_remove_journal_head()
+with the journal head bit spinlock held. __journal_remove_journal_head()
+invokes kmem_cache_free() which must not be called with preemption disabled
+on RT.
+
+Jan suggested to rework the removal function so the actual free happens
+outside the bit-spinlocked region.
+
+Split it into two parts:
+
+ - Do the sanity checks and the buffer head detach under the lock
+
+ - Do the actual free after dropping the lock
+
+There is error case handling in the free part which needs to dereference
+the b_size field of the now detached buffer head. Due to paranoia (caused
+by ignorance) the size is retrieved in the detach function and handed into
+the free function. Might be over-engineered, but better safe than sorry.
+
+This makes the journal head bit-spinlock usage RT compliant and also avoids
+nested locking which is not covered by lockdep.
+
+Suggested-by: Jan Kara <jack@suse.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Cc: linux-ext4@vger.kernel.org
+Cc: "Theodore Ts'o" <tytso@mit.edu>
+Cc: Jan Kara <jack@suse.com>
+Signed-off-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+---
+ fs/jbd2/journal.c | 20 ++++++++++++++------
+ 1 file changed, 14 insertions(+), 6 deletions(-)
+
+--- a/fs/jbd2/journal.c
++++ b/fs/jbd2/journal.c
+@@ -2531,17 +2531,23 @@ static void __journal_remove_journal_hea
+ J_ASSERT_BH(bh, buffer_jbd(bh));
+ J_ASSERT_BH(bh, jh2bh(jh) == bh);
+ BUFFER_TRACE(bh, "remove journal_head");
++
++ /* Unlink before dropping the lock */
++ bh->b_private = NULL;
++ jh->b_bh = NULL; /* debug, really */
++ clear_buffer_jbd(bh);
++}
++
++static void journal_release_journal_head(struct journal_head *jh, size_t b_size)
++{
+ if (jh->b_frozen_data) {
+ printk(KERN_WARNING "%s: freeing b_frozen_data\n", __func__);
+- jbd2_free(jh->b_frozen_data, bh->b_size);
++ jbd2_free(jh->b_frozen_data, b_size);
+ }
+ if (jh->b_committed_data) {
+ printk(KERN_WARNING "%s: freeing b_committed_data\n", __func__);
+- jbd2_free(jh->b_committed_data, bh->b_size);
++ jbd2_free(jh->b_committed_data, b_size);
+ }
+- bh->b_private = NULL;
+- jh->b_bh = NULL; /* debug, really */
+- clear_buffer_jbd(bh);
+ journal_free_journal_head(jh);
+ }
+
+@@ -2559,9 +2565,11 @@ void jbd2_journal_put_journal_head(struc
+ if (!jh->b_jcount) {
+ __journal_remove_journal_head(bh);
+ jbd_unlock_bh_journal_head(bh);
++ journal_release_journal_head(jh, bh->b_size);
+ __brelse(bh);
+- } else
++ } else {
+ jbd_unlock_bh_journal_head(bh);
++ }
+ }
+
+ /*