* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)kern_clock.c 7.1 (Berkeley) %G%
#include "../machine/reg.h"
#include "../machine/psl.h"
#include "../vax/clock.h"
#define ADJTIME /* For now... */
* Clock handling routines.
* This code is written to operate with two timers which run
* independently of each other. The main clock, running at hz
* times per second, is used to do scheduling and timeout calculations.
* The second timer does resource utilization estimation statistically
* based on the state of the machine phz times a second. Both functions
* can be performed by a single clock (ie hz == phz), however the
* statistics will be much more prone to errors. Ideally a machine
* would have separate clocks measuring time spent in user state, system
* state, interrupt state, and idle state. These clocks would allow a non-
* approximate measure of resource utilization.
* time of day, system/user timing, timeouts, profiling on separate timers
* allocate more timeout table slots when table overflows.
* Bump a timeval by a small number of usec's.
#define BUMPTIME(t, usec) { \
register struct timeval *tp = (t); \
if (tp->tv_usec >= 1000000) { \
tp->tv_usec -= 1000000; \
* The hz hardware interval timer.
* We update the events relating to real time.
* If this timer is also being used to gather statistics,
* we run through the statistics gathering routine as well.
register struct callout
*p1
;
* Update real-time timeout queue.
* At front of queue are some number of events which are ``due''.
* The time to these is <= 0 and if negative represents the
* number of ticks which have passed since it was supposed to happen.
* The rest of the q elements (times > 0) are events yet to happen,
* where the time for each is given as a delta from the previous.
* Decrementing just the first of these serves to decrement the time
* Charge the time out based on the mode the cpu is in.
* Here again we fudge for the lack of proper interval timers
* assuming that the current state has been around at least
* CPU was in user state. Increment
* user time counter, and process process-virtual time
BUMPTIME(&u
.u_ru
.ru_utime
, tick
);
if (timerisset(&u
.u_timer
[ITIMER_VIRTUAL
].it_value
) &&
itimerdecr(&u
.u_timer
[ITIMER_VIRTUAL
], tick
) == 0)
psignal(u
.u_procp
, SIGVTALRM
);
* CPU was in system state.
BUMPTIME(&u
.u_ru
.ru_stime
, tick
);
* If the cpu is currently scheduled to a process, then
* charge it with resource utilization for a tick, updating
* statistics which run in (user+system) virtual time,
* such as the cpu time limit and profiling timers.
* This assumes that the current process has been running
if ((u
.u_ru
.ru_utime
.tv_sec
+u
.u_ru
.ru_stime
.tv_sec
+1) >
u
.u_rlimit
[RLIMIT_CPU
].rlim_cur
) {
psignal(u
.u_procp
, SIGXCPU
);
if (u
.u_rlimit
[RLIMIT_CPU
].rlim_cur
<
u
.u_rlimit
[RLIMIT_CPU
].rlim_max
)
u
.u_rlimit
[RLIMIT_CPU
].rlim_cur
+= 5;
if (timerisset(&u
.u_timer
[ITIMER_PROF
].it_value
) &&
itimerdecr(&u
.u_timer
[ITIMER_PROF
], tick
) == 0)
psignal(u
.u_procp
, SIGPROF
);
u
.u_ru
.ru_isrss
+= 0; /* XXX (haven't got this) */
if (u
.u_procp
->p_textp
) {
register int xrss
= u
.u_procp
->p_textp
->x_rssize
;
if (s
> u
.u_ru
.ru_maxrss
)
* We adjust the priority of the current process.
* The priority of a process gets worse as it accumulates
* CPU time. The cpu usage estimator (p_cpu) is increased here
* and the formula for computing priorities (in kern_synch.c)
* will compute a different value each time the p_cpu increases
* by 4. The cpu usage estimator ramps up quite quickly when
* the process is running (linearly), and decays away exponentially,
* at a rate which is proportionally slower when the system is
* busy. The basic principal is that the system will 90% forget
* that a process used a lot of CPU time in 5*loadav seconds.
* This causes the system to favor processes which haven't run
* much recently, and to round-robin among other processes.
* If the alternate clock has not made itself known then
* we must gather the statistics.
* Increment the time-of-day, and schedule
* processing of the callouts at a very low cpu priority,
* so we don't keep the relatively high clock interrupt
* priority any longer than necessary.
bumptime(&time
, tick
-ADJ_TICK
);
bumptime(&time
, tick
+ADJ_TICK
);
delta
= tick
- tickdelta
;
delta
= tick
+ tickdelta
;
int dk_ndrive
= DK_NDRIVE
;
* Gather statistics on resource utilization.
* We make a gross assumption: that the system has been in the
* state it is in (user state, kernel state, interrupt state,
* or idle state) for the entire last time interval, and
* update statistics accordingly.
* Determine what state the cpu is in.
if (u
.u_procp
->p_nice
> NZERO
)
* CPU was in system state. If profiling kernel
* increment a counter. If no process is running
* then this is a system tick if we were running
* at a non-zero IPL (in a driver). If a process is running,
* then we charge it with system time even if we were
* at a non-zero IPL, since the system often runs
* this way during processing of system calls.
* This is approximate, but the lack of true interval
* timers makes doing anything else difficult.
if (noproc
&& BASEPRI(ps
))
if (profiling
< 2 && s
< s_textsize
)
kcount
[s
/ (HISTFRACTION
* sizeof (*kcount
))]++;
* We maintain statistics shown by user-level statistics
* programs: the amount of time in each cpu state, and
* the amount of time each of DK_NDRIVE ``drives'' is busy.
for (s
= 0; s
< DK_NDRIVE
; s
++)
* Software priority level clock interrupt.
* Run periodic events from timeout queue.
register struct callout
*p1
;
if ((p1
= calltodo
.c_next
) == 0 || p1
->c_time
> 0) {
arg
= p1
->c_arg
; func
= p1
->c_func
; a
= p1
->c_time
;
calltodo
.c_next
= p1
->c_next
;
* If trapped user-mode and profiling, give it
register struct proc
*p
= u
.u_procp
;
* Check to see if process has accumulated
* more than 10 minutes of user time. If so
* reduce priority to give others a chance.
if (p
->p_uid
&& p
->p_nice
== NZERO
&&
u
.u_ru
.ru_utime
.tv_sec
> 10 * 60) {
* Arrange that (*fun)(arg) is called in t/hz seconds.
register struct callout
*p1
, *p2
, *pnew
;
register int s
= splhigh();
panic("timeout table overflow");
for (p1
= &calltodo
; (p2
= p1
->c_next
) && p2
->c_time
< t
; p1
= p2
)
* untimeout is called to remove a function timeout call
* from the callout structure.
register struct callout
*p1
, *p2
;
for (p1
= &calltodo
; (p2
= p1
->c_next
) != 0; p1
= p2
) {
if (p2
->c_func
== fun
&& p2
->c_arg
== arg
) {
if (p2
->c_next
&& p2
->c_time
> 0)
p2
->c_next
->c_time
+= p2
->c_time
;
* Compute number of hz until specified time.
* Used to compute third argument to timeout() from an
* If number of milliseconds will fit in 32 bit arithmetic,
* then compute number of milliseconds to time and scale to
* ticks. Otherwise just compute number of hz in time, rounding
* times greater than representible to maximum value.
* Delta times less than 25 days can be computed ``exactly''.
* Maximum value for any timeout in 10ms ticks is 250 days.
sec
= tv
->tv_sec
- time
.tv_sec
;
if (sec
<= 0x7fffffff / 1000 - 1000)
ticks
= ((tv
->tv_sec
- time
.tv_sec
) * 1000 +
(tv
->tv_usec
- time
.tv_usec
) / 1000) / (tick
/ 1000);
else if (sec
<= 0x7fffffff / hz
)
} *uap
= (struct a
*)u
.u_ap
;
register struct uprof
*upp
= &u
.u_prof
;
upp
->pr_base
= uap
->bufbase
;
upp
->pr_size
= uap
->bufsize
;
upp
->pr_off
= uap
->pcoffset
;
upp
->pr_scale
= uap
->pcscale
;
sleep((caddr_t
)&u
, PSLEP
);