/* @(#)clock.c 1.1 */ #include "sys/param.h" #include "sys/config.h" #include "sys/types.h" #include "sys/mmu.h" #include "sys/sysmacros.h" #include "sys/systm.h" #include "sys/sysinfo.h" #include "sys/callo.h" #include "sys/dir.h" #include "sys/signal.h" #include "sys/user.h" #include "sys/proc.h" #include "sys/text.h" #include "sys/psl.h" #include "sys/var.h" #include "sys/reg.h" #ifdef UCB_NET #include "net/misc.h" #include "net/protosw.h" #include "net/socket.h" #include "net/if.h" #include "net/in_systm.h" #endif #ifdef VIRTUAL451 #define args buserr #define a_ps ber_sr #define a_pc ber_pc #define a_dev ber_dev #endif /* * clock is called straight from * the real time clock interrupt. * * Functions: * reprime clock * implement callouts * maintain user/system times * maintain date * profile * alarm clock signals * jab the scheduler */ #define PRF_ON 01 extern prfstat; time_t time, lbolt; #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(). */ extern int protoslow; extern int protofast; extern int ifnetslow; extern short netoff; #endif clock(ap) struct args *ap; { register struct callo *p1, *p2; register struct user *up; register struct proc *pp; register a, ps; static short lticks; static rqlen, sqlen; /* * if panic stop clock */ if (panicstr) { clkstop(); return; } up = &u; ps = ap->a_ps; if (up->u_stack[0] != STKMAGIC) panic("Interrupt stack overflow"); #ifdef UCB_NET /* * Time moves on for protocols. */ if(!netoff) { --protoslow; --protofast; --ifnetslow; if(protoslow<=0 || protofast<=0 || ifnetslow<=0) { schednetisr(NETISR_CLOCK); } } #endif /* * callouts * if none, just continue * else update first non-zero time */ if(callout[0].c_func == NULL) goto out; p2 = &callout[0]; while(p2->c_time<=0 && p2->c_func!=NULL) p2++; p2->c_time--; /* * if ps is high, just return */ if (BASEPRI(ps)) goto out; /* * if any callout active, update first non-zero time * then process necessary callouts */ spltty(); 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: if (prfstat & PRF_ON) prfintr((caddr_t)ap->a_pc, ps); if (USERMODE(ps)) { a = CPU_USER; up->u_utime++; if (up->u_prof.pr_scale) addupc((unsigned)ap->a_pc, &up->u_prof, 1); } else { if (ap->a_dev != 0) { /* dev has old idleflg in it */ if (syswait.iowait+syswait.swap+syswait.physio) { a = CPU_WAIT; if (syswait.iowait) sysinfo.wait[W_IO]++; if (syswait.swap) sysinfo.wait[W_SWAP]++; if (syswait.physio) sysinfo.wait[W_PIO]++; } else a = CPU_IDLE; } else { a = CPU_KERNEL; up->u_stime++; } } sysinfo.cpu[a]++; pp = up->u_procp; if (pp->p_stat==SRUN) { up->u_mem += (unsigned)(v.v_usize+procsize(pp)); if (pp->p_textp) { a = pp->p_textp->x_ccount; if (a==0 || a==1) up->u_mem += pp->p_textp->x_size; else up->u_mem += (unsigned short)pp->p_textp->x_size / (short)a; } } if (pp->p_cpu < 80) pp->p_cpu++; lbolt++; /* time in ticks */ if (--lticks <= 0) { if (BASEPRI(ps)) return; lticks += v.v_hz; ++time; if ((time & 3) == 0) /* entry to load average */ loadav(); runrun++; rqlen = 0; sqlen = 0; for(pp = &proc[0]; pp < (struct proc *)v.ve_proc; pp++) if (pp->p_stat) { if (pp->p_time != 127) pp->p_time++; if (pp->p_clktim) if (--pp->p_clktim == 0) psignal(pp, SIGALRM); pp->p_cpu >>= 1; if (pp->p_pri >= (PUSER-NZERO)) { pp->p_pri = (pp->p_cpu>>1) + PUSER + pp->p_nice - NZERO; } if (pp->p_stat == SRUN) if (pp->p_flag & SLOAD) rqlen++; else sqlen++; } if (rqlen) { sysinfo.runque += rqlen; sysinfo.runocc++; } if (sqlen) { sysinfo.swpque += sqlen; sysinfo.swpocc++; } if (runin!=0) { runin = 0; setrun(&proc[0]); } #ifdef VIRTUAL451 if (runout!=0) { runout = 0; setrun(&proc[0]); } #endif VIRTUAL451 } } /* * 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; int tim; { register struct callo *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++; } if (p1 >= (struct callo *)v.ve_call-1) panic("Timeout table overflow"); p1->c_time -= t; p2 = p1; while(p2->c_func != 0) p2++; 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); } #define PDELAY (PZERO-1) delay(ticks) { extern wakeup(); int s; if (ticks<=0) return; s = spl7(); timeout(wakeup, (caddr_t)u.u_procp+1, ticks); (void) sleep((caddr_t)u.u_procp+1, PDELAY); splx(s); } /* * From here down is load average code */ struct lavnum { unsigned short high; unsigned short low; }; struct lavnum avenrun[3]; /* * Constants for averages over 1, 5, and 15 minutes * when sampling at 4 second intervals. * (Using 'fixed-point' with 16 binary digits to right) */ struct lavnum cexp[3] = { { 61309, 4227 }, /* (x = exp(-1/15) * 65536) , 1 - x */ { 64667, 869 }, /* (x = exp(-1/75) * 65536) , 1 - x */ { 65245, 291 }, /* (x = exp(-1/225) * 65536) , 1 - x */ }; /* called once every four seconds */ loadav() { register struct lavnum *avg; register struct lavnum *rcexp; register unsigned int j; register unsigned short nrun; register struct proc *p; nrun = 0; for (p = &proc[0]; p < (struct proc *)v.ve_proc; p++) { if (p->p_flag & SSYS) continue; if (p->p_stat) { switch (p->p_stat) { case SSLEEP: case SSTOP: if (p->p_pri <= PZERO) nrun++; break; case SRUN: case SIDL: nrun++; break; } } } /* * Compute a tenex style load average of a quantity on * 1, 5 and 15 minute intervals. * (Using 'fixed-point' with 16 binary digits to right) */ avg = avenrun; rcexp = cexp; for ( ; avg < &avenrun[3]; avg++, rcexp++) { j = ((avg->low * rcexp->high + 32768) >> 16) + (avg->high * rcexp->high) + (nrun * rcexp->low); avg->low = j & 65535; avg->high = j >> 16; } }