kern_switch.c revision 74014
1/*
2 * Copyright (c) 1999 Peter Wemm <peter@FreeBSD.org>
3 * All rights reserved.
4 * Copyright (c) 2001 Jake Burkholder <jake@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: head/sys/kern/kern_switch.c 74014 2001-03-09 03:59:50Z jhb $
29 */
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34#include <sys/ktr.h>
35#include <sys/mutex.h>
36#include <sys/proc.h>
37#include <sys/queue.h>
38
39/*
40 * Global run queue.
41 */
42static struct runq runq;
43SYSINIT(runq, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, runq_init, &runq)
44
45/*
46 * Wrappers which implement old interface; act on global run queue.
47 */
48
49struct proc *
50chooseproc(void)
51{
52	return runq_choose(&runq);
53}
54
55int
56procrunnable(void)
57{
58	return runq_check(&runq);
59}
60
61void
62remrunqueue(struct proc *p)
63{
64	runq_remove(&runq, p);
65}
66
67void
68setrunqueue(struct proc *p)
69{
70	runq_add(&runq, p);
71}
72
73/*
74 * Clear the status bit of the queue corresponding to priority level pri,
75 * indicating that it is empty.
76 */
77static __inline void
78runq_clrbit(struct runq *rq, int pri)
79{
80	struct rqbits *rqb;
81
82	rqb = &rq->rq_status;
83	CTR4(KTR_RUNQ, "runq_clrbit: bits=%#x %#x bit=%#x word=%d",
84	    rqb->rqb_bits[RQB_WORD(pri)],
85	    rqb->rqb_bits[RQB_WORD(pri)] & ~RQB_BIT(pri),
86	    RQB_BIT(pri), RQB_WORD(pri));
87	rqb->rqb_bits[RQB_WORD(pri)] &= ~RQB_BIT(pri);
88}
89
90/*
91 * Find the index of the first non-empty run queue.  This is done by
92 * scanning the status bits, a set bit indicates a non-empty queue.
93 */
94static __inline int
95runq_findbit(struct runq *rq)
96{
97	struct rqbits *rqb;
98	int pri;
99	int i;
100
101	rqb = &rq->rq_status;
102	for (i = 0; i < RQB_LEN; i++)
103		if (rqb->rqb_bits[i]) {
104			pri = (RQB_FFS(rqb->rqb_bits[i]) - 1) +
105			    (i << RQB_L2BPW);
106			CTR3(KTR_RUNQ, "runq_findbit: bits=%#x i=%d pri=%d",
107			    rqb->rqb_bits[i], i, pri);
108			return (pri);
109		}
110
111	return (-1);
112}
113
114/*
115 * Set the status bit of the queue corresponding to priority level pri,
116 * indicating that it is non-empty.
117 */
118static __inline void
119runq_setbit(struct runq *rq, int pri)
120{
121	struct rqbits *rqb;
122
123	rqb = &rq->rq_status;
124	CTR4(KTR_RUNQ, "runq_setbit: bits=%#x %#x bit=%#x word=%d",
125	    rqb->rqb_bits[RQB_WORD(pri)],
126	    rqb->rqb_bits[RQB_WORD(pri)] | RQB_BIT(pri),
127	    RQB_BIT(pri), RQB_WORD(pri));
128	rqb->rqb_bits[RQB_WORD(pri)] |= RQB_BIT(pri);
129}
130
131/*
132 * Add the process to the queue specified by its priority, and set the
133 * corresponding status bit.
134 */
135void
136runq_add(struct runq *rq, struct proc *p)
137{
138	struct rqhead *rqh;
139	int pri;
140
141	mtx_assert(&sched_lock, MA_OWNED);
142	KASSERT(p->p_stat == SRUN, ("runq_add: proc %p (%s) not SRUN",
143	    p, p->p_comm));
144	pri = p->p_pri.pri_level / RQ_PPQ;
145	p->p_rqindex = pri;
146	runq_setbit(rq, pri);
147	rqh = &rq->rq_queues[pri];
148	CTR4(KTR_RUNQ, "runq_add: p=%p pri=%d %d rqh=%p",
149	    p, p->p_pri.pri_level, pri, rqh);
150	TAILQ_INSERT_TAIL(rqh, p, p_procq);
151}
152
153/*
154 * Return true if there are runnable processes of any priority on the run
155 * queue, false otherwise.  Has no side effects, does not modify the run
156 * queue structure.
157 */
158int
159runq_check(struct runq *rq)
160{
161	struct rqbits *rqb;
162	int i;
163
164	rqb = &rq->rq_status;
165	for (i = 0; i < RQB_LEN; i++)
166		if (rqb->rqb_bits[i]) {
167			CTR2(KTR_RUNQ, "runq_check: bits=%#x i=%d",
168			    rqb->rqb_bits[i], i);
169			return (1);
170		}
171	CTR0(KTR_RUNQ, "runq_check: empty");
172
173	return (0);
174}
175
176/*
177 * Find and remove the highest priority process from the run queue.
178 * If there are no runnable processes, the per-cpu idle process is
179 * returned.  Will not return NULL under any circumstances.
180 */
181struct proc *
182runq_choose(struct runq *rq)
183{
184	struct rqhead *rqh;
185	struct proc *p;
186	int pri;
187
188	mtx_assert(&sched_lock, MA_OWNED);
189	if ((pri = runq_findbit(rq)) != -1) {
190		rqh = &rq->rq_queues[pri];
191		p = TAILQ_FIRST(rqh);
192		KASSERT(p != NULL, ("runq_choose: no proc on busy queue"));
193		KASSERT(p->p_stat == SRUN,
194		    ("runq_chose: process %d(%s) in state %d", p->p_pid,
195		    p->p_comm, p->p_stat));
196		CTR3(KTR_RUNQ, "runq_choose: pri=%d p=%p rqh=%p", pri, p, rqh);
197		TAILQ_REMOVE(rqh, p, p_procq);
198		if (TAILQ_EMPTY(rqh)) {
199			CTR0(KTR_RUNQ, "runq_choose: empty");
200			runq_clrbit(rq, pri);
201		}
202		return (p);
203	}
204	CTR1(KTR_RUNQ, "runq_choose: idleproc pri=%d", pri);
205
206	return (PCPU_GET(idleproc));
207}
208
209/*
210 * Initialize a run structure.
211 */
212void
213runq_init(struct runq *rq)
214{
215	int i;
216
217	bzero(rq, sizeof *rq);
218	for (i = 0; i < RQ_NQS; i++)
219		TAILQ_INIT(&rq->rq_queues[i]);
220}
221
222/*
223 * Remove the process from the queue specified by its priority, and clear the
224 * corresponding status bit if the queue becomes empty.
225 */
226void
227runq_remove(struct runq *rq, struct proc *p)
228{
229	struct rqhead *rqh;
230	int pri;
231
232	mtx_assert(&sched_lock, MA_OWNED);
233	pri = p->p_rqindex;
234	rqh = &rq->rq_queues[pri];
235	CTR4(KTR_RUNQ, "runq_remove: p=%p pri=%d %d rqh=%p",
236	    p, p->p_pri.pri_level, pri, rqh);
237	KASSERT(p != NULL, ("runq_remove: no proc on busy queue"));
238	TAILQ_REMOVE(rqh, p, p_procq);
239	if (TAILQ_EMPTY(rqh)) {
240		CTR0(KTR_RUNQ, "runq_remove: empty");
241		runq_clrbit(rq, pri);
242	}
243}
244