Ah, good feedback. Applied in bd8621c6cd8a. Anything else? Warner
On Fri, Jun 13, 2025 at 2:17 AM Konstantin Belousov <kostik...@gmail.com> wrote: > > 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> > >