aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/irq/chip.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2006-09-19 05:14:34 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-19 10:57:20 -0400
commit86998aa6534e839ec003ed2ef7067d6fe8696ccc (patch)
treeca2e8fff9371ef26f339f2893c6c1d50c5f4e2fe /kernel/irq/chip.c
parent47a5c6fa0e204a2b63309c648bb2fde36836c826 (diff)
[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 <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/irq/chip.c')
-rw-r--r--kernel/irq/chip.c6
1 files changed, 3 insertions, 3 deletions
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 9336f2e89e40..ac1f850d4937 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)
252 mask_ack_irq(desc, irq); 252 mask_ack_irq(desc, irq);
253 253
254 if (unlikely(desc->status & IRQ_INPROGRESS)) 254 if (unlikely(desc->status & IRQ_INPROGRESS))
255 goto out; 255 goto out_unlock;
256 desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); 256 desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
257 kstat_cpu(cpu).irqs[irq]++; 257 kstat_cpu(cpu).irqs[irq]++;
258 258
@@ -263,7 +263,7 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs)
263 action = desc->action; 263 action = desc->action;
264 if (unlikely(!action || (desc->status & IRQ_DISABLED))) { 264 if (unlikely(!action || (desc->status & IRQ_DISABLED))) {
265 desc->status |= IRQ_PENDING; 265 desc->status |= IRQ_PENDING;
266 goto out; 266 goto out_unlock;
267 } 267 }
268 268
269 desc->status |= IRQ_INPROGRESS; 269 desc->status |= IRQ_INPROGRESS;
@@ -276,9 +276,9 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs)
276 276
277 spin_lock(&desc->lock); 277 spin_lock(&desc->lock);
278 desc->status &= ~IRQ_INPROGRESS; 278 desc->status &= ~IRQ_INPROGRESS;
279out:
280 if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) 279 if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
281 desc->chip->unmask(irq); 280 desc->chip->unmask(irq);
281out_unlock:
282 spin_unlock(&desc->lock); 282 spin_unlock(&desc->lock);
283} 283}
284 284