kernel - Major signal path adjustments to fix races, tsleep race fixes, +more
[dragonfly.git] / sys / kern / kern_time.c
index 3c39c05..3058bf0 100644 (file)
@@ -32,7 +32,6 @@
  *
  *     @(#)kern_time.c 8.1 (Berkeley) 6/10/93
  * $FreeBSD: src/sys/kern/kern_time.c,v 1.68.2.1 2002/10/01 08:00:41 bde Exp $
- * $DragonFly: src/sys/kern/kern_time.c,v 1.40 2008/04/02 14:16:16 sephe Exp $
  */
 
 #include <sys/param.h>
@@ -42,7 +41,6 @@
 #include <sys/resourcevar.h>
 #include <sys/signalvar.h>
 #include <sys/kernel.h>
-#include <sys/systm.h>
 #include <sys/sysent.h>
 #include <sys/sysunion.h>
 #include <sys/proc.h>
 #include <sys/kern_syscall.h>
 #include <vm/vm.h>
 #include <vm/vm_extern.h>
+
 #include <sys/msgport2.h>
 #include <sys/thread2.h>
+#include <sys/mplock2.h>
 
 struct timezone tz;
 
@@ -68,12 +68,24 @@ struct timezone tz;
  * timers when they expire.
  */
 
-static int     nanosleep1(struct timespec *rqt, struct timespec *rmt);
 static int     settime(struct timeval *);
 static void    timevalfix(struct timeval *);
 
-static int     sleep_hard_us = 100;
-SYSCTL_INT(_kern, OID_AUTO, sleep_hard_us, CTLFLAG_RW, &sleep_hard_us, 0, "")
+/*
+ * Nanosleep tries very hard to sleep for a precisely requested time
+ * interval, down to 1uS.  The administrator can impose a minimum delay
+ * and a delay below which we hard-loop instead of initiate a timer
+ * interrupt and sleep.
+ *
+ * For machines under high loads it might be beneficial to increase min_us
+ * to e.g. 1000uS (1ms) so spining processes sleep meaningfully.
+ */
+static int     nanosleep_min_us = 10;
+static int     nanosleep_hard_us = 100;
+SYSCTL_INT(_kern, OID_AUTO, nanosleep_min_us, CTLFLAG_RW,
+          &nanosleep_min_us, 0, "")
+SYSCTL_INT(_kern, OID_AUTO, nanosleep_hard_us, CTLFLAG_RW,
+          &nanosleep_hard_us, 0, "")
 
 static int
 settime(struct timeval *tv)
@@ -140,6 +152,9 @@ settime(struct timeval *tv)
        return (0);
 }
 
+/*
+ * MPSAFE
+ */
 int
 kern_clock_gettime(clockid_t clock_id, struct timespec *ats)
 {
@@ -159,7 +174,9 @@ kern_clock_gettime(clockid_t clock_id, struct timespec *ats)
        return (error);
 }
 
-/* ARGSUSED */
+/*
+ * MPSAFE
+ */
 int
 sys_clock_gettime(struct clock_gettime_args *uap)
 {
@@ -192,7 +209,9 @@ kern_clock_settime(clockid_t clock_id, struct timespec *ats)
        return (error);
 }
 
-/* ARGSUSED */
+/*
+ * MPALMOSTSAFE
+ */
 int
 sys_clock_settime(struct clock_settime_args *uap)
 {
@@ -202,9 +221,15 @@ sys_clock_settime(struct clock_settime_args *uap)
        if ((error = copyin(uap->tp, &ats, sizeof(ats))) != 0)
                return (error);
 
-       return (kern_clock_settime(uap->clock_id, &ats));
+       get_mplock();
+       error = kern_clock_settime(uap->clock_id, &ats);
+       rel_mplock();
+       return (error);
 }
 
+/*
+ * MPSAFE
+ */
 int
 kern_clock_getres(clockid_t clock_id, struct timespec *ts)
 {
@@ -231,6 +256,9 @@ kern_clock_getres(clockid_t clock_id, struct timespec *ts)
        return(error);
 }
 
