subr_smp.c revision 126763
1/*
2 * Copyright (c) 2001
3 *	John Baldwin <jhb@FreeBSD.org>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the author nor the names of any co-contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY JOHN BALDWIN AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL JOHN BALDWIN OR THE VOICES IN HIS HEAD
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/*
31 * This module holds the global variables and machine independent functions
32 * used for the kernel SMP support.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/sys/kern/subr_smp.c 126763 2004-03-09 03:37:21Z njl $");
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/kernel.h>
41#include <sys/ktr.h>
42#include <sys/proc.h>
43#include <sys/bus.h>
44#include <sys/lock.h>
45#include <sys/mutex.h>
46#include <sys/pcpu.h>
47#include <sys/smp.h>
48#include <sys/sysctl.h>
49
50#include <machine/smp.h>
51
52#ifdef SMP
53volatile u_int stopped_cpus;
54volatile u_int started_cpus;
55
56void (*cpustop_restartfunc)(void);
57#endif
58
59int mp_ncpus;
60/* export this for libkvm consumers. */
61int mp_maxcpus = MAXCPU;
62
63struct cpu_top *smp_topology;
64volatile int smp_started;
65u_int all_cpus;
66u_int mp_maxid;
67
68SYSCTL_NODE(_kern, OID_AUTO, smp, CTLFLAG_RD, NULL, "Kernel SMP");
69
70SYSCTL_INT(_kern_smp, OID_AUTO, maxcpus, CTLFLAG_RD, &mp_maxcpus, 0,
71    "Max number of CPUs that the system was compiled for.");
72
73int smp_active = 0;	/* are the APs allowed to run? */
74SYSCTL_INT(_kern_smp, OID_AUTO, active, CTLFLAG_RW, &smp_active, 0,
75    "Number of Auxillary Processors (APs) that were successfully started");
76
77int smp_disabled = 0;	/* has smp been disabled? */
78SYSCTL_INT(_kern_smp, OID_AUTO, disabled, CTLFLAG_RDTUN, &smp_disabled, 0,
79    "SMP has been disabled from the loader");
80TUNABLE_INT("kern.smp.disabled", &smp_disabled);
81
82int smp_cpus = 1;	/* how many cpu's running */
83SYSCTL_INT(_kern_smp, OID_AUTO, cpus, CTLFLAG_RD, &smp_cpus, 0,
84    "Number of CPUs online");
85
86static void	cpu_identify(driver_t *driver, device_t parent);
87static device_t	cpu_add_child(device_t bus, int order, const char *name,
88			int unit);
89
90static device_method_t cpu_methods[] = {
91	/* Device interface */
92	DEVMETHOD(device_identify,	cpu_identify),
93	DEVMETHOD(device_probe,		bus_generic_probe),
94	DEVMETHOD(device_attach,	bus_generic_attach),
95	DEVMETHOD(device_detach,	bus_generic_detach),
96	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
97	DEVMETHOD(device_suspend,	bus_generic_suspend),
98	DEVMETHOD(device_resume,	bus_generic_resume),
99
100	/* Bus interface */
101	DEVMETHOD(bus_print_child,	bus_generic_print_child),
102	DEVMETHOD(bus_add_child,	cpu_add_child),
103	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
104	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
105	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
106	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
107	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
108	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
109#ifdef notyet
110	DEVMETHOD(bus_set_resource,	bus_generic_set_resource),
111	DEVMETHOD(bus_get_resource,	bus_generic_get_resource),
112	DEVMETHOD(bus_delete_resource,	bus_generic_delete_resource),
113#endif
114
115	{ 0, 0 }
116};
117
118static driver_t cpu_driver = {
119	"cpu",
120	cpu_methods,
121	1,		/* no softc */
122};
123static devclass_t cpu_devclass;
124DRIVER_MODULE(cpu, nexus, cpu_driver, cpu_devclass, 0, 0);
125
126#ifdef SMP
127/* Enable forwarding of a signal to a process running on a different CPU */
128static int forward_signal_enabled = 1;
129SYSCTL_INT(_kern_smp, OID_AUTO, forward_signal_enabled, CTLFLAG_RW,
130	   &forward_signal_enabled, 0,
131	   "Forwarding of a signal to a process on a different CPU");
132
133/* Enable forwarding of roundrobin to all other cpus */
134static int forward_roundrobin_enabled = 1;
135SYSCTL_INT(_kern_smp, OID_AUTO, forward_roundrobin_enabled, CTLFLAG_RW,
136	   &forward_roundrobin_enabled, 0,
137	   "Forwarding of roundrobin to all other CPUs");
138
139/* Variables needed for SMP rendezvous. */
140static void (*smp_rv_setup_func)(void *arg);
141static void (*smp_rv_action_func)(void *arg);
142static void (*smp_rv_teardown_func)(void *arg);
143static void *smp_rv_func_arg;
144static volatile int smp_rv_waiters[2];
145static struct mtx smp_rv_mtx;
146
147/*
148 * Let the MD SMP code initialize mp_maxid very early if it can.
149 */
150static void
151mp_setmaxid(void *dummy)
152{
153	cpu_mp_setmaxid();
154}
155SYSINIT(cpu_mp_setmaxid, SI_SUB_TUNABLES, SI_ORDER_FIRST, mp_setmaxid, NULL)
156
157/*
158 * Call the MD SMP initialization code.
159 */
160static void
161mp_start(void *dummy)
162{
163
164	/* Probe for MP hardware. */
165	if (smp_disabled != 0 || cpu_mp_probe() == 0) {
166		mp_ncpus = 1;
167		all_cpus = PCPU_GET(cpumask);
168		return;
169	}
170
171	mtx_init(&smp_rv_mtx, "smp rendezvous", NULL, MTX_SPIN);
172	cpu_mp_start();
173	printf("FreeBSD/SMP: Multiprocessor System Detected: %d CPUs\n",
174	    mp_ncpus);
175	cpu_mp_announce();
176}
177SYSINIT(cpu_mp, SI_SUB_CPU, SI_ORDER_SECOND, mp_start, NULL)
178
179void
180forward_signal(struct thread *td)
181{
182	int id;
183
184	/*
185	 * signotify() has already set TDF_ASTPENDING and TDF_NEEDSIGCHECK on
186	 * this thread, so all we need to do is poke it if it is currently
187	 * executing so that it executes ast().
188	 */
189	mtx_assert(&sched_lock, MA_OWNED);
190	KASSERT(TD_IS_RUNNING(td),
191	    ("forward_signal: thread is not TDS_RUNNING"));
192
193	CTR1(KTR_SMP, "forward_signal(%p)", td->td_proc);
194
195	if (!smp_started || cold || panicstr)
196		return;
197	if (!forward_signal_enabled)
198		return;
199
200	/* No need to IPI ourself. */
201	if (td == curthread)
202		return;
203
204	id = td->td_oncpu;
205	if (id == NOCPU)
206		return;
207	ipi_selected(1 << id, IPI_AST);
208}
209
210void
211forward_roundrobin(void)
212{
213	struct pcpu *pc;
214	struct thread *td;
215	u_int id, map;
216
217	mtx_assert(&sched_lock, MA_OWNED);
218
219	CTR0(KTR_SMP, "forward_roundrobin()");
220
221	if (!smp_started || cold || panicstr)
222		return;
223	if (!forward_roundrobin_enabled)
224		return;
225	map = 0;
226	SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
227		td = pc->pc_curthread;
228		id = pc->pc_cpumask;
229		if (id != PCPU_GET(cpumask) && (id & stopped_cpus) == 0 &&
230		    td != pc->pc_idlethread) {
231			td->td_flags |= TDF_NEEDRESCHED;
232			map |= id;
233		}
234	}
235	ipi_selected(map, IPI_AST);
236}
237
238/*
239 * When called the executing CPU will send an IPI to all other CPUs
240 *  requesting that they halt execution.
241 *
242 * Usually (but not necessarily) called with 'other_cpus' as its arg.
243 *
244 *  - Signals all CPUs in map to stop.
245 *  - Waits for each to stop.
246 *
247 * Returns:
248 *  -1: error
249 *   0: NA
250 *   1: ok
251 *
252 * XXX FIXME: this is not MP-safe, needs a lock to prevent multiple CPUs
253 *            from executing at same time.
254 */
255int
256stop_cpus(u_int map)
257{
258	int i;
259
260	if (!smp_started)
261		return 0;
262
263	CTR1(KTR_SMP, "stop_cpus(%x)", map);
264
265	/* send the stop IPI to all CPUs in map */
266	ipi_selected(map, IPI_STOP);
267
268	i = 0;
269	while ((atomic_load_acq_int(&stopped_cpus) & map) != map) {
270		/* spin */
271		i++;
272#ifdef DIAGNOSTIC
273		if (i == 100000) {
274			printf("timeout stopping cpus\n");
275			break;
276		}
277#endif
278	}
279
280	return 1;
281}
282
283
284/*
285 * Called by a CPU to restart stopped CPUs.
286 *
287 * Usually (but not necessarily) called with 'stopped_cpus' as its arg.
288 *
289 *  - Signals all CPUs in map to restart.
290 *  - Waits for each to restart.
291 *
292 * Returns:
293 *  -1: error
294 *   0: NA
295 *   1: ok
296 */
297int
298restart_cpus(u_int map)
299{
300
301	if (!smp_started)
302		return 0;
303
304	CTR1(KTR_SMP, "restart_cpus(%x)", map);
305
306	/* signal other cpus to restart */
307	atomic_store_rel_int(&started_cpus, map);
308
309	/* wait for each to clear its bit */
310	while ((atomic_load_acq_int(&stopped_cpus) & map) != 0)
311		;	/* nothing */
312
313	return 1;
314}
315
316/*
317 * All-CPU rendezvous.  CPUs are signalled, all execute the setup function
318 * (if specified), rendezvous, execute the action function (if specified),
319 * rendezvous again, execute the teardown function (if specified), and then
320 * resume.
321 *
322 * Note that the supplied external functions _must_ be reentrant and aware
323 * that they are running in parallel and in an unknown lock context.
324 */
325void
326smp_rendezvous_action(void)
327{
328
329	/* setup function */
330	if (smp_rv_setup_func != NULL)
331		smp_rv_setup_func(smp_rv_func_arg);
332	/* spin on entry rendezvous */
333	atomic_add_int(&smp_rv_waiters[0], 1);
334	while (atomic_load_acq_int(&smp_rv_waiters[0]) < mp_ncpus)
335		;	/* nothing */
336	/* action function */
337	if (smp_rv_action_func != NULL)
338		smp_rv_action_func(smp_rv_func_arg);
339	/* spin on exit rendezvous */
340	atomic_add_int(&smp_rv_waiters[1], 1);
341	while (atomic_load_acq_int(&smp_rv_waiters[1]) < mp_ncpus)
342		;	/* nothing */
343	/* teardown function */
344	if (smp_rv_teardown_func != NULL)
345		smp_rv_teardown_func(smp_rv_func_arg);
346}
347
348void
349smp_rendezvous(void (* setup_func)(void *),
350	       void (* action_func)(void *),
351	       void (* teardown_func)(void *),
352	       void *arg)
353{
354
355	if (!smp_started) {
356		if (setup_func != NULL)
357			setup_func(arg);
358		if (action_func != NULL)
359			action_func(arg);
360		if (teardown_func != NULL)
361			teardown_func(arg);
362		return;
363	}
364
365	/* obtain rendezvous lock */
366	mtx_lock_spin(&smp_rv_mtx);
367
368	/* set static function pointers */
369	smp_rv_setup_func = setup_func;
370	smp_rv_action_func = action_func;
371	smp_rv_teardown_func = teardown_func;
372	smp_rv_func_arg = arg;
373	smp_rv_waiters[0] = 0;
374	smp_rv_waiters[1] = 0;
375
376	/* signal other processors, which will enter the IPI with interrupts off */
377	ipi_all_but_self(IPI_RENDEZVOUS);
378
379	/* call executor function */
380	smp_rendezvous_action();
381
382	/* release lock */
383	mtx_unlock_spin(&smp_rv_mtx);
384}
385#else /* !SMP */
386
387/*
388 * Provide dummy SMP support for UP kernels.  Modules that need to use SMP
389 * APIs will still work using this dummy support.
390 */
391static void
392mp_setvariables_for_up(void *dummy)
393{
394	mp_ncpus = 1;
395	mp_maxid = PCPU_GET(cpuid);
396	all_cpus = PCPU_GET(cpumask);
397	KASSERT(PCPU_GET(cpuid) == 0, ("UP must have a CPU ID of zero"));
398}
399SYSINIT(cpu_mp_setvariables, SI_SUB_TUNABLES, SI_ORDER_FIRST,
400    mp_setvariables_for_up, NULL)
401
402void
403smp_rendezvous(void (* setup_func)(void *),
404	       void (* action_func)(void *),
405	       void (* teardown_func)(void *),
406	       void *arg)
407{
408
409	if (setup_func != NULL)
410		setup_func(arg);
411	if (action_func != NULL)
412		action_func(arg);
413	if (teardown_func != NULL)
414		teardown_func(arg);
415}
416#endif /* SMP */
417
418static void
419cpu_identify(driver_t *driver, device_t parent)
420{
421	struct pcpu *pc;
422	int i;
423
424	/* Protect against multiple scans of the bus. */
425	if (!cold || device_find_child(parent, "cpu", 0) != NULL)
426		return;
427
428	for (i = 0; i <= mp_maxid; i++)
429		if (!CPU_ABSENT(i)) {
430			pc = pcpu_find(i);
431			KASSERT(pc != NULL, ("pcpu_find failed"));
432			pc->pc_device = BUS_ADD_CHILD(parent, 0, "cpu", i);
433			if (pc->pc_device == NULL)
434				panic("failed adding cpu child");
435		}
436}
437
438static device_t
439cpu_add_child(device_t bus, int order, const char *name, int unit)
440{
441	return (device_add_child_ordered(bus, order, name, unit));
442}
443