Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
genirq: Unmask oneshot irqs when thread was not woken
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>
Cc: stable@vger.kernel.org
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
  • Loading branch information
Thomas Gleixner committed Feb 15, 2012
1 parent 7ada1dd commit ac56376
Showing 1 changed file with 23 additions and 2 deletions.
25 changes: 23 additions & 2 deletions kernel/irq/chip.c
Expand Up @@ -330,6 +330,24 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc)
}
EXPORT_SYMBOL_GPL(handle_simple_irq);

/*
* Called unconditionally from handle_level_irq() and only for oneshot
* interrupts from handle_fasteoi_irq()
*/
static void cond_unmask_irq(struct irq_desc *desc)
{
/*
* We need to unmask in the following cases:
* - Standard level irq (IRQF_ONESHOT is not set)
* - Oneshot irq which did not wake the thread (caused by a
* spurious interrupt or a primary handler handling it
* completely).
*/
if (!irqd_irq_disabled(&desc->irq_data) &&
irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot)
unmask_irq(desc);
}

/**
* handle_level_irq - Level type irq handler
* @irq: the interrupt number
Expand Down Expand Up @@ -362,8 +380,8 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)

handle_irq_event(desc);

if (!irqd_irq_disabled(&desc->irq_data) && !(desc->istate & IRQS_ONESHOT))
unmask_irq(desc);
cond_unmask_irq(desc);

out_unlock:
raw_spin_unlock(&desc->lock);
}
Expand Down Expand Up @@ -417,6 +435,9 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
preflow_handler(desc);
handle_irq_event(desc);

if (desc->istate & IRQS_ONESHOT)
cond_unmask_irq(desc);

out_eoi:
desc->irq_data.chip->irq_eoi(&desc->irq_data);
out_unlock:
Expand Down

0 comments on commit ac56376

Please sign in to comment.