+/*
+ * MPSAFE
+ */
 int
 sys_clock_getres(struct clock_getres_args *uap)
 {
@@ -262,42 +290,46 @@ sys_clock_getres(struct clock_getres_args *uap)
  *     tsleep, then handle the fine-grained delay on the next
  *     loop.  This usually results in two sleeps occuring, a long one
  *     and a short one.
+ *
+ * MPSAFE
  */
 static void
-ns1_systimer(systimer_t info)
+ns1_systimer(systimer_t info, int in_ipi __unused,
+    struct intrframe *frame __unused)
 {
        lwkt_schedule(info->data);
 }
 
-static int
+int
 nanosleep1(struct timespec *rqt, struct timespec *rmt)
 {
        static int nanowait;
        struct timespec ts, ts2, ts3;
        struct timeval tv;
        int error;
-       int tried_yield;
 
        if (rqt->tv_nsec < 0 || rqt->tv_nsec >= 1000000000)
                return (EINVAL);
+       /* XXX: imho this should return EINVAL at least for tv_sec < 0 */
        if (rqt->tv_sec < 0 || (rqt->tv_sec == 0 && rqt->tv_nsec == 0))
                return (0);
        nanouptime(&ts);
        timespecadd(&ts, rqt);          /* ts = target timestamp compare */
        TIMESPEC_TO_TIMEVAL(&tv, rqt);  /* tv = sleep interval */
-       tried_yield = 0;
 
        for (;;) {
                int ticks;
                struct systimer info;
 
-               ticks = tv.tv_usec / tick;      /* approximate */
+               ticks = tv.tv_usec / ustick;    /* approximate */
 
                if (tv.tv_sec == 0 && ticks == 0) {
                        thread_t td = curthread;
-                       if (tried_yield || tv.tv_usec < sleep_hard_us) {
-                               tried_yield = 0;
-                               uio_yield();
+                       if (tv.tv_usec > 0 && tv.tv_usec < nanosleep_min_us)
+                               tv.tv_usec = nanosleep_min_us;
+                       if (tv.tv_usec < nanosleep_hard_us) {
+                               lwkt_user_yield();
+                               cpu_pause();
                        } else {
                                crit_enter_quick(td);
                                systimer_init_oneshot(&info, ns1_systimer,
@@ -334,7 +366,9 @@ nanosleep1(struct timespec *rqt, struct timespec *rmt)
        }
 }
 
-/* ARGSUSED */
+/*
+ * MPSAFE
+ */
 int
 sys_nanosleep(struct nanosleep_args *uap)
 {
@@ -361,7 +395,9 @@ sys_nanosleep(struct nanosleep_args *uap)
        return (error);
 }
 
-/* ARGSUSED */
+/*
+ * MPSAFE
+ */
 int
 sys_gettimeofday(struct gettimeofday_args *uap)
 {
@@ -380,7 +416,9 @@ sys_gettimeofday(struct gettimeofday_args *uap)
        return (error);
 }
 
-/* ARGSUSED */
+/*
+ * MPALMOSTSAFE
+ */
 int
 sys_settimeofday(struct settimeofday_args *uap)
 {
@@ -402,8 +440,13 @@ sys_settimeofday(struct settimeofday_args *uap)
        if (uap->tzp &&
            (error = copyin((caddr_t)uap->tzp, (caddr_t)&atz, sizeof(atz))))
                return (error);
-       if (uap->tv && (error = settime(&atv)))
+
+       get_mplock();
+       if (uap->tv && (error = settime(&atv))) {
+               rel_mplock();
                return (error);
+       }
+       rel_mplock();
        if (uap->tzp)
                tz = atz;
        return (0);
@@ -492,7 +535,9 @@ kern_adjfreq(int64_t rate)
                lwkt_setcpu_self(globaldata_find(origcpu));
 }
 
-/* ARGSUSED */
+/*
+ * MPALMOSTSAFE
+ */
 int
 sys_adjtime(struct adjtime_args *uap)
 {
@@ -503,8 +548,8 @@ sys_adjtime(struct adjtime_args *uap)
 
        if ((error = priv_check(td, PRIV_ADJTIME)))
                return (error);
-       if ((error =
-           copyin((caddr_t)uap->delta, (caddr_t)&atv, sizeof(struct timeval))))
+       error = copyin(uap->delta, &atv, sizeof(struct timeval));
+       if (error)
                return (error);
 
        /*
@@ -515,13 +560,14 @@ sys_adjtime(struct adjtime_args *uap)
         * overshoot and start taking us away from the desired final time.
         */
        ndelta = (int64_t)atv.tv_sec * 1000000000 + atv.tv_usec * 1000;
+       get_mplock();
        kern_adjtime(ndelta, &odelta);
+       rel_mplock();
 
        if (uap->olddelta) {
                atv.tv_sec = odelta / 1000000000;
                atv.tv_usec = odelta % 1000000000 / 1000;
-               (void) copyout((caddr_t)&atv, (caddr_t)uap->olddelta,
-                   sizeof(struct timeval));
+               copyout(&atv, uap->olddelta, sizeof(struct timeval));
        }
        return (0);
 }
@@ -646,8 +692,9 @@ SYSCTL_PROC(_kern_ntp, OID_AUTO, adjust,
  * 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.
+ *
+ * MPALMOSTSAFE
  */
-/* ARGSUSED */
 int
 sys_getitimer(struct getitimer_args *uap)
 {
@@ -657,7 +704,7 @@ sys_getitimer(struct getitimer_args *uap)
 
        if (uap->which > ITIMER_PROF)
                return (EINVAL);
-       crit_enter();
+       lwkt_gettoken(&p->p_token);
        if (uap->which == ITIMER_REAL) {
                /*
                 * Convert from absolute to relative time in .it_value
@@ -676,12 +723,13 @@ sys_getitimer(struct getitimer_args *uap)
        } else {
                aitv = p->p_timer[uap->which];
        }
-       crit_exit();
-       return (copyout((caddr_t)&aitv, (caddr_t)uap->itv,
-           sizeof (struct itimerval)));
+       lwkt_reltoken(&p->p_token);
+       return (copyout(&aitv, uap->itv, sizeof (struct itimerval)));
 }
 
-/* ARGSUSED */
+/*
+ * MPALMOSTSAFE
+ */
 int
 sys_setitimer(struct setitimer_args *uap)
 {
@@ -708,7 +756,7 @@ sys_setitimer(struct setitimer_args *uap)
                timevalclear(&aitv.it_interval);
        else if (itimerfix(&aitv.it_interval))
                return (EINVAL);
-       crit_enter();
+       lwkt_gettoken(&p->p_token);
        if (uap->which == ITIMER_REAL) {
                if (timevalisset(&p->p_realtimer.it_value))
                        callout_stop(&p->p_ithandle);
@@ -720,8 +768,16 @@ sys_setitimer(struct setitimer_args *uap)
                p->p_realtimer = aitv;
        } else {
                p->p_timer[uap->which] = aitv;
+               switch(uap->which) {
+               case ITIMER_VIRTUAL:
+                       p->p_flags &= ~P_SIGVTALRM;
+                       break;
+               case ITIMER_PROF:
+                       p->p_flags &= ~P_SIGPROF;
+                       break;
+               }
        }
-       crit_exit();
+       lwkt_reltoken(&p->p_token);
        return (0);
 }
 
@@ -744,26 +800,27 @@ realitexpire(void *arg)
        struct timeval ctv, ntv;
 
        p = (struct proc *)arg;
+       lwkt_gettoken(&p->p_token);
        ksignal(p, SIGALRM);
        if (!timevalisset(&p->p_realtimer.it_interval)) {
                timevalclear(&p->p_realtimer.it_value);
+               lwkt_reltoken(&p->p_token);
                return;
        }
        for (;;) {
-               crit_enter();
                timevaladd(&p->p_realtimer.it_value,
-                   &p->p_realtimer.it_interval);
+                          &p->p_realtimer.it_interval);
                getmicrouptime(&ctv);
                if (timevalcmp(&p->p_realtimer.it_value, &ctv, >)) {
                        ntv = p->p_realtimer.it_value;
                        timevalsub(&ntv, &ctv);
                        callout_reset(&p->p_ithandle, tvtohz_low(&ntv),
                                      realitexpire, p);
-                       crit_exit();
+                       lwkt_reltoken(&p->p_token);
                        return;
                }
-               crit_exit();
        }
+       lwkt_reltoken(&p->p_token);
 }
 
 /*
@@ -771,6 +828,8 @@ realitexpire(void *arg)
  * .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.)
+ *
+ * MPSAFE
  */
 int
 itimerfix(struct timeval *tv)
@@ -779,8 +838,8 @@ itimerfix(struct timeval *tv)
        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;
+       if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < ustick)
+               tv->tv_usec = ustick;
        return (0);
 }