From c687224a11addaf40c05f6d38ba0c80f6c36560a Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Wed, 25 Mar 2020 09:12:35 +0100 Subject: [PATCH] reference: improve check for close leap second Improve the check to work with the actual timestamp of the leap second instead of the closest midnight and don't turn it off on the leap timeout. Also allow sample times to be checked in addition to the system time and NTP time to avoid accumulation of samples mixing pre-leap and post-leap timestamps (causing error of +/-0.5 or +/-1.0 seconds). --- reference.c | 30 +++++++++++++++++++----------- reference.h | 6 +++--- sources.c | 4 ++-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/reference.c b/reference.c index 9a2f973..c76e586 100644 --- a/reference.c +++ b/reference.c @@ -112,6 +112,9 @@ static void update_drift_file(double, double); /* Leap second handling mode */ static REF_LeapMode leap_mode; +/* Time of UTC midnight of the upcoming or previous leap second */ +static time_t leap_when; + /* Flag indicating the clock was recently corrected for leap second and it may not have correct time yet (missing 23:59:60 in the UTC time scale) */ static int leap_in_progress; @@ -246,6 +249,7 @@ REF_Initialise(void) enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance); UTI_ZeroTimespec(&local_ref_time); + leap_when = 0; leap_timeout_id = 0; leap_in_progress = 0; leap_mode = CNF_GetLeapSecMode(); @@ -720,10 +724,12 @@ set_leap_timeout(time_t now) if (!our_leap_sec) return; + leap_when = (now / (24 * 3600) + 1) * (24 * 3600); + /* Insert leap second at 0:00:00 UTC, delete at 23:59:59 UTC. If the clock will be corrected by the system, timeout slightly sooner to be sure it will happen before the system correction. */ - when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600); + when.tv_sec = leap_when; when.tv_nsec = 0; if (our_leap_sec < 0) when.tv_sec--; @@ -767,7 +773,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset) } if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset) - && !REF_IsLeapSecondClose()) { + && !REF_IsLeapSecondClose(NULL, 0.0)) { our_leap_sec = leap_sec; our_tai_offset = tai_offset; @@ -1324,22 +1330,24 @@ REF_DisableLocal(void) #define LEAP_SECOND_CLOSE 5 -int REF_IsLeapSecondClose(void) +static int +is_leap_close(time_t t) +{ + return t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE; +} + +/* ================================================== */ + +int REF_IsLeapSecondClose(struct timespec *ts, double offset) { struct timespec now, now_raw; - time_t t; - - if (!our_leap_sec) - return 0; SCH_GetLastEventTime(&now, NULL, &now_raw); - t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec; - if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE) + if (is_leap_close(now.tv_sec) || is_leap_close(now_raw.tv_sec)) return 1; - t = now_raw.tv_sec > 0 ? now_raw.tv_sec : -now_raw.tv_sec; - if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE) + if (ts && (is_leap_close(ts->tv_sec) || is_leap_close(ts->tv_sec + offset))) return 1; return 0; diff --git a/reference.h b/reference.h index 4f19af1..09400d4 100644 --- a/reference.h +++ b/reference.h @@ -184,9 +184,9 @@ extern void REF_ModifyMakestep(int limit, double threshold); extern void REF_EnableLocal(int stratum, double distance, int orphan); extern void REF_DisableLocal(void); -/* Check if current raw or cooked time is close to a leap second - and is better to discard any measurements */ -extern int REF_IsLeapSecondClose(void); +/* Check if either of the current raw and cooked time, and optionally a + provided timestamp with an offset, is close to a leap second */ +extern int REF_IsLeapSecondClose(struct timespec *ts, double offset); /* Return TAI-UTC offset corresponding to a time in UTC if available */ extern int REF_GetTaiOffset(struct timespec *ts); diff --git a/sources.c b/sources.c index bb4e6e3..0fa9eeb 100644 --- a/sources.c +++ b/sources.c @@ -361,7 +361,7 @@ get_leap_status(void) void SRC_SetLeapStatus(SRC_Instance inst, NTP_Leap leap) { - if (REF_IsLeapSecondClose()) + if (REF_IsLeapSecondClose(NULL, 0.0)) return; inst->leap = leap; @@ -390,7 +390,7 @@ SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample) source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset, sample->root_delay, sample->root_dispersion, sample->stratum); - if (REF_IsLeapSecondClose()) { + if (REF_IsLeapSecondClose(&sample->time, sample->offset)) { LOG(LOGS_INFO, "Dropping sample around leap second"); return; }