Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/sys kern: Make time_second and time_uptime macros that w...



details:   https://anonhg.NetBSD.org/src/rev/c231857432e9
branches:  trunk
changeset: 377557:c231857432e9
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Mon Jul 17 12:55:20 2023 +0000

description:
kern: Make time_second and time_uptime macros that work atomically.

These use atomic load on platforms with atomic 64-bit load, and
seqlocks on platforms without.

This has the unfortunate side effect of slightly reducing the real
times available on 32-bit platforms, from ending some time in the
year 584942417218 AD, available on 64-bit platforms, to ending some
time in the year 584942417355 AD.  But during that slightly shorter
time, 32-bit platforms can avoid bugs arising from non-atomic access
to time_uptime and time_second.

Note: All platforms still have non-atomic access problems for
bintime, binuptime, nanotime, nanouptime, &c.  This can be addressed
by putting a seqlock around timebasebin and possibly some other
variable -- to be done in a later change.

XXX kernel ABI change -- deleting symbols

diffstat:

 share/man/man9/time_second.9 |   34 ++++++++---
 sys/kern/kern_tc.c           |  120 ++++++++++++++++++++++++++++++++++++++++--
 sys/sys/timevar.h            |   50 ++++++++++++++++-
 3 files changed, 183 insertions(+), 21 deletions(-)

diffs (truncated from 314 to 300 lines):

diff -r 983df8579fa1 -r c231857432e9 share/man/man9/time_second.9
--- a/share/man/man9/time_second.9      Mon Jul 17 12:55:03 2023 +0000
+++ b/share/man/man9/time_second.9      Mon Jul 17 12:55:20 2023 +0000
@@ -1,4 +1,4 @@
-.\" $NetBSD: time_second.9,v 1.9 2020/04/18 18:55:20 wiz Exp $
+.\" $NetBSD: time_second.9,v 1.10 2023/07/17 12:55:20 riastradh Exp $
 .\"
 .\" Copyright (c) 1994 Christopher G. Demetriou
 .\" All rights reserved.
@@ -32,23 +32,27 @@
 .\"
 .\" <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
 .\"
-.Dd April 17, 2020
+.Dd July 16, 2023
 .Dt TIME_SECOND 9
 .Os
 .Sh NAME
 .Nm time_second ,
-.Nm time_uptime
+.Nm time_uptime ,
+.Nm time_uptime32
 .Nd system time variables
 .Sh SYNOPSIS
 .In sys/time.h
-.Vt extern time_t time_second;
-.Vt extern time_t time_uptime;
+.Vt extern const time_t time_second;
+.Vt extern const time_t time_uptime;
+.Vt extern const uint32_t time_uptime32;
 .Sh DESCRIPTION
 The
 .Va time_second
 variable is the system's
-.Dq wall time
-clock.
+.Dq wall clock ,
+giving the number of seconds since midnight (0 hour),
+January 1, 1970, (proleptic) UTC,
+minus the number of leap seconds.
 It is set at boot by
 .Xr inittodr 9 ,
 and is updated periodically via
@@ -64,9 +68,19 @@ It is set at boot, and is updated period
 (It is not updated by
 .Xr settimeofday 2 . )
 .Pp
-All of these variables contain times
-expressed in seconds since midnight (0 hour),
-January 1, 1970, UTC.
+The
+.Va time_uptime32
+variable is the low-order 32 bits of
+.Va time_uptime ,
+which is cheaper to read on 32-bit platforms.
+.Pp
+You must only read the variables
+.Va time_second ,
+.Va time_uptime ,
+and
+.Va time_uptime32 ;
+you may not write to them or take their addresses.
+They may be implemented as macros.
 .Pp
 The
 .Xr bintime 9 ,
