Home | History | Annotate | Line # | Download | only in rumpkern
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