[PARISC] Fix our interrupts not to use smp_call_function
authorJames Bottomley <jejb@parisc-linux.org>
Thu, 17 Nov 2005 21:27:02 +0000 (16:27 -0500)
committerKyle McMartin <kyle@parisc-linux.org>
Thu, 17 Nov 2005 21:27:02 +0000 (16:27 -0500)
Fix our interrupts not to use smp_call_function

On K and D class smp, the generic code calls this under an irq
spinlock, which causes the WARN_ON() message in smp_call_function()
(and is also illegal because it could deadlock).

The fix is to use a new scheme based on the IPI_NOP.

Signed-off-by: James Bottomley <jejb@parisc-linux.org>
Signed-off-by: Kyle McMartin <kyle@parisc-linux.org>
arch/parisc/kernel/irq.c
arch/parisc/kernel/smp.c
include/asm-parisc/smp.h

index 21a9c5a..3998c0c 100644 (file)
@@ -43,26 +43,34 @@ extern irqreturn_t ipi_interrupt(int, void *, struct pt_regs *);
 */
 static volatile unsigned long cpu_eiem = 0;
 
-static void cpu_set_eiem(void *info)
-{
-       set_eiem((unsigned long) info);
-}
-
-static inline void cpu_disable_irq(unsigned int irq)
+static void cpu_disable_irq(unsigned int irq)
 {
        unsigned long eirr_bit = EIEM_MASK(irq);
 
        cpu_eiem &= ~eirr_bit;
-        on_each_cpu(cpu_set_eiem, (void *) cpu_eiem, 1, 1);
+       /* Do nothing on the other CPUs.  If they get this interrupt,
+        * The & cpu_eiem in the do_cpu_irq_mask() ensures they won't
+        * handle it, and the set_eiem() at the bottom will ensure it
+        * then gets disabled */
 }
 
 static void cpu_enable_irq(unsigned int irq)
 {
        unsigned long eirr_bit = EIEM_MASK(irq);
 
-       mtctl(eirr_bit, 23);    /* clear EIRR bit before unmasking */
        cpu_eiem |= eirr_bit;
-        on_each_cpu(cpu_set_eiem, (void *) cpu_eiem, 1, 1);
+
+       /* FIXME: while our interrupts aren't nested, we cannot reset
+        * the eiem mask if we're already in an interrupt.  Once we
+        * implement nested interrupts, this can go away
+        */
+       if (!in_interrupt())
+               set_eiem(cpu_eiem);
+
+       /* This is just a simple NOP IPI.  But what it does is cause
+        * all the other CPUs to do a set_eiem(cpu_eiem) at the end
+        * of the interrupt handler */
+       smp_send_all_nop();
 }
 
 static unsigned int cpu_startup_irq(unsigned int irq)
index 268b0f2..ce89da0 100644 (file)
@@ -181,12 +181,19 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                while (ops) {
                        unsigned long which = ffz(~ops);
 
+                       ops &= ~(1 << which);
+
                        switch (which) {
+                       case IPI_NOP:
+#if (kDEBUG>=100)
+                               printk(KERN_DEBUG "CPU%d IPI_NOP\n",this_cpu);
+#endif /* kDEBUG */
+                               break;
+                               
                        case IPI_RESCHEDULE:
 #if (kDEBUG>=100)
                                printk(KERN_DEBUG "CPU%d IPI_RESCHEDULE\n",this_cpu);
 #endif /* kDEBUG */
-                               ops &= ~(1 << IPI_RESCHEDULE);
                                /*
                                 * Reschedule callback.  Everything to be
                                 * done is done by the interrupt return path.
@@ -197,7 +204,6 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 #if (kDEBUG>=100)
                                printk(KERN_DEBUG "CPU%d IPI_CALL_FUNC\n",this_cpu);
 #endif /* kDEBUG */
-                               ops &= ~(1 << IPI_CALL_FUNC);
                                {
                                        volatile struct smp_call_struct *data;
                                        void (*func)(void *info);
@@ -231,7 +237,6 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 #if (kDEBUG>=100)
                                printk(KERN_DEBUG "CPU%d IPI_CPU_START\n",this_cpu);
 #endif /* kDEBUG */
-                               ops &= ~(1 << IPI_CPU_START);
 #ifdef ENTRY_SYS_CPUS
                                p->state = STATE_RUNNING;
 #endif
@@ -241,7 +246,6 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 #if (kDEBUG>=100)
                                printk(KERN_DEBUG "CPU%d IPI_CPU_STOP\n",this_cpu);
 #endif /* kDEBUG */
-                               ops &= ~(1 << IPI_CPU_STOP);
 #ifdef ENTRY_SYS_CPUS
 #else
                                halt_processor();
@@ -252,13 +256,11 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 #if (kDEBUG>=100)
                                printk(KERN_DEBUG "CPU%d is alive!\n",this_cpu);
 #endif /* kDEBUG */
-                               ops &= ~(1 << IPI_CPU_TEST);
                                break;
 
                        default:
                                printk(KERN_CRIT "Unknown IPI num on CPU%d: %lu\n",
                                        this_cpu, which);
-                               ops &= ~(1 << which);
                                return IRQ_NONE;
                        } /* Switch */
                } /* while (ops) */
@@ -312,6 +314,12 @@ smp_send_start(void)       { send_IPI_allbutself(IPI_CPU_START); }
 void 
 smp_send_reschedule(int cpu) { send_IPI_single(cpu, IPI_RESCHEDULE); }
 
+void
+smp_send_all_nop(void)
+{
+       send_IPI_allbutself(IPI_NOP);
+}
+
 
 /**
  * Run a function on all other CPUs.
index 9413f67..a519195 100644 (file)
@@ -29,6 +29,7 @@ extern cpumask_t cpu_online_map;
 #define cpu_logical_map(cpu)   (cpu)
 
 extern void smp_send_reschedule(int cpu);
+extern void smp_send_all_nop(void);
 
 #endif /* !ASSEMBLY */