summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/mips64/sgi-ip27/ip27-irq.c42
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);
+ }
}
}