aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/irq/chip.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2012-02-07 11:58:03 -0500
committerLuis Henriques <luis.henriques@canonical.com>2012-03-26 05:26:46 -0400
commit107ae9b50a9160948a11f2e78072188e71886ebb (patch)
tree9ff7d5c1ad17dd6185d5bca76e5173fb6fb4bfb0 /kernel/irq/chip.c
parent8209b9c6e9dd07857437cf9677df2d649b0e0a5d (diff)
genirq: Unmask oneshot irqs when thread was not woken
BugLink: http://bugs.launchpad.net/bugs/954576 commit ac5637611150281f398bb7a47e3fcb69a09e7803 upstream. When the primary handler of an interrupt which is marked IRQ_ONESHOT returns IRQ_HANDLED or IRQ_NONE, then the interrupt thread is not woken and the unmask logic of the interrupt line is never invoked. This keeps the interrupt masked forever. This was not noticed as most IRQ_ONESHOT users wake the thread unconditionally (usually because they cannot access the underlying device from hard interrupt context). Though this behaviour was nowhere documented and not necessarily intentional. Some drivers can avoid the thread wakeup in certain cases and run into the situation where the interrupt line s kept masked. Handle it gracefully. Reported-and-tested-by: Lothar Wassmann <lw@karo-electronics.de> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel/irq/chip.c')
-rw-r--r--kernel/irq/chip.c25
1 files changed, 23 insertions, 2 deletions
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index dc5114b4c16..3322a34d9ee 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -312,6 +312,24 @@ out_unlock:
312} 312}
313EXPORT_SYMBOL_GPL(handle_simple_irq); 313EXPORT_SYMBOL_GPL(handle_simple_irq);
314 314
315/*
316 * Called unconditionally from handle_level_irq() and only for oneshot
317 * interrupts from handle_fasteoi_irq()
318 */
319static void cond_unmask_irq(struct irq_desc *desc)
320{
321 /*
322 * We need to unmask in the following cases:
323 * - Standard level irq (IRQF_ONESHOT is not set)
324 * - Oneshot irq which did not wake the thread (caused by a
325 * spurious interrupt or a primary handler handling it
326 * completely).
327 */
328 if (!irqd_irq_disabled(&desc->irq_data) &&
329 irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot)
330 unmask_irq(desc);
331}
332
315/** 333/**
316 * handle_level_irq - Level type irq handler 334 * handle_level_irq - Level type irq handler
317 * @irq: the interrupt number 335 * @irq: the interrupt number
@@ -344,8 +362,8 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
344 362
345 handle_irq_event(desc); 363 handle_irq_event(desc);
346 364
347 if (!irqd_irq_disabled(&desc->irq_data) && !(desc->istate & IRQS_ONESHOT)) 365 cond_unmask_irq(desc);
348 unmask_irq(desc); 366
349out_unlock: 367out_unlock:
350 raw_spin_unlock(&desc->lock); 368 raw_spin_unlock(&desc->lock);
351} 369}
@@ -399,6 +417,9 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
399 preflow_handler(desc); 417 preflow_handler(desc);
400 handle_irq_event(desc); 418 handle_irq_event(desc);
401 419
420 if (desc->istate & IRQS_ONESHOT)
421 cond_unmask_irq(desc);
422
402out_eoi: 423out_eoi:
403 desc->irq_data.chip->irq_eoi(&desc->irq_data); 424 desc->irq_data.chip->irq_eoi(&desc->irq_data);
404out_unlock: 425out_unlock: