/* kern_clock.c 4.26 81/10/16 */
#include "../h/callout.h"
* Hardclock is called straight from
* the real time clock interrupt.
* We limit the work we do at real clock interrupt time to:
* decrementing time to callouts
* recording cpu time usage
* modifying priority of current process
* arrange for soft clock interrupt
* At software (softclock) interrupt time we:
* lightning bolt wakeup (every second)
* On the vax softclock interrupts are implemented by
* software interrupts. Note that we may have multiple softclock
* interrupts compressed into one (due to excessive interrupt load),
* but that hardclock interrupts should never be lost.
register struct callout
*p1
;
register struct proc
*pp
;
for (p1
= calltodo
.c_next
; p1
&& p1
->c_time
<= 0; p1
= p1
->c_next
)
* Maintain iostat and per-process cpu statistics
if (u
.u_procp
->p_textp
) {
register int xrss
= u
.u_procp
->p_textp
->x_rssize
;
if (s
> u
.u_vm
.vm_maxrss
)
if ((u
.u_vm
.vm_utime
+u
.u_vm
.vm_stime
+1)/hz
> u
.u_limit
[LIM_CPU
]) {
psignal(u
.u_procp
, SIGXCPU
);
if (u
.u_limit
[LIM_CPU
] < INFINITY
- 5)
* Update iostat information.
if(u
.u_procp
->p_nice
> NZERO
)
int k
= ((int)pc
& 0x7fffffff) / 8;
for (s
= 0; s
< DK_NDRIVE
; s
++)
* Adjust priority of current process.
pp
->p_pri
= pp
->p_usrpri
;
* On 780's, impelement a fast UBA watcher,
* to make sure uba's don't get stuck.
if (cpu
== VAX_780
&& panicstr
== 0 && !BASEPRI(ps
))
* Schedule a software interrupt for the rest
* The digital decay cpu usage priority assignment is scaled to run in
* time as expanded by the 1 minute load average. Each second we
* multiply the the previous cpu usage estimate by
* The following relates the load average to the period over which
* cpu usage is 90% forgotten:
* This is a great improvement on the previous algorithm which
* decayed the priorities by a constant, and decayed away all knowledge
* of previous activity in about 20 seconds. Under heavy load,
* the previous algorithm degenerated to round-robin with poor response
* time when there was a high load average.
#define ave(a,b) ((int)(((int)(a*b))/(b+1)))
* Constant for decay filter for cpu usage field
* in process table (used by ps au).
double ccpu
= 0.95122942450071400909; /* exp(-1/20) */
* Software clock interrupt.
* This routine runs at lower priority than device interrupts.
register struct callout
*p1
;
register struct proc
*pp
;
* Perform callouts (but not after panic's!)
if ((p1
= calltodo
.c_next
) == 0 || p1
->c_time
> 0) {
calltodo
.c_next
= p1
->c_next
;
s
= spl5(); dhtimer(); splx(s
);
s
= spl5(); dztimer(); splx(s
);
* If idling and processes are waiting to swap in,
* Run paging daemon every 1/4 sec.
if (lbolt
% (hz
/4) == 0) {
* Reschedule every 1/10 sec.
if (lbolt
% (hz
/10) == 0) {
* Lightning bolt every second:
* process priority recomputation
* virtual memory metering
* kick swapper if processes want in
* This doesn't mean much on VAX since we run at
* software interrupt time... if hardclock()
* calls softclock() directly, it prevents
* this code from running when the priority
* was raised when the clock interrupt occurred.
* If we didn't run a few times because of
* long blockage at high ipl, we don't
* really want to run this code several times,
* so squish out all multiples of hz here.
* Wakeup lightning bolt sleepers.
* Processes sleep on lbolt to wait
* for short amounts of time (e.g. 1 second).
* Recompute process priority and process
* sleep() system calls as well as internal
* sleeps with timeouts (tsleep() kernel routine).
for (pp
= proc
; pp
< procNPROC
; pp
++)
if (pp
->p_stat
&& pp
->p_stat
!=SZOMB
) {
* Increase resident time, to max of 127 seconds
* (it is kept in a character.) For
* loaded processes this is time in core; for
* swapped processes, this is time on drum.
* If process has clock counting down, and it
* expires, set it running (if this is a tsleep()),
* or give it an SIGALRM (if the user process
* is using alarm signals.
if (pp
->p_clktim
&& --pp
->p_clktim
== 0)
if (pp
->p_flag
& STIMO
) {
* If process is blocked, increment computed
* time blocked. This is used in swap scheduling.
if (pp
->p_stat
==SSLEEP
|| pp
->p_stat
==SSTOP
)
if (pp
->p_slptime
!= 127)
* Update digital filter estimation of process
* cpu utilization for loaded processes.
pp
->p_pctcpu
= ccpu
* pp
->p_pctcpu
+
(1.0 - ccpu
) * (pp
->p_cpticks
/(float)hz
);
* Recompute process priority. The number p_cpu
* is a weighted estimate of cpu time consumed.
* A process which consumes cpu time has this
* increase regularly. We here decrease it by
* a fraction based on load average giving a digital
* decay filter which damps out in about 5 seconds
* when seconds are measured in time expanded by the
* If a process is niced, then the nice directly
* affects the new priority. The final priority
* is in the range 0 to 255, to fit in a character.
a
= ave((pp
->p_cpu
& 0377), avenrun
[0]*nrscale
) +
* Now have computed new process priority
* in p->p_usrpri. Carefully change p->p_pri.
* A process is on a run queue associated with
* this priority, so we must block out process
* state changes during the transition.
if (pp
->p_pri
>= PUSER
) {
if ((pp
!= u
.u_procp
|| noproc
) &&
pp
->p_pri
!= pp
->p_usrpri
) {
pp
->p_pri
= pp
->p_usrpri
;
pp
->p_pri
= pp
->p_usrpri
;
* Perform virtual memory metering.
* If the swap process is trying to bring
* a process in, have it look again to see
* If there are pages that have been cleaned,
* jolt the pageout daemon to process them.
* We do this here so that these pages will be
* freed if there is an abundance of memory and the
* daemon would not be awakened otherwise.
wakeup((caddr_t
)&proc
[2]);
* If the trap occurred from usermode,
* then check to see if it has now been
* running more than 10 minutes of user time
* and should thus run with reduced priority
* to give other processes a chance.
if (pp
->p_uid
&& pp
->p_nice
== NZERO
&&
u
.u_vm
.vm_utime
> 600 * hz
)
pp
->p_pri
= pp
->p_usrpri
;
* If trapped user-mode, give it a profiling tick.
if (USERMODE(ps
) && u
.u_prof
.pr_scale
) {
u
.u_procp
->p_flag
|= SOWEUPC
;
* Timeout is called to arrange that
* fun(arg) is called in tim/hz seconds.
* An entry is linked into the callout
* structure. The time in each structure
* entry is the number of hz's more
* than the previous entry.
* In this way, decrementing the
* first entry has the effect of
* The panic is there because there is nothing
* intelligent to be done if an entry won't fit.
register struct callout
*p1
, *p2
, *pnew
;
if (fun
== ttrstrt
&& arg
== 0)
panic("timeout ttrstr arg");
panic("timeout table overflow");
for (p1
= &calltodo
; (p2
= p1
->c_next
) && p2
->c_time
< t
; p1
= p2
)