]> Zhao Yanbai Git Server - minix.git/commitdiff
SMP - fixed IPI livelock
authorTomas Hruby <thruby@few.vu.nl>
Wed, 26 Oct 2011 15:43:36 +0000 (15:43 +0000)
committerTomas Hruby <tom@minix3.org>
Fri, 13 Jan 2012 11:30:00 +0000 (11:30 +0000)
- two CPUs can issue IPI to each other now without any hazzard

- we must be able to handle synchronous scheduling IPIs from
  other CPUs when we are waiting for attention from another one.
  Otherwise we might livelock.

- necessary barriers to prevent reordering

kernel/arch/i386/arch_clock.c
kernel/smp.c
kernel/smp.h
kernel/utility.c

index 5a64a836770aab9b8cb80a8dbfddf7005e2ce947..4af6b35ae43a6faaaba48c68e6700b9ec5d3f363 100644 (file)
 #ifdef USE_APIC
 #include "apic.h"
 #endif
+
 #include "spinlock.h"
 
+#ifdef CONFIG_SMP
+#include "kernel/smp.h"
+#endif
+
 #define CLOCK_ACK_BIT   0x80    /* PS/2 clock interrupt acknowledge bit */
 
 /* Clock parameters. */
@@ -235,6 +240,20 @@ PUBLIC void context_stop(struct proc * p)
                bkl_succ[cpu] += !(!(succ == 0));
 
                p->p_cycles = add64(p->p_cycles, sub64(tsc, *__tsc_ctr_switch));
+
+#ifdef CONFIG_SMP
+               /*
+                * Since at the time we got a scheduling IPI we might have been
+                * waiting for BKL already, we may miss it due to a similar IPI to
+                * the cpu which is already waiting for us to handle its. This
+                * results in a live-lock of these two cpus.
+                *
+                * Therefore we always check if there is one pending and if so,
+                * we handle it straight away so the other cpu can continue and
+                * we do not deadlock.
+                */
+               smp_sched_handler();
+#endif
        }
 #else
        read_tsc_64(&tsc);
index aa03f691ea4ba6ba9c8d0536618387806562f288..6ead02a0695854a6cc28c8f04ab656c96122e922 100644 (file)
@@ -66,11 +66,13 @@ PUBLIC void smp_schedule(unsigned cpu)
         * check if the cpu is processing some other ipi already. If yes, no
         * need to wake it up
         */
-       if ((volatile unsigned)sched_ipi_data[cpu].flags != 0)
+       if (sched_ipi_data[cpu].flags != 0)
                return;
        arch_send_smp_schedule_ipi(cpu);
 }
 
+_PROTOTYPE(void smp_sched_handler, (void));
+
 /*
  * tell another cpu about a task to do and return only after the cpu acks that
  * the task is finished. Also wait before it finishes task sent by another cpu
@@ -79,24 +81,39 @@ PUBLIC void smp_schedule(unsigned cpu)
 PRIVATE void smp_schedule_sync(struct proc * p, unsigned task)
 {
        unsigned cpu = p->p_cpu;
+       unsigned mycpu = cpuid;
 
-       /* 
+       assert(cpu != mycpu);
+       /*
         * if some other cpu made a request to the same cpu, wait until it is
         * done before proceeding
         */
-       if ((volatile unsigned)sched_ipi_data[cpu].flags != 0) {
+       if (sched_ipi_data[cpu].flags != 0) {
                BKL_UNLOCK();
-               while ((volatile unsigned)sched_ipi_data[cpu].flags != 0);
+               while (sched_ipi_data[cpu].flags != 0) {
+                       if (sched_ipi_data[mycpu].flags) {
+                               BKL_LOCK();
+                               smp_sched_handler();
+                               BKL_UNLOCK();
+                       }
+               }
                BKL_LOCK();
        }
 
-       sched_ipi_data[cpu].flags |= task;
        sched_ipi_data[cpu].data = (u32_t) p;
+       sched_ipi_data[cpu].flags |= task;
+       __insn_barrier();
        arch_send_smp_schedule_ipi(cpu);
 
        /* wait until the destination cpu finishes its job */
        BKL_UNLOCK();
-       while ((volatile unsigned)sched_ipi_data[cpu].flags != 0);
+       while (sched_ipi_data[cpu].flags != 0) {
+               if (sched_ipi_data[mycpu].flags) {
+                       BKL_LOCK();
+                       smp_sched_handler();
+                       BKL_UNLOCK();
+               }
+       }
        BKL_LOCK();
 }
 
@@ -142,20 +159,16 @@ PUBLIC void smp_schedule_migrate_proc(struct proc * p, unsigned dest_cpu)
        RTS_UNSET(p, RTS_PROC_STOP);
 }
 
-PUBLIC void smp_ipi_sched_handler(void)
+PUBLIC void smp_sched_handler(void)
 {
-       struct proc * curr;
-       unsigned mycpu = cpuid;
        unsigned flgs;
-       
-       ipi_ack();
-       
-       curr = get_cpu_var(mycpu, proc_ptr);
-       flgs = sched_ipi_data[mycpu].flags;
+       unsigned cpu = cpuid;
+
+       flgs = sched_ipi_data[cpu].flags;
 
        if (flgs) {
                struct proc * p;
-               p = (struct proc *)sched_ipi_data[mycpu].data;
+               p = (struct proc *)sched_ipi_data[cpu].data;
 
                if (flgs & SCHED_IPI_STOP_PROC) {
                        RTS_SET(p, RTS_PROC_STOP);
@@ -174,9 +187,25 @@ PUBLIC void smp_ipi_sched_handler(void)
                        RTS_SET(p, RTS_VMINHIBIT);
                }
        }
-       else if (curr->p_endpoint != IDLE) {
+
+       __insn_barrier();
+       sched_ipi_data[cpu].flags = 0;
+}
+
+/*
+ * This function gets always called only after smp_sched_handler() has been
+ * already called. It only serves the purpose of acknowledging the IPI and
+ * preempting the current process if the CPU was not idle.
+ */
+PUBLIC void smp_ipi_sched_handler(void)
+{
+       struct proc * curr;
+
+       ipi_ack();
+
+       curr = get_cpulocal_var(proc_ptr);
+       if (curr->p_endpoint != IDLE) {
                RTS_SET(curr, RTS_PREEMPTED);
        }
-       sched_ipi_data[cpuid].flags = 0;
 }
 
index b637ad3726615e5481ec150a1e06ef39a438b25d..e35b75d6e2ea77cad43a934f52909682512b6a23 100644 (file)
@@ -73,6 +73,9 @@ _PROTOTYPE(void smp_schedule_migrate_proc,
 _PROTOTYPE(void arch_send_smp_schedule_ipi, (unsigned cpu));
 _PROTOTYPE(void arch_smp_halt_cpu, (void));
 
+/* deal with x-cpu scheduling event */
+_PROTOTYPE(void smp_sched_handler, (void));
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* CONFIG_SMP */
index 0de0d5bd7efc806846cac1bac2a87d009394eaed..fdae2506f816df917fbed4b595106ddc9944d7f1 100644 (file)
@@ -33,9 +33,12 @@ PUBLIC void panic(const char *fmt, ...)
        printf("\n");
   }
 
-  printf("kernel: ");
+  printf("kernel on CPU %d: ", cpuid);
   util_stacktrace();
 
+  printf("current process : ");
+  proc_stacktrace(get_cpulocal_var(proc_ptr));
+
   /* Abort MINIX. */
   minix_shutdown(NULL);
 }