From 6cf9b4f61029a3495200d1e1b0b64ab58022ca14 Mon Sep 17 00:00:00 2001 From: Kanoj Sarcar Date: Sat, 13 May 2000 07:16:24 +0000 Subject: 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. --- arch/mips64/sgi-ip27/ip27-irq.c | 42 +++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) (limited to 'arch') 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); + } } } -- cgit v1.2.3