diff options
author | Peter Zijlstra <peterz@infradead.org> | 2013-11-19 10:42:47 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-11-19 11:07:00 -0500 |
commit | f1a83e652bedef88d6d77d3dc58250e08e7062bd (patch) | |
tree | 8ab638d983ed32cde49b28db44a1e4d9df98ede6 /kernel/softirq.c | |
parent | 9dd1220114e00d8ec5cdc20085bbe198b21e1985 (diff) |
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: Sebastian 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: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/n/tip-dgwc5cdksbn0jk09vbmcc9sa@git.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/softirq.c')
-rw-r--r-- | kernel/softirq.c | 54 |
1 files changed, 45 insertions, 9 deletions
diff --git a/kernel/softirq.c b/kernel/softirq.c index b24988353458..eb0acf44b063 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c | |||
@@ -213,14 +213,52 @@ EXPORT_SYMBOL(local_bh_enable_ip); | |||
213 | #define MAX_SOFTIRQ_TIME msecs_to_jiffies(2) | 213 | #define MAX_SOFTIRQ_TIME msecs_to_jiffies(2) |
214 | #define MAX_SOFTIRQ_RESTART 10 | 214 | #define MAX_SOFTIRQ_RESTART 10 |
215 | 215 | ||
216 | #ifdef CONFIG_TRACE_IRQFLAGS | ||
217 | /* | ||
218 | * Convoluted means of passing __do_softirq() a message through the various | ||
219 | * architecture execute_on_stack() bits. | ||
220 | * | ||
221 | * When we run softirqs from irq_exit() and thus on the hardirq stack we need | ||
222 | * to keep the lockdep irq context tracking as tight as possible in order to | ||
223 | * not miss-qualify lock contexts and miss possible deadlocks. | ||
224 | */ | ||
225 | static DEFINE_PER_CPU(int, softirq_from_hardirq); | ||
226 | |||
227 | static inline void lockdep_softirq_from_hardirq(void) | ||
228 | { | ||
229 | this_cpu_write(softirq_from_hardirq, 1); | ||
230 | } | ||
231 | |||
232 | static inline void lockdep_softirq_start(void) | ||
233 | { | ||
234 | if (this_cpu_read(softirq_from_hardirq)) | ||
235 | trace_hardirq_exit(); | ||
236 | lockdep_softirq_enter(); | ||
237 | } | ||
238 | |||
239 | static inline void lockdep_softirq_end(void) | ||
240 | { | ||
241 | lockdep_softirq_exit(); | ||
242 | if (this_cpu_read(softirq_from_hardirq)) { | ||
243 | this_cpu_write(softirq_from_hardirq, 0); | ||
244 | trace_hardirq_enter(); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | #else | ||
249 | static inline void lockdep_softirq_from_hardirq(void) { } | ||
250 | static inline void lockdep_softirq_start(void) { } | ||
251 | static inline void lockdep_softirq_end(void) { } | ||
252 | #endif | ||
253 | |||
216 | asmlinkage void __do_softirq(void) | 254 | asmlinkage void __do_softirq(void) |
217 | { | 255 | { |
218 | struct softirq_action *h; | ||
219 | __u32 pending; | ||
220 | unsigned long end = jiffies + MAX_SOFTIRQ_TIME; | 256 | unsigned long end = jiffies + MAX_SOFTIRQ_TIME; |
221 | int cpu; | ||
222 | unsigned long old_flags = current->flags; | 257 | unsigned long old_flags = current->flags; |
223 | int max_restart = MAX_SOFTIRQ_RESTART; | 258 | int max_restart = MAX_SOFTIRQ_RESTART; |
259 | struct softirq_action *h; | ||
260 | __u32 pending; | ||
261 | int cpu; | ||
224 | 262 | ||
225 | /* | 263 | /* |
226 | * Mask out PF_MEMALLOC s current task context is borrowed for the | 264 | * Mask out PF_MEMALLOC s current task context is borrowed for the |
@@ -233,7 +271,7 @@ asmlinkage void __do_softirq(void) | |||
233 | account_irq_enter_time(current); | 271 | account_irq_enter_time(current); |
234 | 272 | ||
235 | __local_bh_disable(_RET_IP_, SOFTIRQ_OFFSET); | 273 | __local_bh_disable(_RET_IP_, SOFTIRQ_OFFSET); |
236 | lockdep_softirq_enter(); | 274 | lockdep_softirq_start(); |
237 | 275 | ||
238 | cpu = smp_processor_id(); | 276 | cpu = smp_processor_id(); |
239 | restart: | 277 | restart: |
@@ -280,16 +318,13 @@ restart: | |||
280 | wakeup_softirqd(); | 318 | wakeup_softirqd(); |
281 | } | 319 | } |
282 | 320 | ||
283 | lockdep_softirq_exit(); | 321 | lockdep_softirq_end(); |
284 | |||
285 | account_irq_exit_time(current); | 322 | account_irq_exit_time(current); |
286 | __local_bh_enable(SOFTIRQ_OFFSET); | 323 | __local_bh_enable(SOFTIRQ_OFFSET); |
287 | WARN_ON_ONCE(in_interrupt()); | 324 | WARN_ON_ONCE(in_interrupt()); |
288 | tsk_restore_flags(current, old_flags, PF_MEMALLOC); | 325 | tsk_restore_flags(current, old_flags, PF_MEMALLOC); |
289 | } | 326 | } |
290 | 327 | ||
291 | |||
292 | |||
293 | asmlinkage void do_softirq(void) | 328 | asmlinkage void do_softirq(void) |
294 | { | 329 | { |
295 | __u32 pending; | 330 | __u32 pending; |
@@ -332,6 +367,7 @@ void irq_enter(void) | |||
332 | static inline void invoke_softirq(void) | 367 | static inline void invoke_softirq(void) |
333 | { | 368 | { |
334 | if (!force_irqthreads) { | 369 | if (!force_irqthreads) { |
370 | lockdep_softirq_from_hardirq(); | ||
335 | #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK | 371 | #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK |
336 | /* | 372 | /* |
337 | * We can safely execute softirq on the current stack if | 373 | * We can safely execute softirq on the current stack if |
@@ -377,13 +413,13 @@ void irq_exit(void) | |||
377 | #endif | 413 | #endif |
378 | 414 | ||
379 | account_irq_exit_time(current); | 415 | account_irq_exit_time(current); |
380 | trace_hardirq_exit(); | ||
381 | preempt_count_sub(HARDIRQ_OFFSET); | 416 | preempt_count_sub(HARDIRQ_OFFSET); |
382 | if (!in_interrupt() && local_softirq_pending()) | 417 | if (!in_interrupt() && local_softirq_pending()) |
383 | invoke_softirq(); | 418 | invoke_softirq(); |
384 | 419 | ||
385 | tick_irq_exit(); | 420 | tick_irq_exit(); |
386 | rcu_irq_exit(); | 421 | rcu_irq_exit(); |
422 | trace_hardirq_exit(); /* must be last! */ | ||
387 | } | 423 | } |
388 | 424 | ||
389 | /* | 425 | /* |