summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2020-11-04 14:06:23 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-11-10 12:39:08 +0100
commit1f7a50f1d7ba92977d6c880aefaad0a6ef234029 (patch)
treeea3161e3d6baabaaaad0b5866868077fa098c069
parent362dfa5e0205a5ea70bf3ac2ae00487e1a5bb8f5 (diff)
downloadlinux-1f7a50f1d7ba92977d6c880aefaad0a6ef234029.tar.gz
linux-1f7a50f1d7ba92977d6c880aefaad0a6ef234029.tar.bz2
linux-1f7a50f1d7ba92977d6c880aefaad0a6ef234029.zip
entry: Fix the incorrect ordering of lockdep and RCU check
commit 9d820f68b2bdba5b2e7bf135123c3f57c5051d05 upstream. When an exception/interrupt hits kernel space and the kernel is not currently in the idle task then RCU must be watching. irqentry_enter() validates this via rcu_irq_enter_check_tick(), which in turn invokes lockdep when taking a lock. But at that point lockdep does not yet know about the fact that interrupts have been disabled by the CPU, which triggers a lockdep splat complaining about inconsistent state. Invoking trace_hardirqs_off() before rcu_irq_enter_check_tick() defeats the point of rcu_irq_enter_check_tick() because trace_hardirqs_off() uses RCU. So use the same sequence as for the idle case and tell lockdep about the irq state change first, invoke the RCU check and then do the lockdep and tracer update. Fixes: a5497bab5f72 ("entry: Provide generic interrupt entry/exit code") Reported-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Tested-by: Mark Rutland <mark.rutland@arm.com> Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/87y2jhl19s.fsf@nanos.tec.linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--kernel/entry/common.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/kernel/entry/common.c b/kernel/entry/common.c
index 6fdb6105e6d6..73f4e33cf92e 100644
--- a/kernel/entry/common.c
+++ b/kernel/entry/common.c
@@ -338,10 +338,10 @@ noinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)
* already contains a warning when RCU is not watching, so no point
* in having another one here.
*/
+ lockdep_hardirqs_off(CALLER_ADDR0);
instrumentation_begin();
rcu_irq_enter_check_tick();
- /* Use the combo lockdep/tracing function */
- trace_hardirqs_off();
+ trace_hardirqs_off_finish();
instrumentation_end();
return ret;