1 /* $OpenBSD: kern_sched.c,v 1.8 2008/11/06 19:49:13 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2007 Artur Grabowski <art@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 20 #include <sys/sched.h> 21 #include <sys/proc.h> 22 #include <sys/kthread.h> 23 #include <sys/systm.h> 24 #include <sys/resourcevar.h> 25 #include <sys/signalvar.h> 26 #include <sys/mutex.h> 27 28 #include <uvm/uvm_extern.h> 29 30 void sched_kthreads_create(void *); 31 void sched_idle(void *); 32 33 /* 34 * A few notes about cpu_switchto that is implemented in MD code. 35 * 36 * cpu_switchto takes two arguments, the old proc and the proc 37 * it should switch to. The new proc will never be NULL, so we always have 38 * a saved state that we need to switch to. The old proc however can 39 * be NULL if the process is exiting. NULL for the old proc simply 40 * means "don't bother saving old state". 41 * 42 * cpu_switchto is supposed to atomically load the new state of the process 43 * including the pcb, pmap and setting curproc, the p_cpu pointer in the 44 * proc and p_stat to SONPROC. Atomically with respect to interrupts, other 45 * cpus in the system must not depend on this state being consistent. 46 * Therefore no locking is necessary in cpu_switchto other than blocking 47 * interrupts during the context switch. 48 */ 49 50 /* 51 * sched_init_cpu is called from main() for the boot cpu, then it's the 52 * responsibility of the MD code to call it for all other cpus. 53 */ 54 void 55 sched_init_cpu(struct cpu_info *ci) 56 { 57 struct schedstate_percpu *spc = &ci->ci_schedstate; 58 59 spc->spc_idleproc = NULL; 60 61 kthread_create_deferred(sched_kthreads_create, ci); 62 63 LIST_INIT(&spc->spc_deadproc); 64 } 65 66 void 67 sched_kthreads_create(void *v) 68 { 69 struct cpu_info *ci = v; 70 struct schedstate_percpu *spc = &ci->ci_schedstate; 71 static int num; 72 73 if (kthread_create(sched_idle, ci, &spc->spc_idleproc, "idle%d", num)) 74 panic("fork idle"); 75 76 num++; 77 } 78 79 void 80 sched_idle(void *v) 81 { 82 struct proc *p = curproc; 83 struct cpu_info *ci = v; 84 int s; 85 86 KERNEL_PROC_UNLOCK(p); 87 88 /* 89 * First time we enter here, we're not supposed to idle, 90 * just go away for a while. 91 */ 92 SCHED_LOCK(s); 93 p->p_stat = SSLEEP; 94 mi_switch(); 95 SCHED_UNLOCK(s); 96 97 while (1) { 98 KASSERT(ci == curcpu()); 99 KASSERT(curproc == ci->ci_schedstate.spc_idleproc); 100 101 while (!sched_is_idle()) { 102 struct schedstate_percpu *spc = &ci->ci_schedstate; 103 struct proc *dead; 104 105 SCHED_LOCK(s); 106 p->p_stat = SSLEEP; 107 mi_switch(); 108 SCHED_UNLOCK(s); 109 110 while ((dead = LIST_FIRST(&spc->spc_deadproc))) { 111 LIST_REMOVE(dead, p_hash); 112 exit2(dead); 113 } 114 } 115 116 splassert(IPL_NONE); 117 118 cpu_idle_enter(); 119 while (sched_is_idle()) 120 cpu_idle_cycle(); 121 cpu_idle_leave(); 122 } 123 } 124 125 /* 126 * To free our address space we have to jump through a few hoops. 127 * The freeing is done by the reaper, but until we have one reaper 128 * per cpu, we have no way of putting this proc on the deadproc list 129 * and waking up the reaper without risking having our address space and 130 * stack torn from under us before we manage to switch to another proc. 131 * Therefore we have a per-cpu list of dead processes where we put this 132 * proc and have idle clean up that list and move it to the reaper list. 133 * All this will be unnecessary once we can bind the reaper this cpu 134 * and not risk having it switch to another in case it sleeps. 135 */ 136 void 137 sched_exit(struct proc *p) 138 { 139 struct schedstate_percpu *spc = &curcpu()->ci_schedstate; 140 struct timeval tv; 141 struct proc *idle; 142 int s; 143 144 microuptime(&tv); 145 timersub(&tv, &spc->spc_runtime, &tv); 146 timeradd(&p->p_rtime, &tv, &p->p_rtime); 147 148 LIST_INSERT_HEAD(&spc->spc_deadproc, p, p_hash); 149 150 #ifdef MULTIPROCESSOR 151 KASSERT(__mp_lock_held(&kernel_lock) == 0); 152 #endif 153 154 SCHED_LOCK(s); 155 idle = spc->spc_idleproc; 156 idle->p_stat = SRUN; 157 cpu_switchto(NULL, idle); 158 panic("cpu_switchto returned"); 159 } 160 161 /* 162 * Run queue management. 163 * 164 * The run queue management is just like before, except that it's with 165 * a bit more modern queue handling. 166 */ 167 168 TAILQ_HEAD(prochead, proc) sched_qs[NQS]; 169 volatile int sched_whichqs; 170 171 void 172 sched_init_runqueues(void) 173 { 174 int i; 175 176 for (i = 0; i < NQS; i++) 177 TAILQ_INIT(&sched_qs[i]); 178 179 #ifdef MULTIPROCESSOR 180 __mp_lock_init(&sched_lock); 181 #endif 182 } 183 184 void 185 setrunqueue(struct proc *p) 186 { 187 int queue = p->p_priority >> 2; 188 189 SCHED_ASSERT_LOCKED(); 190 191 TAILQ_INSERT_TAIL(&sched_qs[queue], p, p_runq); 192 sched_whichqs |= (1 << queue); 193 } 194 195 void 196 remrunqueue(struct proc *p) 197 { 198 int queue = p->p_priority >> 2; 199 200 SCHED_ASSERT_LOCKED(); 201 202 TAILQ_REMOVE(&sched_qs[queue], p, p_runq); 203 if (TAILQ_EMPTY(&sched_qs[queue])) 204 sched_whichqs &= ~(1 << queue); 205 } 206 207 struct proc * 208 sched_chooseproc(void) 209 { 210 struct proc *p; 211 int queue; 212 213 SCHED_ASSERT_LOCKED(); 214 215 again: 216 if (sched_is_idle()) { 217 p = curcpu()->ci_schedstate.spc_idleproc; 218 if (p == NULL) { 219 int s; 220 /* 221 * We get here if someone decides to switch during 222 * boot before forking kthreads, bleh. 223 * This is kind of like a stupid idle loop. 224 */ 225 #ifdef MULTIPROCESSOR 226 __mp_unlock(&sched_lock); 227 #endif 228 spl0(); 229 delay(10); 230 SCHED_LOCK(s); 231 goto again; 232 } 233 KASSERT(p); 234 p->p_stat = SRUN; 235 } else { 236 queue = ffs(sched_whichqs) - 1; 237 p = TAILQ_FIRST(&sched_qs[queue]); 238 TAILQ_REMOVE(&sched_qs[queue], p, p_runq); 239 if (TAILQ_EMPTY(&sched_qs[queue])) 240 sched_whichqs &= ~(1 << queue); 241 } 242 243 return (p); 244 } 245