genirq: Allow check_wakeup_irqs to notice level-triggered interrupts
authorThomas Gleixner <tglx@linutronix.de>
Wed, 25 Apr 2012 10:54:54 +0000 (12:54 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 4 May 2012 21:38:50 +0000 (23:38 +0200)
Level triggered interrupts do not cause IRQS_PENDING to be set when
they fire while "disabled" as the 'pending' state is always present in
the level - they automatically refire where re-enabled.

However the IRQS_PENDING flag is also used to abort a suspend cycle -
if any 'is_wakeup_set' interrupt is PENDING, check_wakeup_irqs() will
cause suspend to abort. Without IRQS_PENDING, suspend won't abort.

Consequently, level-triggered interrupts that fire during the 'noirq'
phase of suspend do not currently abort suspend.

So set IRQS_PENDING even for level triggered interrupts, and make sure
to clear the flag in check_irq_resend.

[ Changelog by courtesy of Neil ]

Tested-by: NeilBrown <neilb@suse.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
kernel/irq/chip.c
kernel/irq/resend.c

index 6080f6bc8c33f452782c1d259bb14fb778b345c5..741f83643da66a83bee1900d0b34d63892bf9cbd 100644 (file)
@@ -379,8 +379,10 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
         * If its disabled or no action available
         * keep it masked and get out of here
         */
-       if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data)))
+       if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
+               desc->istate |= IRQS_PENDING;
                goto out_unlock;
+       }
 
        handle_irq_event(desc);
 
index 14dd5761e8c96e1508c72499e12c93edeb45a7ab..6454db7b6a4d18ae2e4feb67dc7a572a5d8deac4 100644 (file)
@@ -58,10 +58,13 @@ void check_irq_resend(struct irq_desc *desc, unsigned int irq)
        /*
         * We do not resend level type interrupts. Level type
         * interrupts are resent by hardware when they are still
-        * active.
+        * active. Clear the pending bit so suspend/resume does not
+        * get confused.
         */
-       if (irq_settings_is_level(desc))
+       if (irq_settings_is_level(desc)) {
+               desc->istate &= ~IRQS_PENDING;
                return;
+       }
        if (desc->istate & IRQS_REPLAY)
                return;
        if (desc->istate & IRQS_PENDING) {