Commit f1a83e65 authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar

lockdep: Correctly annotate hardirq context in irq_exit()

There was a reported deadlock on -rt which lockdep didn't report.

It turns out that in irq_exit() we tell lockdep that the hardirq
context ends and then do all kinds of locking afterwards.

To fix it, move trace_hardirq_exit() to the very end of irq_exit(), this
ensures all locking in tick_irq_exit() and rcu_irq_exit() are properly
recorded as happening from hardirq context.

This however leads to the 'fun' little problem of running softirqs
while in hardirq context. To cure this make the softirq code a little
more complex (in the CONFIG_TRACE_IRQFLAGS case).

Due to stack swizzling arch dependent trickery we cannot pass an
argument to __do_softirq() to tell it if it was done from hardirq
context or not; so use a side-band argument.

When we do __do_softirq() from hardirq context, 'atomically' flip to
softirq context and back, so that no locking goes without being in
either hard- or soft-irq context.

I didn't find any new problems in mainline using this patch, but it
did show the -rt problem.
Reported-by: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarPeter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/n/tip-dgwc5cdksbn0jk09vbmcc9sa@git.kernel.orgSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 9dd12201
...@@ -213,14 +213,52 @@ EXPORT_SYMBOL(local_bh_enable_ip); ...@@ -213,14 +213,52 @@ EXPORT_SYMBOL(local_bh_enable_ip);
#define MAX_SOFTIRQ_TIME msecs_to_jiffies(2) #define MAX_SOFTIRQ_TIME msecs_to_jiffies(2)
#define MAX_SOFTIRQ_RESTART 10 #define MAX_SOFTIRQ_RESTART 10
#ifdef CONFIG_TRACE_IRQFLAGS
/*
* Convoluted means of passing __do_softirq() a message through the various
* architecture execute_on_stack() bits.
*
* When we run softirqs from irq_exit() and thus on the hardirq stack we need
* to keep the lockdep irq context tracking as tight as possible in order to
* not miss-qualify lock contexts and miss possible deadlocks.
*/
static DEFINE_PER_CPU(int, softirq_from_hardirq);
static inline void lockdep_softirq_from_hardirq(void)
{
this_cpu_write(softirq_from_hardirq, 1);
}
static inline void lockdep_softirq_start(void)
{
if (this_cpu_read(softirq_from_hardirq))
trace_hardirq_exit();
lockdep_softirq_enter();
}
static inline void lockdep_softirq_end(void)
{
lockdep_softirq_exit();
if (this_cpu_read(softirq_from_hardirq)) {
this_cpu_write(softirq_from_hardirq, 0);
trace_hardirq_enter();
}
}
#else
static inline void lockdep_softirq_from_hardirq(void) { }
static inline void lockdep_softirq_start(void) { }
static inline void lockdep_softirq_end(void) { }
#endif
asmlinkage void __do_softirq(void) asmlinkage void __do_softirq(void)
{ {
struct softirq_action *h;
__u32 pending;
unsigned long end = jiffies + MAX_SOFTIRQ_TIME; unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
int cpu;
unsigned long old_flags = current->flags; unsigned long old_flags = current->flags;
int max_restart = MAX_SOFTIRQ_RESTART; int max_restart = MAX_SOFTIRQ_RESTART;
struct softirq_action *h;
__u32 pending;
int cpu;
/* /*
* Mask out PF_MEMALLOC s current task context is borrowed for the * Mask out PF_MEMALLOC s current task context is borrowed for the
...@@ -233,7 +271,7 @@ asmlinkage void __do_softirq(void) ...@@ -233,7 +271,7 @@ asmlinkage void __do_softirq(void)
account_irq_enter_time(current); account_irq_enter_time(current);
__local_bh_disable(_RET_IP_, SOFTIRQ_OFFSET); __local_bh_disable(_RET_IP_, SOFTIRQ_OFFSET);
lockdep_softirq_enter(); lockdep_softirq_start();
cpu = smp_processor_id(); cpu = smp_processor_id();
restart: restart:
...@@ -280,16 +318,13 @@ asmlinkage void __do_softirq(void) ...@@ -280,16 +318,13 @@ asmlinkage void __do_softirq(void)
wakeup_softirqd(); wakeup_softirqd();
} }
lockdep_softirq_exit(); lockdep_softirq_end();
account_irq_exit_time(current); account_irq_exit_time(current);
__local_bh_enable(SOFTIRQ_OFFSET); __local_bh_enable(SOFTIRQ_OFFSET);
WARN_ON_ONCE(in_interrupt()); WARN_ON_ONCE(in_interrupt());
tsk_restore_flags(current, old_flags, PF_MEMALLOC); tsk_restore_flags(current, old_flags, PF_MEMALLOC);
} }
asmlinkage void do_softirq(void) asmlinkage void do_softirq(void)
{ {
__u32 pending; __u32 pending;
...@@ -332,6 +367,7 @@ void irq_enter(void) ...@@ -332,6 +367,7 @@ void irq_enter(void)
static inline void invoke_softirq(void) static inline void invoke_softirq(void)
{ {
if (!force_irqthreads) { if (!force_irqthreads) {
lockdep_softirq_from_hardirq();
#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
/* /*
* We can safely execute softirq on the current stack if * We can safely execute softirq on the current stack if
...@@ -377,13 +413,13 @@ void irq_exit(void) ...@@ -377,13 +413,13 @@ void irq_exit(void)
#endif #endif
account_irq_exit_time(current); account_irq_exit_time(current);
trace_hardirq_exit();
preempt_count_sub(HARDIRQ_OFFSET); preempt_count_sub(HARDIRQ_OFFSET);
if (!in_interrupt() && local_softirq_pending()) if (!in_interrupt() && local_softirq_pending())
invoke_softirq(); invoke_softirq();
tick_irq_exit(); tick_irq_exit();
rcu_irq_exit(); rcu_irq_exit();
trace_hardirq_exit(); /* must be last! */
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment