Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 13 Jun 2025 09:17:36 +0300
From:      Konstantin Belousov <kostikbel@gmail.com>
To:        Warner Losh <imp@freebsd.org>
Cc:        src-committers@freebsd.org, dev-commits-src-all@freebsd.org, dev-commits-src-main@freebsd.org
Subject:   Re: git: 7b7ba7857ce8 - main - Implement CLOCK_TAI
Message-ID:  <aEvCgKpiu-obZtxo@kib.kiev.ua>
In-Reply-To: <202506121825.55CIPemP006355@gitrepo.freebsd.org>
References:  <202506121825.55CIPemP006355@gitrepo.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Thu, Jun 12, 2025 at 06:25:40PM +0000, Warner Losh wrote:
> The branch main has been updated by imp:
> 
> URL: https://cgit.FreeBSD.org/src/commit/?id=7b7ba7857ce8be0bf6ab905d936d8ba1363e4ec2
> 
> commit 7b7ba7857ce8be0bf6ab905d936d8ba1363e4ec2
> Author:     Nathan Whitehorn <nwhitehorn@FreeBSD.org>
> AuthorDate: 2025-06-12 17:52:30 +0000
> Commit:     Warner Losh <imp@FreeBSD.org>
> CommitDate: 2025-06-12 18:25:31 +0000
> 
>     Implement CLOCK_TAI
>     
>     Provide a clock through clock_gettime() that returns the current TAI
>     time (UTC without leap seconds) as a complement to CLOCK_REALTIME. This
>     provides compatibility with Linux, which also provides a CLOCK_TAI since
>     kernel 2.6.26, and this seems to be becoming the standard way to acquire
>     TAI time. Unlike Linux, this code will return EINVAL if the TAI offset
>     (set by ntpd, ptpd, etc.) is not known since it seems pathological for
>     CLOCK_TAI to silently give the wrong (UTC) time if the offset is not
>     known as it does on Linux.
>     
>     Reviewed by: imp
>     Differential Revision:  https://reviews.freebsd.org/D46268
> ---
>  lib/libsys/_umtx_op.2             |  2 ++
>  lib/libsys/clock_gettime.2        | 14 ++++++++++--
>  lib/libsys/nanosleep.2            |  4 +++-
>  lib/libsys/timer_create.2         |  3 ++-
>  lib/libthr/thread/thr_condattr.c  |  1 +
>  share/man/man3/pthread_condattr.3 |  3 ++-
>  sys/kern/kern_ntptime.c           |  3 ++-
>  sys/kern/kern_tc.c                | 10 ++++++++-
>  sys/kern/kern_time.c              | 46 ++++++++++++++++++++++++++-------------
>  sys/kern/kern_umtx.c              |  7 ++++--
>  sys/sys/_clock_id.h               |  4 ++++
>  sys/sys/timeffc.h                 |  4 +++-
>  sys/sys/timex.h                   |  2 +-
>  13 files changed, 77 insertions(+), 26 deletions(-)
> 
> diff --git a/lib/libsys/_umtx_op.2 b/lib/libsys/_umtx_op.2
> index 974850fb8425..c590f8e8e0c8 100644
> --- a/lib/libsys/_umtx_op.2
> +++ b/lib/libsys/_umtx_op.2
> @@ -210,6 +210,8 @@ Valid clock identifiers are a subset of those for
>  .It
>  .Dv CLOCK_SECOND
>  .It
> +.Dv CLOCK_TAI
> +.It
>  .Dv CLOCK_UPTIME
>  .It
>  .Dv CLOCK_UPTIME_FAST
> diff --git a/lib/libsys/clock_gettime.2 b/lib/libsys/clock_gettime.2
> index fcdc5be498f2..1dcfd9d1faf7 100644
> --- a/lib/libsys/clock_gettime.2
> +++ b/lib/libsys/clock_gettime.2
> @@ -27,7 +27,7 @@
>  .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>  .\" SUCH DAMAGE.
>  .\"
> -.Dd June 28, 2024
> +.Dd August 10, 2024
>  .Dt CLOCK_GETTIME 2
>  .Os
>  .Sh NAME
> @@ -99,11 +99,20 @@ query, using an in-kernel cached value of the current second.
>  Returns the execution time of the calling process.
>  .It Dv CLOCK_THREAD_CPUTIME_ID
>  Returns the execution time of the calling thread.
> +.It Dv CLOCK_TAI
> +Increments in SI seconds like a wall clock.
> +It uses a 1970 epoch and implements the TAI timescale.
> +Similar to CLOCK_REALTIME, but without leap seconds.
.Dv CLOCK_REALTIME
> +It will increase monotonically during a leap second.
> +Will return EINVAL if the current offset between TAI and UTC is not known,
.Er EINVAL
> +which may be the case early in boot before NTP or other time daemon has
> +synchronized.
>  .El
>  .Pp
>  The clock IDs
>  .Dv CLOCK_BOOTTIME ,
>  .Dv CLOCK_REALTIME ,
> +.Dv CLOCK_TAI ,
>  .Dv CLOCK_MONOTONIC ,
>  and
>  .Dv CLOCK_UPTIME
> @@ -202,7 +211,8 @@ The clock IDs
>  .Dv CLOCK_MONOTONIC_PRECISE ,
>  .Dv CLOCK_REALTIME_FAST ,
>  .Dv CLOCK_REALTIME_PRECISE ,
> -.Dv CLOCK_SECOND
> +.Dv CLOCK_SECOND ,
> +.Dv CLOCK_TAI ,
>  .Dv CLOCK_UPTIME ,
>  .Dv CLOCK_UPTIME_FAST ,
>  and
> diff --git a/lib/libsys/nanosleep.2 b/lib/libsys/nanosleep.2
> index ba9aae1edf57..290565dbd6e1 100644
> --- a/lib/libsys/nanosleep.2
> +++ b/lib/libsys/nanosleep.2
> @@ -27,7 +27,7 @@
>  .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>  .\" SUCH DAMAGE.
>  .\"
> -.Dd April 29, 2025
> +.Dd May 3, 2025
>  .Dt NANOSLEEP 2
>  .Os
>  .Sh NAME
> @@ -116,6 +116,8 @@ CLOCK_REALTIME_PRECISE
>  .It
>  CLOCK_SECOND
>  .It
> +CLOCK_TAI
> +.It
>  CLOCK_UPTIME
>  .It
>  CLOCK_UPTIME_FAST
> diff --git a/lib/libsys/timer_create.2 b/lib/libsys/timer_create.2
> index e8489b390845..8f6ff2e27c51 100644
> --- a/lib/libsys/timer_create.2
> +++ b/lib/libsys/timer_create.2
> @@ -126,7 +126,8 @@ the value of the timer ID.
>  This implementation supports a
>  .Fa clock_id
>  of
> -.Dv CLOCK_REALTIME
> +.Dv CLOCK_REALTIME ,
> +.Dv CLOCK_TAI ,
>  or
>  .Dv CLOCK_MONOTONIC .
>  .Pp
> diff --git a/lib/libthr/thread/thr_condattr.c b/lib/libthr/thread/thr_condattr.c
> index 0dc3e52bab5e..dc56363fc084 100644
> --- a/lib/libthr/thread/thr_condattr.c
> +++ b/lib/libthr/thread/thr_condattr.c
> @@ -94,6 +94,7 @@ _pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id)
>  	if (attr == NULL || *attr == NULL)
>  		return (EINVAL);
>  	if (clock_id != CLOCK_REALTIME &&
> +	    clock_id != CLOCK_TAI &&
>  	    clock_id != CLOCK_VIRTUAL &&
>  	    clock_id != CLOCK_PROF &&
>  	    clock_id != CLOCK_MONOTONIC) {
> diff --git a/share/man/man3/pthread_condattr.3 b/share/man/man3/pthread_condattr.3
> index 96d30263d7f2..33ad904f9a3d 100644
> --- a/share/man/man3/pthread_condattr.3
> +++ b/share/man/man3/pthread_condattr.3
> @@ -85,7 +85,8 @@ in
>  .Xr pthread_cond_timedwait 3
>  and may be set to
>  .Dv CLOCK_REALTIME
> -(default)
> +(default),
> +.Dv CLOCK_TAI ,
>  or
>  .Dv CLOCK_MONOTONIC .
>  .Pp
> diff --git a/sys/kern/kern_ntptime.c b/sys/kern/kern_ntptime.c
> index 65746021028b..892a6798ab1f 100644
> --- a/sys/kern/kern_ntptime.c
> +++ b/sys/kern/kern_ntptime.c
> @@ -504,7 +504,7 @@ sys_ntp_adjtime(struct thread *td, struct ntp_adjtime_args *uap)
>   * simulation.
>   */
>  void
> -ntp_update_second(int64_t *adjustment, time_t *newsec)
> +ntp_update_second(int64_t *adjustment, time_t *newsec, long *tai_off)
>  {
>  	int tickrate;
>  	l_fp ftemp;		/* 32/64-bit temporary */
> @@ -624,6 +624,7 @@ ntp_update_second(int64_t *adjustment, time_t *newsec)
>  		L_ADD(time_adj, ftemp);
>  	}
>  	*adjustment = time_adj;
> +	*tai_off = time_tai;
>  		
>  #ifdef PPS_SYNC
>  	if (pps_valid > 0)
> diff --git a/sys/kern/kern_tc.c b/sys/kern/kern_tc.c
> index a797a101bf6f..e85812e415c7 100644
> --- a/sys/kern/kern_tc.c
> +++ b/sys/kern/kern_tc.c
> @@ -72,6 +72,7 @@ struct timehands {
>  	uint64_t		th_scale;
>  	u_int			th_large_delta;
>  	u_int	 		th_offset_count;
> +	long			th_tai_offset;
>  	struct bintime		th_offset;
>  	struct bintime		th_bintime;
>  	struct timeval		th_microtime;
> @@ -1066,6 +1067,7 @@ sysclock_getsnapshot(struct sysclock_snap *clock_snap, int fast)
>  		th = timehands;
>  		gen = atomic_load_acq_int(&th->th_generation);
>  		fbi->th_scale = th->th_scale;
> +		fbi->th_tai_offset = th->th_tai_offset;
>  		fbi->tick_time = th->th_offset;
>  #ifdef FFCLOCK
>  		ffth = fftimehands;
> @@ -1139,6 +1141,11 @@ sysclock_snap2bintime(struct sysclock_snap *cs, struct bintime *bt,
>  			getboottimebin(&boottimebin);
>  			bintime_add(bt, &boottimebin);
>  		}
> +		if (!(flags & FBCLOCK_LEAPSEC)) {
> +			if (cs->fb_info.th_tai_offset == 0)
> +				return (EINVAL);
> +			bt->sec += cs->fb_info.th_tai_offset;
> +		}
>  		break;
>  #ifdef FFCLOCK
>  	case SYSCLOCK_FFWD:
> @@ -1433,7 +1440,8 @@ tc_windup(struct bintime *new_boottimebin)
>  
>  		do {
>  			t = bt.sec;
> -			ntp_update_second(&th->th_adjustment, &bt.sec);
> +			ntp_update_second(&th->th_adjustment, &bt.sec,
> +			    &th->th_tai_offset);
>  			if (bt.sec != t)
>  				th->th_boottime.sec += bt.sec - t;
>  			--i;
> diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c
> index 0c31c1563d99..5960136eb515 100644
> --- a/sys/kern/kern_time.c
> +++ b/sys/kern/kern_time.c
> @@ -49,6 +49,7 @@
>  #include <sys/proc.h>
>  #include <sys/posix4.h>
>  #include <sys/time.h>
> +#include <sys/timeffc.h>
>  #include <sys/timers.h>
>  #include <sys/timetc.h>
>  #include <sys/vnode.h>
> @@ -60,7 +61,7 @@
>  #include <vm/vm_extern.h>
>  #include <vm/uma.h>
>  
> -#define MAX_CLOCKS 	(CLOCK_MONOTONIC+1)
> +#define MAX_CLOCKS 	(CLOCK_TAI+1)
>  #define CPUCLOCK_BIT		0x80000000
>  #define CPUCLOCK_PROCESS_BIT	0x40000000
>  #define CPUCLOCK_ID_MASK	(~(CPUCLOCK_BIT|CPUCLOCK_PROCESS_BIT))
> @@ -101,7 +102,6 @@ static int	realtimer_gettime(struct itimer *, struct itimerspec *);
>  static int	realtimer_settime(struct itimer *, int,
>  			struct itimerspec *, struct itimerspec *);
>  static int	realtimer_delete(struct itimer *);
> -static void	realtimer_clocktime(clockid_t, struct timespec *);
>  static void	realtimer_expire(void *);
>  static void	realtimer_expire_l(struct itimer *it, bool proc_locked);
>  
> @@ -318,7 +318,10 @@ int
>  kern_clock_gettime(struct thread *td, clockid_t clock_id, struct timespec *ats)
>  {
>  	struct timeval sys, user;
> +	struct sysclock_snap clk;
> +	struct bintime bt;
>  	struct proc *p;
> +	int err;
We usually spell it as 'error'.

>  
>  	p = td->td_proc;
>  	switch (clock_id) {
> @@ -329,6 +332,14 @@ kern_clock_gettime(struct thread *td, clockid_t clock_id, struct timespec *ats)
>  	case CLOCK_REALTIME_FAST:
>  		getnanotime(ats);
>  		break;
> +	case CLOCK_TAI:
> +		sysclock_getsnapshot(&clk, 0);
> +		err = sysclock_snap2bintime(&clk, &bt, clk.sysclock_active,
> +		    (clk.sysclock_active == SYSCLOCK_FFWD) ? FFCLOCK_LERP : 0);
Extra ()

> +		if (err)
	if (err != 0)

> +			return (err);
> +		bintime2timespec(&bt, ats);
> +		break;
>  	case CLOCK_VIRTUAL:
>  		PROC_LOCK(p);
>  		PROC_STATLOCK(p);
> @@ -451,6 +462,7 @@ kern_clock_getres(struct thread *td, clockid_t clock_id, struct timespec *ts)
>  	case CLOCK_REALTIME:
>  	case CLOCK_REALTIME_FAST:
>  	case CLOCK_REALTIME_PRECISE:
> +	case CLOCK_TAI:
>  	case CLOCK_MONOTONIC:
>  	case CLOCK_MONOTONIC_FAST:
>  	case CLOCK_MONOTONIC_PRECISE:
> @@ -516,6 +528,7 @@ kern_clock_nanosleep(struct thread *td, clockid_t clock_id, int flags,
>  		return (EINVAL);
>  	switch (clock_id) {
>  	case CLOCK_REALTIME:
> +	case CLOCK_TAI:
>  		precise = nanosleep_precise;
>  		is_abs_real = (flags & TIMER_ABSTIME) != 0;
>  		break;
> @@ -1167,6 +1180,7 @@ itimer_start(void)
>  		NULL, NULL, itimer_init, itimer_fini, UMA_ALIGN_PTR, 0);
>  	register_posix_clock(CLOCK_REALTIME,  &rt_clock);
>  	register_posix_clock(CLOCK_MONOTONIC, &rt_clock);
> +	register_posix_clock(CLOCK_TAI, &rt_clock);
>  	p31b_setcfg(CTL_P1003_1B_TIMERS, 200112L);
>  	p31b_setcfg(CTL_P1003_1B_DELAYTIMER_MAX, INT_MAX);
>  	p31b_setcfg(CTL_P1003_1B_TIMER_MAX, TIMER_MAX);
> @@ -1327,6 +1341,7 @@ kern_ktimer_create(struct thread *td, clockid_t clock_id, struct sigevent *evp,
>  		switch (clock_id) {
>  		default:
>  		case CLOCK_REALTIME:
> +		case CLOCK_TAI:
>  			it->it_sigev.sigev_signo = SIGALRM;
>  			break;
>  		case CLOCK_VIRTUAL:
> @@ -1570,10 +1585,14 @@ static int
>  realtimer_gettime(struct itimer *it, struct itimerspec *ovalue)
>  {
>  	struct timespec cts;
> +	int err;
>  
>  	mtx_assert(&it->it_mtx, MA_OWNED);
>  
> -	realtimer_clocktime(it->it_clockid, &cts);
> +	err = kern_clock_gettime(curthread, it->it_clockid, &cts);
> +	if (err)
Again, if (error != 0) and s/err/error/.
And more places below.

> +		return (err);
> +
>  	*ovalue = it->it_time;
>  	if (ovalue->it_value.tv_sec != 0 || ovalue->it_value.tv_nsec != 0) {
>  		timespecsub(&ovalue->it_value, &cts, &ovalue->it_value);
> @@ -1594,6 +1613,7 @@ realtimer_settime(struct itimer *it, int flags, struct itimerspec *value,
>  	struct timespec cts, ts;
>  	struct timeval tv;
>  	struct itimerspec val;
> +	int err;
>  
>  	mtx_assert(&it->it_mtx, MA_OWNED);
>  
> @@ -1613,7 +1633,10 @@ realtimer_settime(struct itimer *it, int flags, struct itimerspec *value,
>  
>  	it->it_time = val;
>  	if (timespecisset(&val.it_value)) {
> -		realtimer_clocktime(it->it_clockid, &cts);
> +		err = kern_clock_gettime(curthread, it->it_clockid, &cts);
> +		if (err)
> +			return (err);
> +
>  		ts = val.it_value;
>  		if ((flags & TIMER_ABSTIME) == 0) {
>  			/* Convert to absolute time. */
> @@ -1636,15 +1659,6 @@ realtimer_settime(struct itimer *it, int flags, struct itimerspec *value,
>  	return (0);
>  }
>  
> -static void
> -realtimer_clocktime(clockid_t id, struct timespec *ts)
> -{
> -	if (id == CLOCK_REALTIME)
> -		getnanotime(ts);
> -	else	/* CLOCK_MONOTONIC */
> -		getnanouptime(ts);
> -}
> -
>  int
>  itimer_accept(struct proc *p, int timerid, ksiginfo_t *ksi)
>  {
> @@ -1689,10 +1703,12 @@ realtimer_expire_l(struct itimer *it, bool proc_locked)
>  	struct timeval tv;
>  	struct proc *p;
>  	uint64_t interval, now, overruns, value;
> +	int err;
> +
> +	err = kern_clock_gettime(curthread, it->it_clockid, &cts);
>  
> -	realtimer_clocktime(it->it_clockid, &cts);
>  	/* Only fire if time is reached. */
> -	if (timespeccmp(&cts, &it->it_time.it_value, >=)) {
> +	if (err == 0 && timespeccmp(&cts, &it->it_time.it_value, >=)) {
>  		if (timespecisset(&it->it_time.it_interval)) {
>  			timespecadd(&it->it_time.it_value,
>  			    &it->it_time.it_interval,
> diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c
> index dc6fee1f8f38..905ebd4f98ac 100644
> --- a/sys/kern/kern_umtx.c
> +++ b/sys/kern/kern_umtx.c
> @@ -693,6 +693,7 @@ umtx_abs_timeout_init(struct umtx_abs_timeout *timo, int clockid,
>  		timo->is_abs_real = clockid == CLOCK_REALTIME ||
>  		    clockid == CLOCK_REALTIME_FAST ||
>  		    clockid == CLOCK_REALTIME_PRECISE ||
> +		    clockid == CLOCK_TAI ||
>  		    clockid == CLOCK_SECOND;
>  	}
>  }
> @@ -787,6 +788,7 @@ umtx_abs_timeout_getsbt(struct umtx_abs_timeout *timo, sbintime_t *sbt,
>  	case CLOCK_PROF:
>  	case CLOCK_THREAD_CPUTIME_ID:
>  	case CLOCK_PROCESS_CPUTIME_ID:
> +	case CLOCK_TAI: /* Boot time is not necessarily stable in TAI */
>  	default:
>  		kern_clock_gettime(curthread, timo->clockid, &timo->cur);
>  		if (timespeccmp(&timo->end, &timo->cur, <=))
> @@ -2953,8 +2955,9 @@ do_cv_wait(struct thread *td, struct ucond *cv, struct umutex *m,
>  			umtx_key_release(&uq->uq_key);
>  			return (EFAULT);
>  		}
> -		if (clockid < CLOCK_REALTIME ||
> -		    clockid >= CLOCK_THREAD_CPUTIME_ID) {
> +		if ((clockid < CLOCK_REALTIME ||
> +		    clockid >= CLOCK_THREAD_CPUTIME_ID) &&
> +		    clockid != CLOCK_TAI) {
>  			/* hmm, only HW clock id will work. */
>  			umtx_key_release(&uq->uq_key);
>  			return (EINVAL);
> diff --git a/sys/sys/_clock_id.h b/sys/sys/_clock_id.h
> index 728346a0f0ab..83130d1f8a16 100644
> --- a/sys/sys/_clock_id.h
> +++ b/sys/sys/_clock_id.h
> @@ -74,6 +74,10 @@
>  #define	CLOCK_PROCESS_CPUTIME_ID 15
>  #endif /* __POSIX_VISIBLE >= 199309 */
>  
> +#ifdef __BSD_VISIBLE
> +#define	CLOCK_TAI		16
> +#endif
> +
>  /*
>   * Linux compatible names.
>   */
> diff --git a/sys/sys/timeffc.h b/sys/sys/timeffc.h
> index a83b62b1672c..8bec73ed48a4 100644
> --- a/sys/sys/timeffc.h
> +++ b/sys/sys/timeffc.h
> @@ -89,7 +89,7 @@ extern int sysclock_active;
>   *			of the kernel tick timer (1/hz [s]).
>   * FFCLOCK_LERP:	Linear interpolation of ffclock time to guarantee
>   *			monotonic time.
> - * FFCLOCK_LEAPSEC:	Include leap seconds.
> + * {FB|FF}CLOCK_LEAPSEC: Include leap seconds.
>   * {FB|FF}CLOCK_UPTIME:	Time stamp should be relative to system boot, not epoch.
>   */
>  #define	FFCLOCK_FAST		0x00000001
> @@ -100,6 +100,7 @@ extern int sysclock_active;
>  
>  #define	FBCLOCK_FAST		0x00010000 /* Currently unused. */
>  #define	FBCLOCK_UPTIME		0x00020000
> +#define	FBCLOCK_LEAPSEC		0x00040000
>  #define	FBCLOCK_MASK		0xffff0000
>  
>  /*
> @@ -111,6 +112,7 @@ struct fbclock_info {
>  	struct bintime		error;
>  	struct bintime		tick_time;
>  	uint64_t		th_scale;
> +	long			th_tai_offset;
>  	int			status;
>  };
>  
> diff --git a/sys/sys/timex.h b/sys/sys/timex.h
> index 072297375792..03632bdb119c 100644
> --- a/sys/sys/timex.h
> +++ b/sys/sys/timex.h
> @@ -154,7 +154,7 @@ struct timex {
>  #ifdef __FreeBSD__
>  
>  #ifdef _KERNEL
> -void	ntp_update_second(int64_t *adjustment, time_t *newsec);
> +void	ntp_update_second(int64_t *adjustment, time_t *newsec, long *tai_off);
>  #else /* !_KERNEL */
>  #include <sys/cdefs.h>
>  



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?aEvCgKpiu-obZtxo>