diff options
author | Kanoj Sarcar <kanoj@engr.sgi.com> | 2000-05-13 07:16:24 +0000 |
---|---|---|
committer | Kanoj Sarcar <kanoj@engr.sgi.com> | 2000-05-13 07:16:24 +0000 |
commit | 6cf9b4f61029a3495200d1e1b0b64ab58022ca14 (patch) | |
tree | 77e991b84162718f027b9412bdfe7994c72f0eb8 /arch/mips64/sgi-ip27 | |
parent | 35fd5eb6a16239d684d509f74380ddef0f59194b (diff) |
Fix races in the low level intr handling code to prevent intr loss
and double servicing of intrs. Read checked in comments for more
details.
Diffstat (limited to 'arch/mips64/sgi-ip27')
-rw-r--r-- | arch/mips64/sgi-ip27/ip27-irq.c | 42 |
1 files changed, 28 insertions, 14 deletions
diff --git a/arch/mips64/sgi-ip27/ip27-irq.c b/arch/mips64/sgi-ip27/ip27-irq.c index 2c0ddcc69..50feeed72 100644 --- a/arch/mips64/sgi-ip27/ip27-irq.c +++ b/arch/mips64/sgi-ip27/ip27-irq.c @@ -170,8 +170,18 @@ static int ms1bit(unsigned long x) return b + (int) (x >> 1); } - -/* For now ... */ + +/* + * This code is unnecessarily complex, because we do SA_INTERRUPT + * intr enabling. Basically, once we grab the set of intrs we need + * to service, we must mask _all_ these interrupts; firstly, to make + * sure the same intr does not intr again, causing recursion that + * can lead to stack overflow. Secondly, we can not just mask the + * one intr we are do_IRQing, because the non-masked intrs in the + * first set might intr again, causing multiple servicings of the + * same intr. This effect is mostly seen for intercpu intrs. + * Kanoj 05.13.00 + */ void ip27_do_irq(struct pt_regs *regs) { int irq, swlevel; @@ -182,19 +192,23 @@ void ip27_do_irq(struct pt_regs *regs) /* copied from Irix intpend0() */ while (((pend0 = LOCAL_HUB_L(PI_INT_PEND0)) & (mask0 = LOCAL_HUB_L(pi_int_mask0))) != 0) { - pend0 &= mask0; - do { - swlevel = ms1bit(pend0); - LOCAL_HUB_S(pi_int_mask0, mask0 & ~(1 << swlevel)); - LOCAL_HUB_CLR_INTR(swlevel); - /* "map" swlevel to irq */ - irq = SWLEVEL_TO_IRQ(swlevel); - do_IRQ(irq, regs); - /* reset INT_MASK0 register */ + pend0 &= mask0; /* Pick intrs we should look at */ + if (pend0) { + /* Prevent any of the picked intrs from recursing */ + LOCAL_HUB_S(pi_int_mask0, mask0 & ~(pend0)); + do { + swlevel = ms1bit(pend0); + LOCAL_HUB_CLR_INTR(swlevel); + /* "map" swlevel to irq */ + irq = SWLEVEL_TO_IRQ(swlevel); + do_IRQ(irq, regs); + /* clear bit in pend0 */ + pend0 ^= 1ULL << swlevel; + } while(pend0); + /* Now allow the set of serviced intrs again */ LOCAL_HUB_S(pi_int_mask0, mask0); - /* clear bit in pend0 */ - pend0 ^= 1ULL << swlevel; - } while (pend0); + LOCAL_HUB_L(PI_INT_PEND0); + } } } |