|
|
1.1 root 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: */
90: hardclock(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:
238: int 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: */
247: gatherstats(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 = CLKF_PC(framep) - 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*/
300: softclock(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: * If trapped user-mode and profiling, give it
324: * a profiling tick.
325: */
326: if (CLKF_USERMODE(&frame)) {
327: register struct proc *p = curproc;
328:
329: if (p->p_stats->p_prof.pr_scale)
330: profile_tick(p, &frame);
331: /*
332: * Check to see if process has accumulated
333: * more than 10 minutes of user time. If so
334: * reduce priority to give others a chance.
335: */
336: if (p->p_ucred->cr_uid && p->p_nice == NZERO &&
337: p->p_utime.tv_sec > 10 * 60) {
338: p->p_nice = NZERO + 4;
339: setpri(p);
340: p->p_pri = p->p_usrpri;
341: }
342: }
343: }
344:
345: /*
346: * Arrange that (*func)(arg) is called in t/hz seconds.
347: */
348: timeout(func, arg, t)
349: int (*func)();
350: caddr_t arg;
351: register int t;
352: {
353: register struct callout *p1, *p2, *pnew;
354: register int s = splhigh();
355:
356: if (t <= 0)
357: t = 1;
358: pnew = callfree;
359: if (pnew == NULL)
360: panic("timeout table overflow");
361: callfree = pnew->c_next;
362: pnew->c_arg = arg;
363: pnew->c_func = func;
364: for (p1 = &calltodo; (p2 = p1->c_next) && p2->c_time < t; p1 = p2)
365: if (p2->c_time > 0)
366: t -= p2->c_time;
367: p1->c_next = pnew;
368: pnew->c_next = p2;
369: pnew->c_time = t;
370: if (p2)
371: p2->c_time -= t;
372: splx(s);
373: }
374:
375: /*
376: * untimeout is called to remove a function timeout call
377: * from the callout structure.
378: */
379: untimeout(func, arg)
380: int (*func)();
381: caddr_t arg;
382: {
383: register struct callout *p1, *p2;
384: register int s;
385:
386: s = splhigh();
387: for (p1 = &calltodo; (p2 = p1->c_next) != 0; p1 = p2) {
388: if (p2->c_func == func && p2->c_arg == arg) {
389: if (p2->c_next && p2->c_time > 0)
390: p2->c_next->c_time += p2->c_time;
391: p1->c_next = p2->c_next;
392: p2->c_next = callfree;
393: callfree = p2;
394: break;
395: }
396: }
397: splx(s);
398: }
399:
400: /*
401: * Compute number of hz until specified time.
402: * Used to compute third argument to timeout() from an
403: * absolute time.
404: */
405: hzto(tv)
406: struct timeval *tv;
407: {
408: register long ticks;
409: register long sec;
410: int s = splhigh();
411:
412: /*
413: * If number of milliseconds will fit in 32 bit arithmetic,
414: * then compute number of milliseconds to time and scale to
415: * ticks. Otherwise just compute number of hz in time, rounding
416: * times greater than representible to maximum value.
417: *
418: * Delta times less than 25 days can be computed ``exactly''.
419: * Maximum value for any timeout in 10ms ticks is 250 days.
420: */
421: sec = tv->tv_sec - time.tv_sec;
422: if (sec <= 0x7fffffff / 1000 - 1000)
423: ticks = ((tv->tv_sec - time.tv_sec) * 1000 +
424: (tv->tv_usec - time.tv_usec) / 1000) / (tick / 1000);
425: else if (sec <= 0x7fffffff / hz)
426: ticks = sec * hz;
427: else
428: ticks = 0x7fffffff;
429: splx(s);
430: return (ticks);
431: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.