summaryrefslogtreecommitdiffstats
path: root/kernel/irq/chip.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2011-02-07 10:34:30 +0100
committerThomas Gleixner <tglx@linutronix.de>2011-02-19 12:58:09 +0100
commitfe200ae48ef5c79bf7941fe8046ff9505c570ff6 (patch)
tree767d2cf011437a266a655ce2ec39360cb85f7f28 /kernel/irq/chip.c
parentd05c65fff0ef672be75429266751f0e015b54d94 (diff)
downloadlinux-fe200ae48ef5c79bf7941fe8046ff9505c570ff6.tar.gz
linux-fe200ae48ef5c79bf7941fe8046ff9505c570ff6.tar.bz2
linux-fe200ae48ef5c79bf7941fe8046ff9505c570ff6.zip
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>
Diffstat (limited to 'kernel/irq/chip.c')
-rw-r--r--kernel/irq/chip.c26
1 files changed, 20 insertions, 6 deletions
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 622b55ac0e09..31258782742c 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -448,6 +448,13 @@ out_unlock:
}
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 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc)
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 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
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 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
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 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc)
*/
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);