123366Smckusick /* 223366Smckusick * Copyright (c) 1982 Regents of the University of California. 323366Smckusick * All rights reserved. The Berkeley software License Agreement 423366Smckusick * specifies the terms and conditions for redistribution. 523366Smckusick * 6*28947Skarels * @(#)kern_clock.c 6.17 (Berkeley) 06/02/86 723366Smckusick */ 89Sbill 99751Ssam #include "../machine/reg.h" 109751Ssam #include "../machine/psl.h" 119751Ssam 1217088Sbloom #include "param.h" 1317088Sbloom #include "systm.h" 1417088Sbloom #include "dk.h" 1517088Sbloom #include "callout.h" 1617088Sbloom #include "dir.h" 1717088Sbloom #include "user.h" 1817088Sbloom #include "kernel.h" 1917088Sbloom #include "proc.h" 2017088Sbloom #include "vm.h" 2117088Sbloom #include "text.h" 229Sbill 2326265Skarels #if defined(vax) 249751Ssam #include "../vax/mtpr.h" 2526265Skarels #include "../vax/clock.h" 269751Ssam #endif 279751Ssam 2810291Smckusick #ifdef GPROF 2917088Sbloom #include "gprof.h" 3010291Smckusick #endif 3110291Smckusick 328124Sroot /* 338124Sroot * Clock handling routines. 348124Sroot * 3511392Ssam * This code is written to operate with two timers which run 3611392Ssam * independently of each other. The main clock, running at hz 3711392Ssam * times per second, is used to do scheduling and timeout calculations. 3811392Ssam * The second timer does resource utilization estimation statistically 3911392Ssam * based on the state of the machine phz times a second. Both functions 4011392Ssam * can be performed by a single clock (ie hz == phz), however the 4111392Ssam * statistics will be much more prone to errors. Ideally a machine 4211392Ssam * would have separate clocks measuring time spent in user state, system 4311392Ssam * state, interrupt state, and idle state. These clocks would allow a non- 4411392Ssam * approximate measure of resource utilization. 458124Sroot */ 461559Sbill 478124Sroot /* 488124Sroot * TODO: 4912747Ssam * time of day, system/user timing, timeouts, profiling on separate timers 5012747Ssam * allocate more timeout table slots when table overflows. 518124Sroot */ 5226265Skarels 5317007Smckusick /* 5417007Smckusick * Bump a timeval by a small number of usec's. 5517007Smckusick */ 5617007Smckusick #define BUMPTIME(t, usec) { \ 5717007Smckusick register struct timeval *tp = (t); \ 5817007Smckusick \ 5917007Smckusick tp->tv_usec += (usec); \ 6017007Smckusick if (tp->tv_usec >= 1000000) { \ 6117007Smckusick tp->tv_usec -= 1000000; \ 6217007Smckusick tp->tv_sec++; \ 6317007Smckusick } \ 6417007Smckusick } 6517007Smckusick 668124Sroot /* 6711392Ssam * The hz hardware interval timer. 6811392Ssam * We update the events relating to real time. 6911392Ssam * If this timer is also being used to gather statistics, 7011392Ssam * we run through the statistics gathering routine as well. 718124Sroot */ 722609Swnj /*ARGSUSED*/ 732442Swnj hardclock(pc, ps) 742450Swnj caddr_t pc; 758944Sroot int ps; 769Sbill { 772768Swnj register struct callout *p1; 788097Sroot register struct proc *p; 7924524Sbloom register int s; 8016172Skarels int needsoft = 0; 81*28947Skarels extern int tickdelta; 82*28947Skarels extern long timedelta; 839Sbill 848124Sroot /* 858124Sroot * Update real-time timeout queue. 868124Sroot * At front of queue are some number of events which are ``due''. 878124Sroot * The time to these is <= 0 and if negative represents the 888124Sroot * number of ticks which have passed since it was supposed to happen. 898124Sroot * The rest of the q elements (times > 0) are events yet to happen, 908124Sroot * where the time for each is given as a delta from the previous. 918124Sroot * Decrementing just the first of these serves to decrement the time 928124Sroot * to all events. 938124Sroot */ 9412747Ssam p1 = calltodo.c_next; 9512747Ssam while (p1) { 9612747Ssam if (--p1->c_time > 0) 9712747Ssam break; 9816172Skarels needsoft = 1; 9912747Ssam if (p1->c_time == 0) 10012747Ssam break; 10112747Ssam p1 = p1->c_next; 10212747Ssam } 103138Sbill 1048124Sroot /* 1058124Sroot * Charge the time out based on the mode the cpu is in. 1068124Sroot * Here again we fudge for the lack of proper interval timers 1078124Sroot * assuming that the current state has been around at least 1088124Sroot * one tick. 1098124Sroot */ 1109Sbill if (USERMODE(ps)) { 11116172Skarels if (u.u_prof.pr_scale) 11216172Skarels needsoft = 1; 1138124Sroot /* 1148124Sroot * CPU was in user state. Increment 1158124Sroot * user time counter, and process process-virtual time 1169604Ssam * interval timer. 1178124Sroot */ 11817007Smckusick BUMPTIME(&u.u_ru.ru_utime, tick); 1198097Sroot if (timerisset(&u.u_timer[ITIMER_VIRTUAL].it_value) && 1208097Sroot itimerdecr(&u.u_timer[ITIMER_VIRTUAL], tick) == 0) 1218097Sroot psignal(u.u_procp, SIGVTALRM); 1229Sbill } else { 1238124Sroot /* 12424524Sbloom * CPU was in system state. 1258124Sroot */ 12626265Skarels if (!noproc) 12717007Smckusick BUMPTIME(&u.u_ru.ru_stime, tick); 1289Sbill } 1298097Sroot 1308124Sroot /* 13110388Ssam * If the cpu is currently scheduled to a process, then 13210388Ssam * charge it with resource utilization for a tick, updating 13310388Ssam * statistics which run in (user+system) virtual time, 13410388Ssam * such as the cpu time limit and profiling timers. 13510388Ssam * This assumes that the current process has been running 13610388Ssam * the entire last tick. 13710388Ssam */ 13818585Skarels if (noproc == 0) { 13910388Ssam if ((u.u_ru.ru_utime.tv_sec+u.u_ru.ru_stime.tv_sec+1) > 14010388Ssam u.u_rlimit[RLIMIT_CPU].rlim_cur) { 14110388Ssam psignal(u.u_procp, SIGXCPU); 14210388Ssam if (u.u_rlimit[RLIMIT_CPU].rlim_cur < 14310388Ssam u.u_rlimit[RLIMIT_CPU].rlim_max) 14410388Ssam u.u_rlimit[RLIMIT_CPU].rlim_cur += 5; 14510388Ssam } 14610388Ssam if (timerisset(&u.u_timer[ITIMER_PROF].it_value) && 14710388Ssam itimerdecr(&u.u_timer[ITIMER_PROF], tick) == 0) 14810388Ssam psignal(u.u_procp, SIGPROF); 14910388Ssam s = u.u_procp->p_rssize; 15026265Skarels u.u_ru.ru_idrss += s; 15126265Skarels #ifdef notdef 15226265Skarels u.u_ru.ru_isrss += 0; /* XXX (haven't got this) */ 15326265Skarels #endif 15410388Ssam if (u.u_procp->p_textp) { 15510388Ssam register int xrss = u.u_procp->p_textp->x_rssize; 15610388Ssam 15710388Ssam s += xrss; 15810388Ssam u.u_ru.ru_ixrss += xrss; 15910388Ssam } 16010388Ssam if (s > u.u_ru.ru_maxrss) 16110388Ssam u.u_ru.ru_maxrss = s; 16210388Ssam } 16310388Ssam 16410388Ssam /* 1658124Sroot * We adjust the priority of the current process. 1668124Sroot * The priority of a process gets worse as it accumulates 1678124Sroot * CPU time. The cpu usage estimator (p_cpu) is increased here 1688124Sroot * and the formula for computing priorities (in kern_synch.c) 1698124Sroot * will compute a different value each time the p_cpu increases 1708124Sroot * by 4. The cpu usage estimator ramps up quite quickly when 1718124Sroot * the process is running (linearly), and decays away exponentially, 1728124Sroot * at a rate which is proportionally slower when the system is 1738124Sroot * busy. The basic principal is that the system will 90% forget 1748124Sroot * that a process used a lot of CPU time in 5*loadav seconds. 1758124Sroot * This causes the system to favor processes which haven't run 1768124Sroot * much recently, and to round-robin among other processes. 1778124Sroot */ 1789Sbill if (!noproc) { 1798097Sroot p = u.u_procp; 1808097Sroot p->p_cpticks++; 1818097Sroot if (++p->p_cpu == 0) 1828097Sroot p->p_cpu--; 1838124Sroot if ((p->p_cpu&3) == 0) { 1848097Sroot (void) setpri(p); 1858097Sroot if (p->p_pri >= PUSER) 1868097Sroot p->p_pri = p->p_usrpri; 1879Sbill } 1889Sbill } 1898124Sroot 1908124Sroot /* 19111392Ssam * If the alternate clock has not made itself known then 19211392Ssam * we must gather the statistics. 19311392Ssam */ 19411392Ssam if (phz == 0) 19511392Ssam gatherstats(pc, ps); 19611392Ssam 19711392Ssam /* 1988124Sroot * Increment the time-of-day, and schedule 1998124Sroot * processing of the callouts at a very low cpu priority, 2008124Sroot * so we don't keep the relatively high clock interrupt 2018124Sroot * priority any longer than necessary. 2028124Sroot */ 20328828Skarels if (timedelta == 0) 20417356Skarels BUMPTIME(&time, tick) 20517356Skarels else { 20617356Skarels register delta; 20717356Skarels 20828828Skarels if (timedelta < 0) { 20928828Skarels delta = tick - tickdelta; 21028828Skarels timedelta += tickdelta; 21117356Skarels } else { 21228828Skarels delta = tick + tickdelta; 21328828Skarels timedelta -= tickdelta; 21417356Skarels } 21517356Skarels BUMPTIME(&time, delta); 21617356Skarels } 21716525Skarels if (needsoft) { 21816525Skarels if (BASEPRI(ps)) { 21916525Skarels /* 22016525Skarels * Save the overhead of a software interrupt; 22116525Skarels * it will happen as soon as we return, so do it now. 22216525Skarels */ 22316525Skarels (void) splsoftclock(); 22416525Skarels softclock(pc, ps); 22516525Skarels } else 22616525Skarels setsoftclock(); 22716525Skarels } 2282442Swnj } 2292442Swnj 23015191Ssam int dk_ndrive = DK_NDRIVE; 2318124Sroot /* 23211392Ssam * Gather statistics on resource utilization. 23311392Ssam * 23411392Ssam * We make a gross assumption: that the system has been in the 23511392Ssam * state it is in (user state, kernel state, interrupt state, 23611392Ssam * or idle state) for the entire last time interval, and 23711392Ssam * update statistics accordingly. 23811392Ssam */ 23912747Ssam /*ARGSUSED*/ 24011392Ssam gatherstats(pc, ps) 24111392Ssam caddr_t pc; 24211392Ssam int ps; 24311392Ssam { 24426265Skarels register int cpstate, s; 24511392Ssam 24611392Ssam /* 24711392Ssam * Determine what state the cpu is in. 24811392Ssam */ 24911392Ssam if (USERMODE(ps)) { 25011392Ssam /* 25111392Ssam * CPU was in user state. 25211392Ssam */ 25311392Ssam if (u.u_procp->p_nice > NZERO) 25411392Ssam cpstate = CP_NICE; 25511392Ssam else 25611392Ssam cpstate = CP_USER; 25711392Ssam } else { 25811392Ssam /* 25911392Ssam * CPU was in system state. If profiling kernel 26024524Sbloom * increment a counter. If no process is running 26124524Sbloom * then this is a system tick if we were running 26224524Sbloom * at a non-zero IPL (in a driver). If a process is running, 26324524Sbloom * then we charge it with system time even if we were 26424524Sbloom * at a non-zero IPL, since the system often runs 26524524Sbloom * this way during processing of system calls. 26624524Sbloom * This is approximate, but the lack of true interval 26724524Sbloom * timers makes doing anything else difficult. 26811392Ssam */ 26911392Ssam cpstate = CP_SYS; 27011392Ssam if (noproc && BASEPRI(ps)) 27111392Ssam cpstate = CP_IDLE; 27211392Ssam #ifdef GPROF 27311392Ssam s = pc - s_lowpc; 27411392Ssam if (profiling < 2 && s < s_textsize) 27511392Ssam kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 27611392Ssam #endif 27711392Ssam } 27811392Ssam /* 27911392Ssam * We maintain statistics shown by user-level statistics 28011392Ssam * programs: the amount of time in each cpu state, and 28111392Ssam * the amount of time each of DK_NDRIVE ``drives'' is busy. 28211392Ssam */ 28311392Ssam cp_time[cpstate]++; 28411392Ssam for (s = 0; s < DK_NDRIVE; s++) 28526265Skarels if (dk_busy & (1 << s)) 28611392Ssam dk_time[s]++; 28711392Ssam } 28811392Ssam 28911392Ssam /* 2908124Sroot * Software priority level clock interrupt. 2918124Sroot * Run periodic events from timeout queue. 2928124Sroot */ 2932609Swnj /*ARGSUSED*/ 2942442Swnj softclock(pc, ps) 2952450Swnj caddr_t pc; 2968944Sroot int ps; 2972442Swnj { 2982442Swnj 2998097Sroot for (;;) { 3008124Sroot register struct callout *p1; 3018124Sroot register caddr_t arg; 3028124Sroot register int (*func)(); 3038124Sroot register int a, s; 3048124Sroot 30526265Skarels s = splhigh(); 3068097Sroot if ((p1 = calltodo.c_next) == 0 || p1->c_time > 0) { 3078097Sroot splx(s); 3088097Sroot break; 3092442Swnj } 3108124Sroot arg = p1->c_arg; func = p1->c_func; a = p1->c_time; 3118097Sroot calltodo.c_next = p1->c_next; 3128097Sroot p1->c_next = callfree; 3138097Sroot callfree = p1; 3149157Ssam splx(s); 3158112Sroot (*func)(arg, a); 3162442Swnj } 3179604Ssam /* 31813127Ssam * If trapped user-mode and profiling, give it 31913127Ssam * a profiling tick. 3209604Ssam */ 32113127Ssam if (USERMODE(ps)) { 32213127Ssam register struct proc *p = u.u_procp; 32313127Ssam 32413127Ssam if (u.u_prof.pr_scale) { 32513127Ssam p->p_flag |= SOWEUPC; 32613127Ssam aston(); 32713127Ssam } 32813127Ssam /* 32913127Ssam * Check to see if process has accumulated 33013127Ssam * more than 10 minutes of user time. If so 33113127Ssam * reduce priority to give others a chance. 33213127Ssam */ 33313127Ssam if (p->p_uid && p->p_nice == NZERO && 33413127Ssam u.u_ru.ru_utime.tv_sec > 10 * 60) { 33513127Ssam p->p_nice = NZERO+4; 33613127Ssam (void) setpri(p); 33713127Ssam p->p_pri = p->p_usrpri; 33813127Ssam } 3399604Ssam } 3409Sbill } 3419Sbill 3429Sbill /* 34312747Ssam * Arrange that (*fun)(arg) is called in t/hz seconds. 34412747Ssam */ 34512747Ssam timeout(fun, arg, t) 3462450Swnj int (*fun)(); 3472450Swnj caddr_t arg; 34812747Ssam register int t; 3499Sbill { 3503542Swnj register struct callout *p1, *p2, *pnew; 35126265Skarels register int s = splhigh(); 3529Sbill 35318282Smckusick if (t <= 0) 35412747Ssam t = 1; 3553542Swnj pnew = callfree; 3563542Swnj if (pnew == NULL) 3573542Swnj panic("timeout table overflow"); 3583542Swnj callfree = pnew->c_next; 3593542Swnj pnew->c_arg = arg; 3603542Swnj pnew->c_func = fun; 3613542Swnj for (p1 = &calltodo; (p2 = p1->c_next) && p2->c_time < t; p1 = p2) 3629742Ssam if (p2->c_time > 0) 3639742Ssam t -= p2->c_time; 3643542Swnj p1->c_next = pnew; 3653542Swnj pnew->c_next = p2; 3663542Swnj pnew->c_time = t; 3673542Swnj if (p2) 3683542Swnj p2->c_time -= t; 3699Sbill splx(s); 3709Sbill } 3717305Ssam 3727305Ssam /* 3737305Ssam * untimeout is called to remove a function timeout call 3747305Ssam * from the callout structure. 3757305Ssam */ 3768097Sroot untimeout(fun, arg) 3777305Ssam int (*fun)(); 3787305Ssam caddr_t arg; 3797305Ssam { 3807305Ssam register struct callout *p1, *p2; 3817305Ssam register int s; 3827305Ssam 38326265Skarels s = splhigh(); 3847305Ssam for (p1 = &calltodo; (p2 = p1->c_next) != 0; p1 = p2) { 3857305Ssam if (p2->c_func == fun && p2->c_arg == arg) { 3868112Sroot if (p2->c_next && p2->c_time > 0) 3877305Ssam p2->c_next->c_time += p2->c_time; 3887305Ssam p1->c_next = p2->c_next; 3897305Ssam p2->c_next = callfree; 3907305Ssam callfree = p2; 3917305Ssam break; 3927305Ssam } 3937305Ssam } 3947305Ssam splx(s); 3957305Ssam } 3968112Sroot 3978124Sroot /* 3988124Sroot * Compute number of hz until specified time. 3998124Sroot * Used to compute third argument to timeout() from an 4008124Sroot * absolute time. 4018124Sroot */ 4028112Sroot hzto(tv) 4038112Sroot struct timeval *tv; 4048112Sroot { 4058124Sroot register long ticks; 4068124Sroot register long sec; 40726265Skarels int s = splhigh(); 4088112Sroot 4098124Sroot /* 4108124Sroot * If number of milliseconds will fit in 32 bit arithmetic, 4118124Sroot * then compute number of milliseconds to time and scale to 4128124Sroot * ticks. Otherwise just compute number of hz in time, rounding 4138124Sroot * times greater than representible to maximum value. 4148124Sroot * 4158124Sroot * Delta times less than 25 days can be computed ``exactly''. 4168124Sroot * Maximum value for any timeout in 10ms ticks is 250 days. 4178124Sroot */ 4188124Sroot sec = tv->tv_sec - time.tv_sec; 4198124Sroot if (sec <= 0x7fffffff / 1000 - 1000) 4208124Sroot ticks = ((tv->tv_sec - time.tv_sec) * 1000 + 4218124Sroot (tv->tv_usec - time.tv_usec) / 1000) / (tick / 1000); 4228124Sroot else if (sec <= 0x7fffffff / hz) 4238124Sroot ticks = sec * hz; 4248124Sroot else 4258124Sroot ticks = 0x7fffffff; 4268112Sroot splx(s); 4278112Sroot return (ticks); 4288112Sroot } 42912747Ssam 43012747Ssam profil() 43112747Ssam { 43212747Ssam register struct a { 43312747Ssam short *bufbase; 43412747Ssam unsigned bufsize; 43512747Ssam unsigned pcoffset; 43612747Ssam unsigned pcscale; 43712747Ssam } *uap = (struct a *)u.u_ap; 43812747Ssam register struct uprof *upp = &u.u_prof; 43912747Ssam 44012747Ssam upp->pr_base = uap->bufbase; 44112747Ssam upp->pr_size = uap->bufsize; 44212747Ssam upp->pr_off = uap->pcoffset; 44312747Ssam upp->pr_scale = uap->pcscale; 44412747Ssam } 44512747Ssam 44626265Skarels #ifdef COMPAT 44712747Ssam opause() 44812747Ssam { 44912747Ssam 45012747Ssam for (;;) 45112747Ssam sleep((caddr_t)&u, PSLEP); 45212747Ssam } 45326265Skarels #endif 454