scheduler.c revision 1.10 1 /* $NetBSD: scheduler.c,v 1.10 2010/04/17 13:13:45 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.10 2010/04/17 13:13:45 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 = 1;
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 void
76 rump_scheduler_init()
77 {
78 struct rumpcpu *rcpu;
79 struct cpu_info *ci;
80 int i;
81
82 rumpuser_mutex_init(&schedmtx);
83 rumpuser_cv_init(&schedcv);
84 rumpuser_cv_init(&lwp0cv);
85 for (i = 0; i < ncpu; i++) {
86 rcpu = &rcpu_storage[i];
87 ci = &rump_cpus[i];
88 rump_cpu_bootstrap(ci);
89 ci->ci_schedstate.spc_mutex =
90 mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE);
91 ci->ci_schedstate.spc_flags = SPCF_RUNNING;
92 rcpu->rcpu_ci = ci;
93 LIST_INSERT_HEAD(&cpu_freelist, rcpu, rcpu_entries);
94 rcpu->rcpu_flags = RCPU_FREELIST;
95 rumpuser_cv_init(&rcpu->rcpu_cv);
96 }
97 }
98
99 void
100 rump_schedule()
101 {
102 struct lwp *l;
103
104 /*
105 * If there is no dedicated lwp, allocate a temp one and
106 * set it to be free'd upon unschedule(). Use lwp0 context
107 * for reserving the necessary resources.
108 */
109 l = rumpuser_get_curlwp();
110 if (l == NULL) {
111 /* busy lwp0 */
112 rumpuser_mutex_enter_nowrap(schedmtx);
113 while (lwp0busy)
114 rumpuser_cv_wait_nowrap(lwp0cv, schedmtx);
115 lwp0busy = true;
116 rumpuser_mutex_exit(schedmtx);
117
118 /* schedule cpu and use lwp0 */
119 rump_schedule_cpu(&lwp0);
120 rumpuser_set_curlwp(&lwp0);
121 l = rump_lwp_alloc(0, rump_nextlid());
122
123 /* release lwp0 */
124 rump_lwp_switch(l);
125 rumpuser_mutex_enter_nowrap(schedmtx);
126 lwp0busy = false;
127 rumpuser_cv_signal(lwp0cv);
128 rumpuser_mutex_exit(schedmtx);
129
130 /* mark new lwp as dead-on-exit */
131 rump_lwp_release(l);
132 } else {
133 rump_schedule_cpu(l);
134 }
135 }
136
137 void
138 rump_schedule_cpu(struct lwp *l)
139 {
140 struct rumpcpu *rcpu;
141
142 rumpuser_mutex_enter_nowrap(schedmtx);
143 if (l->l_pflag & LP_BOUND) {
144 KASSERT(l->l_cpu != NULL);
145 rcpu = &rcpu_storage[l->l_cpu-&rump_cpus[0]];
146 if (rcpu->rcpu_flags & RCPU_BUSY) {
147 KASSERT((rcpu->rcpu_flags & RCPU_FREELIST) == 0);
148 while (rcpu->rcpu_flags & RCPU_BUSY) {
149 rcpu->rcpu_flags |= RCPU_WANTED;
150 rumpuser_cv_wait_nowrap(rcpu->rcpu_cv,
151 schedmtx);
152 }
153 rcpu->rcpu_flags &= ~RCPU_WANTED;
154 } else {
155 KASSERT(rcpu->rcpu_flags & (RCPU_FREELIST|RCPU_WANTED));
156 }
157 if (rcpu->rcpu_flags & RCPU_FREELIST) {
158 LIST_REMOVE(rcpu, rcpu_entries);
159 rcpu->rcpu_flags &= ~RCPU_FREELIST;
160 }
161 } else {
162 while ((rcpu = LIST_FIRST(&cpu_freelist)) == NULL) {
163 rumpuser_cv_wait_nowrap(schedcv, schedmtx);
164 }
165 KASSERT(rcpu->rcpu_flags & RCPU_FREELIST);
166 LIST_REMOVE(rcpu, rcpu_entries);
167 rcpu->rcpu_flags &= ~RCPU_FREELIST;
168 KASSERT(l->l_cpu == NULL);
169 l->l_cpu = rcpu->rcpu_ci;
170 }
171 rcpu->rcpu_flags |= RCPU_BUSY;
172 rumpuser_mutex_exit(schedmtx);
173 l->l_mutex = rcpu->rcpu_ci->ci_schedstate.spc_mutex;
174 }
175
176 void
177 rump_unschedule()
178 {
179 struct lwp *l;
180
181 l = rumpuser_get_curlwp();
182 KASSERT(l->l_mutex == l->l_cpu->ci_schedstate.spc_mutex);
183 rump_unschedule_cpu(l);
184 l->l_mutex = NULL;
185
186 /*
187 * If we're using a temp lwp, need to take lwp0 for rump_lwp_free().
188 * (we could maybe cache idle lwp's to avoid constant bouncing)
189 */
190 if (l->l_flag & LW_WEXIT) {
191 rumpuser_set_curlwp(NULL);
192
193 /* busy lwp0 */
194 rumpuser_mutex_enter_nowrap(schedmtx);
195 while (lwp0busy)
196 rumpuser_cv_wait_nowrap(lwp0cv, schedmtx);
197 lwp0busy = true;
198 rumpuser_mutex_exit(schedmtx);
199
200 rump_schedule_cpu(&lwp0);
201 rumpuser_set_curlwp(&lwp0);
202 rump_lwp_free(l);
203 rump_unschedule_cpu(&lwp0);
204 rumpuser_set_curlwp(NULL);
205
206 rumpuser_mutex_enter_nowrap(schedmtx);
207 lwp0busy = false;
208 rumpuser_cv_signal(lwp0cv);
209 rumpuser_mutex_exit(schedmtx);
210 }
211 }
212
213 void
214 rump_unschedule_cpu(struct lwp *l)
215 {
216
217 if ((l->l_pflag & LP_INTR) == 0)
218 rump_softint_run(l->l_cpu);
219 rump_unschedule_cpu1(l);
220 }
221
222 void
223 rump_unschedule_cpu1(struct lwp *l)
224 {
225 struct rumpcpu *rcpu;
226 struct cpu_info *ci;
227
228 ci = l->l_cpu;
229 if ((l->l_pflag & LP_BOUND) == 0) {
230 l->l_cpu = NULL;
231 }
232 rcpu = &rcpu_storage[ci-&rump_cpus[0]];
233 KASSERT(rcpu->rcpu_ci == ci);
234 KASSERT(rcpu->rcpu_flags & RCPU_BUSY);
235
236 rumpuser_mutex_enter_nowrap(schedmtx);
237 if (rcpu->rcpu_flags & RCPU_WANTED) {
238 /*
239 * The assumption is that there will usually be max 1
240 * thread waiting on the rcpu_cv, so broadcast is fine.
241 * (and the current structure requires it because of
242 * only a bitmask being used for wanting).
243 */
244 rumpuser_cv_broadcast(rcpu->rcpu_cv);
245 } else {
246 LIST_INSERT_HEAD(&cpu_freelist, rcpu, rcpu_entries);
247 rcpu->rcpu_flags |= RCPU_FREELIST;
248 rumpuser_cv_signal(schedcv);
249 }
250 rcpu->rcpu_flags &= ~RCPU_BUSY;
251 rumpuser_mutex_exit(schedmtx);
252 }
253
254 /* Give up and retake CPU (perhaps a different one) */
255 void
256 yield()
257 {
258 struct lwp *l = curlwp;
259 int nlocks;
260
261 KERNEL_UNLOCK_ALL(l, &nlocks);
262 rump_unschedule_cpu(l);
263 rump_schedule_cpu(l);
264 KERNEL_LOCK(nlocks, l);
265 }
266
267 void
268 preempt()
269 {
270
271 yield();
272 }
273
274 bool
275 kpreempt(uintptr_t where)
276 {
277
278 return false;
279 }
280
281 /*
282 * There is no kernel thread preemption in rump currently. But call
283 * the implementing macros anyway in case they grow some side-effects
284 * down the road.
285 */
286 void
287 kpreempt_disable(void)
288 {
289
290 KPREEMPT_DISABLE(curlwp);
291 }
292
293 void
294 kpreempt_enable(void)
295 {
296
297 KPREEMPT_ENABLE(curlwp);
298 }
299
300 void
301 suspendsched(void)
302 {
303
304 /*
305 * Could wait until everyone is out and block further entries,
306 * but skip that for now.
307 */
308 }
309