From: Ingo Molnar Date: Tue, 19 Sep 2006 09:14:34 +0000 (+0200) Subject: [PATCH] genirq core: fix handle_level_irq() X-Git-Tag: v2.6.18~9 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=86998aa6534e839ec003ed2ef7067d6fe8696ccc;p=linux-2.6 [PATCH] genirq core: fix handle_level_irq() while porting the -rt tree to 2.6.18-rc7 i noticed the following screaming-IRQ scenario on an SMP system: 2274 0Dn.:1 0.001ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) 2274 0Dn.:1 0.010ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) 2274 0Dn.:1 0.020ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) 2274 0Dn.:1 0.029ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) 2274 0Dn.:1 0.039ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) 2274 0Dn.:1 0.048ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) 2274 0Dn.:1 0.058ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) 2274 0Dn.:1 0.068ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) 2274 0Dn.:1 0.077ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) 2274 0Dn.:1 0.087ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) 2274 0Dn.:1 0.097ms: do_IRQ+0xc/0x103 <= (ret_from_intr+0x0/0xf) as it turns out, the bug is caused by handle_level_irq(), which if it races with another CPU already handling this IRQ, it _unmasks_ the IRQ line on the way out. This is not how 2.6.17 works, and we introduced this bug in one of the early genirq cleanups right before it went into -mm. (the bug was not in the genirq patchset for a long time, and we didnt notice the bug due to the lack of -rt rebase to the new genirq code. -rt, and hardirq-preemption in particular opens up such races much wider than anything else.) Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Acked-by: Benjamin Herrenschmidt Signed-off-by: Linus Torvalds --- diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 9336f2e89e..ac1f850d49 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -252,7 +252,7 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) mask_ack_irq(desc, irq); if (unlikely(desc->status & IRQ_INPROGRESS)) - goto out; + goto out_unlock; desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); kstat_cpu(cpu).irqs[irq]++; @@ -263,7 +263,7 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) action = desc->action; if (unlikely(!action || (desc->status & IRQ_DISABLED))) { desc->status |= IRQ_PENDING; - goto out; + goto out_unlock; } desc->status |= IRQ_INPROGRESS; @@ -276,9 +276,9 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) spin_lock(&desc->lock); desc->status &= ~IRQ_INPROGRESS; -out: if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) desc->chip->unmask(irq); +out_unlock: spin_unlock(&desc->lock); }