genirq: Mark polled irqs and defer the real handler
With the chip.end() function gone we might run into a situation where
a poll call runs and the real interrupt comes in, sees IRQ_INPROGRESS
and disables the line. That might be a perfect working one, which will
then be masked forever.
So mark them polled while the poll runs. When the real handler sees
IRQ_INPROGRESS it checks the poll flag and waits for the polling to
complete. Add the necessary amount of sanity checks to it to avoid
deadlocks.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 622b55a..3125878 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -448,6 +448,13 @@
}
EXPORT_SYMBOL_GPL(handle_nested_irq);
+static bool irq_check_poll(struct irq_desc *desc)
+{
+ if (!(desc->status & IRQ_POLL_INPROGRESS))
+ return false;
+ return irq_wait_for_poll(desc);
+}
+
/**
* handle_simple_irq - Simple and software-decoded IRQs.
* @irq: the interrupt number
@@ -469,7 +476,9 @@
raw_spin_lock(&desc->lock);
if (unlikely(desc->status & IRQ_INPROGRESS))
- goto out_unlock;
+ if (!irq_check_poll(desc))
+ goto out_unlock;
+
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);
@@ -510,7 +519,9 @@
mask_ack_irq(desc);
if (unlikely(desc->status & IRQ_INPROGRESS))
- goto out_unlock;
+ if (!irq_check_poll(desc))
+ goto out_unlock;
+
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);
@@ -558,7 +569,8 @@
raw_spin_lock(&desc->lock);
if (unlikely(desc->status & IRQ_INPROGRESS))
- goto out;
+ if (!irq_check_poll(desc))
+ goto out;
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);
@@ -620,9 +632,11 @@
*/
if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
!desc->action)) {
- desc->status |= (IRQ_PENDING | IRQ_MASKED);
- mask_ack_irq(desc);
- goto out_unlock;
+ if (!irq_check_poll(desc)) {
+ desc->status |= (IRQ_PENDING | IRQ_MASKED);
+ mask_ack_irq(desc);
+ goto out_unlock;
+ }
}
kstat_incr_irqs_this_cpu(irq, desc);