update from Rick Macklem to generate proper error messages
[unix-history] / usr / src / sys / kern / kern_clock.c
index 7f39466..c8dfcce 100644 (file)
@@ -1,73 +1,85 @@
-/*     kern_clock.c    4.43    82/10/30        */
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ *     @(#)kern_clock.c        7.5 (Berkeley) %G%
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "dkstat.h"
+#include "callout.h"
+#include "user.h"
+#include "kernel.h"
+#include "proc.h"
+#include "vm.h"
+#include "text.h"
+
+#include "machine/reg.h"
+#include "machine/psl.h"
+
+#if defined(vax) || defined(tahoe)
+#include "machine/mtpr.h"
+#include "machine/clock.h"
+#endif
 
 
-#include "../h/param.h"
-#include "../h/systm.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/psl.h"
-#include "../h/vm.h"
-#include "../h/text.h"
-#ifdef MUSH
-#include "../h/quota.h"
-#include "../h/share.h"
+#ifdef GPROF
+#include "gprof.h"
 #endif
 
 #endif
 
+#define ADJTIME                /* For now... */
+#define        ADJ_TICK 1000
+int    adjtimedelta;
+
 /*
  * Clock handling routines.
  *
 /*
  * Clock handling routines.
  *
- * 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.
+ * This code is written to operate with two timers which run
+ * independently of each other. The main clock, running at hz
+ * times per second, is used to do scheduling and timeout calculations.
+ * The second timer does resource utilization estimation statistically
+ * based on the state of the machine phz times a second. Both functions
+ * can be performed by a single clock (ie hz == phz), however the 
+ * statistics will be much more prone to errors. Ideally a machine
+ * would have separate clocks measuring time spent in user state, system
+ * state, interrupt state, and idle state. These clocks would allow a non-
+ * approximate measure of resource utilization.
  */
 
 /*
  * TODO:
  */
 
 /*
  * 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.
+ *     time of day, system/user timing, timeouts, profiling on separate timers
+ *     allocate more timeout table slots when table overflows.
  */
 
  */
 
-/* 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++; \
-       }
+/*
+ * Bump a timeval by a small number of usec's.
+ */
+#define BUMPTIME(t, usec) { \
+       register struct timeval *tp = (t); \
+ \
+       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.
+ * The hz hardware interval timer.
+ * We update the events relating to real time.
+ * If this timer is also being used to gather statistics,
+ * we run through the statistics gathering routine as well.
  */
 /*ARGSUSED*/
  */
 /*ARGSUSED*/
-#if vax
 hardclock(pc, ps)
        caddr_t pc;
        int ps;
 {
 hardclock(pc, ps)
        caddr_t pc;
        int ps;
 {
-#endif
-#if 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 struct callout *p1;
        register struct proc *p;
-       register int s, cpstate;
+       register int s;
 
        /*
         * Update real-time timeout queue.
 
        /*
         * Update real-time timeout queue.
@@ -79,40 +91,13 @@ hardclock(regs)
         * Decrementing just the first of these serves to decrement the time
         * to all events.
         */
         * Decrementing just the first of these serves to decrement the time
         * to all events.
         */
-       for (p1 = calltodo.c_next; p1 && p1->c_time <= 0; p1 = p1->c_next)
-               --p1->c_time;
-       if (p1)
-               --p1->c_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;
-
-                       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);
+       p1 = calltodo.c_next;
+       while (p1) {
+               if (--p1->c_time > 0)
+                       break;
+               if (p1->c_time == 0)
+                       break;
+               p1 = p1->c_next;
        }
 
        /*
        }
 
        /*
@@ -125,51 +110,53 @@ hardclock(regs)
                /*
                 * CPU was in user state.  Increment
                 * user time counter, and process process-virtual time
                /*
                 * CPU was in user state.  Increment
                 * user time counter, and process process-virtual time
-                * interval timer.
+                * interval timer. 
                 */
                 */
-               bumptime(&u.u_ru.ru_utime, tick);
+               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 (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 {
                /*
        } 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.
+                * CPU was in system state.
                 */
                 */
-#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);
-               }
+               if (!noproc)
+                       BUMPTIME(&u.u_ru.ru_stime, tick);
        }
 
        /*
        }
 
        /*
-        * 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.
+        * 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.
         */
         */
-       cp_time[cpstate]++;
-       for (s = 0; s < DK_NDRIVE; s++)
-               if (dk_busy&(1<<s))
-                       dk_time[s]++;
+       if (noproc == 0) {
+               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);
+               s = u.u_procp->p_rssize;
+               u.u_ru.ru_idrss += s;
+#ifdef notdef
+               u.u_ru.ru_isrss += 0;           /* XXX (haven't got this) */
+#endif
+               if (u.u_procp->p_textp) {
+                       register int xrss = u.u_procp->p_textp->x_rssize;
+
+                       s += xrss;
+                       u.u_ru.ru_ixrss += xrss;
+               }
+               if (s > u.u_ru.ru_maxrss)
+                       u.u_ru.ru_maxrss = s;
+       }
 
        /*
         * We adjust the priority of the current process.
 
        /*
         * We adjust the priority of the current process.
@@ -190,11 +177,6 @@ hardclock(regs)
                p->p_cpticks++;
                if (++p->p_cpu == 0)
                        p->p_cpu--;
                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)
                if ((p->p_cpu&3) == 0) {
                        (void) setpri(p);
                        if (p->p_pri >= PUSER)
@@ -202,35 +184,118 @@ hardclock(regs)
                }
        }
 
                }
        }
 
+       /*
+        * If the alternate clock has not made itself known then
+        * we must gather the statistics.
+        */
+       if (phz == 0)
+               gatherstats(pc, ps);
+
        /*
         * 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.
         */
        /*
         * 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);
+#ifdef ADJTIME
+       if (adjtimedelta == 0)
+               bumptime(&time, tick);
+       else {
+               if (adjtimedelta < 0) {
+                       bumptime(&time, tick-ADJ_TICK);
+                       adjtimedelta++;
+               } else {
+                       bumptime(&time, tick+ADJ_TICK);
+                       adjtimedelta--;
+               }
+       }
+#else
+       if (timedelta == 0)
+               BUMPTIME(&time, tick)
+       else {
+               register delta;
+
+               if (timedelta < 0) {
+                       delta = tick - tickdelta;
+                       timedelta += tickdelta;
+               } else {
+                       delta = tick + tickdelta;
+                       timedelta -= tickdelta;
+               }
+               BUMPTIME(&time, delta);
+       }
+#endif
        setsoftclock();
 }
 
        setsoftclock();
 }
 
+int    dk_ndrive = DK_NDRIVE;
+/*
+ * Gather statistics on resource utilization.
+ *
+ * We 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*/
+gatherstats(pc, ps)
+       caddr_t pc;
+       int ps;
+{
+       register int cpstate, s;
+
+       /*
+        * Determine what state the cpu is in.
+        */
+       if (USERMODE(ps)) {
+               /*
+                * CPU was in user state.
+                */
+               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.
+                */
+               cpstate = CP_SYS;
+               if (noproc && BASEPRI(ps))
+                       cpstate = CP_IDLE;
+#ifdef GPROF
+               s = pc - s_lowpc;
+               if (profiling < 2 && s < s_textsize)
+                       kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
+#endif
+       }
+       /*
+        * 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.
+        */
+       cp_time[cpstate]++;
+       for (s = 0; s < DK_NDRIVE; s++)
+               if (dk_busy&(1<<s))
+                       dk_time[s]++;
+}
+
 /*
  * Software priority level clock interrupt.
  * Run periodic events from timeout queue.
  */
 /*ARGSUSED*/
 /*
  * Software priority level clock interrupt.
  * Run periodic events from timeout queue.
  */
 /*ARGSUSED*/
-#if vax
 softclock(pc, ps)
        caddr_t pc;
        int ps;
 {
 softclock(pc, ps)
        caddr_t pc;
        int ps;
 {
-#endif
-#if sun
-softclock(sirret, regs)
-       caddr_t sirreg;
-       struct regs regs;
-{
-       int ps = regs.r_sr;
-       caddr_t pc = (caddr_t)regs.r_pc;
-#endif
 
        for (;;) {
                register struct callout *p1;
 
        for (;;) {
                register struct callout *p1;
@@ -238,7 +303,7 @@ softclock(sirret, regs)
                register int (*func)();
                register int a, s;
 
                register int (*func)();
                register int a, s;
 
-               s = spl7();
+               s = splhigh();
                if ((p1 = calltodo.c_next) == 0 || p1->c_time > 0) {
                        splx(s);
                        break;
                if ((p1 = calltodo.c_next) == 0 || p1->c_time > 0) {
                        splx(s);
                        break;
@@ -247,25 +312,47 @@ softclock(sirret, regs)
                calltodo.c_next = p1->c_next;
                p1->c_next = callfree;
                callfree = p1;
                calltodo.c_next = p1->c_next;
                p1->c_next = callfree;
                callfree = p1;
-               (void) splx(s);
+               splx(s);
                (*func)(arg, a);
        }
                (*func)(arg, a);
        }
+       /*
+        * If trapped user-mode and profiling, give it
+        * a profiling tick.
+        */
+       if (USERMODE(ps)) {
+               register struct proc *p = u.u_procp;
+
+               if (u.u_prof.pr_scale) {
+                       p->p_flag |= SOWEUPC;
+                       aston();
+               }
+               /*
+                * Check to see if process has accumulated
+                * more than 10 minutes of user time.  If so
+                * reduce priority to give others a chance.
+                */
+               if (p->p_uid && p->p_nice == NZERO &&
+                   u.u_ru.ru_utime.tv_sec > 10 * 60) {
+                       p->p_nice = NZERO+4;
+                       (void) setpri(p);
+                       p->p_pri = p->p_usrpri;
+               }
+       }
 }
 
 /*
 }
 
 /*
- * Arrange that (*fun)(arg) is called in tim/hz seconds.
+ * Arrange that (*fun)(arg) is called in t/hz seconds.
  */
  */
-timeout(fun, arg, tim)
+timeout(fun, arg, t)
        int (*fun)();
        caddr_t arg;
        int (*fun)();
        caddr_t arg;
-       int tim;
+       register int t;
 {
        register struct callout *p1, *p2, *pnew;
 {
        register struct callout *p1, *p2, *pnew;
-       register int t;
-       int s;
+       register int s = splhigh();
 
 
-       t = tim;
-       s = spl7();
+       if (t <= 0)
+               t = 1;
        pnew = callfree;
        if (pnew == NULL)
                panic("timeout table overflow");
        pnew = callfree;
        if (pnew == NULL)
                panic("timeout table overflow");
@@ -273,7 +360,8 @@ timeout(fun, arg, tim)
        pnew->c_arg = arg;
        pnew->c_func = fun;
        for (p1 = &calltodo; (p2 = p1->c_next) && p2->c_time < t; p1 = p2)
        pnew->c_arg = arg;
        pnew->c_func = fun;
        for (p1 = &calltodo; (p2 = p1->c_next) && p2->c_time < t; p1 = p2)
-               t -= p2->c_time;
+               if (p2->c_time > 0)
+                       t -= p2->c_time;
        p1->c_next = pnew;
        pnew->c_next = p2;
        pnew->c_time = t;
        p1->c_next = pnew;
        pnew->c_next = p2;
        pnew->c_time = t;
@@ -293,7 +381,7 @@ untimeout(fun, arg)
        register struct callout *p1, *p2;
        register int s;
 
        register struct callout *p1, *p2;
        register int s;
 
-       s = spl7();
+       s = splhigh();
        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)
        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)
@@ -317,7 +405,7 @@ hzto(tv)
 {
        register long ticks;
        register long sec;
 {
        register long ticks;
        register long sec;
-       int s = spl7();
+       int s = splhigh();
 
        /*
         * If number of milliseconds will fit in 32 bit arithmetic,
 
        /*
         * If number of milliseconds will fit in 32 bit arithmetic,
@@ -339,3 +427,19 @@ hzto(tv)
        splx(s);
        return (ticks);
 }
        splx(s);
        return (ticks);
 }
+
+profil()
+{
+       register struct a {
+               short   *bufbase;
+               unsigned bufsize;
+               unsigned pcoffset;
+               unsigned pcscale;
+       } *uap = (struct a *)u.u_ap;
+       register struct uprof *upp = &u.u_prof;
+
+       upp->pr_base = uap->bufbase;
+       upp->pr_size = uap->bufsize;
+       upp->pr_off = uap->pcoffset;
+       upp->pr_scale = uap->pcscale;
+}