X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/5da67d35b1d69550a3f763afc15ca8162decbcfb..961945a80c6f995f4567dbce8881af0bbdee211c:/usr/src/sys/kern/kern_clock.c diff --git a/usr/src/sys/kern/kern_clock.c b/usr/src/sys/kern/kern_clock.c index bede4fbfe8..67698724d1 100644 --- a/usr/src/sys/kern/kern_clock.c +++ b/usr/src/sys/kern/kern_clock.c @@ -1,295 +1,358 @@ -/* kern_clock.c 3.6 %H% */ +/* kern_clock.c 4.48 82/12/17 */ + +#include "../machine/reg.h" +#include "../machine/psl.h" #include "../h/param.h" #include "../h/systm.h" -#include "../h/callo.h" -#include "../h/seg.h" +#include "../h/dk.h" +#include "../h/callout.h" #include "../h/dir.h" #include "../h/user.h" +#include "../h/kernel.h" #include "../h/proc.h" -#include "../h/reg.h" -#include "../h/psl.h" #include "../h/vm.h" -#include "../h/buf.h" #include "../h/text.h" +#ifdef MUSH +#include "../h/quota.h" +#include "../h/share.h" +#endif -#define SCHMAG 9/10 - +#ifdef vax +#include "../vax/mtpr.h" +#endif +# /* - * clock is called straight from - * the real time clock interrupt. + * Clock handling routines. * - * Functions: - * implement callouts - * maintain user/system times - * maintain date - * profile - * lightning bolt wakeup (every second) - * alarm clock signals - * jab the scheduler + * 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. */ -#ifdef KPROF -unsigned short kcount[20000]; -#endif /* - * We handle regular calls to the dh and dz silo input processors - * without using timeouts to save a little time. + * TODO: + * * 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. */ -int rintvl = 4; /* every 1/15'th of sec check receivers */ -int rcnt; -clock(pc, ps) -caddr_t pc; +/* bump a timeval by a small number of usec's */ +#define bumptime(tp, usec) \ + (tp)->tv_usec += usec; \ + if ((tp)->tv_usec >= 1000000) { \ + (tp)->tv_usec -= 1000000; \ + (tp)->tv_sec++; \ + } + +/* + * 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. + */ +/*ARGSUSED*/ +#ifdef vax +hardclock(pc, ps) + caddr_t pc; + int ps; { - register struct callo *p1, *p2; - register struct proc *pp; - register int s; - int a; +#endif +#ifdef sun +hardclock(regs) + struct regs regs; +{ + int ps = regs.r_sr; + caddr_t pc = (caddr_t)regs.r_pc; +#endif + register struct callout *p1; + register struct proc *p; + register int s, cpstate; +#ifdef sun + if (USERMODE(ps)) /* aston needs ar0 */ + u.u_ar0 = ®s.r_r0; +#endif /* - * reprime clock + * 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 + * to all events. */ - clkreld(); + for (p1 = calltodo.c_next; p1 && p1->c_time <= 0; p1 = p1->c_next) + --p1->c_time; + if (p1) + --p1->c_time; /* - * callouts - * else update first non-zero time + * If the cpu is currently scheduled to a process, then + * 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 + * the entire last tick. */ + if (!noproc) { + s = u.u_procp->p_rssize; + 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(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; + s += xrss; + u.u_ru.ru_ixrss += xrss; + } + if (s > u.u_ru.ru_maxrss) + u.u_ru.ru_maxrss = s; + 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); + } /* - * callout + * 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 + * one tick. */ - - 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++; + if (USERMODE(ps)) { + /* + * CPU was in user state. Increment + * user time counter, and process process-virtual time + * interval timer. + */ + 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) + cpstate = CP_NICE; + else + cpstate = CP_USER; + } else { + /* + * 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. + */ +#ifdef GPROF + int k = pc - s_lowpc; + if (profiling < 2 && k < s_textsize) + kcount[k / sizeof (*kcount)]++; +#endif + cpstate = CP_SYS; + if (noproc) { + if (BASEPRI(ps)) + cpstate = CP_IDLE; + } else { + bumptime(&u.u_ru.ru_stime, tick); } } /* - * lightning bolt time-out - * and time of day + * 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. */ -out: + cp_time[cpstate]++; + for (s = 0; s < DK_NDRIVE; s++) + if (dk_busy&(1<= rintvl) { - dhtimer(); - dztimer(); - rcnt = 0; - } else - rcnt++; - if (!noproc) { - s = u.u_procp->p_rssize; - u.u_vm.vm_idsrss += s; - if (u.u_procp->p_textp) { - register int xrss = u.u_procp->p_textp->x_rssize; - - s += xrss; - u.u_vm.vm_ixrss += xrss; - } - if (s > u.u_vm.vm_maxrss) - u.u_vm.vm_maxrss = s; - } - a = dk_busy&07; - if (USERMODE(ps)) { - u.u_vm.vm_utime++; - if(u.u_procp->p_nice > NZERO) - a += 8; - } else { - a += 16; - if (noproc) - a += 8; - else - u.u_vm.vm_stime++; - } - dk_time[a]++; if (!noproc) { - pp = u.u_procp; - if(++pp->p_cpu == 0) - pp->p_cpu--; - if(pp->p_cpu % 16 == 0) { - (void) setpri(pp); - if (pp->p_pri >= PUSER) - pp->p_pri = pp->p_usrpri; + p = u.u_procp; + p->p_cpticks++; + if (++p->p_cpu == 0) + p->p_cpu--; +#ifdef MUSH + 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); +#endif + if ((p->p_cpu&3) == 0) { + (void) setpri(p); + if (p->p_pri >= PUSER) + p->p_pri = p->p_usrpri; } } - ++lbolt; - if (lbolt % (HZ/4) == 0) { - vmpago(); - runrun++; - } - if (lbolt >= HZ) { - if (BASEPRI(ps)) - return; - lbolt -= HZ; - ++time; - (void) spl1(); - runrun++; - wakeup((caddr_t)&lbolt); - for(pp = &proc[0]; pp < &proc[NPROC]; pp++) - if (pp->p_stat && pp->p_statp_time != 127) - pp->p_time++; - if(pp->p_clktim) - if(--pp->p_clktim == 0) - if (pp->p_flag & STIMO) { - s = spl6(); - if (pp->p_stat == SSLEEP) - setrun(pp); - pp->p_flag &= ~STIMO; - splx(s); - } else - psignal(pp, SIGCLK); - if(pp->p_stat==SSLEEP||pp->p_stat==SSTOP) - if (pp->p_slptime != 127) - pp->p_slptime++; - if(pp->p_flag&SLOAD) { - ave(pp->p_aveflt, pp->p_faults, 5); - pp->p_faults = 0; - } - a = (pp->p_cpu & 0377)*SCHMAG + pp->p_nice - NZERO; - if(a < 0) - a = 0; - if(a > 255) - a = 255; - pp->p_cpu = a; - (void) setpri(pp); - s = spl6(); - if(pp->p_pri >= PUSER) { - if ((pp != u.u_procp || noproc) && - pp->p_stat == SRUN && - (pp->p_flag & SLOAD) && - pp->p_pri != pp->p_usrpri) { - remrq(pp); - pp->p_pri = pp->p_usrpri; - setrq(pp); - } else - pp->p_pri = pp->p_usrpri; - } + + /* + * 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. + */ + bumptime(&time, tick); + setsoftclock(); +} + +/* + * Software priority level clock interrupt. + * Run periodic events from timeout queue. + */ +/*ARGSUSED*/ +#ifdef vax +softclock(pc, ps) + caddr_t pc; + int ps; +{ +#endif +#ifdef sun +softclock() +{ + int ps = u.u_ar0[PS]; + caddr_t pc = (caddr_t)u.u_ar0[PC]; +#endif + + for (;;) { + register struct callout *p1; + register caddr_t arg; + register int (*func)(); + register int a, s; + + s = spl7(); + if ((p1 = calltodo.c_next) == 0 || p1->c_time > 0) { splx(s); + break; } - vmmeter(); - if(runin!=0) { - runin = 0; - wakeup((caddr_t)&runin); - } - /* - * 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. - */ - if (bclnlist != NULL) - wakeup((caddr_t)&proc[2]); -#ifdef ERNIE - if (USERMODE(ps)) { - pp = u.u_procp; - if (pp->p_uid) - if (pp->p_nice == NZERO && u.u_vm.vm_utime > 600 * HZ) - pp->p_nice = NZERO+4; - (void) setpri(pp); - pp->p_pri = pp->p_usrpri; - } -#endif - } - if (USERMODE(ps)) { - /* - * We do this last since it - * may block on a page fault in user space. - */ - if (u.u_prof.pr_scale) - addupc(pc, &u.u_prof, 1); + arg = p1->c_arg; func = p1->c_func; a = p1->c_time; + calltodo.c_next = p1->c_next; + p1->c_next = callfree; + callfree = p1; + splx(s); + (*func)(arg, a); } -#ifdef KPROF - else if (!noproc) { - register int indx = ((int)pc & 0x7fffffff) / 4; - - if (indx >= 0 && indx < 20000) - if (++kcount[indx] == 0) - --kcount[indx]; + /* + * If trapped user-mode, give it a profiling tick. + */ + if (USERMODE(ps) && u.u_prof.pr_scale) { + u.u_procp->p_flag |= SOWEUPC; + aston(); } -#endif } /* - * 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. + * Arrange that (*fun)(arg) is called in tim/hz seconds. */ timeout(fun, arg, tim) -int (*fun)(); -caddr_t arg; + int (*fun)(); + caddr_t arg; + int tim; { - register struct callo *p1, *p2; + register struct callout *p1, *p2, *pnew; 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 >= &callout[NCALL-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--; + pnew = callfree; + if (pnew == NULL) + panic("timeout table overflow"); + callfree = pnew->c_next; + pnew->c_arg = arg; + pnew->c_func = fun; + for (p1 = &calltodo; (p2 = p1->c_next) && p2->c_time < t; p1 = p2) + if (p2->c_time > 0) + t -= p2->c_time; + p1->c_next = pnew; + pnew->c_next = p2; + pnew->c_time = t; + if (p2) + p2->c_time -= t; + splx(s); +} + +/* + * untimeout is called to remove a function timeout call + * from the callout structure. + */ +untimeout(fun, arg) + int (*fun)(); + caddr_t arg; +{ + register struct callout *p1, *p2; + register int s; + + s = spl7(); + 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; + p1->c_next = p2->c_next; + p2->c_next = callfree; + callfree = p2; + break; + } } - p1->c_time = t; - p1->c_func = fun; - p1->c_arg = arg; splx(s); } + +/* + * Compute number of hz until specified time. + * Used to compute third argument to timeout() from an + * absolute time. + */ +hzto(tv) + struct timeval *tv; +{ + register long ticks; + register long sec; + int s = spl7(); + + /* + * 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) + ticks = sec * hz; + else + ticks = 0x7fffffff; + splx(s); + return (ticks); +}