diff options
Diffstat (limited to 'sys/kern/subr_smp.c')
-rw-r--r-- | sys/kern/subr_smp.c | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/sys/kern/subr_smp.c b/sys/kern/subr_smp.c index 4c73b61..3b27dce 100644 --- a/sys/kern/subr_smp.c +++ b/sys/kern/subr_smp.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <sys/lock.h> #include <sys/mutex.h> #include <sys/pcpu.h> +#include <sys/sched.h> #include <sys/smp.h> #include <sys/sysctl.h> @@ -734,3 +735,52 @@ smp_no_rendevous_barrier(void *dummy) KASSERT((!smp_started),("smp_no_rendevous called and smp is started")); #endif } + +/* + * Wait specified idle threads to switch once. This ensures that even + * preempted threads have cycled through the switch function once, + * exiting their codepaths. This allows us to change global pointers + * with no other synchronization. + */ +int +quiesce_cpus(cpuset_t map, const char *wmesg, int prio) +{ + struct pcpu *pcpu; + u_int gen[MAXCPU]; + int error; + int cpu; + + error = 0; + for (cpu = 0; cpu <= mp_maxid; cpu++) { + if (!CPU_ISSET(cpu, &map) || CPU_ABSENT(cpu)) + continue; + pcpu = pcpu_find(cpu); + gen[cpu] = pcpu->pc_idlethread->td_generation; + } + for (cpu = 0; cpu <= mp_maxid; cpu++) { + if (!CPU_ISSET(cpu, &map) || CPU_ABSENT(cpu)) + continue; + pcpu = pcpu_find(cpu); + thread_lock(curthread); + sched_bind(curthread, cpu); + thread_unlock(curthread); + while (gen[cpu] == pcpu->pc_idlethread->td_generation) { + error = tsleep(quiesce_cpus, prio, wmesg, 1); + if (error) + goto out; + } + } +out: + thread_lock(curthread); + sched_unbind(curthread); + thread_unlock(curthread); + + return (error); +} + +int +quiesce_all_cpus(const char *wmesg, int prio) +{ + + return quiesce_cpus(all_cpus, wmesg, prio); +} |