mirror of
https://git.freebsd.org/src.git
synced 2026-01-12 06:54:03 +00:00
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
This commit is contained in:
parent
4deb9760a9
commit
7b7ba7857c
13 changed files with 77 additions and 26 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue