more-or-less working with new proc & user structs
[unix-history] / usr / src / sys / kern / kern_time.c
index 4dc95e4..e61955b 100644 (file)
-/*     kern_time.c     5.3     82/09/04        */
+/*
+ * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ *
+ *     @(#)kern_time.c 7.15 (Berkeley) %G%
+ */
 
 
-#include "../h/param.h"
-#include "../h/dir.h"          /* XXX */
-#include "../h/user.h"
-#include "../h/kernel.h"
-#include "../h/reg.h"
-#include "../h/inode.h"
-#include "../h/proc.h"
+#include "param.h"
+#include "resourcevar.h"
+#include "kernel.h"
+#include "proc.h"
 
 
-gettimeofday()
-{
-       register struct a {
+
+/* 
+ * Time of day and interval timer support.
+ *
+ * These routines provide the kernel entry points to get and set
+ * the time-of-day and per-process interval timers.  Subroutines
+ * here provide support for adding and subtracting timeval structures
+ * and decrementing interval timers, optionally reloading the interval
+ * timers when they expire.
+ */
+
+/* ARGSUSED */
+gettimeofday(p, uap, retval)
+       struct proc *p;
+       register struct args {
                struct  timeval *tp;
                struct  timezone *tzp;
                struct  timeval *tp;
                struct  timezone *tzp;
-       } *uap = (struct a *)u.u_ap;
+       } *uap;
+       int *retval;
+{
        struct timeval atv;
        struct timeval atv;
+       int error = 0;
 
 
-       microtime(&atv);
-       if (copyout((caddr_t)&atv, (caddr_t)uap->tp, sizeof (atv))) {
-               u.u_error = EFAULT;
-               return;
-       }
-       if (uap->tzp == 0)
-               return;
-       if (copyout((caddr_t)&tz, uap->tzp, sizeof (tz))) {
-               u.u_error = EFAULT;
-               return;
+       if (uap->tp) {
+               microtime(&atv);
+               if (error = copyout((caddr_t)&atv, (caddr_t)uap->tp,
+                   sizeof (atv)))
+                       return (error);
        }
        }
+       if (uap->tzp)
+               error = copyout((caddr_t)&tz, (caddr_t)uap->tzp,
+                   sizeof (tz));
+       return (error);
 }
 
 }
 
-settimeofday()
+/* ARGSUSED */
+settimeofday(p, uap, retval)
+       struct proc *p;
+       struct args {
+               struct  timeval *tv;
+               struct  timezone *tzp;
+       } *uap;
+       int *retval;
 {
 {
-       register struct a {
-               struct timeval *tv;
-               struct timezone *tzp;
-       } *uap = (struct a *)u.u_ap;
        struct timeval atv;
        struct timezone atz;
        struct timeval atv;
        struct timezone atz;
+       int error, s;
 
 
-       if (copyin((caddr_t)uap->tv, (caddr_t)&atv, sizeof (struct timeval))) {
-               u.u_error = EFAULT;
-               return;
-       }
-       if (suser()) {
-               struct timeval tdelta;
-
-               tdelta = atv;
-
-               timevalsub(&tdelta, &time);
-               timevaladd(&boottime, &tdelta);
-               time = atv;
-               clockset();
-       }
-       if (uap->tzp) {
-               if (copyin((caddr_t)uap->tzp, (caddr_t)&atz, sizeof (atz))) {
-                       u.u_error = EFAULT;
-                       return;
-               }
-               /* XXX */
+       if (error = suser(p->p_ucred, &p->p_acflag))
+               return (error);
+       if (uap->tv) {
+               if (error = copyin((caddr_t)uap->tv, (caddr_t)&atv,
+                   sizeof (struct timeval)))
+                       return (error);
+               /* WHAT DO WE DO ABOUT PENDING REAL-TIME TIMEOUTS??? */
+               boottime.tv_sec += atv.tv_sec - time.tv_sec;
+               s = splhigh(); time = atv; splx(s);
+               resettodr();
        }
        }
+       if (uap->tzp && (error = copyin((caddr_t)uap->tzp, (caddr_t)&atz,
+           sizeof (atz))) == 0)
+               tz = atz;
+       return (error);
 }
 
 }
 
-timevaladd(t1, t2)
-       struct timeval *t1, *t2;
-{
-
-       t1->tv_sec += t2->tv_sec;
-       t1->tv_usec += t2->tv_sec;
-       timevalfix(t1);
-}
+extern int tickadj;                    /* "standard" clock skew, us./tick */
+int    tickdelta;                      /* current clock skew, us. per tick */
+long   timedelta;                      /* unapplied time correction, us. */
+long   bigadj = 1000000;               /* use 10x skew above bigadj us. */
 
 
-timevalsub(t1, t2)
-       struct timeval *t1, *t2;
+/* ARGSUSED */
+adjtime(p, uap, retval)
+       struct proc *p;
+       register struct args {
+               struct timeval *delta;
+               struct timeval *olddelta;
+       } *uap;
+       int *retval;
 {
 {
+       struct timeval atv, oatv;
+       register long ndelta;
+       int s, error;
 
 
-       t1->tv_sec -= t2->tv_sec;
-       t1->tv_usec -= t2->tv_sec;
-       timevalfix(t1);
-}
-
-timevalfix(t1)
-       struct timeval *t1;
-{
+       if (error = suser(p->p_ucred, &p->p_acflag))
+               return (error);
+       if (error =
+           copyin((caddr_t)uap->delta, (caddr_t)&atv, sizeof (struct timeval)))
+               return (error);
+       ndelta = atv.tv_sec * 1000000 + atv.tv_usec;
+       if (timedelta == 0)
+               if (ndelta > bigadj)
+                       tickdelta = 10 * tickadj;
+               else
+                       tickdelta = tickadj;
+       if (ndelta % tickdelta)
+               ndelta = ndelta / tickadj * tickadj;
 
 
-       if (t1->tv_usec < 0) {
-               t1->tv_sec--;
-               t1->tv_usec += 1000000;
-       }
-       if (t1->tv_usec >= 1000000) {
-               t1->tv_sec++;
-               t1->tv_usec -= 1000000;
+       s = splclock();
+       if (uap->olddelta) {
+               oatv.tv_sec = timedelta / 1000000;
+               oatv.tv_usec = timedelta % 1000000;
        }
        }
+       timedelta = ndelta;
+       splx(s);
+
+       if (uap->olddelta)
+               (void) copyout((caddr_t)&oatv, (caddr_t)uap->olddelta,
+                       sizeof (struct timeval));
+       return (0);
 }
 
 }
 
-getitimer()
-{
-       register struct a {
+/*
+ * Get value of an interval timer.  The process virtual and
+ * profiling virtual time timers are kept in the p_stats area, since
+ * they can be swapped out.  These are kept internally in the
+ * way they are specified externally: in time until they expire.
+ *
+ * The real time interval timer is kept in the process table slot
+ * for the process, and its value (it_value) is kept as an
+ * absolute time rather than as a delta, so that it is easy to keep
+ * periodic real-time signals from drifting.
+ *
+ * Virtual time timers are processed in the hardclock() routine of
+ * kern_clock.c.  The real time timer is processed by a timeout
+ * routine, called from the softclock() routine.  Since a callout
+ * may be delayed in real time due to interrupt processing in the system,
+ * it is possible for the real time timeout routine (realitexpire, given below),
+ * to be delayed in real time past when it is supposed to occur.  It
+ * does not suffice, therefore, to reload the real timer .it_value from the
+ * real time timers .it_interval.  Rather, we compute the next time in
+ * absolute time the timer should go off.
+ */
+/* ARGSUSED */
+getitimer(p, uap, retval)
+       struct proc *p;
+       register struct args {
                u_int   which;
                struct  itimerval *itv;
                u_int   which;
                struct  itimerval *itv;
-       } *uap = (struct a *)u.u_ap;
-       register struct itimerval *itp;
+       } *uap;
+       int *retval;
+{
+       struct itimerval aitv;
        int s;
 
        int s;
 
-       if (uap->which > 2) {
-               u.u_error = EINVAL;
-               return;
-       }
-       if (uap->which == ITIMER_REAL)
-               itp = &u.u_procp->p_realtimer;
-       else
-               itp = &u.u_timer[uap->which];
-       s = spl7();
-       if (copyout((caddr_t)itp, uap->itv, sizeof (struct itimerval))) {
-               u.u_error = EFAULT;
-               goto bad;
-       }
-bad:
+       if (uap->which > ITIMER_PROF)
+               return (EINVAL);
+       s = splclock();
+       if (uap->which == ITIMER_REAL) {
+               /*
+                * Convert from absoulte to relative time in .it_value
+                * part of real time timer.  If time for real time timer
+                * has passed return 0, else return difference between
+                * current time and time for the timer to go off.
+                */
+               aitv = p->p_realtimer;
+               if (timerisset(&aitv.it_value))
+                       if (timercmp(&aitv.it_value, &time, <))
+                               timerclear(&aitv.it_value);
+                       else
+                               timevalsub(&aitv.it_value, &time);
+       } else
+               aitv = p->p_stats->p_timer[uap->which];
        splx(s);
        splx(s);
+       return (copyout((caddr_t)&aitv, (caddr_t)uap->itv,
+           sizeof (struct itimerval)));
 }
 
 }
 
-setitimer()
-{
-       register struct a {
+/* ARGSUSED */
+setitimer(p, uap, retval)
+       struct proc *p;
+       register struct args {
                u_int   which;
                u_int   which;
-               struct  itimerval *itv;
-       } *uap = (struct a *)u.u_ap;
+               struct  itimerval *itv, *oitv;
+       } *uap;
+       int *retval;
+{
        struct itimerval aitv;
        struct itimerval aitv;
+       register struct itimerval *itvp;
+       int s, error;
+
+       if (uap->which > ITIMER_PROF)
+               return (EINVAL);
+       itvp = uap->itv;
+       if (itvp && (error = copyin((caddr_t)itvp, (caddr_t)&aitv,
+           sizeof(struct itimerval))))
+               return (error);
+       if ((uap->itv = uap->oitv) && (error = getitimer(p, uap, retval)))
+               return (error);
+       if (itvp == 0)
+               return (0);
+       if (itimerfix(&aitv.it_value) || itimerfix(&aitv.it_interval))
+               return (EINVAL);
+       s = splclock();
+       if (uap->which == ITIMER_REAL) {
+               untimeout(realitexpire, (caddr_t)p);
+               if (timerisset(&aitv.it_value)) {
+                       timevaladd(&aitv.it_value, &time);
+                       timeout(realitexpire, (caddr_t)p, hzto(&aitv.it_value));
+               }
+               p->p_realtimer = aitv;
+       } else
+               p->p_stats->p_timer[uap->which] = aitv;
+       splx(s);
+       return (0);
+}
+
+/*
+ * Real interval timer expired:
+ * send process whose timer expired an alarm signal.
+ * If time is not set up to reload, then just return.
+ * Else compute next time timer should go off which is > current time.
+ * This is where delay in processing this timeout causes multiple
+ * SIGALRM calls to be compressed into one.
+ */
+realitexpire(p)
+       register struct proc *p;
+{
        int s;
 
        int s;
 
-       s = spl7();
-       if (uap->which > 2) {
-               u.u_error = EINVAL;
-               goto bad;
+       psignal(p, SIGALRM);
+       if (!timerisset(&p->p_realtimer.it_interval)) {
+               timerclear(&p->p_realtimer.it_value);
+               return;
        }
        }
-       if (copyin((caddr_t)uap->itv, (caddr_t)&aitv,
-           sizeof (struct itimerval))) {
-               u.u_error = EFAULT;
-               goto bad;
+       for (;;) {
+               s = splclock();
+               timevaladd(&p->p_realtimer.it_value,
+                   &p->p_realtimer.it_interval);
+               if (timercmp(&p->p_realtimer.it_value, &time, >)) {
+                       timeout(realitexpire, (caddr_t)p,
+                           hzto(&p->p_realtimer.it_value));
+                       splx(s);
+                       return;
+               }
+               splx(s);
        }
        }
-       u.u_timer[uap->which] = aitv;
-       if (uap->which == ITIMER_REAL)
-               u.u_procp->p_realtimer = aitv;
-bad:
-       splx(s);
-       return;
 }
 
 }
 
-getandsetitimer()
+/*
+ * Check that a proposed value to load into the .it_value or
+ * .it_interval part of an interval timer is acceptable, and
+ * fix it to have at least minimal value (i.e. if it is less
+ * than the resolution of the clock, round it up.)
+ */
+itimerfix(tv)
+       struct timeval *tv;
 {
 {
-       int s = spl7();
 
 
-       getitimer();
-       if (u.u_error == 0) {
-               u.u_ap[1] = u.u_ap[2];
-               setitimer();
-       }
-       splx(s);
+       if (tv->tv_sec < 0 || tv->tv_sec > 100000000 ||
+           tv->tv_usec < 0 || tv->tv_usec >= 1000000)
+               return (EINVAL);
+       if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < tick)
+               tv->tv_usec = tick;
+       return (0);
 }
 
 }
 
