xref: /freebsd/sys/kern/subr_smp.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
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$");
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/lock.h>
44 #include <sys/mutex.h>
45 #include <sys/pcpu.h>
46 #include <sys/smp.h>
47 #include <sys/sysctl.h>
48 
49 #include <machine/smp.h>
50 
51 volatile u_int stopped_cpus;
52 volatile u_int started_cpus;
53 
54 void (*cpustop_restartfunc)(void);
55 int mp_ncpus;
56 
57 volatile int smp_started;
58 u_int all_cpus;
59 u_int mp_maxid;
60 
61 SYSCTL_NODE(_kern, OID_AUTO, smp, CTLFLAG_RD, NULL, "Kernel SMP");
62 
63 int smp_active = 0;	/* are the APs allowed to run? */
64 SYSCTL_INT(_kern_smp, OID_AUTO, active, CTLFLAG_RW, &smp_active, 0,
65     "Number of Auxillary Processors (APs) that were successfully started");
66 
67 int smp_disabled = 0;	/* has smp been disabled? */
68 SYSCTL_INT(_kern_smp, OID_AUTO, disabled, CTLFLAG_RD, &smp_disabled, 0,
69     "SMP has been disabled from the loader");
70 TUNABLE_INT("kern.smp.disabled", &smp_disabled);
71 
72 int smp_cpus = 1;	/* how many cpu's running */
73 SYSCTL_INT(_kern_smp, OID_AUTO, cpus, CTLFLAG_RD, &smp_cpus, 0,
74     "Number of CPUs online");
75 
76 /* Enable forwarding of a signal to a process running on a different CPU */
77 static int forward_signal_enabled = 1;
78 SYSCTL_INT(_kern_smp, OID_AUTO, forward_signal_enabled, CTLFLAG_RW,
79 	   &forward_signal_enabled, 0,
80 	   "Forwarding of a signal to a process on a different CPU");
81 
82 /* Enable forwarding of roundrobin to all other cpus */
83 static int forward_roundrobin_enabled = 1;
84 SYSCTL_INT(_kern_smp, OID_AUTO, forward_roundrobin_enabled, CTLFLAG_RW,
85 	   &forward_roundrobin_enabled, 0,
86 	   "Forwarding of roundrobin to all other CPUs");
87 
88 /* Variables needed for SMP rendezvous. */
89 static void (*smp_rv_setup_func)(void *arg);
90 static void (*smp_rv_action_func)(void *arg);
91 static void (*smp_rv_teardown_func)(void *arg);
92 static void *smp_rv_func_arg;
93 static volatile int smp_rv_waiters[2];
94 static struct mtx smp_rv_mtx;
95 static int mp_probe_status;
96 
97 /*
98  * Initialize MI SMP variables.
99  */
100 static void
101 mp_probe(void *dummy)
102 {
103 	mp_probe_status = cpu_mp_probe();
104 }
105 SYSINIT(cpu_mp_probe, SI_SUB_TUNABLES, SI_ORDER_FIRST, mp_probe, NULL)
106 
107 /*
108  * Call the MD SMP initialization code.
109  */
110 static void
111 mp_start(void *dummy)
112 {
113 
114 	/* Probe for MP hardware. */
115 	if (mp_probe_status == 0 || smp_disabled != 0)
116 		return;
117 
118 	mtx_init(&smp_rv_mtx, "smp rendezvous", NULL, MTX_SPIN);
119 	cpu_mp_start();
120 	printf("FreeBSD/SMP: Multiprocessor System Detected: %d CPUs\n",
121 	    mp_ncpus);
122 	cpu_mp_announce();
123 }
124 SYSINIT(cpu_mp, SI_SUB_CPU, SI_ORDER_SECOND, mp_start, NULL)
125 
126 void
127 forward_signal(struct thread *td)
128 {
129 	int id;
130 
131 	/*
132 	 * signotify() has already set TDF_ASTPENDING and TDF_NEEDSIGCHECK on
133 	 * this thread, so all we need to do is poke it if it is currently
134 	 * executing so that it executes ast().
135 	 */
136 	mtx_assert(&sched_lock, MA_OWNED);
137 	KASSERT(TD_IS_RUNNING(td),
138 	    ("forward_signal: thread is not TDS_RUNNING"));
139 
140 	CTR1(KTR_SMP, "forward_signal(%p)", td->td_proc);
141 
142 	if (!smp_started || cold || panicstr)
143 		return;
144 	if (!forward_signal_enabled)
145 		return;
146 
147 	/* No need to IPI ourself. */
148 	if (td == curthread)
149 		return;
150 
151 	id = td->td_oncpu;
152 	if (id == NOCPU)
153 		return;
154 	ipi_selected(1 << id, IPI_AST);
155 }
156 
157 void
158 forward_roundrobin(void)
159 {
160 	struct pcpu *pc;
161 	struct thread *td;
162 	u_int id, map;
163 
164 	mtx_assert(&sched_lock, MA_OWNED);
165 
166 	CTR0(KTR_SMP, "forward_roundrobin()");
167 
168 	if (!smp_started || cold || panicstr)
169 		return;
170 	if (!forward_roundrobin_enabled)
171 		return;
172 	map = 0;
173 	SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
174 		td = pc->pc_curthread;
175 		id = pc->pc_cpumask;
176 		if (id != PCPU_GET(cpumask) && (id & stopped_cpus) == 0 &&
177 		    td != pc->pc_idlethread) {
178 			td->td_flags |= TDF_NEEDRESCHED;
179 			map |= id;
180 		}
181 	}
182 	ipi_selected(map, IPI_AST);
183 }
184 
185 /*
186  * When called the executing CPU will send an IPI to all other CPUs
187  *  requesting that they halt execution.
188  *
189  * Usually (but not necessarily) called with 'other_cpus' as its arg.
190  *
191  *  - Signals all CPUs in map to stop.
192  *  - Waits for each to stop.
193  *
194  * Returns:
195  *  -1: error
196  *   0: NA
197  *   1: ok
198  *
199  * XXX FIXME: this is not MP-safe, needs a lock to prevent multiple CPUs
200  *            from executing at same time.
201  */
202 int
203 stop_cpus(u_int map)
204 {
205 	int i;
206 
207 	if (!smp_started)
208 		return 0;
209 
210 	CTR1(KTR_SMP, "stop_cpus(%x)", map);
211 
212 	/* send the stop IPI to all CPUs in map */
213 	ipi_selected(map, IPI_STOP);
214 
215 	i = 0;
216 	while ((atomic_load_acq_int(&stopped_cpus) & map) != map) {
217 		/* spin */
218 		i++;
219 #ifdef DIAGNOSTIC
220 		if (i == 100000) {
221 			printf("timeout stopping cpus\n");
222 			break;
223 		}
224 #endif
225 	}
226 
227 	return 1;
228 }
229 
230 
231 /*
232  * Called by a CPU to restart stopped CPUs.
233  *
234  * Usually (but not necessarily) called with 'stopped_cpus' as its arg.
235  *
236  *  - Signals all CPUs in map to restart.
237  *  - Waits for each to restart.
238  *
239  * Returns:
240  *  -1: error
241  *   0: NA
242  *   1: ok
243  */
244 int
245 restart_cpus(u_int map)
246 {
247 
248 	if (!smp_started)
249 		return 0;
250 
251 	CTR1(KTR_SMP, "restart_cpus(%x)", map);
252 
253 	/* signal other cpus to restart */
254 	atomic_store_rel_int(&started_cpus, map);
255 
256 	/* wait for each to clear its bit */
257 	while ((atomic_load_acq_int(&stopped_cpus) & map) != 0)
258 		;	/* nothing */
259 
260 	return 1;
261 }
262 
263 /*
264  * All-CPU rendezvous.  CPUs are signalled, all execute the setup function
265  * (if specified), rendezvous, execute the action function (if specified),
266  * rendezvous again, execute the teardown function (if specified), and then
267  * resume.
268  *
269  * Note that the supplied external functions _must_ be reentrant and aware
270  * that they are running in parallel and in an unknown lock context.
271  */
272 void
273 smp_rendezvous_action(void)
274 {
275 
276 	/* setup function */
277 	if (smp_rv_setup_func != NULL)
278 		smp_rv_setup_func(smp_rv_func_arg);
279 	/* spin on entry rendezvous */
280 	atomic_add_int(&smp_rv_waiters[0], 1);
281 	while (atomic_load_acq_int(&smp_rv_waiters[0]) < mp_ncpus)
282 		;	/* nothing */
283 	/* action function */
284 	if (smp_rv_action_func != NULL)
285 		smp_rv_action_func(smp_rv_func_arg);
286 	/* spin on exit rendezvous */
287 	atomic_add_int(&smp_rv_waiters[1], 1);
288 	while (atomic_load_acq_int(&smp_rv_waiters[1]) < mp_ncpus)
289 		;	/* nothing */
290 	/* teardown function */
291 	if (smp_rv_teardown_func != NULL)
292 		smp_rv_teardown_func(smp_rv_func_arg);
293 }
294 
295 void
296 smp_rendezvous(void (* setup_func)(void *),
297 	       void (* action_func)(void *),
298 	       void (* teardown_func)(void *),
299 	       void *arg)
300 {
301 
302 	if (!smp_started) {
303 		if (setup_func != NULL)
304 			setup_func(arg);
305 		if (action_func != NULL)
306 			action_func(arg);
307 		if (teardown_func != NULL)
308 			teardown_func(arg);
309 		return;
310 	}
311 
312 	/* obtain rendezvous lock */
313 	mtx_lock_spin(&smp_rv_mtx);
314 
315 	/* set static function pointers */
316 	smp_rv_setup_func = setup_func;
317 	smp_rv_action_func = action_func;
318 	smp_rv_teardown_func = teardown_func;
319 	smp_rv_func_arg = arg;
320 	smp_rv_waiters[0] = 0;
321 	smp_rv_waiters[1] = 0;
322 
323 	/* signal other processors, which will enter the IPI with interrupts off */
324 	ipi_all_but_self(IPI_RENDEZVOUS);
325 
326 	/* call executor function */
327 	smp_rendezvous_action();
328 
329 	/* release lock */
330 	mtx_unlock_spin(&smp_rv_mtx);
331 }
332