/* * SCCS id @(#)clock.c 2.1 (Berkeley) 9/1/83 */ #include "param.h" #include <sys/systm.h> #include <sys/callout.h> #include <sys/seg.h> #include <sys/dir.h> #include <sys/user.h> #include <sys/proc.h> #include <sys/reg.h> #ifdef UCB_METER #include <sys/text.h> #include <sys/vm.h> #endif #ifdef UCB_NET /* * shifted to keep "make depend" from finding these for now... */ # include <sys/protosw.h> # include <sys/socket.h> # include "../net/if.h" # include "../net/in_systm.h" #endif #ifdef UCB_FRCSWAP extern int idleflg; /* If set, allow incore forks and expands */ /* Set before idle(), cleared in clock.c */ #endif #ifdef UCB_NET /* * Protoslow is like lbolt, but for slow protocol timeouts, counting * up to (hz/PR_SLOWHZ), then causing a pfslowtimo(). * Protofast is like lbolt, but for fast protocol timeouts, counting * up to (hz/PR_FASTHZ), then causing a pffasttimo(). */ int protoslow; int protofast; int ifnetslow; int netoff; #endif #define SCHMAG 8/10 /* * 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 */ /*ARGSUSED*/ #ifdef MENLO_KOV clock(dev, sp, r1, ov, nps, r0, pc, ps) #else clock(dev, sp, r1, nps, r0, pc, ps) #endif dev_t dev; caddr_t pc; { register a; mapinfo map; extern caddr_t waitloc; extern char *panicstr; /* * restart clock */ if(lks) *lks = 0115; /* * ensure normal mapping of kernel data */ savemap(map); #ifdef DISPLAY /* * display register */ display(); #endif /* * callouts * never after panics * if none, just continue * else update first non-zero time */ if (panicstr == (char *) 0) { register struct callout *p1, *p2; if(callout[0].c_func == NULL) goto out; p2 = &callout[0]; while(p2->c_time<=0 && p2->c_func!=NULL) p2++; p2->c_time--; #ifdef UCB_NET /* * Time moves on for protocols. */ if(!netoff) { --protoslow; --protofast; --ifnetslow; if(protoslow<=0 || protofast<=0 || ifnetslow<=0) schednetisr(NETISR_CLOCK); } #endif /* * if ps is high, just return */ if (!BASEPRI(ps)) goto out; /* * callout */ if(callout[0].c_time <= 0) { p1 = &callout[0]; while(p1->c_func != 0 && p1->c_time <= 0) { (*p1->c_func)(p1->c_arg); p1++; } p2 = &callout[0]; while(p2->c_func = p1->c_func) { p2->c_time = p1->c_time; p2->c_arg = p1->c_arg; p1++; p2++; } } } out: a = (dk_busy != 0); 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 += 2; } else { a += 4; if (pc == waitloc) a += 2; u.u_stime++; } sy_time[a] += 1; dk_time[dk_busy & ((1 << ndisk) - 1)] += 1; { register struct proc *pp; pp = u.u_procp; if(++pp->p_cpu == 0) pp->p_cpu--; } /* * lightning bolt time-out * and time of day */ if ((++lbolt >= hz) && (BASEPRI(ps))) { register struct proc *pp; lbolt -= hz; #ifdef UCB_FRCSWAP idleflg = 0; #endif ++time; (void) _spl1(); #if defined(UCB_LOAD) || defined(UCB_METER) meter(); #endif 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++; #ifdef UCB_METER if (pp->p_stat == SSLEEP || pp->p_stat == SSTOP) if (pp->p_slptime != 127) pp->p_slptime++; #endif UCB_METER if (pp->p_clktim && --pp->p_clktim == 0) #ifdef UCB_NET /* * 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) { a = spl6(); switch (pp->p_stat) { case SSLEEP: setrun(pp); break; case SSTOP: unsleep(pp); break; } pp->p_flag &= ~STIMO; splx(a); } else #endif psignal(pp, SIGALRM); a = (pp->p_cpu & 0377) * SCHMAG + pp->p_nice - NZERO; if(a < 0) a = 0; if(a > 255) a = 255; pp->p_cpu = a; 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 callout *p1, *p2; register int t; int s; t = tim; p1 = &callout[0]; s = spl7(); while(p1->c_func != 0 && p1->c_time <= t) { t -= p1->c_time; p1++; } p1->c_time -= t; p2 = p1; while(p2->c_func != 0) p2++; if (p2 >= callNCALL) panic("Timeout table overflow"); while(p2 >= p1) { (p2+1)->c_time = p2->c_time; (p2+1)->c_func = p2->c_func; (p2+1)->c_arg = p2->c_arg; p2--; } p1->c_time = t; p1->c_func = fun; p1->c_arg = arg; splx(s); return; } #if defined(UCB_LOAD) || defined(UCB_METER) /* * Count up various things once a second */ short avenrun[3]; /* internal load average in psuedo-floating point */ #define ave(smooth,new,time) (smooth) = (((time)-1) * (smooth) + (new))/ (time) #define ctok(cliks) (((cliks) >> 4) & 07777) /* clicks to KB */ meter() { #ifdef UCB_METER register unsigned *cp, *rp; register long *sp; ave(avefree, ctok(freemem), 5); #endif if (time % 5 == 0) { vmtotal(); #ifdef UCB_METER cp = &cnt.v_first; rp = &rate.v_first; sp = &sum.vs_first; while (cp <= &cnt.v_last) { *rp = *cp; *sp += *cp; *cp = 0; rp++, cp++, sp++; } #endif } } vmtotal() { extern char counted[]; register struct proc *p; register struct text *xq; register nrun = 0; #ifdef UCB_METER int nt; total.t_vmtxt = 0; total.t_avmtxt = 0; total.t_rmtxt = 0; total.t_armtxt = 0; for (xq = text; xq < textNTEXT; xq++) { counted[xq-text]=0; if (xq->x_iptr) { total.t_vmtxt += xq->x_size; if (xq->x_ccount) total.t_rmtxt += xq->x_size; } } total.t_vm = 0; total.t_avm = 0; total.t_rm = 0; total.t_arm = 0; total.t_rq = 0; total.t_dw = 0; total.t_sl = 0; total.t_sw = 0; #endif for (p = &proc[1]; p <= maxproc; p++) { if (p->p_stat) { #ifdef UCB_METER #ifndef VIRUS_VFORK total.t_vm += p->p_size; if (p->p_flag & SLOAD) total.t_rm += p->p_size; #else total.t_vm += p->p_dsize + p->p_ssize + USIZE; if (p->p_flag & SLOAD) total.t_rm += p->p_dsize + p->p_ssize + USIZE; #endif #endif switch (p->p_stat) { case SSLEEP: case SSTOP: if (p->p_pri <= PZERO) nrun++; #ifdef UCB_METER if (p->p_flag & SLOAD) { if (p->p_pri <= PZERO) total.t_dw++; else if (p->p_slptime < MAXSLP) total.t_sl++; } else if (p->p_slptime < MAXSLP) total.t_sw++; if (p->p_slptime < MAXSLP) goto active; #endif break; case SRUN: case SIDL: nrun++; #ifdef UCB_METER if (p->p_flag & SLOAD) total.t_rq++; else total.t_sw++; active: #ifndef VIRUS_VFORK total.t_avm += p->p_size; if (p->p_flag & SLOAD) total.t_arm += p->p_size; #else total.t_avm += p->p_dsize + p->p_ssize + USIZE; if (p->p_flag & SLOAD) total.t_arm += p->p_dsize + p->p_ssize + USIZE; #endif if (p->p_textp) { total.t_avmtxt += p->p_textp->x_size; nt = p->p_textp-text; if (counted[nt]==0) { counted[nt]=1; if (p->p_textp->x_ccount) total.t_armtxt += p->p_textp->x_size; } } #endif break; } } } #ifdef UCB_METER total.t_vm += total.t_vmtxt; total.t_avm += total.t_avmtxt; total.t_rm += total.t_rmtxt; total.t_arm += total.t_armtxt; total.t_free = avefree; #endif loadav(avenrun, nrun); } /* * Compute Tenex style load average. This code is adapted from similar * code by Bill Joy on the Vax system. The major change is that we * avoid floating point since not all pdp-11's have it. This makes * the code quite hard to read - it was derived with some algebra. * * "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. */ /* * 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]); * } */ long cexp[3] = { 0353, /* 256*exp(-1/12) */ 0373, /* 256*exp(-1/60) */ 0376, /* 256*exp(-1/180) */ }; loadav(avg, n) register short *avg; register n; { register int i; for (i = 0; i < 3; i++) avg[i] = (cexp[i] * (avg[i]-(n<<8)) + (((long)n)<<16)) >> 8; } #endif