kernel/clock_gettime: Various fixes.
authorSascha Wildner <saw@online.de>
Sun, 25 Sep 2016 22:36:03 +0000 (00:36 +0200)
committerSascha Wildner <saw@online.de>
Sun, 25 Sep 2016 22:36:03 +0000 (00:36 +0200)
* Fix CLOCK_PROF and CLOCK_VIRTUAL to behave like FreeBSD's. They are
  not meant to return the values of ITIMER_PROF and ITIMER_VIRTUAL
  (which are decreasing, see getitimer(2)), like they were implemented
  by 91810a6f0686477493e9915e98cfc5adcbe91363. This also fixes
  CLOCK_PROCESS_CPUTIME_ID.

* Fix CLOCK_PROCESS_THREAD_ID. It was adding the values wrongly in a
  way that could cause tv_nsec to overflow (i.e. become >= 1000000000).

* Fix clock_getres() for CLOCK_{PROCESS,THREAD}_CPUTIME_ID.

* Mention CLOCK_{PROCESS,THREAD}_CPUTIME_ID in clock_gettime()'s manual
  page.

* Bring in some minor manual page fixes from FreeBSD.

Reported-by: zhtw
Dragonfly-bug: <https://bugs.dragonflybsd.org/issues/2951>

lib/libc/sys/clock_gettime.2
sys/kern/kern_time.c

index 63bde7d..dcab0f0 100644 (file)
@@ -1,5 +1,4 @@
 .\"    $OpenBSD: clock_gettime.2,v 1.4 1997/05/08 20:21:16 kstailey Exp $
-.\" $FreeBSD: src/lib/libc/sys/clock_gettime.2,v 1.3.2.8 2001/12/14 18:34:00 ru Exp $
 .\"
 .\" Copyright (c) 1980, 1991, 1993
 .\"    The Regents of the University of California.  All rights reserved.
@@ -28,7 +27,9 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 17, 2015
+.\" $FreeBSD: head/lib/libc/sys/clock_gettime.2 292777 2015-12-27 15:37:07Z dchagin $
+.\"
+.Dd September 26, 2016
 .Dt CLOCK_GETTIME 2
 .Os
 .Sh NAME
@@ -51,11 +52,13 @@ The
 .Fn clock_gettime
 and
 .Fn clock_settime
-allow the calling process to retrieve or set the value used by a clock
-which is specified by
+system calls allow the calling process to retrieve or set the value
+used by a clock which is specified by
 .Fa clock_id .
 .Pp
+The
 .Fa clock_id
+argument
 can be one of the following values:
 .Dv CLOCK_REALTIME ,
 .Dv CLOCK_REALTIME_PRECISE ,
@@ -78,10 +81,14 @@ for time that increments only when
 the CPU is running in user mode on behalf of the calling process;
 .Dv CLOCK_PROF
 for time that increments when the CPU is running in user or
-kernel mode; or
+kernel mode;
 .Dv CLOCK_SECOND
 which returns the current second without performing a full time counter
-query, using in-kernel cached value of current second.
+query, using in-kernel cached value of current second;
+.Dv CLOCK_PROCESS_CPUTIME_ID
+which returns the CPU-time clock of the calling process; or
+.Dv CLOCK_THREAD_CPUTIME_ID
+which returns the CPU-time clock of the calling thread.
 .Pp
 The clock IDs
 .Dv CLOCK_REALTIME_FAST ,
@@ -111,7 +118,7 @@ and do not incur any system call overhead after a certain amount of calls.
 The structure pointed to by
 .Fa tp
 is defined in
-.In sys/time.h
+.In sys/_timespec.h
 as:
 .Bd -literal
 struct timespec {
@@ -120,7 +127,8 @@ struct timespec {
 };
 .Ed
 .Pp
-Only the super-user may set the time of day.
+Only the super-user may set the time of day, using only
+.Dv CLOCK_REALTIME .
 If the system securelevel is greater than 1 (see
 .Xr init 8 ) ,
 the time may only be advanced.
@@ -132,7 +140,7 @@ system call even when the system is secure.
 .Pp
 The resolution (granularity) of a clock is returned by the
 .Fn clock_getres
-call.
+system call.
 This value is placed in a (non-NULL)
 .Fa *tp .
 .Sh RETURN VALUES
@@ -144,11 +152,9 @@ The following error codes may be set in
 .It Bq Er EINVAL
 The
 .Fa clock_id
-was not a valid value.
-.It Bq Er EFAULT
-The
-.Fa *tp
-argument address referenced invalid memory.
+or
+.Fa tp
+argument was not a valid value.
 .It Bq Er EPERM
 A user other than the super-user attempted to set the time.
 .El
@@ -160,8 +166,10 @@ A user other than the super-user attempted to set the time.
 .Sh STANDARDS
 The
 .Fn clock_gettime ,
-etc.\&
-functions conform to
+.Fn clock_settime ,
+and
+.Fn clock_getres
+system calls conform to
 .St -p1003.1b-93 .
 The clock IDs
 .Dv CLOCK_REALTIME_FAST ,
index 41527c7..cd03e87 100644 (file)
@@ -151,23 +151,38 @@ settime(struct timeval *tv)
        return (0);
 }
 
