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>
 

Reply via email to