diff options
author | msmith <msmith@FreeBSD.org> | 1999-07-20 06:52:35 +0000 |
---|---|---|
committer | msmith <msmith@FreeBSD.org> | 1999-07-20 06:52:35 +0000 |
commit | 84c939ef118a545b74e42d1ab9013ea9ba8b1b3f (patch) | |
tree | f285b114a76c38187b7d9910bdede9788d00ae9e /sys/kern/subr_smp.c | |
parent | 905bcc118433dc6747bf9091a2bae04370334c34 (diff) | |
download | FreeBSD-src-84c939ef118a545b74e42d1ab9013ea9ba8b1b3f.zip FreeBSD-src-84c939ef118a545b74e42d1ab9013ea9ba8b1b3f.tar.gz |
Implement an all-CPU shootdown-style rendezvous facility. This allows
the caller to specify a function to be guarded between an entry and exit
barrier, as well as pre- and post-barrier functions.
The primary use for this function is synchronised update of per-cpu private
data. The implementation is almost (but not quite) MI; with a better
mechanism for masking per-CPU interrupts it could probably be hoisted.
Reviewed by: peter (partially)
Diffstat (limited to 'sys/kern/subr_smp.c')
-rw-r--r-- | sys/kern/subr_smp.c | 86 |
1 files changed, 85 insertions, 1 deletions
diff --git a/sys/kern/subr_smp.c b/sys/kern/subr_smp.c index 9d8be53..e81efef 100644 --- a/sys/kern/subr_smp.c +++ b/sys/kern/subr_smp.c @@ -22,7 +22,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: mp_machdep.c,v 1.104 1999/06/23 21:47:22 luoqi Exp $ + * $Id: mp_machdep.c,v 1.105 1999/06/23 23:02:38 msmith Exp $ */ #include "opt_smp.h" @@ -62,7 +62,10 @@ #include <machine/smp.h> #include <machine/apic.h> +#include <machine/atomic.h> +#include <machine/cpufunc.h> #include <machine/mpapic.h> +#include <machine/psl.h> #include <machine/segments.h> #include <machine/smptests.h> /** TEST_DEFAULT_CONFIG, TEST_TEST1 */ #include <machine/tss.h> @@ -591,6 +594,10 @@ mp_enable(u_int boot_addr) SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif + /* install an inter-CPU IPI for all-CPU rendezvous */ + setidt(XRENDEZVOUS_OFFSET, Xrendezvous, + SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); + /* install an inter-CPU IPI for forcing an additional software trap */ setidt(XCPUAST_OFFSET, Xcpuast, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); @@ -1698,6 +1705,9 @@ struct simplelock com_lock; struct simplelock clock_lock; #endif /* USE_CLOCKLOCK */ +/* lock around the MP rendezvous */ +static struct simplelock smp_rv_lock; + static void init_locks(void) { @@ -1722,6 +1732,7 @@ init_locks(void) s_lock_init((struct simplelock*)&intr_lock); s_lock_init((struct simplelock*)&imen_lock); s_lock_init((struct simplelock*)&cpl_lock); + s_lock_init(&smp_rv_lock); #ifdef USE_COMLOCK s_lock_init((struct simplelock*)&com_lock); @@ -2604,3 +2615,76 @@ set_lapic_isrloc(int intr, int vector) apic_isrbit_location[intr].bit = (1<<(vector & 31)); } #endif + +/* + * All-CPU rendezvous. CPUs are signalled, all execute the setup function + * (if specified), rendezvous, execute the action function (if specified), + * rendezvous again, execute the teardown function (if specified), and then + * resume. + * + * Note that the supplied external functions _must_ be reentrant and aware + * that they are running in parallel and in an unknown lock context. + */ +static void (*smp_rv_setup_func)(void *arg); +static void (*smp_rv_action_func)(void *arg); +static void (*smp_rv_teardown_func)(void *arg); +static void *smp_rv_func_arg; +static volatile int smp_rv_waiters[2]; + +void +smp_rendezvous_action(void) +{ + /* setup function */ + if (smp_rv_setup_func != NULL) + smp_rv_setup_func(smp_rv_func_arg); + /* spin on entry rendezvous */ + atomic_add_int(&smp_rv_waiters[0], 1); + while (smp_rv_waiters[0] < mp_ncpus) + ; + /* action function */ + if (smp_rv_action_func != NULL) + smp_rv_action_func(smp_rv_func_arg); + /* spin on exit rendezvous */ + atomic_add_int(&smp_rv_waiters[1], 1); + while (smp_rv_waiters[1] < mp_ncpus) + ; + /* teardown function */ + if (smp_rv_teardown_func != NULL) + smp_rv_teardown_func(smp_rv_func_arg); +} + +void +smp_rendezvous(void (* setup_func)(void *), + void (* action_func)(void *), + void (* teardown_func)(void *), + void *arg) +{ + u_int efl; + + /* obtain rendezvous lock */ + s_lock(&smp_rv_lock); /* XXX sleep here? NOWAIT flag? */ + + /* set static function pointers */ + smp_rv_setup_func = setup_func; + smp_rv_action_func = action_func; + smp_rv_teardown_func = teardown_func; + smp_rv_func_arg = arg; + smp_rv_waiters[0] = 0; + smp_rv_waiters[1] = 0; + + /* disable interrupts on this CPU, save interrupt status */ + efl = read_eflags(); + write_eflags(efl & ~PSL_I); + + /* signal other processors, which will enter the IPI with interrupts off */ + all_but_self_ipi(XRENDEZVOUS_OFFSET); + + /* call executor function */ + smp_rendezvous_action(); + + /* restore interrupt flag */ + write_eflags(efl); + + /* release lock */ + s_unlock(&smp_rv_lock); +} |