+static void
+get_process_cputime(struct proc *p, struct timespec *ats)
+{
+       struct rusage ru;
+
+       lwkt_gettoken(&p->p_token);
+       calcru_proc(p, &ru);
+       lwkt_reltoken(&p->p_token);
+       timevaladd(&ru.ru_utime, &ru.ru_stime);
+       TIMEVAL_TO_TIMESPEC(&ru.ru_utime, ats);
+}
+
+static void
+get_process_usertime(struct proc *p, struct timespec *ats)
+{
+       struct rusage ru;
+
+       lwkt_gettoken(&p->p_token);
+       calcru_proc(p, &ru);
+       lwkt_reltoken(&p->p_token);
+       TIMEVAL_TO_TIMESPEC(&ru.ru_utime, ats);
+}
+
 static void
 get_curthread_cputime(struct timespec *ats)
 {
        struct thread *td = curthread;
+       struct timeval sys, user;
 
-       crit_enter();
-       /*
-        * These are 64-bit fields but the actual values should never reach
-        * the limit. We don't care about overflows.
-        */
-       ats->tv_sec = td->td_uticks / 1000000;
-       ats->tv_sec += td->td_sticks / 1000000;
-       ats->tv_sec += td->td_iticks / 1000000;
-       ats->tv_nsec = (td->td_uticks % 1000000) * 1000;
-       ats->tv_nsec += (td->td_sticks % 1000000) * 1000;
-       ats->tv_nsec += (td->td_iticks % 1000000) * 1000;
-       crit_exit();
+       calcru(td->td_lwp, &user, &sys);
+       timevaladd(&user, &sys);
+       TIMEVAL_TO_TIMESPEC(&user, ats);
 }
 
 /*
@@ -176,9 +191,9 @@ get_curthread_cputime(struct timespec *ats)
 int
 kern_clock_gettime(clockid_t clock_id, struct timespec *ats)
 {
-       int error = 0;
        struct proc *p;
 
+       p = curproc;
        switch(clock_id) {
        case CLOCK_REALTIME:
        case CLOCK_REALTIME_PRECISE:
@@ -198,17 +213,11 @@ kern_clock_gettime(clockid_t clock_id, struct timespec *ats)
                getnanouptime(ats);
                break;
        case CLOCK_VIRTUAL:
-               p = curproc;
-               ats->tv_sec = p->p_timer[ITIMER_VIRTUAL].it_value.tv_sec;
-               ats->tv_nsec = p->p_timer[ITIMER_VIRTUAL].it_value.tv_usec *
-                              1000;
+               get_process_usertime(p, ats);
                break;
        case CLOCK_PROF:
        case CLOCK_PROCESS_CPUTIME_ID:
-               p = curproc;
-               ats->tv_sec = p->p_timer[ITIMER_PROF].it_value.tv_sec;
-               ats->tv_nsec = p->p_timer[ITIMER_PROF].it_value.tv_usec *
-                              1000;
+               get_process_cputime(p, ats);
                break;
        case CLOCK_SECOND:
                ats->tv_sec = time_second;
@@ -218,10 +227,9 @@ kern_clock_gettime(clockid_t clock_id, struct timespec *ats)
                get_curthread_cputime(ats);
                break;
        default:
-               error = EINVAL;
-               break;
+               return (EINVAL);
        }
-       return (error);
+       return (0);
 }
 
 /*
@@ -283,8 +291,7 @@ sys_clock_settime(struct clock_settime_args *uap)
 int
 kern_clock_getres(clockid_t clock_id, struct timespec *ts)
 {
-       int error;
-
+       ts->tv_sec = 0;
        switch(clock_id) {
        case CLOCK_REALTIME:
        case CLOCK_REALTIME_FAST:
@@ -295,36 +302,32 @@ kern_clock_getres(clockid_t clock_id, struct timespec *ts)
        case CLOCK_UPTIME:
        case CLOCK_UPTIME_FAST:
        case CLOCK_UPTIME_PRECISE:
-       case CLOCK_THREAD_CPUTIME_ID:
-       case CLOCK_PROCESS_CPUTIME_ID:
                /*
                 * Round up the result of the division cheaply
                 * by adding 1.  Rounding up is especially important
                 * if rounding down would give 0.  Perfect rounding
                 * is unimportant.
                 */
-               ts->tv_sec = 0;
                ts->tv_nsec = 1000000000 / sys_cputimer->freq + 1;
-               error = 0;
                break;
        case CLOCK_VIRTUAL:
        case CLOCK_PROF:
                /* Accurately round up here because we can do so cheaply. */
-               ts->tv_sec = 0;
                ts->tv_nsec = (1000000000 + hz - 1) / hz;
-               error = 0;
                break;
        case CLOCK_SECOND:
                ts->tv_sec = 1;
                ts->tv_nsec = 0;
-               error = 0;
                break;
-       default:
-               error = EINVAL;
+       case CLOCK_THREAD_CPUTIME_ID:
+       case CLOCK_PROCESS_CPUTIME_ID:
+               ts->tv_nsec = 1000;
                break;
+       default:
+               return (EINVAL);
        }
 
-       return(error);
+       return (0);
 }
 
 /*