genirq: Prevent chip buslock deadlock
authorThomas Gleixner <tglx@linutronix.de>
Sun, 13 Dec 2015 17:12:30 +0000 (18:12 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 3 Mar 2016 23:06:20 +0000 (15:06 -0800)
commitbf5cd0c632e49ca583cd3531b55693e615a2b332
tree21867f0eb363c90acec8ba13e45998935b91995f
parent341f09c01a7f26a030f3bedb08e4ce91e3ca24d3
genirq: Prevent chip buslock deadlock

commit abc7e40c81d113ef4bacb556f0a77ca63ac81d85 upstream.

If a interrupt chip utilizes chip->buslock then free_irq() can
deadlock in the following way:

CPU0 CPU1
interrupt(X) (Shared or spurious)
free_irq(X) interrupt_thread(X)
chip_bus_lock(X)
   irq_finalize_oneshot(X)
     chip_bus_lock(X)
synchronize_irq(X)

synchronize_irq() waits for the interrupt thread to complete,
i.e. forever.

Solution is simple: Drop chip_bus_lock() before calling
synchronize_irq() as we do with the irq_desc lock. There is nothing to
be protected after the point where irq_desc lock has been released.

This adds chip_bus_lock/unlock() to the remove_irq() code path, but
that's actually correct in the case where remove_irq() is called on
such an interrupt. The current users of remove_irq() are not affected
as none of those interrupts is on a chip which requires buslock.

Reported-by: Fredrik Markström <fredrik.markstrom@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
kernel/irq/manage.c