Subject: Implementation of CLOCK_MONOTONIC (was: mono_time exported? ...)
To: Tad Hunt <tad@entrisphere.com>
From: Simon Burge <simonb@wasabisystems.com>
List: tech-kern
Date: 01/27/2002 01:00:32
Jason R Thorpe wrote:

> On Sat, Jan 26, 2002 at 02:29:32PM +1100, Simon Burge wrote:
> 
>  > I'd suggest either adding a gethrtime() syscall (ala solaris and
>  > possibly other SVR4-type OS's), or (probably better) adding a
>  > CLOCK_MONOTIME or CLOCK_MONOTONIC for clock_{getres,gettime,settime}(2)
>  > to use.  If we do the latter, we can easily do gethrtime() as a libc
>  > function that calls clock_gettime() with the right arguments.
> 
> ...but make sure clock_settime() doesn't actually do anything in the
> MONOTIME case :-)

Heh.  Ok :)

Below is a diff that implements a CLOCK_MONOTONIC for the clock_*(2)
calls.  FWIW, the real time on my pc164 has drifted about 5 seconds
faster that the monotonic time over an uptime about about 2 hours:

	real time: 1012053364.318731000
	mono mono: 1012053359.322848000

I will note that the kernel mono_time variable strickly speaking
doesn't increase monotonically - the "BUMPTIME(&mono_time, delta);" in
kern_clock.c occurs _after_ delta has been adjusted after any possible
adjtime() adjustments have occurred.

If I get no (reasonable!) complaints, I'll commit this in a couple of
days.  At this stage, I see no need for a gethrtime() call.  Comments
on that?

Simon.
--
Simon Burge                            <simonb@wasabisystems.com>
NetBSD CDs, Support and Service:    http://www.wasabisystems.com/


Index: lib/libc/sys/clock_settime.2
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/sys/clock_settime.2,v
retrieving revision 1.9
diff -d -p -u -r1.9 clock_settime.2
--- lib/libc/sys/clock_settime.2	2001/10/16 16:08:55	1.9
+++ lib/libc/sys/clock_settime.2	2002/01/26 13:40:28
@@ -95,6 +95,23 @@ and obtained by
 represent the amount of time (in seconds and nanoseconds)
 since 00:00 Universal Coordinated Time, January 1, 1970.
 .Pp
+A
+.Fa clock_id
+of
+.Dv CLOCK_MONOTONIC
+identifies a clock that increases at a steady rate (monotonically).
+This clock
+is not affected by calls to
+.Xr adjtime 2
+and
+.Xr settimeofday 2
+and will 
+fail with an
+.Er EINVAL
+error if it the clock specified in a call to
+.Fn clock_settime .
+The origin of the clock is unspecified.
+.Pp
 If the calling user is not the syper-user, then the
 .Fn clock_settime
 function in the standard C library will try to use the
@@ -141,6 +158,10 @@ The
 .Fa tp
 argument specified a nanosecond value less than zero of greater than or equal
 1000 million.
+.It Bq Er EINVAL
+The
+.Fa clock_id
+argument is a clock that can not be adjusted.
 .It Bq Er EPERM
 The
 calling process does not have the appropriate privilege to set the specified
Index: sys/kern/kern_time.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/kern_time.c,v
retrieving revision 1.60
diff -d -p -u -r1.60 kern_time.c
--- sys/kern/kern_time.c	2001/12/09 16:10:43	1.60
+++ sys/kern/kern_time.c	2002/01/26 13:40:29
@@ -166,13 +166,24 @@ sys_clock_gettime(p, v, retval)
 	clockid_t clock_id;
 	struct timeval atv;
 	struct timespec ats;
+	int s;
 
 	clock_id = SCARG(uap, clock_id);
-	if (clock_id != CLOCK_REALTIME)
+	switch (clock_id) {
+	case CLOCK_REALTIME:
+		microtime(&atv);
+		TIMEVAL_TO_TIMESPEC(&atv,&ats);
+		break;
+	case CLOCK_MONOTONIC:
+		/* XXX "hz" granularity */
+		s = splclock(); 
+		atv = mono_time;
+		splx(s);
+		TIMEVAL_TO_TIMESPEC(&atv,&ats);
+		break;
+	default:
 		return (EINVAL);
-
-	microtime(&atv);
-	TIMEVAL_TO_TIMESPEC(&atv,&ats);
+	}
 
 	return copyout(&ats, SCARG(uap, tp), sizeof(ats));
 }
@@ -209,12 +220,17 @@ clock_settime1(clock_id, tp)
 	if ((error = copyin(tp, &ats, sizeof(ats))) != 0)
 		return (error);
 
-	if (clock_id != CLOCK_REALTIME)
+	switch (clock_id) {
+	case CLOCK_REALTIME:
+		TIMESPEC_TO_TIMEVAL(&atv, &ats);
+		if ((error = settime(&atv)) != 0)
+			return (error);
+		break;
+	case CLOCK_MONOTONIC:
+		return (EINVAL);	/* read-only clock */
+	default:
 		return (EINVAL);
-
-	TIMESPEC_TO_TIMEVAL(&atv, &ats);
-	if ((error = settime(&atv)) != 0)
-		return (error);
+	}
 
 	return 0;
 }
@@ -234,15 +250,18 @@ sys_clock_getres(p, v, retval)
 	int error = 0;
 
 	clock_id = SCARG(uap, clock_id);
-	if (clock_id != CLOCK_REALTIME)
-		return (EINVAL);
-
-	if (SCARG(uap, tp)) {
+	switch (clock_id) {
+	case CLOCK_REALTIME:
+	case CLOCK_MONOTONIC:
 		ts.tv_sec = 0;
 		ts.tv_nsec = 1000000000 / hz;
+		break;
+	default:
+		return (EINVAL);
+	}
 
+	if (SCARG(uap, tp))
 		error = copyout(&ts, SCARG(uap, tp), sizeof(ts));
-	}
 
 	return error;
 }
Index: sys/sys/time.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/time.h,v
retrieving revision 1.33
diff -d -p -u -r1.33 time.h
--- sys/sys/time.h	2001/12/09 16:10:43	1.33
+++ sys/sys/time.h	2002/01/26 13:40:29
@@ -154,6 +154,7 @@ struct clockinfo {
 #define CLOCK_REALTIME	0
 #define CLOCK_VIRTUAL	1
 #define CLOCK_PROF	2
+#define	CLOCK_MONOTONIC	3
 
 #define TIMER_RELTIME	0x0	/* relative timer */
 #define TIMER_ABSTIME	0x1	/* absolute timer */