瀏覽代碼

genirq: Allow check_wakeup_irqs to notice level-triggered interrupts

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>
Thomas Gleixner 13 年之前
父節點
當前提交
d4dc0f90d2
共有 2 個文件被更改,包括 8 次插入3 次删除
  1. 3 1
      kernel/irq/chip.c
  2. 5 2
      kernel/irq/resend.c

+ 3 - 1
kernel/irq/chip.c

@@ -379,8 +379,10 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
 	 * If its disabled or no action available
 	 * If its disabled or no action available
 	 * keep it masked and get out of here
 	 * 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;
 		goto out_unlock;
+	}
 
 
 	handle_irq_event(desc);
 	handle_irq_event(desc);
 
 

+ 5 - 2
kernel/irq/resend.c

@@ -58,10 +58,13 @@ void check_irq_resend(struct irq_desc *desc, unsigned int irq)
 	/*
 	/*
 	 * We do not resend level type interrupts. Level type
 	 * We do not resend level type interrupts. Level type
 	 * interrupts are resent by hardware when they are still
 	 * 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;
 		return;
+	}
 	if (desc->istate & IRQS_REPLAY)
 	if (desc->istate & IRQS_REPLAY)
 		return;
 		return;
 	if (desc->istate & IRQS_PENDING) {
 	if (desc->istate & IRQS_PENDING) {