Module Name: src Committed By: riastradh Date: Mon Jul 17 12:55:21 UTC 2023
Modified Files: src/share/man/man9: time_second.9 src/sys/kern: kern_tc.c src/sys/sys: timevar.h Log Message: 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 To generate a diff of this commit: cvs rdiff -u -r1.9 -r1.10 src/share/man/man9/time_second.9 cvs rdiff -u -r1.62 -r1.63 src/sys/kern/kern_tc.c cvs rdiff -u -r1.49 -r1.50 src/sys/sys/timevar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/share/man/man9/time_second.9 diff -u src/share/man/man9/time_second.9:1.9 src/share/man/man9/time_second.9:1.10 --- src/share/man/man9/time_second.9:1.9 Sat Apr 18 18:55:20 2020 +++ src/share/man/man9/time_second.9 Mon Jul 17 12:55:20 2023 @@ -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 , Index: src/sys/kern/kern_tc.c diff -u src/sys/kern/kern_tc.c:1.62 src/sys/kern/kern_tc.c:1.63 --- src/sys/kern/kern_tc.c:1.62 Wed Jun 2 21:34:58 2021 +++ src/sys/kern/kern_tc.c Mon Jul 17 12:55:20 2023 @@ -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; Index: src/sys/sys/timevar.h diff -u src/sys/sys/timevar.h:1.49 src/sys/sys/timevar.h:1.50 --- src/sys/sys/timevar.h:1.49 Wed Oct 26 23:23:52 2022 +++ src/sys/sys/timevar.h Mon Jul 17 12:55:20 2023 @@ -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) { - return t - time_uptime + time_second; + return t + getboottime(); } static __inline time_t time_wall_to_mono(time_t t) { - return t - time_second + time_uptime; + return t - getboottime(); } #endif /* !_SYS_TIMEVAR_H_ */