Re: git: 7b7ba7857ce8 - main - Implement CLOCK_TAI
- Reply: Warner Losh : "Re: git: 7b7ba7857ce8 - main - Implement CLOCK_TAI"
- In reply to: Warner Losh : "git: 7b7ba7857ce8 - main - Implement CLOCK_TAI"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 13 Jun 2025 06:17:36 UTC
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>
>