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