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