Commit f72fa707 authored by Pavel Emelianov's avatar Pavel Emelianov Committed by Linus Torvalds

[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: default avatarIngo Molnar <mingo@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 0130b0b3
...@@ -147,7 +147,11 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc, ...@@ -147,7 +147,11 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc,
if (unlikely(irqfixup)) { if (unlikely(irqfixup)) {
/* Don't punish working computers */ /* Don't punish working computers */
if ((irqfixup == 2 && irq == 0) || action_ret == IRQ_NONE) { if ((irqfixup == 2 && irq == 0) || action_ret == IRQ_NONE) {
int ok = misrouted_irq(irq); int ok;
spin_unlock(&desc->lock);
ok = misrouted_irq(irq);
spin_lock(&desc->lock);
if (action_ret == IRQ_NONE) if (action_ret == IRQ_NONE)
desc->irqs_unhandled -= ok; desc->irqs_unhandled -= ok;
} }
......
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