Merge branch 'for-linus' of git://oss.sgi.com:8090/xfs/xfs-2.6
[linux-2.6-block.git] / arch / ia64 / kernel / mca.c
index 4b5daa3cc0feab6723764d67a7c221e74e5b3eb3..6dbf5919d2d0e7600b9bf9d9fc7f7f7116bb04a8 100644 (file)
@@ -75,6 +75,7 @@
 #include <linux/workqueue.h>
 #include <linux/cpumask.h>
 #include <linux/kdebug.h>
+#include <linux/cpu.h>
 
 #include <asm/delay.h>
 #include <asm/machvec.h>
@@ -571,7 +572,7 @@ out:
  *  Outputs
  *      None
  */
-static void __init
+void
 ia64_mca_register_cpev (int cpev)
 {
        /* Register the CPE interrupt vector with SAL */
@@ -701,8 +702,7 @@ ia64_mca_cmc_vector_enable_keventd(struct work_struct *unused)
 /*
  * ia64_mca_wakeup
  *
- *     Send an inter-cpu interrupt to wake-up a particular cpu
- *     and mark that cpu to be out of rendez.
+ *     Send an inter-cpu interrupt to wake-up a particular cpu.
  *
  *  Inputs  :   cpuid
  *  Outputs :   None
@@ -711,14 +711,12 @@ static void
 ia64_mca_wakeup(int cpu)
 {
        platform_send_ipi(cpu, IA64_MCA_WAKEUP_VECTOR, IA64_IPI_DM_INT, 0);
-       ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE;
-
 }
 
 /*
  * ia64_mca_wakeup_all
  *
- *     Wakeup all the cpus which have rendez'ed previously.
+ *     Wakeup all the slave cpus which have rendez'ed previously.
  *
  *  Inputs  :   None
  *  Outputs :   None
@@ -741,7 +739,10 @@ ia64_mca_wakeup_all(void)
  *
  *     This is handler used to put slave processors into spinloop
  *     while the monarch processor does the mca handling and later
- *     wake each slave up once the monarch is done.
+ *     wake each slave up once the monarch is done.  The state
+ *     IA64_MCA_RENDEZ_CHECKIN_DONE indicates the cpu is rendez'ed
+ *     in SAL.  The state IA64_MCA_RENDEZ_CHECKIN_NOTDONE indicates
+ *     the cpu has come out of OS rendezvous.
  *
  *  Inputs  :   None
  *  Outputs :   None
@@ -778,6 +779,7 @@ ia64_mca_rendez_int_handler(int rendez_irq, void *arg)
                       (long)&nd, 0, 0) == NOTIFY_STOP)
                ia64_mca_spin(__FUNCTION__);
 
+       ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE;
        /* Enable all interrupts */
        local_irq_restore(flags);
        return IRQ_HANDLED;
@@ -1135,30 +1137,27 @@ no_mod:
 static void
 ia64_wait_for_slaves(int monarch, const char *type)
 {
-       int c, wait = 0, missing = 0;
-       for_each_online_cpu(c) {
-               if (c == monarch)
-                       continue;
-               if (ia64_mc_info.imi_rendez_checkin[c] == IA64_MCA_RENDEZ_CHECKIN_NOTDONE) {
-                       udelay(1000);           /* short wait first */
-                       wait = 1;
-                       break;
-               }
-       }
-       if (!wait)
-               goto all_in;
-       for_each_online_cpu(c) {
-               if (c == monarch)
-                       continue;
-               if (ia64_mc_info.imi_rendez_checkin[c] == IA64_MCA_RENDEZ_CHECKIN_NOTDONE) {
-                       udelay(5*1000000);      /* wait 5 seconds for slaves (arbitrary) */
-                       if (ia64_mc_info.imi_rendez_checkin[c] == IA64_MCA_RENDEZ_CHECKIN_NOTDONE)
-                               missing = 1;
-                       break;
+       int c, i , wait;
+
+       /*
+        * wait 5 seconds total for slaves (arbitrary)
+        */
+       for (i = 0; i < 5000; i++) {
+               wait = 0;
+               for_each_online_cpu(c) {
+                       if (c == monarch)
+                               continue;
+                       if (ia64_mc_info.imi_rendez_checkin[c]
+                                       == IA64_MCA_RENDEZ_CHECKIN_NOTDONE) {
+                               udelay(1000);           /* short wait */
+                               wait = 1;
+                               break;
+                       }
                }
+               if (!wait)
+                       goto all_in;
        }
-       if (!missing)
-               goto all_in;
+
        /*
         * Maybe slave(s) dead. Print buffered messages immediately.
         */
@@ -1224,26 +1223,27 @@ ia64_mca_handler(struct pt_regs *regs, struct switch_stack *sw,
        if (notify_die(DIE_MCA_MONARCH_ENTER, "MCA", regs, (long)&nd, 0, 0)
                        == NOTIFY_STOP)
                ia64_mca_spin(__FUNCTION__);
+
+       ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_CONCURRENT_MCA;
        if (sos->monarch) {
                ia64_wait_for_slaves(cpu, "MCA");
+
+               /* Wakeup all the processors which are spinning in the
+                * rendezvous loop.  They will leave SAL, then spin in the OS
+                * with interrupts disabled until this monarch cpu leaves the
+                * MCA handler.  That gets control back to the OS so we can
+                * backtrace the other cpus, backtrace when spinning in SAL
+                * does not work.
+                */
+               ia64_mca_wakeup_all();
+               if (notify_die(DIE_MCA_MONARCH_PROCESS, "MCA", regs, (long)&nd, 0, 0)
+                               == NOTIFY_STOP)
+                       ia64_mca_spin(__FUNCTION__);
        } else {
-               ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_CONCURRENT_MCA;
                while (cpu_isset(cpu, mca_cpu))
                        cpu_relax();    /* spin until monarch wakes us */
-               ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE;
         }
 
-       /* Wakeup all the processors which are spinning in the rendezvous loop.
-        * They will leave SAL, then spin in the OS with interrupts disabled
-        * until this monarch cpu leaves the MCA handler.  That gets control
-        * back to the OS so we can backtrace the other cpus, backtrace when
-        * spinning in SAL does not work.
-        */
-       ia64_mca_wakeup_all();
-       if (notify_die(DIE_MCA_MONARCH_PROCESS, "MCA", regs, (long)&nd, 0, 0)
-                       == NOTIFY_STOP)
-               ia64_mca_spin(__FUNCTION__);
-
        /* Get the MCA error record and log it */
        ia64_mca_log_sal_error_record(SAL_INFO_TYPE_MCA);
 
@@ -1277,21 +1277,22 @@ ia64_mca_handler(struct pt_regs *regs, struct switch_stack *sw,
                /* wake up the next monarch cpu,
                 * and put this cpu in the rendez loop.
                 */
-               ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_CONCURRENT_MCA;
                for_each_online_cpu(i) {
                        if (cpu_isset(i, mca_cpu)) {
                                monarch_cpu = i;
                                cpu_clear(i, mca_cpu);  /* wake next cpu */
                                while (monarch_cpu != -1)
                                        cpu_relax();    /* spin until last cpu leaves */
-                               ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE;
                                set_curr_task(cpu, previous_current);
+                               ia64_mc_info.imi_rendez_checkin[cpu]
+                                               = IA64_MCA_RENDEZ_CHECKIN_NOTDONE;
                                return;
                        }
                }
        }
        set_curr_task(cpu, previous_current);
-       monarch_cpu = -1;
+       ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE;
+       monarch_cpu = -1;       /* This frees the slaves and previous monarchs */
 }
 
 static DECLARE_WORK(cmc_disable_work, ia64_mca_cmc_vector_disable_keventd);
@@ -1750,8 +1751,17 @@ format_mca_init_stack(void *mca_data, unsigned long offset,
        strncpy(p->comm, type, sizeof(p->comm)-1);
 }
 
-/* Do per-CPU MCA-related initialization.  */
+/* Caller prevents this from being called after init */
+static void * __init_refok mca_bootmem(void)
+{
+       void *p;
+
+       p = alloc_bootmem(sizeof(struct ia64_mca_cpu) * NR_CPUS +
+                         KERNEL_STACK_SIZE);
+       return (void *)ALIGN((unsigned long)p, KERNEL_STACK_SIZE);
+}
 
+/* Do per-CPU MCA-related initialization.  */
 void __cpuinit
 ia64_mca_cpu_init(void *cpu_data)
 {
@@ -1763,11 +1773,7 @@ ia64_mca_cpu_init(void *cpu_data)
                int cpu;
 
                first_time = 0;
-               mca_data = alloc_bootmem(sizeof(struct ia64_mca_cpu)
-                                        * NR_CPUS + KERNEL_STACK_SIZE);
-               mca_data = (void *)(((unsigned long)mca_data +
-                                       KERNEL_STACK_SIZE - 1) &
-                               (-KERNEL_STACK_SIZE));
+               mca_data = mca_bootmem();
                for (cpu = 0; cpu < NR_CPUS; cpu++) {
                        format_mca_init_stack(mca_data,
                                        offsetof(struct ia64_mca_cpu, mca_stack),
@@ -1808,6 +1814,36 @@ ia64_mca_cpu_init(void *cpu_data)
                                                              PAGE_KERNEL));
 }
 
+static void __cpuinit ia64_mca_cmc_vector_adjust(void *dummy)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       if (!cmc_polling_enabled)
+               ia64_mca_cmc_vector_enable(NULL);
+       local_irq_restore(flags);
+}
+
+static int __cpuinit mca_cpu_callback(struct notifier_block *nfb,
+                                     unsigned long action,
+                                     void *hcpu)
+{
+       int hotcpu = (unsigned long) hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               smp_call_function_single(hotcpu, ia64_mca_cmc_vector_adjust,
+                                        NULL, 1, 0);
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block mca_cpu_notifier __cpuinitdata = {
+       .notifier_call = mca_cpu_callback
+};
+
 /*
  * ia64_mca_init
  *
@@ -1991,6 +2027,8 @@ ia64_mca_late_init(void)
        if (!mca_init)
                return 0;
 
+       register_hotcpu_notifier(&mca_cpu_notifier);
+
        /* Setup the CMCI/P vector and handler */
        init_timer(&cmc_poll_timer);
        cmc_poll_timer.function = ia64_mca_cmc_poll;
@@ -2013,22 +2051,26 @@ ia64_mca_late_init(void)
 
                if (cpe_vector >= 0) {
                        /* If platform supports CPEI, enable the irq. */
-                       cpe_poll_enabled = 0;
-                       for (irq = 0; irq < NR_IRQS; ++irq)
-                               if (irq_to_vector(irq) == cpe_vector) {
-                                       desc = irq_desc + irq;
-                                       desc->status |= IRQ_PER_CPU;
-                                       setup_irq(irq, &mca_cpe_irqaction);
-                                       ia64_cpe_irq = irq;
-                               }
-                       ia64_mca_register_cpev(cpe_vector);
-                       IA64_MCA_DEBUG("%s: CPEI/P setup and enabled.\n", __FUNCTION__);
-               } else {
-                       /* If platform doesn't support CPEI, get the timer going. */
-                       if (cpe_poll_enabled) {
-                               ia64_mca_cpe_poll(0UL);
-                               IA64_MCA_DEBUG("%s: CPEP setup and enabled.\n", __FUNCTION__);
+                       irq = local_vector_to_irq(cpe_vector);
+                       if (irq > 0) {
+                               cpe_poll_enabled = 0;
+                               desc = irq_desc + irq;
+                               desc->status |= IRQ_PER_CPU;
+                               setup_irq(irq, &mca_cpe_irqaction);
+                               ia64_cpe_irq = irq;
+                               ia64_mca_register_cpev(cpe_vector);
+                               IA64_MCA_DEBUG("%s: CPEI/P setup and enabled.\n",
+                                       __FUNCTION__);
+                               return 0;
                        }
+                       printk(KERN_ERR "%s: Failed to find irq for CPE "
+                                       "interrupt handler, vector %d\n",
+                                       __FUNCTION__, cpe_vector);
+               }
+               /* If platform doesn't support CPEI, get the timer going. */
+               if (cpe_poll_enabled) {
+                       ia64_mca_cpe_poll(0UL);
+                       IA64_MCA_DEBUG("%s: CPEP setup and enabled.\n", __FUNCTION__);
                }
        }
 #endif