386BSD 0.1 development
[unix-history] / usr / src / sys.386bsd / kern / kern_clock.c
CommitLineData
c556bd4c
WJ
1/*-
2 * Copyright (c) 1982, 1986, 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * @(#)kern_clock.c 7.16 (Berkeley) 5/9/91
34 */
35
36#include "param.h"
37#include "systm.h"
38#include "dkstat.h"
39#include "callout.h"
40#include "kernel.h"
41#include "proc.h"
42#include "resourcevar.h"
43
44#include "machine/cpu.h"
45
46#ifdef GPROF
47#include "gprof.h"
48#endif
49
50/*
51 * Clock handling routines.
52 *
53 * This code is written to operate with two timers which run
54 * independently of each other. The main clock, running at hz
55 * times per second, is used to do scheduling and timeout calculations.
56 * The second timer does resource utilization estimation statistically
57 * based on the state of the machine phz times a second. Both functions
58 * can be performed by a single clock (ie hz == phz), however the
59 * statistics will be much more prone to errors. Ideally a machine
60 * would have separate clocks measuring time spent in user state, system
61 * state, interrupt state, and idle state. These clocks would allow a non-
62 * approximate measure of resource utilization.
63 */
64
65/*
66 * TODO:
67 * time of day, system/user timing, timeouts, profiling on separate timers
68 * allocate more timeout table slots when table overflows.
69 */
70
71/*
72 * Bump a timeval by a small number of usec's.
73 */
74#define BUMPTIME(t, usec) { \
75 register struct timeval *tp = (t); \
76 \
77 tp->tv_usec += (usec); \
78 if (tp->tv_usec >= 1000000) { \
79 tp->tv_usec -= 1000000; \
80 tp->tv_sec++; \
81 } \
82}
83
84/*
85 * The hz hardware interval timer.
86 * We update the events relating to real time.
87 * If this timer is also being used to gather statistics,
88 * we run through the statistics gathering routine as well.
89 */
90hardclock(frame)
91 clockframe frame;
92{
93 register struct callout *p1;
94 register struct proc *p = curproc;
95 register struct pstats *pstats;
96 register int s;
97 int needsoft = 0;
98 extern int tickdelta;
99 extern long timedelta;
100
101 /*
102 * Update real-time timeout queue.
103 * At front of queue are some number of events which are ``due''.
104 * The time to these is <= 0 and if negative represents the
105 * number of ticks which have passed since it was supposed to happen.
106 * The rest of the q elements (times > 0) are events yet to happen,
107 * where the time for each is given as a delta from the previous.
108 * Decrementing just the first of these serves to decrement the time
109 * to all events.
110 */
111 p1 = calltodo.c_next;
112 while (p1) {
113 if (--p1->c_time > 0)
114 break;
115 needsoft = 1;
116 if (p1->c_time == 0)
117 break;
118 p1 = p1->c_next;
119 }
120
121 /*
122 * Curproc (now in p) is null if no process is running.
123 * We assume that curproc is set in user mode!
124 */
125 if (p)
126 pstats = p->p_stats;
127 /*
128 * Charge the time out based on the mode the cpu is in.
129 * Here again we fudge for the lack of proper interval timers
130 * assuming that the current state has been around at least
131 * one tick.
132 */
133 if (CLKF_USERMODE(&frame)) {
134 if (pstats->p_prof.pr_scale)
135 needsoft = 1;
136 /*
137 * CPU was in user state. Increment
138 * user time counter, and process process-virtual time
139 * interval timer.
140 */
141 BUMPTIME(&p->p_utime, tick);
142 if (timerisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) &&
143 itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0)
144 psignal(p, SIGVTALRM);
145 } else {
146 /*
147 * CPU was in system state.
148 */
149 if (p)
150 BUMPTIME(&p->p_stime, tick);
151 }
152
153 /*
154 * If the cpu is currently scheduled to a process, then
155 * charge it with resource utilization for a tick, updating
156 * statistics which run in (user+system) virtual time,
157 * such as the cpu time limit and profiling timers.
158 * This assumes that the current process has been running
159 * the entire last tick.
160 */
161 if (p) {
162 if ((p->p_utime.tv_sec+p->p_stime.tv_sec+1) >
163 p->p_rlimit[RLIMIT_CPU].rlim_cur) {
164 psignal(p, SIGXCPU);
165 if (p->p_rlimit[RLIMIT_CPU].rlim_cur <
166 p->p_rlimit[RLIMIT_CPU].rlim_max)
167 p->p_rlimit[RLIMIT_CPU].rlim_cur += 5;
168 }
169 if (timerisset(&pstats->p_timer[ITIMER_PROF].it_value) &&
170 itimerdecr(&pstats->p_timer[ITIMER_PROF], tick) == 0)
171 psignal(p, SIGPROF);
172
173 /*
174 * We adjust the priority of the current process.
175 * The priority of a process gets worse as it accumulates
176 * CPU time. The cpu usage estimator (p_cpu) is increased here
177 * and the formula for computing priorities (in kern_synch.c)
178 * will compute a different value each time the p_cpu increases
179 * by 4. The cpu usage estimator ramps up quite quickly when
180 * the process is running (linearly), and decays away
181 * exponentially, * at a rate which is proportionally slower
182 * when the system is busy. The basic principal is that the
183 * system will 90% forget that a process used a lot of CPU
184 * time in 5*loadav seconds. This causes the system to favor
185 * processes which haven't run much recently, and to
186 * round-robin among other processes.
187 */
188 p->p_cpticks++;
189 if (++p->p_cpu == 0)
190 p->p_cpu--;
191 if ((p->p_cpu&3) == 0) {
192 setpri(p);
193 if (p->p_pri >= PUSER)
194 p->p_pri = p->p_usrpri;
195 }
196 }
197
198 /*
199 * If the alternate clock has not made itself known then
200 * we must gather the statistics.
201 */
202 if (phz == 0)
203 gatherstats(&frame);
204
205 /*
206 * Increment the time-of-day, and schedule
207 * processing of the callouts at a very low cpu priority,
208 * so we don't keep the relatively high clock interrupt
209 * priority any longer than necessary.
210 */
211 if (timedelta == 0)
212 BUMPTIME(&time, tick)
213 else {
214 register delta;
215
216 if (timedelta < 0) {
217 delta = tick - tickdelta;
218 timedelta += tickdelta;
219 } else {
220 delta = tick + tickdelta;
221 timedelta -= tickdelta;
222 }
223 BUMPTIME(&time, delta);
224 }
225 if (needsoft) {
226 if (CLKF_BASEPRI(&frame)) {
227 /*
228 * Save the overhead of a software interrupt;
229 * it will happen as soon as we return, so do it now.
230 */
231 (void) splsoftclock();
232 softclock(frame);
233 } else
234 setsoftclock();
235 }
236}
237
238int dk_ndrive = DK_NDRIVE;
239/*
240 * Gather statistics on resource utilization.
241 *
242 * We make a gross assumption: that the system has been in the
243 * state it is in (user state, kernel state, interrupt state,
244 * or idle state) for the entire last time interval, and
245 * update statistics accordingly.
246 */
247gatherstats(framep)
248 clockframe *framep;
249{
250 register int cpstate, s;
251
252 /*
253 * Determine what state the cpu is in.
254 */
255 if (CLKF_USERMODE(framep)) {
256 /*
257 * CPU was in user state.
258 */
259 if (curproc->p_nice > NZERO)
260 cpstate = CP_NICE;
261 else
262 cpstate = CP_USER;
263 } else {
264 /*
265 * CPU was in system state. If profiling kernel
266 * increment a counter. If no process is running
267 * then this is a system tick if we were running
268 * at a non-zero IPL (in a driver). If a process is running,
269 * then we charge it with system time even if we were
270 * at a non-zero IPL, since the system often runs
271 * this way during processing of system calls.
272 * This is approximate, but the lack of true interval
273 * timers makes doing anything else difficult.
274 */
275 cpstate = CP_SYS;
276 if (curproc == NULL && CLKF_BASEPRI(framep))
277 cpstate = CP_IDLE;
278#ifdef GPROF
279 s = (u_long) CLKF_PC(framep) - (u_long) s_lowpc;
280 if (profiling < 2 && s < s_textsize)
281 kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
282#endif
283 }
284 /*
285 * We maintain statistics shown by user-level statistics
286 * programs: the amount of time in each cpu state, and
287 * the amount of time each of DK_NDRIVE ``drives'' is busy.
288 */
289 cp_time[cpstate]++;
290 for (s = 0; s < DK_NDRIVE; s++)
291 if (dk_busy&(1<<s))
292 dk_time[s]++;
293}
294
295/*
296 * Software priority level clock interrupt.
297 * Run periodic events from timeout queue.
298 */
299/*ARGSUSED*/
300softclock(frame)
301 clockframe frame;
302{
303
304 for (;;) {
305 register struct callout *p1;
306 register caddr_t arg;
307 register int (*func)();
308 register int a, s;
309
310 s = splhigh();
311 if ((p1 = calltodo.c_next) == 0 || p1->c_time > 0) {
312 splx(s);
313 break;
314 }
315 arg = p1->c_arg; func = p1->c_func; a = p1->c_time;
316 calltodo.c_next = p1->c_next;
317 p1->c_next = callfree;
318 callfree = p1;
319 splx(s);
320 (*func)(arg, a);
321 }
322
323 /*
324 * If no process to work with, we're finished.
325 */
326 if (curproc == 0) return;
327
328 /*
329 * If trapped user-mode and profiling, give it
330 * a profiling tick.
331 */
332 if (CLKF_USERMODE(&frame)) {
333 register struct proc *p = curproc;
334
335 if (p->p_stats->p_prof.pr_scale)
336 profile_tick(p, &frame);
337 /*
338 * Check to see if process has accumulated
339 * more than 10 minutes of user time. If so
340 * reduce priority to give others a chance.
341 */
342 if (p->p_ucred->cr_uid && p->p_nice == NZERO &&
343 p->p_utime.tv_sec > 10 * 60) {
344 p->p_nice = NZERO + 4;
345 setpri(p);
346 p->p_pri = p->p_usrpri;
347 }
348 }
349}
350
351/*
352 * Arrange that (*func)(arg) is called in t/hz seconds.
353 */
354timeout(func, arg, t)
355 int (*func)();
356 caddr_t arg;
357 register int t;
358{
359 register struct callout *p1, *p2, *pnew;
360 register int s = splhigh();
361
362 if (t <= 0)
363 t = 1;
364 pnew = callfree;
365 if (pnew == NULL)
366 panic("timeout table overflow");
367 callfree = pnew->c_next;
368 pnew->c_arg = arg;
369 pnew->c_func = func;
370 for (p1 = &calltodo; (p2 = p1->c_next) && p2->c_time < t; p1 = p2)
371 if (p2->c_time > 0)
372 t -= p2->c_time;
373 p1->c_next = pnew;
374 pnew->c_next = p2;
375 pnew->c_time = t;
376 if (p2)
377 p2->c_time -= t;
378 splx(s);
379}
380
381/*
382 * untimeout is called to remove a function timeout call
383 * from the callout structure.
384 */
385untimeout(func, arg)
386 int (*func)();
387 caddr_t arg;
388{
389 register struct callout *p1, *p2;
390 register int s;
391
392 s = splhigh();
393 for (p1 = &calltodo; (p2 = p1->c_next) != 0; p1 = p2) {
394 if (p2->c_func == func && p2->c_arg == arg) {
395 if (p2->c_next && p2->c_time > 0)
396 p2->c_next->c_time += p2->c_time;
397 p1->c_next = p2->c_next;
398 p2->c_next = callfree;
399 callfree = p2;
400 break;
401 }
402 }
403 splx(s);
404}
405
406/*
407 * Compute number of hz until specified time.
408 * Used to compute third argument to timeout() from an
409 * absolute time.
410 */
411hzto(tv)
412 struct timeval *tv;
413{
414 register long ticks;
415 register long sec;
416 int s = splhigh();
417
418 /*
419 * If number of milliseconds will fit in 32 bit arithmetic,
420 * then compute number of milliseconds to time and scale to
421 * ticks. Otherwise just compute number of hz in time, rounding
422 * times greater than representible to maximum value.
423 *
424 * Delta times less than 25 days can be computed ``exactly''.
425 * Maximum value for any timeout in 10ms ticks is 250 days.
426 */
427 sec = tv->tv_sec - time.tv_sec;
428 if (sec <= 0x7fffffff / 1000 - 1000)
429 ticks = ((tv->tv_sec - time.tv_sec) * 1000 +
430 (tv->tv_usec - time.tv_usec) / 1000) / (tick / 1000);
431 else if (sec <= 0x7fffffff / hz)
432 ticks = sec * hz;
433 else
434 ticks = 0x7fffffff;
435 splx(s);
436 return (ticks);
437}