]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/subr_smp.c
Introduce support for Mandatory Access Control and extensible
[FreeBSD/FreeBSD.git] / sys / kern / subr_smp.c
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 }