scheduler.c revision 1.13 1 /* $NetBSD: scheduler.c,v 1.13 2010/04/28 00:42:16 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2009 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by
7 * The Finnish Cultural Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: scheduler.c,v 1.13 2010/04/28 00:42:16 pooka Exp $");
33
34 #include <sys/param.h>
35 #include <sys/cpu.h>
36 #include <sys/kmem.h>
37 #include <sys/mutex.h>
38 #include <sys/namei.h>
39 #include <sys/queue.h>
40 #include <sys/select.h>
41 #include <sys/systm.h>
42
43 #include <rump/rumpuser.h>
44
45 #include "rump_private.h"
46
47 /* should go for MAXCPUS at some point */
48 static struct cpu_info rump_cpus[MAXCPUS];
49 static struct rumpcpu {
50 struct cpu_info *rcpu_ci;
51 int rcpu_flags;
52 struct rumpuser_cv *rcpu_cv;
53 LIST_ENTRY(rumpcpu) rcpu_entries;
54 } rcpu_storage[MAXCPUS];
55 struct cpu_info *rump_cpu = &rump_cpus[0];
56 int ncpu;
57
58 #define RCPU_WANTED 0x01 /* someone wants this specific CPU */
59 #define RCPU_BUSY 0x02 /* CPU is busy */
60 #define RCPU_FREELIST 0x04 /* CPU is on freelist */
61
62 static LIST_HEAD(,rumpcpu) cpu_freelist = LIST_HEAD_INITIALIZER(cpu_freelist);
63 static struct rumpuser_mtx *schedmtx;
64 static struct rumpuser_cv *schedcv, *lwp0cv;
65
66 static bool lwp0busy = false;
67
68 struct cpu_info *
69 cpu_lookup(u_int index)
70 {
71
72 return &rump_cpus[index];
73 }
74
75 /* this could/should be mi_attach_cpu? */
76 void
77 rump_cpus_bootstrap(int num)
78 {
79 struct rumpcpu *rcpu;
80 struct cpu_info *ci;
81 int i;
82
83 if (num > MAXCPUS) {
84 aprint_verbose("CPU limit: %d wanted, %d (MAXCPUS) available\n",
85 num, MAXCPUS);
86 num = MAXCPUS;
87 }
88
89 for (i = 0; i < num; i++) {
90 rcpu = &rcpu_storage[i];
91 ci = &rump_cpus[i];
92 ci->ci_index = i;
93 rump_cpu_attach(ci);
94 ncpu++;
95 }
96 }
97
98 void
99 rump_scheduler_init()
100 {
101 struct rumpcpu *rcpu;
102 struct cpu_info *ci;
103 int i;
104
105 rumpuser_mutex_init(&schedmtx);
106 rumpuser_cv_init(&schedcv);
107 rumpuser_cv_init(&lwp0cv);
108 for (i = 0; i < ncpu; i++) {
109 rcpu = &rcpu_storage[i];
110 ci = &rump_cpus[i];
111 rcpu->rcpu_ci = ci;
112 ci->ci_schedstate.spc_mutex =
113 mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE);
114 ci->ci_schedstate.spc_flags = SPCF_RUNNING;
115 LIST_INSERT_HEAD(&cpu_freelist, rcpu, rcpu_entries);
116 rcpu->rcpu_flags = RCPU_FREELIST;
117 rumpuser_cv_init(&rcpu->rcpu_cv);
118 }
119 }
120
121 void
122 rump_schedule()
123 {
124 struct lwp *l;
125
126 /*
127 * If there is no dedicated lwp, allocate a temp one and
128 * set it to be free'd upon unschedule(). Use lwp0 context
129 * for reserving the necessary resources.
130 */
131 l = rumpuser_get_curlwp();
132 if (l == NULL) {
133 /* busy lwp0 */
134 rumpuser_mutex_enter_nowrap(schedmtx);
135 while (lwp0busy)
136 rumpuser_cv_wait_nowrap(lwp0cv, schedmtx);
137 lwp0busy = true;
138 rumpuser_mutex_exit(schedmtx);
139
140 /* schedule cpu and use lwp0 */
141 rump_schedule_cpu(&lwp0);
142 rumpuser_set_curlwp(&lwp0);
143 l = rump_lwp_alloc(0, rump_nextlid());
144
145 /* release lwp0 */
146 rump_lwp_switch(l);
147 rumpuser_mutex_enter_nowrap(schedmtx);
148 lwp0busy = false;
149 rumpuser_cv_signal(lwp0cv);
150 rumpuser_mutex_exit(schedmtx);
151
152 /* mark new lwp as dead-on-exit */
153 rump_lwp_release(l);
154 } else {
155 rump_schedule_cpu(l);
156 }
157 }
158
159 void
160 rump_schedule_cpu(struct lwp *l)
161 {
162 struct rumpcpu *rcpu;
163
164 rumpuser_mutex_enter_nowrap(schedmtx);
165 if (l->l_pflag & LP_BOUND) {
166 KASSERT(l->l_cpu != NULL);
167 rcpu = &rcpu_storage[l->l_cpu-&rump_cpus[0]];
168 if (rcpu->rcpu_flags & RCPU_BUSY) {
169 KASSERT((rcpu->rcpu_flags & RCPU_FREELIST) == 0);
170 while (rcpu->rcpu_flags & RCPU_BUSY) {
171 rcpu->rcpu_flags |= RCPU_WANTED;
172 rumpuser_cv_wait_nowrap(rcpu->rcpu_cv,
173 schedmtx);
174 }
175 rcpu->rcpu_flags &= ~RCPU_WANTED;
176 } else {
177 KASSERT(rcpu->rcpu_flags & (RCPU_FREELIST|RCPU_WANTED));
178 }
179 if (rcpu->rcpu_flags & RCPU_FREELIST) {
180 LIST_REMOVE(rcpu, rcpu_entries);
181 rcpu->rcpu_flags &= ~RCPU_FREELIST;
182 }
183 } else {
184 while ((rcpu = LIST_FIRST(&cpu_freelist)) == NULL) {
185 rumpuser_cv_wait_nowrap(schedcv, schedmtx);
186 }
187 KASSERT(rcpu->rcpu_flags & RCPU_FREELIST);
188 LIST_REMOVE(rcpu, rcpu_entries);
189 rcpu->rcpu_flags &= ~RCPU_FREELIST;
190 KASSERT(l->l_cpu == NULL);
191 l->l_cpu = rcpu->rcpu_ci;
192 }
193 rcpu->rcpu_flags |= RCPU_BUSY;
194 rumpuser_mutex_exit(schedmtx);
195 l->l_mutex = rcpu->rcpu_ci->ci_schedstate.spc_mutex;
196 }
197
198 void
199 rump_unschedule()
200 {
201 struct lwp *l;
202
203 l = rumpuser_get_curlwp();
204 KASSERT(l->l_mutex == l->l_cpu->ci_schedstate.spc_mutex);
205 rump_unschedule_cpu(l);
206 l->l_mutex = NULL;
207
208 /*
209 * If we're using a temp lwp, need to take lwp0 for rump_lwp_free().
210 * (we could maybe cache idle lwp's to avoid constant bouncing)
211 */
212 if (l->l_flag & LW_WEXIT) {
213 rumpuser_set_curlwp(NULL);
214
215 /* busy lwp0 */
216 rumpuser_mutex_enter_nowrap(schedmtx);
217 while (lwp0busy)
218 rumpuser_cv_wait_nowrap(lwp0cv, schedmtx);
219 lwp0busy = true;
220 rumpuser_mutex_exit(schedmtx);
221
222 rump_schedule_cpu(&lwp0);
223 rumpuser_set_curlwp(&lwp0);
224 rump_lwp_free(l);
225 rump_unschedule_cpu(&lwp0);
226 rumpuser_set_curlwp(NULL);
227
228 rumpuser_mutex_enter_nowrap(schedmtx);
229 lwp0busy = false;
230 rumpuser_cv_signal(lwp0cv);
231 rumpuser_mutex_exit(schedmtx);
232 }
233 }
234
235 void
236 rump_unschedule_cpu(struct lwp *l)
237 {
238
239 if ((l->l_pflag & LP_INTR) == 0)
240 rump_softint_run(l->l_cpu);
241 rump_unschedule_cpu1(l);
242 }
243
244 void
245 rump_unschedule_cpu1(struct lwp *l)
246 {
247 struct rumpcpu *rcpu;
248 struct cpu_info *ci;
249
250 ci = l->l_cpu;
251 if ((l->l_pflag & LP_BOUND) == 0) {
252 l->l_cpu = NULL;
253 }
254 rcpu = &rcpu_storage[ci-&rump_cpus[0]];
255 KASSERT(rcpu->rcpu_ci == ci);
256 KASSERT(rcpu->rcpu_flags & RCPU_BUSY);
257
258 rumpuser_mutex_enter_nowrap(schedmtx);
259 if (rcpu->rcpu_flags & RCPU_WANTED) {
260 /*
261 * The assumption is that there will usually be max 1
262 * thread waiting on the rcpu_cv, so broadcast is fine.
263 * (and the current structure requires it because of
264 * only a bitmask being used for wanting).
265 */
266 rumpuser_cv_broadcast(rcpu->rcpu_cv);
267 } else {
268 LIST_INSERT_HEAD(&cpu_freelist, rcpu, rcpu_entries);
269 rcpu->rcpu_flags |= RCPU_FREELIST;
270 rumpuser_cv_signal(schedcv);
271 }
272 rcpu->rcpu_flags &= ~RCPU_BUSY;
273 rumpuser_mutex_exit(schedmtx);
274 }
275
276 /* Give up and retake CPU (perhaps a different one) */
277 void
278 yield()
279 {
280 struct lwp *l = curlwp;
281 int nlocks;
282
283 KERNEL_UNLOCK_ALL(l, &nlocks);
284 rump_unschedule_cpu(l);
285 rump_schedule_cpu(l);
286 KERNEL_LOCK(nlocks, l);
287 }
288
289 void
290 preempt()
291 {
292
293 yield();
294 }
295
296 bool
297 kpreempt(uintptr_t where)
298 {
299
300 return false;
301 }
302
303 /*
304 * There is no kernel thread preemption in rump currently. But call
305 * the implementing macros anyway in case they grow some side-effects
306 * down the road.
307 */
308 void
309 kpreempt_disable(void)
310 {
311
312 KPREEMPT_DISABLE(curlwp);
313 }
314
315 void
316 kpreempt_enable(void)
317 {
318
319 KPREEMPT_ENABLE(curlwp);
320 }
321
322 void
323 suspendsched(void)
324 {
325
326 /*
327 * Could wait until everyone is out and block further entries,
328 * but skip that for now.
329 */
330 }
331
332 void
333 sched_nice(struct proc *p, int level)
334 {
335
336 /* nothing to do for now */
337 }
338