[IA64] Support multiple CPUs going through OS_MCA
authorRuss Anderson <rja@sgi.com>
Fri, 18 May 2007 22:17:17 +0000 (17:17 -0500)
committerTony Luck <tony.luck@intel.com>
Wed, 11 Jul 2007 18:50:11 +0000 (11:50 -0700)
Linux does not gracefully deal with multiple processors going
through OS_MCA aa part of the same MCA event.  The first cpu
into OS_MCA grabs the ia64_mca_serialize lock.  Subsequent
cpus wait for that lock, preventing them from reporting in as
rendezvoused.  The first cpu waits 5 seconds then complains
that all the cpus have not rendezvoused.  The first cpu then
handles its MCA and frees up all the rendezvoused cpus and
releases the ia64_mca_serialize lock.  One of the subsequent
cpus going thought OS_MCA then gets the ia64_mca_serialize
lock, waits another 5 seconds and then complains that none of
the other cpus have rendezvoused.

This patch allows multiple CPUs to gracefully go through OS_MCA.

The first CPU into ia64_mca_handler() grabs a mca_count lock.
Subsequent CPUs into ia64_mca_handler() are added to a list of cpus
that need to go through OS_MCA (a bit set in mca_cpu), and report
in as rendezvoused, and but spin waiting their turn.

The first CPU sees everyone rendezvous, handles his MCA, wakes up
one of the other CPUs waiting to process their MCA (by clearing
one mca_cpu bit), and then waits for the other cpus to complete
their MCA handling.  The next CPU handles his MCA and the process
repeats until all the CPUs have handled their MCA.  When the last
CPU has handled it's MCA, it sets monarch_cpu to -1, releasing all
the CPUs.

In testing this works more reliably and faster.

Thanks to Keith Owens for suggesting numerous improvements
to this code.

Signed-off-by: Russ Anderson <rja@sgi.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
arch/ia64/kernel/mca.c
arch/ia64/kernel/mca_asm.S
include/asm-ia64/mca.h

index 1ead5ea6c5ce71afaf259eb6629abc8f65a0c11e..4b5daa3cc0feab6723764d67a7c221e74e5b3eb3 100644 (file)
@@ -57,6 +57,9 @@
  *
  * 2006-09-15 Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
  *           Add printing support for MCA/INIT.
+ *
+ * 2007-04-27 Russ Anderson <rja@sgi.com>
+ *           Support multiple cpus going through OS_MCA in the same event.
  */
 #include <linux/types.h>
 #include <linux/init.h>
@@ -96,7 +99,6 @@
 #endif
 
 /* Used by mca_asm.S */
-u32                            ia64_mca_serialize;
 DEFINE_PER_CPU(u64, ia64_mca_data); /* == __per_cpu_mca[smp_processor_id()] */
 DEFINE_PER_CPU(u64, ia64_mca_per_cpu_pte); /* PTE to map per-CPU area */
 DEFINE_PER_CPU(u64, ia64_mca_pal_pte);     /* PTE to map PAL code */
@@ -963,11 +965,12 @@ ia64_mca_modify_original_stack(struct pt_regs *regs,
                goto no_mod;
        }
 
