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 <nwhiteh...@freebsd.org>
> AuthorDate: 2025-06-12 17:52:30 +0000
> Commit:     Warner Losh <i...@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>
>  

Reply via email to