Skip to content

Commit 574f2e7

Browse files
Peter Zijlstrarostedt
authored andcommitted
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. Cc: [email protected] Reported-by: Sebastian Andrzej Siewior <[email protected]> Cc: Frederic Weisbecker <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Andrew Morton <[email protected]> Signed-off-by: Peter Zijlstra <[email protected]> Link: http://lkml.kernel.org/n/[email protected] Signed-off-by: Ingo Molnar <[email protected]> Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
1 parent 6f07e21 commit 574f2e7

File tree

1 file changed

+42
-4
lines changed

1 file changed

+42
-4
lines changed

kernel/softirq.c

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,44 @@ EXPORT_SYMBOL(local_bh_enable_ip);
355355
#define MAX_SOFTIRQ_TIME msecs_to_jiffies(2)
356356
#define MAX_SOFTIRQ_RESTART 10
357357

358+
#ifdef CONFIG_TRACE_IRQFLAGS
359+
/*
360+
* Convoluted means of passing __do_softirq() a message through the various
361+
* architecture execute_on_stack() bits.
362+
*
363+
* When we run softirqs from irq_exit() and thus on the hardirq stack we need
364+
* to keep the lockdep irq context tracking as tight as possible in order to
365+
* not miss-qualify lock contexts and miss possible deadlocks.
366+
*/
367+
static DEFINE_PER_CPU(int, softirq_from_hardirq);
368+
369+
static inline void lockdep_softirq_from_hardirq(void)
370+
{
371+
this_cpu_write(softirq_from_hardirq, 1);
372+
}
373+
374+
static inline void lockdep_softirq_start(void)
375+
{
376+
if (this_cpu_read(softirq_from_hardirq))
377+
trace_hardirq_exit();
378+
lockdep_softirq_enter();
379+
}
380+
381+
static inline void lockdep_softirq_end(void)
382+
{
383+
lockdep_softirq_exit();
384+
if (this_cpu_read(softirq_from_hardirq)) {
385+
this_cpu_write(softirq_from_hardirq, 0);
386+
trace_hardirq_enter();
387+
}
388+
}
389+
390+
#else
391+
static inline void lockdep_softirq_from_hardirq(void) { }
392+
static inline void lockdep_softirq_start(void) { }
393+
static inline void lockdep_softirq_end(void) { }
394+
#endif
395+
358396
asmlinkage void __do_softirq(void)
359397
{
360398
__u32 pending;
@@ -375,7 +413,7 @@ asmlinkage void __do_softirq(void)
375413

376414
__local_bh_disable((unsigned long)__builtin_return_address(0),
377415
SOFTIRQ_OFFSET);
378-
lockdep_softirq_enter();
416+
lockdep_softirq_start();
379417

380418
cpu = smp_processor_id();
381419
restart:
@@ -393,8 +431,7 @@ asmlinkage void __do_softirq(void)
393431
wakeup_softirqd();
394432
}
395433

396-
lockdep_softirq_exit();
397-
434+
lockdep_softirq_end();
398435
account_irq_exit_time(current);
399436
__local_bh_enable(SOFTIRQ_OFFSET);
400437
tsk_restore_flags(current, old_flags, PF_MEMALLOC);
@@ -700,6 +737,7 @@ static inline void invoke_softirq(void)
700737
{
701738
#ifndef CONFIG_PREEMPT_RT_FULL
702739
if (!force_irqthreads) {
740+
lockdep_softirq_from_hardirq();
703741
/*
704742
* We can safely execute softirq on the current stack if
705743
* it is the irq stack, because it should be near empty
@@ -749,13 +787,13 @@ void irq_exit(void)
749787
#endif
750788

751789
account_irq_exit_time(current);
752-
trace_hardirq_exit();
753790
sub_preempt_count(HARDIRQ_OFFSET);
754791
if (!in_interrupt() && local_softirq_pending())
755792
invoke_softirq();
756793

757794
tick_irq_exit();
758795
rcu_irq_exit();
796+
trace_hardirq_exit(); /* must be last! */
759797
}
760798

761799
void raise_softirq(unsigned int nr)

0 commit comments

Comments
 (0)