Ultrix-3.1/sys/sys/clock.c
/**********************************************************************
* Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. *
* All Rights Reserved. *
* Reference "/usr/src/COPYRIGHT" for applicable restrictions. *
**********************************************************************/
/*
* SCCSID: @(#)clock.c 3.0 4/21/86
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/callo.h>
#include <sys/seg.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/reg.h>
#define SCHMAG 8/10
int hz; /* line frequency, see c.c */
struct callo *callhead, *callfree;
/*
* clock is called straight from
* the real time clock interrupt.
*
* Functions:
* reprime clock
* copy *switches to display
* implement callouts
* maintain user/system times
* maintain date
* profile
* lightning bolt wakeup (every second)
* alarm clock signals
* jab the scheduler
*/
clock(dev, sp, r1, ov, nps, r0, pc, ps)
dev_t dev;
caddr_t pc;
{
int a;
mapinfo map;
extern caddr_t waitloc;
extern char *panicstr;
/*
* restart clock
*/
lks->r[0] = 0115;
/*
* ensure normal mapping of kernel data
*/
savemap(map);
/*
* display register
*/
display();
/*
* callouts
* never after panics
* if none, just continue
* else update first non-zero time
*/
if (panicstr == (char *)0) {
register struct callo *p1, *p2;
if ((p1 = callhead) == NULL)
goto out;
do {
if (p1->c_time > 0) {
p1->c_time--;
break;
}
} while (p1 = p1->c_next);
/*
* if ps is high, just return
*/
if (BASEPRI(ps))
goto out;
/*
* callout
*/
spl5(); /* berkeley 2.9 doesn't have this */
p1 = callhead;
if (p1->c_time <= 0) {
do {
(*p1->c_func)(p1->c_arg);
p2 = p1;
p1 = p1->c_next;
} while (p1 && p1->c_time <= 0);
p2->c_next = callfree;
callfree = callhead;
callhead = p1;
}
}
/*
* lightning bolt time-out
* and time of day
*/
out:
a = 0; /* user */
if (USERMODE(ps)) {
u.u_utime++;
if(u.u_prof.pr_scale)
addupc(pc, &u.u_prof, 1);
if(u.u_procp->p_nice > NZERO)
a++; /* nice */
} else {
a = 2; /* system */
if (pc == waitloc)
a++; /* idle */
u.u_stime++;
}
cp_time[a]++; /* tally cpu time */
/*
* Disk I/O instrumentation
*/
{
register struct ios *dp;
register int i, j;
int k;
k = 0;
for (i = DK_NC-1; i >= 0; --i) {
if (dp = dk_iop[i]) {
for (j = dk_nd[i]; j > 0; dp++, --j)
if(dp->dk_busy) {
dp->dk_time++;
k++;
}
}
}
if(k && (a == 3)) /* tally idle with I/O active */
cp_time[4]++;
}
{
register struct proc *pp;
pp = u.u_procp;
/*
* el_c_pid is the process ID of the
* error log copy process (elc),
* DO NOT NICE this process !
*/
if((++pp->p_cpu == 0) || (pp->p_pid == el_c_pid))
pp->p_cpu--;
}
if (++lbolt >= hz && !(BASEPRI(ps)))
{
register struct proc *pp;
register int s;
lbolt -= hz;
++time;
spl1();
#ifdef UCB_LOAD
if ((time&03) == 0) /* every 4 seconds */
meter();
#endif UCB_LOAD
runrun++;
wakeup((caddr_t)&lbolt);
for(pp = &proc[0]; pp <= maxproc; pp++)
if (pp->p_stat && pp->p_stat<SZOMB) {
if (pp->p_time != 127)
pp->p_time++;
if(pp->p_clktim && --pp->p_clktim == 0)
#ifdef SELECT
/*
* 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_flag & STIMO) {
s = spl6();
switch (pp->p_stat) {
case SSLEEP:
setrun(pp);
break;
case SSTOP:
unsleep(pp);
break;
}
pp->p_flag &= ~STIMO;
splx(s);
} else
#endif SELECT
psignal(pp, SIGALRM);
s = (pp->p_cpu & 0377)*SCHMAG + pp->p_nice - NZERO;
if(s < 0)
s = 0;
if(s > 255)
s = 255;
pp->p_cpu = s;
if(pp->p_pri >= PUSER)
setpri(pp);
}
if(runin!=0) {
runin = 0;
wakeup((caddr_t)&runin);
}
}
restormap(map);
}
/*
* timeout is called to arrange that
* fun(arg) is called in tim/HZ seconds.
* An entry is sorted 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
* updating all entries.
*
* The panic is there because there is nothing
* intelligent to be done if an entry won't fit.
*/
timeout(fun, arg, tim)
int (*fun)();
caddr_t arg;
{
register struct callo *p1, *p2;
register int t;
int s;
mapinfo map; /* save map so callout[] can be after mb_end */
savemap(map);
t = tim;
s = spl7();
if (callfree == NULL) {
restormap(map);
panic("Timeout table overflow");
}
for (p2 = NULL, p1 = callhead; p1; p2 = p1, p1 = p1->c_next) {
if (p1->c_time > t) {
p1->c_time -= t;
break;
}
t -= p1->c_time;
}
p1 = callfree;
callfree = p1->c_next;
if (p2) {
p1->c_next = p2->c_next;
p2->c_next = p1;
} else {
p1->c_next = callhead;
callhead = p1;
}
p1->c_time = t;
p1->c_func = fun;
p1->c_arg = arg;
splx(s);
restormap(map);
}
calloinit()
{
register struct callo *p;
callhead = (struct callo *)NULL;
callfree = (struct callo *)NULL;
for (p = &callout[0]; p < &callout[ncall]; p++) {
p->c_next = callfree;
callfree = p;
}
}
#ifdef UCB_LOAD
/*
* Compute Tenex style load average. This code based on the 2.9 code,
* which is adapted from similar code by Bill Joy on the Vax system.
* There are two major changes: 1) We avoid floating point since not all
* pdp-11's have it. "floating point" numbers here are stored in a 16
* bit short, with 8 bits on each side of the decimal point. Some partial
* products will have 16 bits to the right. See the comments below on how
* the equation was devived. 2) We do the proc counts every 4 seconds
* instead of every 5, and we do the averaging every 16 seconds.
*
* We count every 4 seconds, because it's easier to see if 4 seconds
* have gone by (do a bit test) than if 5 seconds have gone by (have
* to call lrem). Since we only have 8 bits to hold the e^x values,
* and they are so close to 1, by averaging every 4 values and computing
* the load average every 16 seconds, we can use e^x values a bit
* farther from 1, giving us more precision in the load average (especially
* in the 15 minute load average).
*/
/*
* The Vax algorithm is:
*
* /*
* * Constants for averages over 1, 5, and 15 minutes
* * when sampling at 5 second intervals.
* * /
* double cexp[3] = {
* 0.9200444146293232, /* exp(-1/12) * /
* 0.9834714538216174, /* exp(-1/60) * /
* 0.9944598480048967, /* exp(-1/180 * /
* };
*
* /*
* * Compute a tenex style load average of a quantity on
* * 1, 5, and 15 minute intervals.
* * /
* loadav(avg, n)
* register double *avg;
* int n;
* {
* register int i;
*
* for (i = 0; i < 3; i++)
* avg[i] = cexp[i] * avg[i] + n * (1.0 - cexp[i]);
* }
*/
short avenrun[3]; /* internal load average in psuedo-floating point */
long cexp[3] = {
0304, /* 256*exp(-4/15) */
0363, /* 256*exp(-4/75) */
0373 /* 256*exp(-4/225) */
};
static /* so we don't have to go through the thunk */
meter()
{
static nrun = 0, cycle = 0;
register n = 0;
{
register struct proc *p;
for (p = &proc[1]; p <= maxproc; p++) {
if (p->p_stat) {
switch(p->p_stat) {
case SSLEEP:
case SSTOP:
if (p->p_pri <= PZERO)
n++;
break;
case SRUN:
case SIDL:
n++;
break;
}
}
}
}
if ((++cycle)&3) {
nrun += n;
} else {
register short *avg = avenrun;
register int i;
n += nrun;
nrun = 0;
/*
* rn = regular number
* pfp = psudeo-floating point number.
* Given: 1) rn * 256 = pfp
* 2) rn * rn = rn
* pfp = rn * 256
* = rn * rn * 256
* = rn * (256 / 256) * rn * 256
* = (rn * 256) / 256 * (rn * 256)
* = pfp / 256 * pfp
* = (pfp * pfp)/256
* Therefore: when multiplying two pfp's, the
* result must be divided by 256 to yeild a pfp.
* pfp + pfp = rn * 256 + rn * 256
* = (rn + rn) * 256
* = rn * 256
* = pfp
* Therefore: two pfp's add up to another pfp.
*
* w = cexp[i] (in pfp)
* v = *avg (in pfp)
* n' = new value to weight in (not in pfp)
*
* w'*v' + n'*(1-w')
* convert n',w',v' & 1 to pfp, taking into
* account where we multiply two pfp's.
* (w*v)/256 + (n'*256*(256 - w))/256
* (w*v + n'*256*256 - n'*256*w)/256
* ((w*v - n'*256*w) + n'*256*256)/256
* (w*(v - n'*256) + n'*256*256)/256
* n' is sum of 4 values, divide by 4 to get the average
* (w*(v - n'/4*256) + n'/4*256*256)/256
* (w*(v - n'*64) + n'*64*256)/256
* (w*(v - n'<<6) + n'<<14)>>8
*/
for (i = 0; i < 3; avg++, i++) {
*avg = (cexp[i] * ((*avg)-(n<<6)) + (((long)n)<<14)) >> 8;
}
}
}
#endif