subr_smp.c revision 134227
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 134227 2004-08-23 21:39:29Z peter $");
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 cpumask_t stopped_cpus;
54volatile cpumask_t 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;
65cpumask_t 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
86#ifdef SMP
87/* Enable forwarding of a signal to a process running on a different CPU */
88static int forward_signal_enabled = 1;
89SYSCTL_INT(_kern_smp, OID_AUTO, forward_signal_enabled, CTLFLAG_RW,
90	   &forward_signal_enabled, 0,
91	   "Forwarding of a signal to a process on a different CPU");
92
93/* Enable forwarding of roundrobin to all other cpus */
94static int forward_roundrobin_enabled = 1;
95SYSCTL_INT(_kern_smp, OID_AUTO, forward_roundrobin_enabled, CTLFLAG_RW,
96	   &forward_roundrobin_enabled, 0,
97	   "Forwarding of roundrobin to all other CPUs");
98
99/* Variables needed for SMP rendezvous. */
100static void (*smp_rv_setup_func)(void *arg);
101static void (*smp_rv_action_func)(void *arg);
102static void (*smp_rv_teardown_func)(void *arg);
103static void *smp_rv_func_arg;
104static volatile int smp_rv_waiters[2];
105
106/*
107 * Shared mutex to restrict busywaits between smp_rendezvous() and
108 * smp(_targeted)_tlb_shootdown().  A deadlock occurs if both of these
109 * functions trigger at once and cause multiple CPUs to busywait with
110 * interrupts disabled.
111 */
112struct mtx smp_rv_mtx;
113
114/*
115 * Let the MD SMP code initialize mp_maxid very early if it can.
116 */
117static void
118mp_setmaxid(void *dummy)
119{
120	cpu_mp_setmaxid();
121}
122SYSINIT(cpu_mp_setmaxid, SI_SUB_TUNABLES, SI_ORDER_FIRST, mp_setmaxid, NULL)
123
124/*
125 * Call the MD SMP initialization code.
126 */
127static void
128mp_start(void *dummy)
129{
130
131	/* Probe for MP hardware. */
132	if (smp_disabled != 0 || cpu_mp_probe() == 0) {
133		mp_ncpus = 1;
134		all_cpus = PCPU_GET(cpumask);
135		return;
136	}
137
138	mtx_init(&smp_rv_mtx, "smp rendezvous", NULL, MTX_SPIN);
139	cpu_mp_start();
140	printf("FreeBSD/SMP: Multiprocessor System Detected: %d CPUs\n",
141	    mp_ncpus);
142	cpu_mp_announce();
143}
144SYSINIT(cpu_mp, SI_SUB_CPU, SI_ORDER_SECOND, mp_start, NULL)
145
146void
147forward_signal(struct thread *td)
148{
149	int id;
150
151	/*
152	 * signotify() has already set TDF_ASTPENDING and TDF_NEEDSIGCHECK on
153	 * this thread, so all we need to do is poke it if it is currently
154	 * executing so that it executes ast().
155	 */
156	mtx_assert(&sched_lock, MA_OWNED);
157	KASSERT(TD_IS_RUNNING(td),
158	    ("forward_signal: thread is not TDS_RUNNING"));
159
160	CTR1(KTR_SMP, "forward_signal(%p)", td->td_proc);
161
162	if (!smp_started || cold || panicstr)
163		return;
164	if (!forward_signal_enabled)
165		return;
166
167	/* No need to IPI ourself. */
168	if (td == curthread)
169		return;
170
171	id = td->td_oncpu;
172	if (id == NOCPU)
173		return;
174	ipi_selected(1 << id, IPI_AST);
175}
176
177void
178forward_roundrobin(void)
179{
180	struct pcpu *pc;
181	struct thread *td;
182	cpumask_t id, map, me;
183
184	mtx_assert(&sched_lock, MA_OWNED);
185
186	CTR0(KTR_SMP, "forward_roundrobin()");
187
188	if (!smp_started || cold || panicstr)
189		return;
190	if (!forward_roundrobin_enabled)
191		return;
192	map = 0;
193	me = PCPU_GET(cpumask);
194	SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
195		td = pc->pc_curthread;
196		id = pc->pc_cpumask;
197		if (id != me && (id & stopped_cpus) == 0 &&
198		    td != pc->pc_idlethread) {
199			td->td_flags |= TDF_NEEDRESCHED;
200			map |= id;
201		}
202	}
203	ipi_selected(map, IPI_AST);
204}
205
206/*
207 * When called the executing CPU will send an IPI to all other CPUs
208 *  requesting that they halt execution.
209 *
210 * Usually (but not necessarily) called with 'other_cpus' as its arg.
211 *
212 *  - Signals all CPUs in map to stop.
213 *  - Waits for each to stop.
214 *
215 * Returns:
216 *  -1: error
217 *   0: NA
218 *   1: ok
219 *
220 * XXX FIXME: this is not MP-safe, needs a lock to prevent multiple CPUs
221 *            from executing at same time.
222 */
223int
224stop_cpus(cpumask_t map)
225{
226	int i;
227
228	if (!smp_started)
229		return 0;
230
231	CTR1(KTR_SMP, "stop_cpus(%x)", map);
232
233	/* send the stop IPI to all CPUs in map */
234	ipi_selected(map, IPI_STOP);
235
236	i = 0;
237	while ((atomic_load_acq_int(&stopped_cpus) & map) != map) {
238		/* spin */
239		i++;
240#ifdef DIAGNOSTIC
241		if (i == 100000) {
242			printf("timeout stopping cpus\n");
243			break;
244		}
245#endif
246	}
247
248	return 1;
249}
250
251
252/*
253 * Called by a CPU to restart stopped CPUs.
254 *
255 * Usually (but not necessarily) called with 'stopped_cpus' as its arg.
256 *
257 *  - Signals all CPUs in map to restart.
258 *  - Waits for each to restart.
259 *
260 * Returns:
261 *  -1: error
262 *   0: NA
263 *   1: ok
264 */
265int
266restart_cpus(cpumask_t map)
267{
268
269	if (!smp_started)
270		return 0;
271
272	CTR1(KTR_SMP, "restart_cpus(%x)", map);
273
274	/* signal other cpus to restart */
275	atomic_store_rel_int(&started_cpus, map);
276
277	/* wait for each to clear its bit */
278	while ((atomic_load_acq_int(&stopped_cpus) & map) != 0)
279		;	/* nothing */
280
281	return 1;
282}
283
284/*
285 * All-CPU rendezvous.  CPUs are signalled, all execute the setup function
286 * (if specified), rendezvous, execute the action function (if specified),
287 * rendezvous again, execute the teardown function (if specified), and then
288 * resume.
289 *
290 * Note that the supplied external functions _must_ be reentrant and aware
291 * that they are running in parallel and in an unknown lock context.
292 */
293void
294smp_rendezvous_action(void)
295{
296
297	/* setup function */
298	if (smp_rv_setup_func != NULL)
299		smp_rv_setup_func(smp_rv_func_arg);
300	/* spin on entry rendezvous */
301	atomic_add_int(&smp_rv_waiters[0], 1);
302	while (atomic_load_acq_int(&smp_rv_waiters[0]) < mp_ncpus)
303		;	/* nothing */
304	/* action function */
305	if (smp_rv_action_func != NULL)
306		smp_rv_action_func(smp_rv_func_arg);
307	/* spin on exit rendezvous */
308	atomic_add_int(&smp_rv_waiters[1], 1);
309	while (atomic_load_acq_int(&smp_rv_waiters[1]) < mp_ncpus)
310		;	/* nothing */
311	/* teardown function */
312	if (smp_rv_teardown_func != NULL)
313		smp_rv_teardown_func(smp_rv_func_arg);
314}
315
316void
317smp_rendezvous(void (* setup_func)(void *),
318	       void (* action_func)(void *),
319	       void (* teardown_func)(void *),
320	       void *arg)
321{
322
323	if (!smp_started) {
324		if (setup_func != NULL)
325			setup_func(arg);
326		if (action_func != NULL)
327			action_func(arg);
328		if (teardown_func != NULL)
329			teardown_func(arg);
330		return;
331	}
332
333	/* obtain rendezvous lock */
334	mtx_lock_spin(&smp_rv_mtx);
335
336	/* set static function pointers */
337	smp_rv_setup_func = setup_func;
338	smp_rv_action_func = action_func;
339	smp_rv_teardown_func = teardown_func;
340	smp_rv_func_arg = arg;
341	smp_rv_waiters[0] = 0;
342	smp_rv_waiters[1] = 0;
343
344	/* signal other processors, which will enter the IPI with interrupts off */
345	ipi_all_but_self(IPI_RENDEZVOUS);
346
347	/* call executor function */
348	smp_rendezvous_action();
349
350	/* release lock */
351	mtx_unlock_spin(&smp_rv_mtx);
352}
353#else /* !SMP */
354
355/*
356 * Provide dummy SMP support for UP kernels.  Modules that need to use SMP
357 * APIs will still work using this dummy support.
358 */
359static void
360mp_setvariables_for_up(void *dummy)
361{
362	mp_ncpus = 1;
363	mp_maxid = PCPU_GET(cpuid);
364	all_cpus = PCPU_GET(cpumask);
365	KASSERT(PCPU_GET(cpuid) == 0, ("UP must have a CPU ID of zero"));
366}
367SYSINIT(cpu_mp_setvariables, SI_SUB_TUNABLES, SI_ORDER_FIRST,
368    mp_setvariables_for_up, NULL)
369
370void
371smp_rendezvous(void (* setup_func)(void *),
372	       void (* action_func)(void *),
373	       void (* teardown_func)(void *),
374	       void *arg)
375{
376
377	if (setup_func != NULL)
378		setup_func(arg);
379	if (action_func != NULL)
380		action_func(arg);
381	if (teardown_func != NULL)
382		teardown_func(arg);
383}
384#endif /* SMP */
385