Index: sys/xcall.h =================================================================== RCS file: sys/xcall.h diff -N sys/xcall.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/xcall.h 4 Jul 2025 09:05:37 -0000 @@ -0,0 +1,86 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2025 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * CPU crosscall API + * + * Work to execute on a CPU is wrapped in a struct xcall, which is + * like a task or timeout. Each CPU (in an MP kernel) has an array + * of pointers to xcall structs. The local CPU uses CAS ops to try + * and swap their xcall onto one of the slots on the remote CPU, + * and will spin until space becomes available. Once the xcall has + * been added, an IPI is sent to the remote CPU to kick of processing + * of the array. The xcall IPI handler defers processing of the + * xcall array to a low IPL level using a softintr handler. + * + * To implement this API on an architecture requires the following: + * + * 1. A device has to depend on the xcall attribute to have the + * xcall code included in the kernel build. e.g., on amd64 the cpu + * device depends on the xcall attribute, as defined in + * src/sys/arch/amd64/conf/files.amd64: + * + * device cpu: xcall + * + * The rest of the changes are only necessary for MULTIPROCESSOR builds: + * + * 2. struct cpu_info has to have a `struct xcall_cpu ci_xcall` member. + * + * 3. cpu_xcall_establish has to be called against each cpu_info struct. + * + * 4. cpu_xcall_ipi has to be provided by machine/intr.h. + * + * 5. The MD xcall IPI handler has to call cpu_xcall_dispatch. + */ + +#ifndef _SYS_XCALL_H +#define _SYS_XCALL_H + +struct xcall { + void (*xc_func)(void *); + void *xc_arg; +}; + +#define XCALL_CPU_SLOTS 4 + +/* MD code adds this to struct cpu_info as ci_xcall */ +struct xcall_cpu { + struct xcall *xci_xcalls[XCALL_CPU_SLOTS]; + void *xci_softintr; +}; + +#ifdef _KERNEL +#define XCALL_INITIALIZER(_f, _a) { \ + .xc_func = _f, \ + .xc_arg = _a, \ +} + +void cpu_xcall_init(struct xcall *, void (*)(void *), void *); +void cpu_xcall(struct cpu_info *, struct xcall *); + +void cpu_xcall_sync(struct cpu_info *, void (*)(void *), void *, + const char *); + +/* MD cpu setup calls this */ +void cpu_xcall_establish(struct cpu_info *); +/* MD xcall ipi handler calls this */ +void cpu_xcall_dispatch(struct cpu_info *); + +#endif /* _KERNEL */ + +#endif /* _SYS_XCALL_H */ Index: kern/kern_xcall.c =================================================================== RCS file: kern/kern_xcall.c diff -N kern/kern_xcall.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ kern/kern_xcall.c 4 Jul 2025 09:05:37 -0000 @@ -0,0 +1,168 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2025 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include + +#include + +#define IPL_XCALL IPL_SOFTCLOCK + +void +cpu_xcall_init(struct xcall *xc, void (*func)(void *), void *arg) +{ + xc->xc_func = func; + xc->xc_arg = arg; +} + +static inline void +cpu_xcall_self(struct xcall *xc) +{ + int s; + + s = splraise(IPL_XCALL); + xc->xc_func(xc->xc_arg); + splx(s); +} + +#ifdef MULTIPROCESSOR + +void +cpu_xcall(struct cpu_info *ci, struct xcall *xc) +{ + struct xcall_cpu *xci = &ci->ci_xcall; + size_t i; + + if (ci == curcpu()) { + /* execute the task immediately on the local cpu */ + cpu_xcall_self(xc); + return; + } + + for (;;) { + for (i = 0; i < nitems(xci->xci_xcalls); i++) { + if (atomic_cas_ptr(&xci->xci_xcalls[i], + NULL, xc) != NULL) + continue; + + cpu_xcall_ipi(ci); + return; + } + + CPU_BUSY_CYCLE(); + } +} + +struct xcall_wrapper { + struct xcall w_xc; + struct cond w_c; +}; + +static void +cpu_xcall_done(void *arg) +{ + struct xcall_wrapper *w = arg; + struct xcall *xc = &w->w_xc; + + xc->xc_func(xc->xc_arg); + + cond_signal(&w->w_c); +} + +void +cpu_xcall_sync(struct cpu_info *ci, void (*func)(void *), void *arg, + const char *wmesg) +{ + struct xcall_wrapper w = { + .w_xc = XCALL_INITIALIZER(func, arg), + .w_c = COND_INITIALIZER(), + }; + struct xcall xc = XCALL_INITIALIZER(cpu_xcall_done, &w); + + cpu_xcall(ci, &xc); + + cond_wait(&w.w_c, wmesg); +} + +/* + * This is the glue between the MD IPI code and the calling of xcalls + */ +static void +cpu_xcall_handler(void *arg) +{ + struct xcall_cpu *xci = arg; + struct xcall *xc; + size_t i; + + for (i = 0; i < nitems(xci->xci_xcalls); i++) { + xc = xci->xci_xcalls[i]; + if (xc != NULL) { + xci->xci_xcalls[i] = NULL; + (*xc->xc_func)(xc->xc_arg); + } + } +} + +void +cpu_xcall_establish(struct cpu_info *ci) +{ + struct xcall_cpu *xci = &ci->ci_xcall; + size_t i; + + for (i = 0; i < nitems(xci->xci_xcalls); i++) + xci->xci_xcalls[i] = NULL; + + xci->xci_softintr = softintr_establish(IPL_XCALL | IPL_MPSAFE, + cpu_xcall_handler, xci); +} + +void +cpu_xcall_dispatch(struct cpu_info *ci) +{ + struct xcall_cpu *xci = &ci->ci_xcall; + + softintr_schedule(xci->xci_softintr); +} + +#else /* MULTIPROCESSOR */ + +/* + * uniprocessor implementation + */ + +void +cpu_xcall(struct cpu_info *ci, struct xcall *xc) +{ + cpu_xcall_self(xc); +} + +void +cpu_xcall_sync(struct cpu_info *ci, void (*func)(void *), void *arg, + const char *wmesg) +{ + int s; + + s = splraise(IPL_XCALL); + func(arg); + splx(s); +} +#endif /* MULTIPROCESSOR */