1 /* $NetBSD: scheduler.c,v 1.9 2010/01/25 18:37:51 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.9 2010/01/25 18:37:51 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 42 #include <rump/rumpuser.h> 43 44 #include "rump_private.h" 45 46 /* should go for MAXCPUS at some point */ 47 static struct cpu_info rump_cpus[MAXCPUS]; 48 static struct rumpcpu { 49 struct cpu_info *rcpu_ci; 50 int rcpu_flags; 51 struct rumpuser_cv *rcpu_cv; 52 LIST_ENTRY(rumpcpu) rcpu_entries; 53 } rcpu_storage[MAXCPUS]; 54 struct cpu_info *rump_cpu = &rump_cpus[0]; 55 int ncpu = 1; 56 57 #define RCPU_WANTED 0x01 /* someone wants this specific CPU */ 58 #define RCPU_BUSY 0x02 /* CPU is busy */ 59 #define RCPU_FREELIST 0x04 /* CPU is on freelist */ 60 61 static LIST_HEAD(,rumpcpu) cpu_freelist = LIST_HEAD_INITIALIZER(cpu_freelist); 62 static struct rumpuser_mtx *schedmtx; 63 static struct rumpuser_cv *schedcv, *lwp0cv; 64 65 static bool lwp0busy = false; 66 67 struct cpu_info * 68 cpu_lookup(u_int index) 69 { 70 71 return &rump_cpus[index]; 72 } 73 74 void 75 rump_scheduler_init() 76 { 77 struct rumpcpu *rcpu; 78 struct cpu_info *ci; 79 int i; 80 81 rumpuser_mutex_init(&schedmtx); 82 rumpuser_cv_init(&schedcv); 83 rumpuser_cv_init(&lwp0cv); 84 for (i = 0; i < ncpu; i++) { 85 rcpu = &rcpu_storage[i]; 86 ci = &rump_cpus[i]; 87 rump_cpu_bootstrap(ci); 88 ci->ci_schedstate.spc_mutex = 89 mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE); 90 ci->ci_schedstate.spc_flags = SPCF_RUNNING; 91 rcpu->rcpu_ci = ci; 92 LIST_INSERT_HEAD(&cpu_freelist, rcpu, rcpu_entries); 93 rcpu->rcpu_flags = RCPU_FREELIST; 94 rumpuser_cv_init(&rcpu->rcpu_cv); 95 } 96 } 97 98 void 99 rump_schedule() 100 { 101 struct lwp *l; 102 103 /* 104 * If there is no dedicated lwp, allocate a temp one and 105 * set it to be free'd upon unschedule(). Use lwp0 context 106 * for reserving the necessary resources. 107 */ 108 l = rumpuser_get_curlwp(); 109 if (l == NULL) { 110 /* busy lwp0 */ 111 rumpuser_mutex_enter_nowrap(schedmtx); 112 while (lwp0busy) 113 rumpuser_cv_wait_nowrap(lwp0cv, schedmtx); 114 lwp0busy = true; 115 rumpuser_mutex_exit(schedmtx); 116 117 /* schedule cpu and use lwp0 */ 118 rump_schedule_cpu(&lwp0); 119 rumpuser_set_curlwp(&lwp0); 120 l = rump_lwp_alloc(0, rump_nextlid()); 121 122 /* release lwp0 */ 123 rump_lwp_switch(l); 124 rumpuser_mutex_enter_nowrap(schedmtx); 125 lwp0busy = false; 126 rumpuser_cv_signal(lwp0cv); 127 rumpuser_mutex_exit(schedmtx); 128 129 /* mark new lwp as dead-on-exit */ 130 rump_lwp_release(l); 131 } else { 132 rump_schedule_cpu(l); 133 } 134 } 135 136 void 137 rump_schedule_cpu(struct lwp *l) 138 { 139 struct rumpcpu *rcpu; 140 141 rumpuser_mutex_enter_nowrap(schedmtx); 142 if (l->l_pflag & LP_BOUND) { 143 KASSERT(l->l_cpu != NULL); 144 rcpu = &rcpu_storage[l->l_cpu-&rump_cpus[0]]; 145 if (rcpu->rcpu_flags & RCPU_BUSY) { 146 KASSERT((rcpu->rcpu_flags & RCPU_FREELIST) == 0); 147 while (rcpu->rcpu_flags & RCPU_BUSY) { 148 rcpu->rcpu_flags |= RCPU_WANTED; 149 rumpuser_cv_wait_nowrap(rcpu->rcpu_cv, 150 schedmtx); 151 } 152 rcpu->rcpu_flags &= ~RCPU_WANTED; 153 } else { 154 KASSERT(rcpu->rcpu_flags & (RCPU_FREELIST|RCPU_WANTED)); 155 } 156 if (rcpu->rcpu_flags & RCPU_FREELIST) { 157 LIST_REMOVE(rcpu, rcpu_entries); 158 rcpu->rcpu_flags &= ~RCPU_FREELIST; 159 } 160 } else { 161 while ((rcpu = LIST_FIRST(&cpu_freelist)) == NULL) { 162 rumpuser_cv_wait_nowrap(schedcv, schedmtx); 163 } 164 KASSERT(rcpu->rcpu_flags & RCPU_FREELIST); 165 LIST_REMOVE(rcpu, rcpu_entries); 166 rcpu->rcpu_flags &= ~RCPU_FREELIST; 167 KASSERT(l->l_cpu == NULL); 168 l->l_cpu = rcpu->rcpu_ci; 169 } 170 rcpu->rcpu_flags |= RCPU_BUSY; 171 rumpuser_mutex_exit(schedmtx); 172 l->l_mutex = rcpu->rcpu_ci->ci_schedstate.spc_mutex; 173 } 174 175 void 176 rump_unschedule() 177 { 178 struct lwp *l; 179 180 l = rumpuser_get_curlwp(); 181 KASSERT(l->l_mutex == l->l_cpu->ci_schedstate.spc_mutex); 182 rump_unschedule_cpu(l); 183 l->l_mutex = NULL; 184 185 /* 186 * If we're using a temp lwp, need to take lwp0 for rump_lwp_free(). 187 * (we could maybe cache idle lwp's to avoid constant bouncing) 188 */ 189 if (l->l_flag & LW_WEXIT) { 190 rumpuser_set_curlwp(NULL); 191 192 /* busy lwp0 */ 193 rumpuser_mutex_enter_nowrap(schedmtx); 194 while (lwp0busy) 195 rumpuser_cv_wait_nowrap(lwp0cv, schedmtx); 196 lwp0busy = true; 197 rumpuser_mutex_exit(schedmtx); 198 199 rump_schedule_cpu(&lwp0); 200 rumpuser_set_curlwp(&lwp0); 201 rump_lwp_free(l); 202 rump_unschedule_cpu(&lwp0); 203 rumpuser_set_curlwp(NULL); 204 205 rumpuser_mutex_enter_nowrap(schedmtx); 206 lwp0busy = false; 207 rumpuser_cv_signal(lwp0cv); 208 rumpuser_mutex_exit(schedmtx); 209 } 210 } 211 212 void 213 rump_unschedule_cpu(struct lwp *l) 214 { 215 216 if ((l->l_pflag & LP_INTR) == 0) 217 rump_softint_run(l->l_cpu); 218 rump_unschedule_cpu1(l); 219 } 220 221 void 222 rump_unschedule_cpu1(struct lwp *l) 223 { 224 struct rumpcpu *rcpu; 225 struct cpu_info *ci; 226 227 ci = l->l_cpu; 228 if ((l->l_pflag & LP_BOUND) == 0) { 229 l->l_cpu = NULL; 230 } 231 rcpu = &rcpu_storage[ci-&rump_cpus[0]]; 232 KASSERT(rcpu->rcpu_ci == ci); 233 KASSERT(rcpu->rcpu_flags & RCPU_BUSY); 234 235 rumpuser_mutex_enter_nowrap(schedmtx); 236 if (rcpu->rcpu_flags & RCPU_WANTED) { 237 /* 238 * The assumption is that there will usually be max 1 239 * thread waiting on the rcpu_cv, so broadcast is fine. 240 * (and the current structure requires it because of 241 * only a bitmask being used for wanting). 242 */ 243 rumpuser_cv_broadcast(rcpu->rcpu_cv); 244 } else { 245 LIST_INSERT_HEAD(&cpu_freelist, rcpu, rcpu_entries); 246 rcpu->rcpu_flags |= RCPU_FREELIST; 247 rumpuser_cv_signal(schedcv); 248 } 249 rcpu->rcpu_flags &= ~RCPU_BUSY; 250 rumpuser_mutex_exit(schedmtx); 251 } 252 253 /* Give up and retake CPU (perhaps a different one) */ 254 void 255 yield() 256 { 257 struct lwp *l = curlwp; 258 int nlocks; 259 260 KERNEL_UNLOCK_ALL(l, &nlocks); 261 rump_unschedule_cpu(l); 262 rump_schedule_cpu(l); 263 KERNEL_LOCK(nlocks, l); 264 } 265 266 void 267 preempt() 268 { 269 270 yield(); 271 } 272