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> >