/* kern_clock.c 4.49 82/12/30 */
#include "../machine/reg.h"
#include "../machine/psl.h"
#include "../h/callout.h"
* Clock handling routines.
* This code is written for a machine with only one interval timer,
* and does timing and resource utilization estimation statistically
* based on the state of the machine hz times a second. A machine
* with proper clocks (running separately in user state, system state,
* interrupt state and idle state) as well as a time-of-day clock
* would allow a non-approximate implementation.
* * Keep more accurate statistics by simulating good interval timers.
* * Use the time-of-day clock on the VAX to keep more accurate time
* than is possible by repeated use of the interval timer.
* * Allocate more timeout table slots when table overflows.
/* bump a timeval by a small number of usec's */
#define bumptime(tp, usec) \
if ((tp)->tv_usec >= 1000000) { \
(tp)->tv_usec -= 1000000; \
* The (single) hardware interval timer.
* We update the events relating to real time, and then
* 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.
caddr_t pc
= (caddr_t
)regs
.r_pc
;
register struct callout
*p1
;
if (USERMODE(ps
)) /* aston needs ar0 */
* 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
for (p1
= calltodo
.c_next
; p1
&& p1
->c_time
<= 0; p1
= p1
->c_next
)
* 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
);
if (u
.u_procp
->p_nice
> NZERO
)
* 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
u
.u_ru
.ru_idrss
+= s
; u
.u_ru
.ru_isrss
+= 0; /* XXX */
if (u
.u_procp
->p_textp
) {
register int xrss
= u
.u_procp
->p_textp
->x_rssize
;
if (s
> u
.u_ru
.ru_maxrss
)
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
);
* 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 (profiling
< 2 && k
< s_textsize
)
kcount
[k
/ sizeof (*kcount
)]++;
bumptime(&u
.u_ru
.ru_stime
, tick
);
* 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
++)
* 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.
p
->p_quota
->q_cost
+= (p
->p_nice
> NZERO
?
(shconsts
.sc_tic
* ((2*NZERO
)-p
->p_nice
)) / NZERO
:
shconsts
.sc_tic
) * (((int)avenrun
[0]+2)/3);
* 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.
* Software priority level clock interrupt.
* Run periodic events from timeout queue.
caddr_t pc
= (caddr_t
)u
.u_ar0
[PC
];
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, give it a profiling tick.
if (USERMODE(ps
) && u
.u_prof
.pr_scale
) {
u
.u_procp
->p_flag
|= SOWEUPC
;
* Arrange that (*fun)(arg) is called in tim/hz seconds.
register struct callout
*p1
, *p2
, *pnew
;
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
)