+/*
+ * Decrement an interval timer by a specified number
+ * of microseconds, which must be less than a second,
+ * i.e. < 1000000.  If the timer expires, then reload
+ * it.  In this case, carry over (usec - old value) to
+ * reducint the value reloaded into the timer so that
+ * the timer does not drift.  This routine assumes
+ * that it is called in a context where the timers
+ * on which it is operating cannot change in value.
+ */
 itimerdecr(itp, usec)
        register struct itimerval *itp;
        int usec;
 {
 
 itimerdecr(itp, usec)
        register struct itimerval *itp;
        int usec;
 {
 
-       while (itp->itimer_value.tv_usec < usec) {
-               if (itp->itimer_value.tv_sec == 0)
+       if (itp->it_value.tv_usec < usec) {
+               if (itp->it_value.tv_sec == 0) {
+                       /* expired, and already in next interval */
+                       usec -= itp->it_value.tv_usec;
                        goto expire;
                        goto expire;
-               itp->itimer_value.tv_usec += 1000000;
-               itp->itimer_value.tv_sec--;
+               }
+               itp->it_value.tv_usec += 1000000;
+               itp->it_value.tv_sec--;
        }
        }
-       itp->itimer_value.tv_usec -= usec;
-       if (timerisset(&itp->itimer_value))
+       itp->it_value.tv_usec -= usec;
+       usec = 0;
+       if (timerisset(&itp->it_value))
                return (1);
                return (1);
+       /* expired, exactly at end of interval */
 expire:
 expire:
-       if (itp->itimer_reload == 0)
-               itp->itimer_value.tv_usec = 0;
-       else
-               itp->itimer_value = itp->itimer_interval;
+       if (timerisset(&itp->it_interval)) {
+               itp->it_value = itp->it_interval;
+               itp->it_value.tv_usec -= usec;
+               if (itp->it_value.tv_usec < 0) {
+                       itp->it_value.tv_usec += 1000000;
+                       itp->it_value.tv_sec--;
+               }
+       } else
+               itp->it_value.tv_usec = 0;              /* sec is already 0 */
        return (0);
 }
 
        return (0);
 }
 
-#ifndef NOCOMPAT
-otime()
+/*
+ * Add and subtract routines for timevals.
+ * N.B.: subtract routine doesn't deal with
+ * results which are before the beginning,
+ * it just gets very confused in this case.
+ * Caveat emptor.
+ */
+timevaladd(t1, t2)
+       struct timeval *t1, *t2;
 {
 
 {
 
-       u.u_r.r_time = time.tv_sec;
+       t1->tv_sec += t2->tv_sec;
+       t1->tv_usec += t2->tv_usec;
+       timevalfix(t1);
 }
 
 }
 
-#include "../h/timeb.h"
+timevalsub(t1, t2)
+       struct timeval *t1, *t2;
+{
+
+       t1->tv_sec -= t2->tv_sec;
+       t1->tv_usec -= t2->tv_usec;
+       timevalfix(t1);
+}
 
 
-oftime()
+timevalfix(t1)
+       struct timeval *t1;
 {
 {
-       register struct a {
-               struct  timeb   *tp;
-       } *uap;
-       struct timeb t;
-
-       uap = (struct a *)u.u_ap;
-       (void) spl7();
-       t.time = time.tv_sec;
-       t.millitm = time.tv_usec / 1000;
-       (void) spl0();
-       t.timezone = tz.tz_minuteswest;
-       t.dstflag = tz.tz_dsttime;
-       if (copyout((caddr_t)&t, (caddr_t)uap->tp, sizeof(t)) < 0)
-               u.u_error = EFAULT;
+
+       if (t1->tv_usec < 0) {
+               t1->tv_sec--;
+               t1->tv_usec += 1000000;
+       }
+       if (t1->tv_usec >= 1000000) {
+               t1->tv_sec++;
+               t1->tv_usec -= 1000000;
+       }
 }
 }