[PARISC] Disable nesting of interrupts
authorGrant Grundler <grundler@parisc-linux.org>
Thu, 17 Nov 2005 21:26:20 +0000 (16:26 -0500)
committerKyle McMartin <kyle@parisc-linux.org>
Thu, 17 Nov 2005 21:26:20 +0000 (16:26 -0500)
Disable nesting of interrupts - still has holes

The offending sequence starts out like this:
1) take external interrupt
2) set_eiem() to only allow TIMER_IRQ; local interrupts still disabled
3) read the EIRR to get a "list" of pending interrupts
4) clear EIRR of pending interrupts we intend to handle
5) call __do_IRQ() to handle IRQ.
6) handle_IRQ_event() enables local interrupts (I-Bit)
7) take a timer interrupt
8) read EIRR to get a new list of pending interrupts
9) clear EIRR of pending interrupts we just read
10) handle pending interrupts found in (8)
11) set_eiem(cpu_eiem) and return
        [ TROUBLE! all enabled CPU IRQs are unmasked. }
12) handle remaining interrupts pending from (3)
        e.g. call __do_IRQ() -> handle_IRQ_event()..etc
        [ TROUBLE! call to handle_IRQ_event() can now enable *any* IRQ. }
13) set_eiem(cpu_eiem) and return

The problem is we now get into ugly race conditions with Timer and IPI
interrupts at this point.  I'm not exactly sure what happens when
things go wrong (perhaps nest calls to IPI or timer interrupt?).
But I'm certain it's not good.

This sequence will break sooner if (10) would accidentally leave
interrupts enabled.

I'm pretty sure the right answer is now to make cpu_eiem
a per CPU variable since all external interrupts on parisc
are per CPU. This means we will NOT need to send an IPI to
every CPU in the system when enabling or disabling an IRQ
since only one CPU needs to change it's EIEM.

Thanks to James Bottomley for (once again) pointing out the problem.

Signed-off-by: Grant Grundler <grundler@parisc-linux.org>
Signed-off-by: Kyle McMartin <kyle@parisc-linux.org>
arch/parisc/kernel/irq.c

index f7ae2bcd49a5731a0d4b88f2b73f547bb44ecba5..21a9c5ad580bc079e9a2d897f753a41a028af215 100644 (file)
@@ -250,10 +250,11 @@ void do_cpu_irq_mask(struct pt_regs *regs)
        irq_enter();
 
        /*
-        * Only allow interrupt processing to be interrupted by the
-        * timer tick
+        * Don't allow TIMER or IPI nested interrupts.
+        * Allowing any single interrupt to nest can lead to that CPU
+        * handling interrupts with all enabled interrupts unmasked.
         */
-       set_eiem(EIEM_MASK(TIMER_IRQ));
+       set_eiem(0UL);
 
        /* 1) only process IRQs that are enabled/unmasked (cpu_eiem)
         * 2) We loop here on EIRR contents in order to avoid
@@ -267,9 +268,6 @@ void do_cpu_irq_mask(struct pt_regs *regs)
                if (!eirr_val)
                        break;
 
-               if (eirr_val & EIEM_MASK(TIMER_IRQ))
-                       set_eiem(0);
-
                mtctl(eirr_val, 23); /* reset bits we are going to process */
 
                /* Work our way from MSb to LSb...same order we alloc EIRs */
@@ -283,7 +281,8 @@ void do_cpu_irq_mask(struct pt_regs *regs)
                        __do_IRQ(irq, regs);
                }
        }
-       set_eiem(cpu_eiem);
+
+       set_eiem(cpu_eiem);     /* restore original mask */
        irq_exit();
 }