The more code & documentation I read, the more I'm convinced that coordinating state changes between logical processors isn't necessary and actually is responsible for the hangs people have been seeing.
So here is a diff that does away with it all. I've tested it on a few laptops here, but it could use testing on a somewhat wider range of machines. I'm especially interested in seeing this tested on a dual socket machine with apmd -A. Index: i386/i386/mp_setperf.c =================================================================== RCS file: /cvs/src/sys/arch/i386/i386/mp_setperf.c,v retrieving revision 1.5 diff -u -p -r1.5 mp_setperf.c --- i386/i386/mp_setperf.c 29 Jun 2014 01:01:20 -0000 1.5 +++ i386/i386/mp_setperf.c 8 Sep 2014 20:43:50 -0000 @@ -17,13 +17,10 @@ #include <sys/param.h> #include <sys/systm.h> -#include <sys/proc.h> #include <sys/sysctl.h> #include <sys/mutex.h> #include <machine/cpu.h> -#include <machine/cpufunc.h> - #include <machine/intr.h> struct mutex setperf_mp_mutex = MUTEX_INITIALIZER(IPL_HIGH); @@ -31,14 +28,7 @@ struct mutex setperf_mp_mutex = MUTEX_IN /* underlying setperf mechanism e.g. k8_powernow_setperf() */ void (*ul_setperf)(int); -#define MP_SETPERF_STEADY 0 /* steady state - normal operation */ -#define MP_SETPERF_INTRANSIT 1 /* in transition */ -#define MP_SETPERF_PROCEED 2 /* proceed with transition */ -#define MP_SETPERF_FINISH 3 /* return from IPI */ - - /* protected by setperf_mp_mutex */ -volatile int mp_setperf_state = MP_SETPERF_STEADY; volatile int mp_perflevel; void mp_setperf(int); @@ -46,102 +36,27 @@ void mp_setperf(int); void mp_setperf(int level) { - CPU_INFO_ITERATOR cii; - struct cpu_info *ci; - int notready, s; - - if (mp_setperf_state == MP_SETPERF_STEADY) { - mtx_enter(&setperf_mp_mutex); - disable_intr(); - - mp_perflevel = level; - - curcpu()->ci_setperf_state = CI_SETPERF_INTRANSIT; - /* ask all other processors to drop what they are doing */ - CPU_INFO_FOREACH(cii, ci) { - if (ci->ci_setperf_state != CI_SETPERF_INTRANSIT) { - ci->ci_setperf_state = - CI_SETPERF_SHOULDSTOP; - i386_send_ipi(ci, I386_IPI_SETPERF); - } - } - - - /* Loop until all processors report ready */ - do { - CPU_INFO_FOREACH(cii, ci) { - if ((notready = (ci->ci_setperf_state - != CI_SETPERF_INTRANSIT))) - break; - } - } while (notready); - - mp_setperf_state = MP_SETPERF_PROCEED; /* release the hounds */ - - s = splipi(); - - ul_setperf(mp_perflevel); - - splx(s); - - curcpu()->ci_setperf_state = CI_SETPERF_DONE; - /* Loop until all processors report done */ - do { - CPU_INFO_FOREACH(cii, ci) { - if ((notready = (ci->ci_setperf_state - != CI_SETPERF_DONE))) - break; - } - } while (notready); - - mp_setperf_state = MP_SETPERF_FINISH; - /* delay a little for potential straglers */ - DELAY(2); - curcpu()->ci_setperf_state = CI_SETPERF_READY; - mp_setperf_state = MP_SETPERF_STEADY; /* restore normallity */ - enable_intr(); - mtx_leave(&setperf_mp_mutex); - } + mtx_enter(&setperf_mp_mutex); + mp_perflevel = level; + ul_setperf(mp_perflevel); + i386_broadcast_ipi(I386_IPI_SETPERF); + mtx_leave(&setperf_mp_mutex); } void i386_setperf_ipi(struct cpu_info *ci) { - - disable_intr(); - - if (ci->ci_setperf_state == CI_SETPERF_SHOULDSTOP) - ci->ci_setperf_state = CI_SETPERF_INTRANSIT; - - while (mp_setperf_state != MP_SETPERF_PROCEED) - ; - ul_setperf(mp_perflevel); - - ci->ci_setperf_state = CI_SETPERF_DONE; - - while (mp_setperf_state != MP_SETPERF_FINISH) - ; - ci->ci_setperf_state = CI_SETPERF_READY; - - enable_intr(); } void -mp_setperf_init() +mp_setperf_init(void) { - CPU_INFO_ITERATOR cii; - struct cpu_info *ci; - if (!cpu_setperf) return; - ul_setperf = cpu_setperf; + ul_setperf = cpu_setperf; cpu_setperf = mp_setperf; - - CPU_INFO_FOREACH(cii, ci) { - ci->ci_setperf_state = CI_SETPERF_READY; - } mtx_init(&setperf_mp_mutex, IPL_HIGH); } Index: i386/include/cpu.h =================================================================== RCS file: /cvs/src/sys/arch/i386/include/cpu.h,v retrieving revision 1.134 diff -u -p -r1.134 cpu.h --- i386/include/cpu.h 11 Jul 2014 10:53:07 -0000 1.134 +++ i386/include/cpu.h 8 Sep 2014 20:43:50 -0000 @@ -147,12 +147,6 @@ struct cpu_info { #define CI_DDB_ENTERDDB 3 #define CI_DDB_INDDB 4 - volatile int ci_setperf_state; -#define CI_SETPERF_READY 0 -#define CI_SETPERF_SHOULDSTOP 1 -#define CI_SETPERF_INTRANSIT 2 -#define CI_SETPERF_DONE 3 - struct ksensordev ci_sensordev; struct ksensor ci_sensor; #ifdef GPROF Index: amd64/amd64/mp_setperf.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/mp_setperf.c,v retrieving revision 1.4 diff -u -p -r1.4 mp_setperf.c --- amd64/amd64/mp_setperf.c 29 Jun 2014 01:01:20 -0000 1.4 +++ amd64/amd64/mp_setperf.c 8 Sep 2014 20:43:50 -0000 @@ -17,13 +17,10 @@ #include <sys/param.h> #include <sys/systm.h> -#include <sys/proc.h> #include <sys/sysctl.h> #include <sys/mutex.h> #include <machine/cpu.h> -#include <machine/cpufunc.h> - #include <machine/intr.h> struct mutex setperf_mp_mutex = MUTEX_INITIALIZER(IPL_HIGH); @@ -31,14 +28,7 @@ struct mutex setperf_mp_mutex = MUTEX_IN /* underlying setperf mechanism e.g. k8_powernow_setperf() */ void (*ul_setperf)(int); -#define MP_SETPERF_STEADY 0 /* steady state - normal operation */ -#define MP_SETPERF_INTRANSIT 1 /* in transition */ -#define MP_SETPERF_PROCEED 2 /* proceed with transition */ -#define MP_SETPERF_FINISH 3 /* return from IPI */ - - /* protected by setperf_mp_mutex */ -volatile int mp_setperf_state = MP_SETPERF_STEADY; volatile int mp_perflevel; void mp_setperf(int); @@ -46,101 +36,28 @@ void mp_setperf(int); void mp_setperf(int level) { - CPU_INFO_ITERATOR cii; - struct cpu_info *ci; - int notready, s; - - if (mp_setperf_state == MP_SETPERF_STEADY) { - mtx_enter(&setperf_mp_mutex); - disable_intr(); - mp_perflevel = level; - - curcpu()->ci_setperf_state = CI_SETPERF_INTRANSIT; - /* ask all other processors to drop what they are doing */ - CPU_INFO_FOREACH(cii, ci) { - if (ci->ci_setperf_state != CI_SETPERF_INTRANSIT) { - ci->ci_setperf_state = - CI_SETPERF_SHOULDSTOP; - x86_send_ipi(ci, X86_IPI_SETPERF); - } - } - - - /* Loop until all processors report ready */ - do { - CPU_INFO_FOREACH(cii, ci) { - if ((notready = (ci->ci_setperf_state - != CI_SETPERF_INTRANSIT))) - break; - } - } while (notready); - - mp_setperf_state = MP_SETPERF_PROCEED; /* release the hounds */ - - s = splipi(); - - ul_setperf(mp_perflevel); - - splx(s); - - curcpu()->ci_setperf_state = CI_SETPERF_DONE; - /* Loop until all processors report done */ - do { - CPU_INFO_FOREACH(cii, ci) { - if ((notready = (ci->ci_setperf_state - != CI_SETPERF_DONE))) - break; - } - } while (notready); - - mp_setperf_state = MP_SETPERF_FINISH; - /* delay a little for potential straglers */ - DELAY(2); - curcpu()->ci_setperf_state = CI_SETPERF_READY; - mp_setperf_state = MP_SETPERF_STEADY; /* restore normallity */ - enable_intr(); - mtx_leave(&setperf_mp_mutex); - } + mtx_enter(&setperf_mp_mutex); + mp_perflevel = level; + + ul_setperf(mp_perflevel); + x86_broadcast_ipi(X86_IPI_SETPERF); + mtx_leave(&setperf_mp_mutex); } void x86_setperf_ipi(struct cpu_info *ci) { - - disable_intr(); - - if (ci->ci_setperf_state == CI_SETPERF_SHOULDSTOP) - ci->ci_setperf_state = CI_SETPERF_INTRANSIT; - - while (mp_setperf_state != MP_SETPERF_PROCEED) - ; - ul_setperf(mp_perflevel); - - ci->ci_setperf_state = CI_SETPERF_DONE; - - while (mp_setperf_state != MP_SETPERF_FINISH) - ; - ci->ci_setperf_state = CI_SETPERF_READY; - - enable_intr(); } void -mp_setperf_init() +mp_setperf_init(void) { - CPU_INFO_ITERATOR cii; - struct cpu_info *ci; - if (!cpu_setperf) return; - ul_setperf = cpu_setperf; + ul_setperf = cpu_setperf; cpu_setperf = mp_setperf; - - CPU_INFO_FOREACH(cii, ci) { - ci->ci_setperf_state = CI_SETPERF_READY; - } mtx_init(&setperf_mp_mutex, IPL_HIGH); } Index: amd64/include/cpu.h =================================================================== RCS file: /cvs/src/sys/arch/amd64/include/cpu.h,v retrieving revision 1.85 diff -u -p -r1.85 cpu.h --- amd64/include/cpu.h 11 Jul 2014 10:53:07 -0000 1.85 +++ amd64/include/cpu.h 8 Sep 2014 20:43:50 -0000 @@ -136,12 +136,6 @@ struct cpu_info { #define CI_DDB_ENTERDDB 3 #define CI_DDB_INDDB 4 - volatile int ci_setperf_state; -#define CI_SETPERF_READY 0 -#define CI_SETPERF_SHOULDSTOP 1 -#define CI_SETPERF_INTRANSIT 2 -#define CI_SETPERF_DONE 3 - struct ksensordev ci_sensordev; struct ksensor ci_sensor; #ifdef GPROF