Skip to content
This repository has been archived by the owner on Jan 13, 2024. It is now read-only.

Commit

Permalink
lockdep: Correctly annotate hardirq context in irq_exit()
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
Peter Zijlstra authored and Steven Rostedt committed Dec 18, 2013
1 parent 627b882 commit cb40dc0
Showing 1 changed file with 42 additions and 3 deletions.
45 changes: 42 additions & 3 deletions kernel/softirq.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,44 @@ EXPORT_SYMBOL(local_bh_enable_ip);
*/
#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)
{
__u32 pending;
Expand All @@ -369,7 +407,7 @@ asmlinkage void __do_softirq(void)

__local_bh_disable((unsigned long)__builtin_return_address(0),
SOFTIRQ_OFFSET);
lockdep_softirq_enter();
lockdep_softirq_start();

cpu = smp_processor_id();
restart:
Expand All @@ -385,7 +423,7 @@ asmlinkage void __do_softirq(void)
if (pending)
wakeup_softirqd();

lockdep_softirq_exit();
lockdep_softirq_end();

vtime_account_irq_exit(current);
__local_bh_enable(SOFTIRQ_OFFSET);
Expand Down Expand Up @@ -692,6 +730,7 @@ static inline void invoke_softirq(void)
{
#ifndef CONFIG_PREEMPT_RT_FULL
if (!force_irqthreads) {
lockdep_softirq_from_hardirq();
#ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED
__do_softirq();
#else
Expand Down Expand Up @@ -720,7 +759,6 @@ static inline void invoke_softirq(void)
void irq_exit(void)
{
vtime_account_irq_exit(current);
trace_hardirq_exit();
sub_preempt_count(IRQ_EXIT_OFFSET);
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
Expand All @@ -731,6 +769,7 @@ void irq_exit(void)
tick_nohz_irq_exit();
#endif
rcu_irq_exit();
trace_hardirq_exit(); /* must be last! */
sched_preempt_enable_no_resched();
}

Expand Down

0 comments on commit cb40dc0

Please sign in to comment.