Annotation of src/sys/rump/librump/rumpkern/scheduler.c, Revision 1.20
1.20 ! pooka 1: /* $NetBSD: scheduler.c,v 1.19 2010/09/01 19:37:59 pooka Exp $ */
1.1 pooka 2:
3: /*
1.15 pooka 4: * Copyright (c) 2010 Antti Kantee. All Rights Reserved.
1.1 pooka 5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: * 2. Redistributions in binary form must reproduce the above copyright
12: * notice, this list of conditions and the following disclaimer in the
13: * documentation and/or other materials provided with the distribution.
14: *
15: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16: * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25: * SUCH DAMAGE.
26: */
27:
28: #include <sys/cdefs.h>
1.20 ! pooka 29: __KERNEL_RCSID(0, "$NetBSD: scheduler.c,v 1.19 2010/09/01 19:37:59 pooka Exp $");
1.1 pooka 30:
31: #include <sys/param.h>
1.16 pooka 32: #include <sys/atomic.h>
1.1 pooka 33: #include <sys/cpu.h>
1.2 pooka 34: #include <sys/kmem.h>
1.1 pooka 35: #include <sys/mutex.h>
1.8 pooka 36: #include <sys/namei.h>
1.1 pooka 37: #include <sys/queue.h>
38: #include <sys/select.h>
1.10 pooka 39: #include <sys/systm.h>
1.1 pooka 40:
41: #include <rump/rumpuser.h>
42:
43: #include "rump_private.h"
44:
1.8 pooka 45: static struct cpu_info rump_cpus[MAXCPUS];
1.1 pooka 46: static struct rumpcpu {
1.15 pooka 47: /* needed in fastpath */
1.1 pooka 48: struct cpu_info *rcpu_ci;
1.15 pooka 49: void *rcpu_prevlwp;
50:
51: /* needed in slowpath */
52: struct rumpuser_mtx *rcpu_mtx;
1.8 pooka 53: struct rumpuser_cv *rcpu_cv;
1.15 pooka 54: int rcpu_wanted;
55:
56: /* offset 20 (P=4) or 36 (P=8) here */
57:
58: /*
59: * Some stats. Not really that necessary, but we should
60: * have room. Note that these overflow quite fast, so need
61: * to be collected often.
62: */
63: unsigned int rcpu_fastpath;
64: unsigned int rcpu_slowpath;
65: unsigned int rcpu_migrated;
66:
67: /* offset 32 (P=4) or 50 (P=8) */
68:
69: int rcpu_align[0] __aligned(CACHE_LINE_SIZE);
1.8 pooka 70: } rcpu_storage[MAXCPUS];
1.1 pooka 71: struct cpu_info *rump_cpu = &rump_cpus[0];
1.12 pooka 72: int ncpu;
1.1 pooka 73:
1.15 pooka 74: #define RCPULWP_BUSY ((void *)-1)
75: #define RCPULWP_WANTED ((void *)-2)
1.8 pooka 76:
1.15 pooka 77: static struct rumpuser_mtx *lwp0mtx;
78: static struct rumpuser_cv *lwp0cv;
79: static unsigned nextcpu;
1.14 pooka 80:
1.19 pooka 81: static bool lwp0isbusy = false;
1.3 pooka 82:
1.15 pooka 83: /*
84: * Keep some stats.
85: *
86: * Keeping track of there is not really critical for speed, unless
87: * stats happen to be on a different cache line (CACHE_LINE_SIZE is
88: * really just a coarse estimate), so default for the performant case
89: * (i.e. no stats).
90: */
91: #ifdef RUMPSCHED_STATS
92: #define SCHED_FASTPATH(rcpu) rcpu->rcpu_fastpath++;
93: #define SCHED_SLOWPATH(rcpu) rcpu->rcpu_slowpath++;
94: #define SCHED_MIGRATED(rcpu) rcpu->rcpu_migrated++;
95: #else
96: #define SCHED_FASTPATH(rcpu)
97: #define SCHED_SLOWPATH(rcpu)
98: #define SCHED_MIGRATED(rcpu)
99: #endif
1.1 pooka 100:
101: struct cpu_info *
102: cpu_lookup(u_int index)
103: {
104:
105: return &rump_cpus[index];
106: }
107:
1.15 pooka 108: static inline struct rumpcpu *
109: getnextcpu(void)
110: {
111: unsigned newcpu;
112:
113: newcpu = atomic_inc_uint_nv(&nextcpu);
114: if (__predict_false(ncpu > UINT_MAX/2))
115: atomic_and_uint(&nextcpu, 0);
116: newcpu = newcpu % ncpu;
117:
118: return &rcpu_storage[newcpu];
119: }
120:
1.12 pooka 121: /* this could/should be mi_attach_cpu? */
122: void
123: rump_cpus_bootstrap(int num)
124: {
125: struct rumpcpu *rcpu;
126: struct cpu_info *ci;
127: int i;
128:
1.13 pooka 129: if (num > MAXCPUS) {
130: aprint_verbose("CPU limit: %d wanted, %d (MAXCPUS) available\n",
131: num, MAXCPUS);
132: num = MAXCPUS;
133: }
134:
1.12 pooka 135: for (i = 0; i < num; i++) {
136: rcpu = &rcpu_storage[i];
137: ci = &rump_cpus[i];
138: ci->ci_index = i;
139: }
1.20 ! pooka 140:
! 141: /* attach first cpu for bootstrap */
! 142: rump_cpu_attach(&rump_cpus[0]);
! 143: ncpu = 1;
1.12 pooka 144: }
145:
1.1 pooka 146: void
1.20 ! pooka 147: rump_scheduler_init(int numcpu)
1.1 pooka 148: {
149: struct rumpcpu *rcpu;
150: struct cpu_info *ci;
151: int i;
152:
1.15 pooka 153: rumpuser_mutex_init(&lwp0mtx);
1.3 pooka 154: rumpuser_cv_init(&lwp0cv);
1.20 ! pooka 155: for (i = 0; i < numcpu; i++) {
1.1 pooka 156: rcpu = &rcpu_storage[i];
157: ci = &rump_cpus[i];
1.12 pooka 158: rcpu->rcpu_ci = ci;
1.4 pooka 159: ci->ci_schedstate.spc_mutex =
160: mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE);
1.9 pooka 161: ci->ci_schedstate.spc_flags = SPCF_RUNNING;
1.15 pooka 162: rcpu->rcpu_wanted = 0;
1.8 pooka 163: rumpuser_cv_init(&rcpu->rcpu_cv);
1.15 pooka 164: rumpuser_mutex_init(&rcpu->rcpu_mtx);
1.1 pooka 165: }
166: }
167:
1.14 pooka 168: /*
169: * condvar ops using scheduler lock as the rumpuser interlock.
170: */
171: void
172: rump_schedlock_cv_wait(struct rumpuser_cv *cv)
173: {
1.15 pooka 174: struct lwp *l = curlwp;
175: struct rumpcpu *rcpu = &rcpu_storage[l->l_cpu-&rump_cpus[0]];
1.14 pooka 176:
1.15 pooka 177: /* mutex will be taken and released in cpu schedule/unschedule */
178: rumpuser_cv_wait(cv, rcpu->rcpu_mtx);
1.14 pooka 179: }
180:
181: int
182: rump_schedlock_cv_timedwait(struct rumpuser_cv *cv, const struct timespec *ts)
183: {
1.15 pooka 184: struct lwp *l = curlwp;
185: struct rumpcpu *rcpu = &rcpu_storage[l->l_cpu-&rump_cpus[0]];
1.14 pooka 186:
1.15 pooka 187: /* mutex will be taken and released in cpu schedule/unschedule */
188: return rumpuser_cv_timedwait(cv, rcpu->rcpu_mtx,
189: ts->tv_sec, ts->tv_nsec);
1.14 pooka 190: }
191:
1.19 pooka 192: static void
193: lwp0busy(void)
194: {
195:
196: /* busy lwp0 */
197: KASSERT(curlwp == NULL || curlwp->l_cpu == NULL);
198: rumpuser_mutex_enter_nowrap(lwp0mtx);
199: while (lwp0isbusy)
200: rumpuser_cv_wait_nowrap(lwp0cv, lwp0mtx);
201: lwp0isbusy = true;
202: rumpuser_mutex_exit(lwp0mtx);
203: }
204:
205: static void
206: lwp0rele(void)
207: {
208:
209: rumpuser_mutex_enter_nowrap(lwp0mtx);
210: KASSERT(lwp0isbusy == true);
211: lwp0isbusy = false;
212: rumpuser_cv_signal(lwp0cv);
213: rumpuser_mutex_exit(lwp0mtx);
214: }
215:
1.1 pooka 216: void
217: rump_schedule()
218: {
1.3 pooka 219: struct lwp *l;
1.2 pooka 220:
221: /*
222: * If there is no dedicated lwp, allocate a temp one and
1.3 pooka 223: * set it to be free'd upon unschedule(). Use lwp0 context
1.15 pooka 224: * for reserving the necessary resources. Don't optimize
225: * for this case -- anyone who cares about performance will
226: * start a real thread.
1.2 pooka 227: */
1.19 pooka 228: if (__predict_true((l = rumpuser_get_curlwp()) != NULL)) {
229: rump_schedule_cpu(l);
230: LWP_CACHE_CREDS(l, l->l_proc);
231: } else {
232: lwp0busy();
1.3 pooka 233:
234: /* schedule cpu and use lwp0 */
1.4 pooka 235: rump_schedule_cpu(&lwp0);
1.3 pooka 236: rumpuser_set_curlwp(&lwp0);
237:
1.19 pooka 238: /* allocate thread, switch to it, and release lwp0 */
239: l = rump__lwproc_allockernlwp();
240: rump_lwproc_switch(l);
241: lwp0rele();
1.3 pooka 242:
1.19 pooka 243: /*
244: * mark new thread dead-on-unschedule. this
245: * means that we'll be running with l_refcnt == 0.
246: * relax, it's fine.
247: */
248: rump_lwproc_releaselwp();
1.2 pooka 249: }
250: }
251:
1.4 pooka 252: void
253: rump_schedule_cpu(struct lwp *l)
1.2 pooka 254: {
1.14 pooka 255:
256: rump_schedule_cpu_interlock(l, NULL);
257: }
258:
1.15 pooka 259: /*
260: * Schedule a CPU. This optimizes for the case where we schedule
261: * the same thread often, and we have nCPU >= nFrequently-Running-Thread
262: * (where CPU is virtual rump cpu, not host CPU).
263: */
1.14 pooka 264: void
265: rump_schedule_cpu_interlock(struct lwp *l, void *interlock)
266: {
1.1 pooka 267: struct rumpcpu *rcpu;
1.15 pooka 268: void *old;
269: bool domigrate;
270: bool bound = l->l_pflag & LP_BOUND;
271:
272: /*
273: * First, try fastpath: if we were the previous user of the
274: * CPU, everything is in order cachewise and we can just
275: * proceed to use it.
276: *
277: * If we are a different thread (i.e. CAS fails), we must go
278: * through a memory barrier to ensure we get a truthful
279: * view of the world.
280: */
1.14 pooka 281:
1.17 pooka 282: KASSERT(l->l_target_cpu != NULL);
1.15 pooka 283: rcpu = &rcpu_storage[l->l_target_cpu-&rump_cpus[0]];
284: if (atomic_cas_ptr(&rcpu->rcpu_prevlwp, l, RCPULWP_BUSY) == l) {
285: if (__predict_true(interlock == rcpu->rcpu_mtx))
286: rumpuser_mutex_exit(rcpu->rcpu_mtx);
287: SCHED_FASTPATH(rcpu);
288: /* jones, you're the man */
289: goto fastlane;
290: }
1.1 pooka 291:
1.15 pooka 292: /*
293: * Else, it's the slowpath for us. First, determine if we
294: * can migrate.
295: */
296: if (ncpu == 1)
297: domigrate = false;
298: else
299: domigrate = true;
300:
301: /* Take lock. This acts as a load barrier too. */
302: if (__predict_true(interlock != rcpu->rcpu_mtx))
303: rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx);
304:
305: for (;;) {
306: SCHED_SLOWPATH(rcpu);
307: old = atomic_swap_ptr(&rcpu->rcpu_prevlwp, RCPULWP_WANTED);
308:
309: /* CPU is free? */
310: if (old != RCPULWP_BUSY && old != RCPULWP_WANTED) {
311: if (atomic_cas_ptr(&rcpu->rcpu_prevlwp,
312: RCPULWP_WANTED, RCPULWP_BUSY) == RCPULWP_WANTED) {
313: break;
1.8 pooka 314: }
315: }
1.15 pooka 316:
317: /*
318: * Do we want to migrate once?
319: * This may need a slightly better algorithm, or we
320: * might cache pingpong eternally for non-frequent
321: * threads.
322: */
323: if (domigrate && !bound) {
324: domigrate = false;
325: SCHED_MIGRATED(rcpu);
326: rumpuser_mutex_exit(rcpu->rcpu_mtx);
327: rcpu = getnextcpu();
328: rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx);
329: continue;
1.8 pooka 330: }
1.15 pooka 331:
332: /* Want CPU, wait until it's released an retry */
333: rcpu->rcpu_wanted++;
334: rumpuser_cv_wait_nowrap(rcpu->rcpu_cv, rcpu->rcpu_mtx);
335: rcpu->rcpu_wanted--;
1.8 pooka 336: }
1.15 pooka 337: rumpuser_mutex_exit(rcpu->rcpu_mtx);
338:
339: fastlane:
340: l->l_cpu = l->l_target_cpu = rcpu->rcpu_ci;
1.4 pooka 341: l->l_mutex = rcpu->rcpu_ci->ci_schedstate.spc_mutex;
1.18 pooka 342: l->l_ncsw++;
1.1 pooka 343: }
344:
345: void
346: rump_unschedule()
347: {
1.2 pooka 348: struct lwp *l;
349:
350: l = rumpuser_get_curlwp();
1.4 pooka 351: KASSERT(l->l_mutex == l->l_cpu->ci_schedstate.spc_mutex);
1.2 pooka 352: rump_unschedule_cpu(l);
1.4 pooka 353: l->l_mutex = NULL;
1.6 pooka 354:
355: /*
1.19 pooka 356: * Check special conditions:
357: * 1) do we need to free the lwp which just unscheduled?
358: * (locking order: lwp0, cpu)
359: * 2) do we want to clear curlwp for the current host thread
1.6 pooka 360: */
1.19 pooka 361: if (__predict_false(l->l_flag & LW_WEXIT)) {
362: lwp0busy();
363:
364: /* Now that we have lwp0, we can schedule a CPU again */
365: rump_schedule_cpu(l);
1.6 pooka 366:
1.19 pooka 367: /* switch to lwp0. this frees the old thread */
368: KASSERT(l->l_flag & LW_WEXIT);
369: rump_lwproc_switch(&lwp0);
1.6 pooka 370:
1.19 pooka 371: /* release lwp0 */
1.6 pooka 372: rump_unschedule_cpu(&lwp0);
1.19 pooka 373: lwp0.l_mutex = NULL;
374: lwp0.l_pflag &= ~LP_RUNNING;
375: lwp0rele();
1.6 pooka 376: rumpuser_set_curlwp(NULL);
377:
1.19 pooka 378: } else if (__predict_false(l->l_flag & LW_RUMP_CLEAR)) {
379: rumpuser_set_curlwp(NULL);
380: l->l_flag &= ~LW_RUMP_CLEAR;
1.2 pooka 381: }
382: }
383:
384: void
385: rump_unschedule_cpu(struct lwp *l)
386: {
1.8 pooka 387:
1.14 pooka 388: rump_unschedule_cpu_interlock(l, NULL);
389: }
390:
391: void
392: rump_unschedule_cpu_interlock(struct lwp *l, void *interlock)
393: {
394:
1.8 pooka 395: if ((l->l_pflag & LP_INTR) == 0)
396: rump_softint_run(l->l_cpu);
1.14 pooka 397: rump_unschedule_cpu1(l, interlock);
1.8 pooka 398: }
399:
400: void
1.14 pooka 401: rump_unschedule_cpu1(struct lwp *l, void *interlock)
1.8 pooka 402: {
1.1 pooka 403: struct rumpcpu *rcpu;
404: struct cpu_info *ci;
1.15 pooka 405: void *old;
1.1 pooka 406:
407: ci = l->l_cpu;
1.15 pooka 408: l->l_cpu = NULL;
1.1 pooka 409: rcpu = &rcpu_storage[ci-&rump_cpus[0]];
1.15 pooka 410:
1.1 pooka 411: KASSERT(rcpu->rcpu_ci == ci);
412:
1.15 pooka 413: /*
414: * Make sure all stores are seen before the CPU release. This
415: * is relevant only in the non-fastpath scheduling case, but
416: * we don't know here if that's going to happen, so need to
417: * expect the worst.
418: */
419: membar_exit();
420:
421: /* Release the CPU. */
422: old = atomic_swap_ptr(&rcpu->rcpu_prevlwp, l);
423:
424: /* No waiters? No problems. We're outta here. */
425: if (old == RCPULWP_BUSY) {
426: /* Was the scheduler interlock requested? */
427: if (__predict_false(interlock == rcpu->rcpu_mtx))
428: rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx);
429: return;
430: }
431:
432: KASSERT(old == RCPULWP_WANTED);
433:
434: /*
435: * Ok, things weren't so snappy.
436: *
437: * Snailpath: take lock and signal anyone waiting for this CPU.
438: */
1.14 pooka 439:
1.15 pooka 440: rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx);
441: if (rcpu->rcpu_wanted)
1.8 pooka 442: rumpuser_cv_broadcast(rcpu->rcpu_cv);
1.14 pooka 443:
1.15 pooka 444: if (__predict_true(interlock != rcpu->rcpu_mtx))
445: rumpuser_mutex_exit(rcpu->rcpu_mtx);
1.1 pooka 446: }
1.5 pooka 447:
448: /* Give up and retake CPU (perhaps a different one) */
449: void
450: yield()
451: {
452: struct lwp *l = curlwp;
453: int nlocks;
454:
455: KERNEL_UNLOCK_ALL(l, &nlocks);
456: rump_unschedule_cpu(l);
457: rump_schedule_cpu(l);
458: KERNEL_LOCK(nlocks, l);
459: }
460:
461: void
462: preempt()
463: {
464:
465: yield();
466: }
1.10 pooka 467:
468: bool
469: kpreempt(uintptr_t where)
470: {
471:
472: return false;
473: }
474:
475: /*
476: * There is no kernel thread preemption in rump currently. But call
477: * the implementing macros anyway in case they grow some side-effects
478: * down the road.
479: */
480: void
481: kpreempt_disable(void)
482: {
483:
484: KPREEMPT_DISABLE(curlwp);
485: }
486:
487: void
488: kpreempt_enable(void)
489: {
490:
491: KPREEMPT_ENABLE(curlwp);
492: }
493:
494: void
495: suspendsched(void)
496: {
497:
498: /*
499: * Could wait until everyone is out and block further entries,
500: * but skip that for now.
501: */
502: }
1.11 pooka 503:
504: void
505: sched_nice(struct proc *p, int level)
506: {
507:
508: /* nothing to do for now */
509: }
CVSweb <webmaster@jp.NetBSD.org>