diff -r 983df8579fa1 -r c231857432e9 sys/kern/kern_tc.c
--- a/sys/kern/kern_tc.c        Mon Jul 17 12:55:03 2023 +0000
+++ b/sys/kern/kern_tc.c        Mon Jul 17 12:55:20 2023 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_tc.c,v 1.62 2021/06/02 21:34:58 riastradh Exp $ */
+/* $NetBSD: kern_tc.c,v 1.63 2023/07/17 12:55:20 riastradh Exp $ */
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -40,17 +40,19 @@
 
 #include <sys/cdefs.h>
 /* __FBSDID("$FreeBSD: src/sys/kern/kern_tc.c,v 1.166 2005/09/19 22:16:31 andre Exp $"); */
-__KERNEL_RCSID(0, "$NetBSD: kern_tc.c,v 1.62 2021/06/02 21:34:58 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_tc.c,v 1.63 2023/07/17 12:55:20 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ntp.h"
 #endif
 
 #include <sys/param.h>
+
 #include <sys/atomic.h>
 #include <sys/evcnt.h>
 #include <sys/kauth.h>
 #include <sys/kernel.h>
+#include <sys/lock.h>
 #include <sys/mutex.h>
 #include <sys/reboot.h>        /* XXX just to get AB_VERBOSE */
 #include <sys/sysctl.h>
@@ -131,8 +133,18 @@ static struct timehands *volatile timeha
 struct timecounter *timecounter = &dummy_timecounter;
 static struct timecounter *timecounters = &dummy_timecounter;
 
-volatile time_t time_second __cacheline_aligned = 1;
-volatile time_t time_uptime __cacheline_aligned = 1;
+volatile time_t time__second __cacheline_aligned = 1;
+volatile time_t time__uptime __cacheline_aligned = 1;
+
+#ifndef __HAVE_ATOMIC64_LOADSTORE
+static volatile struct {
+       uint32_t lo, hi;
+} time__uptime32 __cacheline_aligned = {
+       .lo = 1,
+}, time__second32 __cacheline_aligned = {
+       .lo = 1,
+};
+#endif
 
 static struct bintime timebasebin;
 
@@ -143,6 +155,103 @@ static u_int timecounter_mods;
 static volatile int timecounter_removals = 1;
 static u_int timecounter_bad;
 
+#ifdef __HAVE_ATOMIC64_LOADSTORE
+
+static inline void
+setrealuptime(time_t second, time_t uptime)
+{
+
+       atomic_store_relaxed(&time__second, second);
+       atomic_store_relaxed(&time__uptime, uptime);
+}
+
+#else
+
+static inline void
+setrealuptime(time_t second, time_t uptime)
+{
+       uint32_t seclo = second & 0xffffffff, sechi = second >> 32;
+       uint32_t uplo = uptime & 0xffffffff, uphi = uptime >> 32;
+
+       KDASSERT(mutex_owned(&timecounter_lock));
+
+       /*
+        * Fast path -- no wraparound, just updating the low bits, so
+        * no need for seqlocked access.
+        */
+       if (__predict_true(sechi == time__second32.hi) &&
+           __predict_true(uphi == time__uptime32.hi)) {
+               atomic_store_relaxed(&time__second32.lo, seclo);
+               atomic_store_relaxed(&time__uptime32.lo, uplo);
+               return;
+       }
+
+       atomic_store_relaxed(&time__second32.hi, 0xffffffff);
+       atomic_store_relaxed(&time__uptime32.hi, 0xffffffff);
+       membar_producer();
+       atomic_store_relaxed(&time__second32.lo, seclo);
+       atomic_store_relaxed(&time__uptime32.lo, uplo);
+       membar_producer();
+       atomic_store_relaxed(&time__second32.hi, sechi);
+       atomic_store_relaxed(&time__second32.lo, seclo);
+}
+
+time_t
+getrealtime(void)
+{
+       uint32_t lo, hi;
+
+       do {
+               for (;;) {
+                       hi = atomic_load_relaxed(&time__second32.hi);
+                       if (__predict_true(hi != 0xffffffff))
+                               break;
+                       SPINLOCK_BACKOFF_HOOK;
+               }
+               membar_consumer();
+               lo = atomic_load_relaxed(&time__second32.lo);
+               membar_consumer();
+       } while (hi != atomic_load_relaxed(&time__second32.hi));
+
+       return ((time_t)hi << 32) | lo;
+}
+
+time_t
+getuptime(void)
+{
+       uint32_t lo, hi;
+
+       do {
+               for (;;) {
+                       hi = atomic_load_relaxed(&time__uptime32.hi);
+                       if (__predict_true(hi != 0xffffffff))
+                               break;
+                       SPINLOCK_BACKOFF_HOOK;
+               }
+               membar_consumer();
+               lo = atomic_load_relaxed(&time__uptime32.lo);
+               membar_consumer();
+       } while (hi != atomic_load_relaxed(&time__uptime32.hi));
+
+       return ((time_t)hi << 32) | lo;
+}
+
+time_t
+getboottime(void)
+{
+
+       return getrealtime() - getuptime();
+}
+
+uint32_t
+getuptime32(void)
+{
+
+       return atomic_load_relaxed(&time__uptime32.lo);
+}
+
+#endif /* !defined(__HAVE_ATOMIC64_LOADSTORE) */
+
 /*
  * sysctl helper routine for kern.timercounter.hardware
  */
@@ -878,8 +987,7 @@ tc_windup(void)
         * Go live with the new struct timehands.  Ensure changes are
         * globally visible before changing.
         */
-       time_second = th->th_microtime.tv_sec;
-       time_uptime = th->th_offset.sec;
+       setrealuptime(th->th_microtime.tv_sec, th->th_offset.sec);
        membar_producer();
        timehands = th;
 
diff -r 983df8579fa1 -r c231857432e9 sys/sys/timevar.h
--- a/sys/sys/timevar.h Mon Jul 17 12:55:03 2023 +0000
+++ b/sys/sys/timevar.h Mon Jul 17 12:55:20 2023 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: timevar.h,v 1.49 2022/10/26 23:23:52 riastradh Exp $   */
+/*     $NetBSD: timevar.h,v 1.50 2023/07/17 12:55:20 riastradh Exp $   */
 
 /*
  *  Copyright (c) 2005, 2008, 2020 The NetBSD Foundation, Inc.
@@ -60,6 +60,7 @@
 #ifndef _SYS_TIMEVAR_H_
 #define _SYS_TIMEVAR_H_
 
+#include <sys/atomic.h>
 #include <sys/callout.h>
 #include <sys/queue.h>
 #include <sys/signal.h>
@@ -234,8 +235,47 @@ void       itimer_gettime(const struct itimer 
 void   ptimer_tick(struct lwp *, bool);
 void   ptimers_free(struct proc *, int);
 
-extern volatile time_t time_second;    /* current second in the epoch */
-extern volatile time_t time_uptime;    /* system uptime in seconds */
+extern volatile time_t time__second;   /* current second in the epoch */
+extern volatile time_t time__uptime;   /* system uptime in seconds */
+
+#define        time_second     getrealtime()
+#define        time_uptime     getuptime()
+#define        time_uptime32   getuptime32()
+
+#ifdef __HAVE_ATOMIC64_LOADSTORE
+
+static inline time_t
+getrealtime(void)
+{
+       return atomic_load_relaxed(&time__second);
+}
+
+static inline time_t
+getuptime(void)
+{
+       return atomic_load_relaxed(&time__uptime);
+}
+
+static inline time_t
+getboottime(void)
+{
+       return getrealtime() - getuptime();
+}
+
+static inline uint32_t
+getuptime32(void)
+{
+       return getuptime() & 0xffffffff;
+}
+
+#else
+
+time_t         getrealtime(void);
+time_t         getuptime(void);
+time_t         getboottime(void);
+uint32_t       getuptime32(void);
+
+#endif
 
 extern int time_adjusted;
 
@@ -248,13 +288,13 @@ extern int time_adjusted;
 static __inline time_t time_mono_to_wall(time_t t)



Home | Main Index | Thread Index | Old Index