xref: /openbsd-src/sys/kern/kern_sched.c (revision 935ea7a7246129d55ab01c451edd33ac2d12eaef)
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