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