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. +It will increase monotonically during a leap second. +Will return EINVAL if the current offset between TAI and UTC is not known, +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; 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); + if (err) + 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) + 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>