aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/subr_smp.c
diff options
context:
space:
mode:
authorAttilio Rao <attilio@FreeBSD.org>2011-05-17 22:03:01 +0000
committerAttilio Rao <attilio@FreeBSD.org>2011-05-17 22:03:01 +0000
commitfea3a3fa94758f2a1a8c1254b75c76554fd10ff0 (patch)
tree503ef8bd8899d6d40d5974ca42eea2134c42a00f /sys/kern/subr_smp.c
parent7e7a34e520e25d71c65d23269753de1512bae5ff (diff)
parent7a18096ceacb88e0804cac1c0175300f60bc6a08 (diff)
downloadsrc-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.c71
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);
}