+       if (r13 != sos->prev_IA64_KR_CURRENT) {
+               msg = "inconsistent previous current and r13";
+               goto no_mod;
+       }
+
        if (!mca_recover_range(ms->pmsa_iip)) {
-               if (r13 != sos->prev_IA64_KR_CURRENT) {
-                       msg = "inconsistent previous current and r13";
-                       goto no_mod;
-               }
                if ((r12 - r13) >= KERNEL_STACK_SIZE) {
                        msg = "inconsistent r12 and r13";
                        goto no_mod;
@@ -1187,6 +1190,13 @@ all_in:
  *     further MCA logging is enabled by clearing logs.
  *     Monarch also has the duty of sending wakeup-IPIs to pull the
  *     slave processors out of rendezvous spinloop.
+ *
+ *     If multiple processors call into OS_MCA, the first will become
+ *     the monarch.  Subsequent cpus will be recorded in the mca_cpu
+ *     bitmask.  After the first monarch has processed its MCA, it
+ *     will wake up the next cpu in the mca_cpu bitmask and then go
+ *     into the rendezvous loop.  When all processors have serviced
+ *     their MCA, the last monarch frees up the rest of the processors.
  */
 void
 ia64_mca_handler(struct pt_regs *regs, struct switch_stack *sw,
@@ -1196,16 +1206,32 @@ ia64_mca_handler(struct pt_regs *regs, struct switch_stack *sw,
        struct task_struct *previous_current;
        struct ia64_mca_notify_die nd =
                { .sos = sos, .monarch_cpu = &monarch_cpu };
+       static atomic_t mca_count;
+       static cpumask_t mca_cpu;
 
+       if (atomic_add_return(1, &mca_count) == 1) {
+               monarch_cpu = cpu;
+               sos->monarch = 1;
+       } else {
+               cpu_set(cpu, mca_cpu);
+               sos->monarch = 0;
+       }
        mprintk(KERN_INFO "Entered OS MCA handler. PSP=%lx cpu=%d "
                "monarch=%ld\n", sos->proc_state_param, cpu, sos->monarch);
 
        previous_current = ia64_mca_modify_original_stack(regs, sw, sos, "MCA");
-       monarch_cpu = cpu;
+
        if (notify_die(DIE_MCA_MONARCH_ENTER, "MCA", regs, (long)&nd, 0, 0)
                        == NOTIFY_STOP)
                ia64_mca_spin(__FUNCTION__);
-       ia64_wait_for_slaves(cpu, "MCA");
+       if (sos->monarch) {
+               ia64_wait_for_slaves(cpu, "MCA");
+       } 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
@@ -1244,6 +1270,26 @@ ia64_mca_handler(struct pt_regs *regs, struct switch_stack *sw,
                        == NOTIFY_STOP)
                ia64_mca_spin(__FUNCTION__);
 
+
+       if (atomic_dec_return(&mca_count) > 0) {
+               int i;
+
+               /* 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);
+                               return;
+                       }
+               }
+       }
        set_curr_task(cpu, previous_current);
        monarch_cpu = -1;
 }
index 8c9c26aa6ae092e130f93a4076025d17a297ab92..0f5965fcdf85133fbf0c455f714dcbea091eea2d 100644 (file)
@@ -133,14 +133,6 @@ ia64_do_tlb_purge:
 //StartMain////////////////////////////////////////////////////////////////////
 
 ia64_os_mca_dispatch:
-       // Serialize all MCA processing
-       mov     r3=1;;
-       LOAD_PHYSICAL(p0,r2,ia64_mca_serialize);;
-ia64_os_mca_spin:
-       xchg4   r4=[r2],r3;;
-       cmp.ne  p6,p0=r4,r0
-(p6)   br ia64_os_mca_spin
-
        mov r3=IA64_MCA_CPU_MCA_STACK_OFFSET    // use the MCA stack
        LOAD_PHYSICAL(p0,r2,1f)                 // return address
        mov r19=1                               // All MCA events are treated as monarch (for now)
@@ -291,10 +283,6 @@ END(ia64_os_mca_virtual_begin)
 
        mov             b0=r12                  // SAL_CHECK return address
 
-       // release lock
-       LOAD_PHYSICAL(p0,r3,ia64_mca_serialize);;
-       st4.rel         [r3]=r0
-
        br              b0
 
 //EndMain//////////////////////////////////////////////////////////////////////
index 41098f459684c41f82cc26c6812f3b21deacb400..edd5d01028df93d8d88ad68dbdd1322261bee790 100644 (file)
@@ -48,6 +48,7 @@ enum {
        IA64_MCA_RENDEZ_CHECKIN_NOTDONE =       0x0,
        IA64_MCA_RENDEZ_CHECKIN_DONE    =       0x1,
        IA64_MCA_RENDEZ_CHECKIN_INIT    =       0x2,
+       IA64_MCA_RENDEZ_CHECKIN_CONCURRENT_MCA  =       0x3,
 };
 
 /* Information maintained by the MC infrastructure */