diff options
author | Attilio Rao <attilio@FreeBSD.org> | 2011-05-17 22:03:01 +0000 |
---|---|---|
committer | Attilio Rao <attilio@FreeBSD.org> | 2011-05-17 22:03:01 +0000 |
commit | fea3a3fa94758f2a1a8c1254b75c76554fd10ff0 (patch) | |
tree | 503ef8bd8899d6d40d5974ca42eea2134c42a00f /sys/kern/subr_smp.c | |
parent | 7e7a34e520e25d71c65d23269753de1512bae5ff (diff) | |
parent | 7a18096ceacb88e0804cac1c0175300f60bc6a08 (diff) | |
download | src-fea3a3fa94758f2a1a8c1254b75c76554fd10ff0.tar.gz src-fea3a3fa94758f2a1a8c1254b75c76554fd10ff0.zip |
MFC
Notes
Notes:
svn path=/projects/largeSMP/; revision=222038
Diffstat (limited to 'sys/kern/subr_smp.c')
-rw-r--r-- | sys/kern/subr_smp.c | 71 |
1 files changed, 52 insertions, 19 deletions
diff --git a/sys/kern/subr_smp.c b/sys/kern/subr_smp.c index ae061f36c3b5..83655e83159f 100644 --- a/sys/kern/subr_smp.c +++ b/sys/kern/subr_smp.c @@ -53,10 +53,10 @@ __FBSDID("$FreeBSD$"); #include "opt_sched.h" #ifdef SMP -volatile cpuset_t stopped_cpus; -volatile cpuset_t started_cpus; -cpuset_t hlt_cpus_mask; -cpuset_t logical_cpus_mask; +volatile cpumask_t stopped_cpus; +volatile cpumask_t started_cpus; +cpumask_t hlt_cpus_mask; +cpumask_t logical_cpus_mask; void (*cpustop_restartfunc)(void); #endif @@ -110,6 +110,7 @@ static void (*volatile smp_rv_action_func)(void *arg); static void (*volatile smp_rv_teardown_func)(void *arg); static void *volatile smp_rv_func_arg; static volatile int smp_rv_waiters[3]; +static volatile int smp_rv_generation; /* * Shared mutex to restrict busywaits between smp_rendezvous() and @@ -317,39 +318,63 @@ restart_cpus(cpuset_t map) void smp_rendezvous_action(void) { - void* local_func_arg = smp_rv_func_arg; - void (*local_setup_func)(void*) = smp_rv_setup_func; - void (*local_action_func)(void*) = smp_rv_action_func; - void (*local_teardown_func)(void*) = smp_rv_teardown_func; + void *local_func_arg; + void (*local_setup_func)(void*); + void (*local_action_func)(void*); + void (*local_teardown_func)(void*); + int generation; /* Ensure we have up-to-date values. */ atomic_add_acq_int(&smp_rv_waiters[0], 1); while (smp_rv_waiters[0] < smp_rv_ncpus) cpu_spinwait(); - /* setup function */ + /* Fetch rendezvous parameters after acquire barrier. */ + local_func_arg = smp_rv_func_arg; + local_setup_func = smp_rv_setup_func; + local_action_func = smp_rv_action_func; + local_teardown_func = smp_rv_teardown_func; + generation = smp_rv_generation; + + /* + * If requested, run a setup function before the main action + * function. Ensure all CPUs have completed the setup + * function before moving on to the action function. + */ if (local_setup_func != smp_no_rendevous_barrier) { if (smp_rv_setup_func != NULL) smp_rv_setup_func(smp_rv_func_arg); - - /* spin on entry rendezvous */ atomic_add_int(&smp_rv_waiters[1], 1); while (smp_rv_waiters[1] < smp_rv_ncpus) cpu_spinwait(); } - /* action function */ if (local_action_func != NULL) local_action_func(local_func_arg); - /* spin on exit rendezvous */ + /* + * Signal that the main action has been completed. If a + * full exit rendezvous is requested, then all CPUs will + * wait here until all CPUs have finished the main action. + * + * Note that the write by the last CPU to finish the action + * may become visible to different CPUs at different times. + * As a result, the CPU that initiated the rendezvous may + * exit the rendezvous and drop the lock allowing another + * rendezvous to be initiated on the same CPU or a different + * CPU. In that case the exit sentinel may be cleared before + * all CPUs have noticed causing those CPUs to hang forever. + * Workaround this by using a generation count to notice when + * this race occurs and to exit the rendezvous in that case. + */ + MPASS(generation == smp_rv_generation); atomic_add_int(&smp_rv_waiters[2], 1); if (local_teardown_func == smp_no_rendevous_barrier) return; - while (smp_rv_waiters[2] < smp_rv_ncpus) + while (smp_rv_waiters[2] < smp_rv_ncpus && + generation == smp_rv_generation) cpu_spinwait(); - /* teardown function */ if (local_teardown_func != NULL) local_teardown_func(local_func_arg); } @@ -380,10 +405,11 @@ smp_rendezvous_cpus(cpuset_t map, if (ncpus == 0) panic("ncpus is 0 with non-zero map"); - /* obtain rendezvous lock */ mtx_lock_spin(&smp_ipi_mtx); - /* set static function pointers */ + atomic_add_acq_int(&smp_rv_generation, 1); + + /* Pass rendezvous parameters via global variables. */ smp_rv_ncpus = ncpus; smp_rv_setup_func = setup_func; smp_rv_action_func = action_func; @@ -393,7 +419,10 @@ smp_rendezvous_cpus(cpuset_t map, smp_rv_waiters[2] = 0; atomic_store_rel_int(&smp_rv_waiters[0], 0); - /* signal other processors, which will enter the IPI with interrupts off */ + /* + * Signal other processors, which will enter the IPI with + * interrupts off. + */ curcpumap = CPU_ISSET(curcpu, &map); CPU_CLR(curcpu, &map); ipi_selected(map, IPI_RENDEZVOUS); @@ -402,11 +431,15 @@ smp_rendezvous_cpus(cpuset_t map, if (curcpumap != 0) smp_rendezvous_action(); + /* + * If the caller did not request an exit barrier to be enforced + * on each CPU, ensure that this CPU waits for all the other + * CPUs to finish the rendezvous. + */ if (teardown_func == smp_no_rendevous_barrier) while (atomic_load_acq_int(&smp_rv_waiters[2]) < ncpus) cpu_spinwait(); - /* release lock */ mtx_unlock_spin(&smp_ipi_mtx); } |