[PATCH] Fix misrouted interrupts deadlocks

While testing kernel on machine with "irqpoll" option I've caught such a
lockup:

__do_IRQ()
spin_lock(&desc->lock);
desc->chip->ack(); /* IRQ is ACKed */
note_interrupt()
misrouted_irq()
handle_IRQ_event()
if (...)
local_irq_enable_in_hardirq();
/* interrupts are enabled from now */
...
__do_IRQ() /* same IRQ we've started from */
spin_lock(&desc->lock); /* LOCKUP */

Looking at misrouted_irq() code I've found that a potential deadlock like
this can also take place:

1CPU:
__do_IRQ()
spin_lock(&desc->lock); /* irq = A */
misrouted_irq()
for (i = 1; i < NR_IRQS; i++) {
spin_lock(&desc->lock); /* irq = B */
if (desc->status & IRQ_INPROGRESS) {

2CPU:
__do_IRQ()
spin_lock(&desc->lock); /* irq = B */
misrouted_irq()
for (i = 1; i < NR_IRQS; i++) {
spin_lock(&desc->lock); /* irq = A */
if (desc->status & IRQ_INPROGRESS) {

As the second lock on both CPUs is taken before checking that this irq is
being handled in another processor this may cause a deadlock. This issue
is only theoretical.

I propose the attached patch to fix booth problems: when trying to handle
misrouted IRQ active desc->lock may be unlocked.

Acked-by: Ingo Molnar <mingo@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by Pavel Emelianov and committed by Linus Torvalds f72fa707 0130b0b3

+5 -1
+5 -1
kernel/irq/spurious.c
··· 147 if (unlikely(irqfixup)) { 148 /* Don't punish working computers */ 149 if ((irqfixup == 2 && irq == 0) || action_ret == IRQ_NONE) { 150 - int ok = misrouted_irq(irq); 151 if (action_ret == IRQ_NONE) 152 desc->irqs_unhandled -= ok; 153 }
··· 147 if (unlikely(irqfixup)) { 148 /* Don't punish working computers */ 149 if ((irqfixup == 2 && irq == 0) || action_ret == IRQ_NONE) { 150 + int ok; 151 + 152 + spin_unlock(&desc->lock); 153 + ok = misrouted_irq(irq); 154 + spin_lock(&desc->lock); 155 if (action_ret == IRQ_NONE) 156 desc->irqs_unhandled -= ok; 157 }