xref: /netbsd-src/sys/rump/librump/rumpkern/scheduler.c (revision bbde328be4e75ea9ad02e9715ea13ca54b797ada)
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