Module Name: src Committed By: rmind Date: Sun May 25 15:34:20 UTC 2014
Modified Files: src/distrib/sets/lists/comp: mi src/share/man/man9: Makefile src/sys/kern: subr_ipi.c src/sys/sys: cpu_data.h ipi.h Added Files: src/share/man/man9: ipi.9 Log Message: MI IPI interface: - Implement support for the asynchronous IPI calls. - Rework synchronous IPI code to reuse the asynchronous mechanism. - Add ipi(9) manual page; needs wizd(8). Note: MD code can now provide a low level primitive for the ipi(9) and reuse this interface instead of open-coding. Portmasters are encouraged to convert. Ride 6.99.43! To generate a diff of this commit: cvs rdiff -u -r1.1892 -r1.1893 src/distrib/sets/lists/comp/mi cvs rdiff -u -r1.377 -r1.378 src/share/man/man9/Makefile cvs rdiff -u -r0 -r1.1 src/share/man/man9/ipi.9 cvs rdiff -u -r1.1 -r1.2 src/sys/kern/subr_ipi.c cvs rdiff -u -r1.36 -r1.37 src/sys/sys/cpu_data.h cvs rdiff -u -r1.1 -r1.2 src/sys/sys/ipi.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/comp/mi diff -u src/distrib/sets/lists/comp/mi:1.1892 src/distrib/sets/lists/comp/mi:1.1893 --- src/distrib/sets/lists/comp/mi:1.1892 Thu May 15 16:32:28 2014 +++ src/distrib/sets/lists/comp/mi Sun May 25 15:34:19 2014 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1892 2014/05/15 16:32:28 apb Exp $ +# $NetBSD: mi,v 1.1893 2014/05/25 15:34:19 rmind Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -10273,6 +10273,7 @@ ./usr/share/man/cat9/ioctl.0 comp-sys-catman .cat ./usr/share/man/cat9/ioctl_copyin.0 comp-sys-catman .cat ./usr/share/man/cat9/ioctl_copyout.0 comp-sys-catman .cat +./usr/share/man/cat9/ipi.0 comp-sys-catman .cat ./usr/share/man/cat9/ipkdb.0 comp-sys-catman .cat ./usr/share/man/cat9/ipkdb_connect.0 comp-sys-catman .cat ./usr/share/man/cat9/ipkdb_init.0 comp-sys-catman .cat @@ -16970,6 +16971,7 @@ ./usr/share/man/html9/ioctl.html comp-sys-htmlman html ./usr/share/man/html9/ioctl_copyin.html comp-sys-htmlman html ./usr/share/man/html9/ioctl_copyout.html comp-sys-htmlman html +./usr/share/man/html9/ipi.html comp-sys-htmlman html ./usr/share/man/html9/ipkdb.html comp-sys-htmlman html ./usr/share/man/html9/ipkdb_connect.html comp-sys-htmlman html ./usr/share/man/html9/ipkdb_init.html comp-sys-htmlman html @@ -23820,6 +23822,7 @@ ./usr/share/man/man9/ioctl.9 comp-sys-man .man ./usr/share/man/man9/ioctl_copyin.9 comp-sys-man .man ./usr/share/man/man9/ioctl_copyout.9 comp-sys-man .man +./usr/share/man/man9/ipi.9 comp-sys-man .man ./usr/share/man/man9/ipkdb.9 comp-sys-man .man ./usr/share/man/man9/ipkdb_connect.9 comp-sys-man .man ./usr/share/man/man9/ipkdb_init.9 comp-sys-man .man Index: src/share/man/man9/Makefile diff -u src/share/man/man9/Makefile:1.377 src/share/man/man9/Makefile:1.378 --- src/share/man/man9/Makefile:1.377 Mon Mar 24 13:42:40 2014 +++ src/share/man/man9/Makefile Sun May 25 15:34:20 2014 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.377 2014/03/24 13:42:40 hannken Exp $ +# $NetBSD: Makefile,v 1.378 2014/05/25 15:34:20 rmind Exp $ # Makefile for section 9 (kernel function and variable) manual pages. @@ -25,7 +25,7 @@ MAN= accept_filter.9 accf_data.9 accf_ht ieee80211_node.9 ieee80211_output.9 ieee80211_proto.9 \ ieee80211_radiotap.9 iic.9 imax.9 \ in_getifa.9 \ - in4_cksum.9 inittodr.9 intro.9 ioasic.9 ioctl.9 ipkdb.9 isa.9 \ + in4_cksum.9 inittodr.9 intro.9 ioasic.9 ioctl.9 ipkdb.9 ipi.9 isa.9 \ isapnp.9 itimerfix.9 kauth.9 kcopy.9 kcpuset.9 kmem.9 \ kpause.9 \ kfilter_register.9 knote.9 \ Index: src/sys/kern/subr_ipi.c diff -u src/sys/kern/subr_ipi.c:1.1 src/sys/kern/subr_ipi.c:1.2 --- src/sys/kern/subr_ipi.c:1.1 Mon May 19 22:47:54 2014 +++ src/sys/kern/subr_ipi.c Sun May 25 15:34:19 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: subr_ipi.c,v 1.1 2014/05/19 22:47:54 rmind Exp $ */ +/* $NetBSD: subr_ipi.c,v 1.2 2014/05/25 15:34:19 rmind Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -30,11 +30,13 @@ */ /* - * Inter-processor interrupt (IPI) interface with cross-call support. + * Inter-processor interrupt (IPI) interface: asynchronous IPIs to + * invoke functions with a constant argument and synchronous IPIs + * with the cross-call support. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: subr_ipi.c,v 1.1 2014/05/19 22:47:54 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: subr_ipi.c,v 1.2 2014/05/25 15:34:19 rmind Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -46,10 +48,25 @@ __KERNEL_RCSID(0, "$NetBSD: subr_ipi.c,v #include <sys/kcpuset.h> #include <sys/kmem.h> #include <sys/lock.h> +#include <sys/mutex.h> + +/* + * An array of the IPI handlers used for asynchronous invocation. + * The lock protects the slot allocation. + */ + +typedef struct { + ipi_func_t func; + void * arg; +} ipi_intr_t; + +static kmutex_t ipi_mngmt_lock; +static ipi_intr_t ipi_intrs[IPI_MAXREG] __cacheline_aligned; /* * Per-CPU mailbox for IPI messages: it is a single cache line storing - * up to IPI_MSG_MAX messages. + * up to IPI_MSG_MAX messages. This interface is built on top of the + * synchronous IPIs. */ #define IPI_MSG_SLOTS (CACHE_LINE_SIZE / sizeof(ipi_msg_t *)) @@ -59,8 +76,14 @@ typedef struct { ipi_msg_t * msg[IPI_MSG_SLOTS]; } ipi_mbox_t; + +/* Mailboxes for the synchronous IPIs. */ static ipi_mbox_t * ipi_mboxes __read_mostly; static struct evcnt ipi_mboxfull_ev __cacheline_aligned; +static void ipi_msg_cpu_handler(void *); + +/* Handler for the synchronous IPIs - it must be zero. */ +#define IPI_SYNCH_ID 0 #ifndef MULTIPROCESSOR #define cpu_ipi(ci) KASSERT(ci == NULL) @@ -71,15 +94,100 @@ ipi_sysinit(void) { const size_t len = ncpu * sizeof(ipi_mbox_t); + /* Initialise the per-CPU bit fields. */ + for (u_int i = 0; i < ncpu; i++) { + struct cpu_info *ci = cpu_lookup(i); + memset(&ci->ci_ipipend, 0, sizeof(ci->ci_ipipend)); + } + mutex_init(&ipi_mngmt_lock, MUTEX_DEFAULT, IPL_NONE); + memset(ipi_intrs, 0, sizeof(ipi_intrs)); + /* Allocate per-CPU IPI mailboxes. */ ipi_mboxes = kmem_zalloc(len, KM_SLEEP); KASSERT(ipi_mboxes != NULL); + /* + * Register the handler for synchronous IPIs. This mechanism + * is built on top of the asynchronous interface. Slot zero is + * reserved permanently; it is also handy to use zero as a failure + * for other registers (as it is potentially less error-prone). + */ + ipi_intrs[IPI_SYNCH_ID].func = ipi_msg_cpu_handler; + evcnt_attach_dynamic(&ipi_mboxfull_ev, EVCNT_TYPE_MISC, NULL, "ipi", "full"); } /* + * ipi_register: register an asynchronous IPI handler. + * + * => Returns IPI ID which is greater than zero; on failure - zero. + */ +u_int +ipi_register(ipi_func_t func, void *arg) +{ + mutex_enter(&ipi_mngmt_lock); + for (u_int i = 0; i < IPI_MAXREG; i++) { + if (ipi_intrs[i].func == NULL) { + /* Register the function. */ + ipi_intrs[i].func = func; + ipi_intrs[i].arg = arg; + mutex_exit(&ipi_mngmt_lock); + + KASSERT(i != IPI_SYNCH_ID); + return i; + } + } + mutex_exit(&ipi_mngmt_lock); + printf("WARNING: ipi_register: table full, increase IPI_MAXREG\n"); + return 0; +} + +/* + * ipi_unregister: release the IPI handler given the ID. + */ +void +ipi_unregister(u_int ipi_id) +{ + ipi_msg_t ipimsg = { .func = (ipi_func_t)nullop }; + + KASSERT(ipi_id != IPI_SYNCH_ID); + KASSERT(ipi_id < IPI_MAXREG); + + /* Release the slot. */ + mutex_enter(&ipi_mngmt_lock); + KASSERT(ipi_intrs[ipi_id].func != NULL); + ipi_intrs[ipi_id].func = NULL; + + /* Ensure that there are no IPIs in flight. */ + kpreempt_disable(); + ipi_broadcast(&ipimsg); + ipi_wait(&ipimsg); + kpreempt_enable(); + mutex_exit(&ipi_mngmt_lock); +} + +/* + * ipi_trigger: asynchronously send an IPI to the specified CPU. + */ +void +ipi_trigger(u_int ipi_id, struct cpu_info *ci) +{ + const u_int i = ipi_id >> IPI_BITW_SHIFT; + const uint32_t bitm = 1U << (ipi_id & IPI_BITW_MASK); + + KASSERT(ipi_id < IPI_MAXREG); + KASSERT(kpreempt_disabled()); + KASSERT(curcpu() != ci); + + /* Mark as pending and send an IPI. */ + if (membar_consumer(), (ci->ci_ipipend[i] & bitm) == 0) { + atomic_or_32(&ci->ci_ipipend[i], bitm); + cpu_ipi(ci); + } +} + +/* * put_msg: insert message into the mailbox. */ static inline void @@ -106,11 +214,43 @@ again: void ipi_cpu_handler(void) { + struct cpu_info * const ci = curcpu(); + + /* + * Handle asynchronous IPIs: inspect per-CPU bit field, extract + * IPI ID numbers and execute functions in those slots. + */ + for (u_int i = 0; i < IPI_BITWORDS; i++) { + uint32_t pending, bit; + + if (ci->ci_ipipend[i] == 0) { + continue; + } + pending = atomic_swap_32(&ci->ci_ipipend[i], 0); +#ifndef __HAVE_ATOMIC_AS_MEMBAR + membar_producer(); +#endif + while ((bit = ffs(pending)) != 0) { + const u_int ipi_id = (i << IPI_BITW_SHIFT) | --bit; + ipi_intr_t *ipi_hdl = &ipi_intrs[ipi_id]; + + pending &= ~(1U << bit); + KASSERT(ipi_hdl->func != NULL); + ipi_hdl->func(ipi_hdl->arg); + } + } +} + +/* + * ipi_msg_cpu_handler: handle synchronous IPIs - iterate mailbox, + * execute the passed functions and acknowledge the messages. + */ +static void +ipi_msg_cpu_handler(void *arg __unused) +{ const struct cpu_info * const ci = curcpu(); ipi_mbox_t *mbox = &ipi_mboxes[cpu_index(ci)]; - KASSERT(curcpu() == ci); - for (u_int i = 0; i < IPI_MSG_MAX; i++) { ipi_msg_t *msg; @@ -148,7 +288,7 @@ ipi_unicast(ipi_msg_t *msg, struct cpu_i membar_producer(); put_msg(&ipi_mboxes[id], msg); - cpu_ipi(ci); + ipi_trigger(IPI_SYNCH_ID, ci); } /* @@ -182,7 +322,7 @@ ipi_multicast(ipi_msg_t *msg, const kcpu continue; } put_msg(&ipi_mboxes[id], msg); - cpu_ipi(ci); + ipi_trigger(IPI_SYNCH_ID, ci); } if (local) { msg->func(msg->arg); @@ -216,8 +356,8 @@ ipi_broadcast(ipi_msg_t *msg) } id = cpu_index(ci); put_msg(&ipi_mboxes[id], msg); + ipi_trigger(IPI_SYNCH_ID, ci); } - cpu_ipi(NULL); /* Finally, execute locally. */ msg->func(msg->arg); Index: src/sys/sys/cpu_data.h diff -u src/sys/sys/cpu_data.h:1.36 src/sys/sys/cpu_data.h:1.37 --- src/sys/sys/cpu_data.h:1.36 Mon Nov 25 03:02:30 2013 +++ src/sys/sys/cpu_data.h Sun May 25 15:34:19 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: cpu_data.h,v 1.36 2013/11/25 03:02:30 christos Exp $ */ +/* $NetBSD: cpu_data.h,v 1.37 2014/05/25 15:34:19 rmind Exp $ */ /*- * Copyright (c) 2004, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -43,6 +43,7 @@ struct lwp; #include <sys/percpu_types.h> #include <sys/queue.h> #include <sys/kcpuset.h> +#include <sys/ipi.h> /* * MI per-cpu data @@ -71,6 +72,7 @@ struct cpu_data { kcondvar_t cpu_xcall; /* cross-call support */ int cpu_xcall_pending; /* cross-call support */ lwp_t *cpu_onproc; /* bottom level LWP */ + uint32_t cpu_ipipend[IPI_BITWORDS]; /* pending IPIs */ cpuid_t cpu_package_id; cpuid_t cpu_core_id; @@ -123,6 +125,7 @@ struct cpu_data { #define ci_lkdebug_recurse ci_data.cpu_lkdebug_recurse #define ci_pcu_curlwp ci_data.cpu_pcu_curlwp #define ci_kcpuset ci_data.cpu_kcpuset +#define ci_ipipend ci_data.cpu_ipipend #define ci_package_id ci_data.cpu_package_id #define ci_core_id ci_data.cpu_core_id Index: src/sys/sys/ipi.h diff -u src/sys/sys/ipi.h:1.1 src/sys/sys/ipi.h:1.2 --- src/sys/sys/ipi.h:1.1 Mon May 19 22:47:54 2014 +++ src/sys/sys/ipi.h Sun May 25 15:34:19 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: ipi.h,v 1.1 2014/05/19 22:47:54 rmind Exp $ */ +/* $NetBSD: ipi.h,v 1.2 2014/05/25 15:34:19 rmind Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -47,13 +47,28 @@ typedef struct { volatile u_int _pending; } ipi_msg_t; +/* + * Internal constants and implementation hooks. + * + * IPI_MAXREG: the maximum number of asynchronous handlers which can + * be registered on the system (normally, this should not be high). + */ +#define IPI_MAXREG 32 + +#define IPI_BITW_SHIFT 5 +#define IPI_BITW_MASK (32 - 1) +#define IPI_BITWORDS (IPI_MAXREG >> IPI_BITW_SHIFT) + void ipi_sysinit(void); void ipi_cpu_handler(void); void cpu_ipi(struct cpu_info *); -/* - * Public ipi(9) API. - */ +/* Public interface: asynchronous IPIs. */ +u_int ipi_register(ipi_func_t, void *); +void ipi_unregister(u_int); +void ipi_trigger(u_int, struct cpu_info *); + +/* Public interface: synchronous IPIs. */ void ipi_unicast(ipi_msg_t *, struct cpu_info *); void ipi_multicast(ipi_msg_t *, const kcpuset_t *); void ipi_broadcast(ipi_msg_t *); Added files: Index: src/share/man/man9/ipi.9 diff -u /dev/null src/share/man/man9/ipi.9:1.1 --- /dev/null Sun May 25 15:34:20 2014 +++ src/share/man/man9/ipi.9 Sun May 25 15:34:20 2014 @@ -0,0 +1,177 @@ +.\" $NetBSD: ipi.9,v 1.1 2014/05/25 15:34:20 rmind Exp $ +.\" +.\" Copyright (c) 2014 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Mindaugas Rasiukevicius. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd May 25, 2014 +.Dt IPI 9 +.Os +.Sh NAME +.Nm ipi +.Nd MI IPI interface +.Sh SYNOPSIS +.In sys/ipi.h +.Vt typedef void (*ipi_func_t)(void *); +.\" ----- +.Ft u_int +.Fn ipi_register "ipi_func_t func" "void *arg" +.Ft void +.Fn ipi_unregister "u_int ipi_id" +.Ft void +.Fn ipi_trigger "u_int ipi_id" "struct cpu_info *ci" +.\" ----- +.Ft void +.Fn ipi_unicast "ipi_msg_t *msg" "struct cpu_info *ci" +.Ft void +.Fn ipi_multicast "ipi_msg_t *msg" "const kcpuset_t *target" +.Ft void +.Fn ipi_broadcast "ipi_msg_t *msg" +.Ft void +.Fn ipi_wait "ipi_msg_t *msg" +.\" ----- +.Sh DESCRIPTION +The machine-independent +.Nm +interface provides capability to send inter-processor interrupts (IPIs) +amongst CPUs. +The interface has two mechanisms: asynchronous IPI to invoke functions +with a constant argument and synchronous IPIs with the cross-call support. +.Pp +Other synchronization interfaces are built using the MI IPI interface. +For a general purpose inter-processor cross-calls or remote interrupts, use +.Xr xcall 9 +or +.Xr softint 9 +interfaces. +.Pp +The primary use cases of the MI IPIs include the following: +.Bl -hyphen -compact +.It +provide a facility for +.Xr softint 9 +subsystem to schedule software interrupts on the remote CPUs +.It +provide a facility for +.Xr xcall 9 +subsystem +.It +abstract IPI handling and facilitate the machine-dependent code +.El +.Pp +.\" ----- +.Ss Asynchronous IPI interface +This interface allows dynamic registration of IPI handlers with a constant +argument and asynchronous triggering of the interrupts. +.Bl -tag -width compact +.It Fn ipi_register "func" "arg" +Register an IPI handler +.Fa func +with an arbitrary argument +.Fa arg . +Returns non-zero IPI identifier on success and zero on failure. +.It Fn ipi_unregister "ipi_id" +Unregister the IPI handler identified by the +.Fa ipi_id . +.It Fn ipi_trigger "ipi_id" "ci" +Trigger an IPI identified by +.Fa ipi_id +on a remote CPU specified by +.Fa ci . +This function must be called with the kernel preemption disabled and +the target CPU must be remote. +.El +.Pp +.\" ----- +.Ss Synchronous IPI interface +This interface provides capability to perform cross-calls, i.e. invoke +an arbitrary function on a remote CPU. +The invocations are performed synchronously and the caller must wait +for completion. +The cross-call is described by an IPI "message". +The caller has to fill +.Vt ipi_msg_t +structure which has the following public members: +.Bd -literal + ipi_func_t func; + void arg; +.Ed +.Pp +The +.Ar func +member specifies a function to invoke and +.Ar arg +is the argument to be passed to the function. +.Pp +.Bl -tag -width compact +.It Fn ipi_unicast "msg" "ci" +Send an IPI to a remote CPU specified by +.Fa ci . +.It Fn ipi_multicast "msg" "target" +Send IPIs to a CPU set specified by the +.Fa target . +.It Fn ipi_broadcast "msg" +Send IPIs to all CPUs. +.It Fn ipi_wait "msg" +Wait until all IPIs complete. +.El +.Pp +All described functions, except +.Fn ipi_wait , +must be called with the kernel preemption disabled. +All synchronous IPI invocations must be awaited for completion with +.Fn ipi_wait +function, before IPI message structure can be destroyed or new +cross-call requests performed. +.\" ----- +.Sh NOTES +Functions being called must be lightweight. +They run at +.Em IPL_HIGH +and should generally not use any other synchronization interfaces, +such as +.Xr mutex 9 . +If spin-locks are used, they must be used carefully and have no contention. +.\" ----- +.Sh CODE REFERENCES +The +.Nm +interface is implemented within the file +.Pa sys/kern/subr_ipi.c . +.\" ----- +.Sh SEE ALSO +.Xr softint 9 , +.Xr spl 9 , +.Xr kcpuset 9 , +.Xr kpreempt 9 , +.Xr xcall 9 +.Sh HISTORY +The +.Nm +interface first appeared in +.Nx 7.0 . +.Sh AUTHORS +.An Mindaugas Rasiukevicius Aq Mt rm...@netbsd.org