summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/i86pc/os/mp_machdep.c64
-rw-r--r--usr/src/uts/sun4v/os/mach_startup.c64
2 files changed, 95 insertions, 33 deletions
diff --git a/usr/src/uts/i86pc/os/mp_machdep.c b/usr/src/uts/i86pc/os/mp_machdep.c
index b51ca8462b..7267b4b08e 100644
--- a/usr/src/uts/i86pc/os/mp_machdep.c
+++ b/usr/src/uts/i86pc/os/mp_machdep.c
@@ -200,7 +200,7 @@ cpu_halt(void)
{
cpu_t *cpup = CPU;
processorid_t cpun = cpup->cpu_id;
- cpupart_t *cp;
+ cpupart_t *cp = cpup->cpu_part;
int hset_update = 1;
/*
@@ -212,44 +212,76 @@ cpu_halt(void)
*/
if (cpup->cpu_flags & CPU_OFFLINE || ncpus == 1)
hset_update = 0;
- /*
- * We're on our way to being halted.
- * Disable interrupts now, so that we'll awaken immediately
- * after halting if someone tries to poke us between now and
- * the time we actually halt.
- */
- cli();
/*
* Add ourselves to the partition's halted CPUs bitmask
* and set our HALTED flag, if necessary.
*
+ * When a thread becomes runnable, it is placed on the queue
+ * and then the halted cpuset is checked to determine who
+ * (if anyone) should be awoken. We therefore need to first
+ * add ourselves to the halted cpuset, and and then check if there
+ * is any work available.
+ *
* Note that memory barriers after updating the HALTED flag
* are not necessary since an atomic operation (updating the bitmap)
* immediately follows. On x86 the atomic operation acts as a
* memory barrier for the update of cpu_disp_flags.
- * If and when this code is made common (running on SPARC),
- * membar_producer()s will be needed after the update of
- * cpu_disp_flags to propagate the HALTED flag to global visibility.
*/
if (hset_update) {
cpup->cpu_disp_flags |= CPU_DISP_HALTED;
- cp = cpup->cpu_part;
CPUSET_ATOMIC_ADD(cp->cp_haltset, cpun);
}
/*
* Check to make sure there's really nothing to do.
- * If work becomes available *after* we do this check
- * and it's determined that the work should be ours,
- * we won't miss it since we'll be notified with a "poke"
- * ...which will pop us right back out of the halted state.
+ * Work destined for this CPU may become available after
+ * this check. We'll be notified through the clearing of our
+ * bit in the halted CPU bitmask, and a poke.
*/
if (disp_anywork()) {
if (hset_update) {
cpup->cpu_disp_flags &= ~CPU_DISP_HALTED;
CPUSET_ATOMIC_DEL(cp->cp_haltset, cpun);
}
+ return;
+ }
+
+ /*
+ * We're on our way to being halted.
+ *
+ * Disable interrupts now, so that we'll awaken immediately
+ * after halting if someone tries to poke us between now and
+ * the time we actually halt.
+ *
+ * We check for the presence of our bit after disabling interrupts.
+ * If it's cleared, we'll return. If the bit is cleared after
+ * we check then the poke will pop us out of the halted state.
+ *
+ * This means that the ordering of the poke and the clearing
+ * of the bit by cpu_wakeup is important.
+ * cpu_wakeup() must clear, then poke.
+ * cpu_halt() must disable interrupts, then check for the bit.
+ */
+ cli();
+
+ if (hset_update && !CPU_IN_SET(cp->cp_haltset, cpun)) {
+ cpup->cpu_disp_flags &= ~CPU_DISP_HALTED;
+ sti();
+ return;
+ }
+
+ /*
+ * The check for anything locally runnable is here for performance
+ * and isn't needed for correctness. disp_nrunnable ought to be
+ * in our cache still, so it's inexpensive to check, and if there
+ * is anything runnable we won't have to wait for the poke.
+ */
+ if (cpup->cpu_disp->disp_nrunnable != 0) {
+ if (hset_update) {
+ cpup->cpu_disp_flags &= ~CPU_DISP_HALTED;
+ CPUSET_ATOMIC_DEL(cp->cp_haltset, cpun);
+ }
sti();
return;
}
diff --git a/usr/src/uts/sun4v/os/mach_startup.c b/usr/src/uts/sun4v/os/mach_startup.c
index d37f3dffae..8da9058619 100644
--- a/usr/src/uts/sun4v/os/mach_startup.c
+++ b/usr/src/uts/sun4v/os/mach_startup.c
@@ -78,7 +78,7 @@ cpu_halt(void)
{
cpu_t *cpup = CPU;
processorid_t cpun = cpup->cpu_id;
- cpupart_t *cp;
+ cpupart_t *cp = cpup->cpu_part;
int hset_update = 1;
uint_t s;
@@ -91,42 +91,72 @@ cpu_halt(void)
*/
if (CPU->cpu_flags & CPU_OFFLINE || ncpus == 1)
hset_update = 0;
- /*
- * We're on our way to being halted.
- * Disable interrupts now, so that we'll awaken immediately
- * after halting if someone tries to poke us between now and
- * the time we actually halt.
- */
- s = disable_vec_intr();
/*
* Add ourselves to the partition's halted CPUs bitmask
* and set our HALTED flag, if necessary.
*
- * Note that the memory barrier after updating the HALTED flag
- * is needed to ensure that the HALTED flag has reached global
- * visibility before scanning the run queue for the last time
- * (via disp_anywork) and halting ourself.
+ * When a thread becomes runnable, it is placed on the queue
+ * and then the halted cpuset is checked to determine who
+ * (if anyone) should be awoken. We therefore need to first
+ * add ourselves to the halted cpuset, and then check if there
+ * is any work available.
*/
if (hset_update) {
cpup->cpu_disp_flags |= CPU_DISP_HALTED;
membar_producer();
- cp = cpup->cpu_part;
CPUSET_ATOMIC_ADD(cp->cp_haltset, cpun);
}
/*
* Check to make sure there's really nothing to do.
- * If work becomes available *after* we do this check
- * and it's determined that the work should be ours,
- * we won't miss it since we'll be notified with a "poke"
- * ...which will pop us right back out of the halted state.
+ * Work destined for this CPU may become available after
+ * this check. We'll be notified through the clearing of our
+ * bit in the halted CPU bitmask, and a poke.
*/
if (disp_anywork()) {
if (hset_update) {
cpup->cpu_disp_flags &= ~CPU_DISP_HALTED;
CPUSET_ATOMIC_DEL(cp->cp_haltset, cpun);
}
+ return;
+ }
+
+ /*
+ * We're on our way to being halted.
+ *
+ * Disable interrupts now, so that we'll awaken immediately
+ * after halting if someone tries to poke us between now and
+ * the time we actually halt.
+ *
+ * We check for the presence of our bit after disabling interrupts.
+ * If it's cleared, we'll return. If the bit is cleared after
+ * we check then the poke will pop us out of the halted state.
+ *
+ * The ordering of the poke and the clearing of the bit by cpu_wakeup
+ * is important.
+ * cpu_wakeup() must clear, then poke.
+ * cpu_halt() must disable interrupts, then check for the bit.
+ */
+ s = disable_vec_intr();
+
+ if (hset_update && !CPU_IN_SET(cp->cp_haltset, cpun)) {
+ cpup->cpu_disp_flags &= ~CPU_DISP_HALTED;
+ enable_vec_intr(s);
+ return;
+ }
+
+ /*
+ * The check for anything locally runnable is here for performance
+ * and isn't needed for correctness. disp_nrunnable ought to be
+ * in our cache still, so it's inexpensive to check, and if there
+ * is anything runnable we won't have to wait for the poke.
+ */
+ if (cpup->cpu_disp->disp_nrunnable != 0) {
+ if (hset_update) {
+ cpup->cpu_disp_flags &= ~CPU_DISP_HALTED;
+ CPUSET_ATOMIC_DEL(cp->cp_haltset, cpun);
+ }
enable_vec_intr(s);
return;
}