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