reference: add new leap second handling modes

In addition to the system driver handling add new modes to slew or step
the system clock for leap second, or ignore it completely. This can be
configured with leapsecmode directive.
This commit is contained in:
Miroslav Lichvar 2015-03-24 17:29:44 +01:00
parent c68a92ba80
commit f8db832491
6 changed files with 172 additions and 13 deletions

31
conf.c
View file

@ -63,6 +63,7 @@ static void parse_deny(char *);
static void parse_fallbackdrift(char *); static void parse_fallbackdrift(char *);
static void parse_include(char *); static void parse_include(char *);
static void parse_initstepslew(char *); static void parse_initstepslew(char *);
static void parse_leapsecmode(char *);
static void parse_local(char *); static void parse_local(char *);
static void parse_log(char *); static void parse_log(char *);
static void parse_mailonchange(char *); static void parse_mailonchange(char *);
@ -193,6 +194,9 @@ static double tempcomp_T0, tempcomp_k0, tempcomp_k1, tempcomp_k2;
static int sched_priority = 0; static int sched_priority = 0;
static int lock_memory = 0; static int lock_memory = 0;
/* Leap second handling mode */
static REF_LeapMode leapsec_mode = REF_LeapModeSystem;
/* Name of a system timezone containing leap seconds occuring at midnight */ /* Name of a system timezone containing leap seconds occuring at midnight */
static char *leapsec_tz = NULL; static char *leapsec_tz = NULL;
@ -440,6 +444,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_initstepslew(p); parse_initstepslew(p);
} else if (!strcasecmp(command, "keyfile")) { } else if (!strcasecmp(command, "keyfile")) {
parse_string(p, &keys_file); parse_string(p, &keys_file);
} else if (!strcasecmp(command, "leapsecmode")) {
parse_leapsecmode(p);
} else if (!strcasecmp(command, "leapsectz")) { } else if (!strcasecmp(command, "leapsectz")) {
parse_string(p, &leapsec_tz); parse_string(p, &leapsec_tz);
} else if (!strcasecmp(command, "linux_freq_scale")) { } else if (!strcasecmp(command, "linux_freq_scale")) {
@ -830,6 +836,23 @@ parse_initstepslew(char *line)
/* ================================================== */ /* ================================================== */
static void
parse_leapsecmode(char *line)
{
if (!strcasecmp(line, "system"))
leapsec_mode = REF_LeapModeSystem;
else if (!strcasecmp(line, "slew"))
leapsec_mode = REF_LeapModeSlew;
else if (!strcasecmp(line, "step"))
leapsec_mode = REF_LeapModeStep;
else if (!strcasecmp(line, "ignore"))
leapsec_mode = REF_LeapModeIgnore;
else
command_parse_error();
}
/* ================================================== */
static void static void
parse_clientloglimit(char *line) parse_clientloglimit(char *line)
{ {
@ -1664,6 +1687,14 @@ CNF_GetPidFile(void)
/* ================================================== */ /* ================================================== */
REF_LeapMode
CNF_GetLeapSecMode(void)
{
return leapsec_mode;
}
/* ================================================== */
char * char *
CNF_GetLeapSecTimezone(void) CNF_GetLeapSecTimezone(void)
{ {

2
conf.h
View file

@ -29,6 +29,7 @@
#define GOT_CONF_H #define GOT_CONF_H
#include "addressing.h" #include "addressing.h"
#include "reference.h"
extern void CNF_Initialise(int restarted); extern void CNF_Initialise(int restarted);
extern void CNF_Finalise(void); extern void CNF_Finalise(void);
@ -75,6 +76,7 @@ extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr); extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr); extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetPidFile(void); extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void); extern char *CNF_GetLeapSecTimezone(void);
/* Value returned in ppm, as read from file */ /* Value returned in ppm, as read from file */

16
local.c
View file

@ -506,6 +506,20 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
/* ================================================== */ /* ================================================== */
void
LCL_NotifyLeap(int leap)
{
struct timeval raw, cooked;
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
/* Dispatch to all handlers as if the clock was stepped */
invoke_parameter_change_handlers(&raw, &cooked, 0.0, -leap, LCL_ChangeStep);
}
/* ================================================== */
void void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate) LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{ {
@ -599,7 +613,7 @@ LCL_MakeStep(void)
/* ================================================== */ /* ================================================== */
void void
LCL_SetLeap(int leap) LCL_SetSystemLeap(int leap)
{ {
if (drv_set_leap) { if (drv_set_leap) {
(drv_set_leap)(leap); (drv_set_leap)(leap);

12
local.h
View file

@ -166,6 +166,10 @@ extern void LCL_ApplyStepOffset(double offset);
extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked, extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
double offset, double dispersion); double offset, double dispersion);
/* Routine to invoke notify handlers on leap second when the system clock
doesn't correct itself */
extern void LCL_NotifyLeap(int leap);
/* Perform the combination of modifying the frequency and applying /* Perform the combination of modifying the frequency and applying
a slew, in one easy step */ a slew, in one easy step */
extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate); extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
@ -194,10 +198,10 @@ extern void LCL_Finalise(void);
to a timezone problem. */ to a timezone problem. */
extern int LCL_MakeStep(void); extern int LCL_MakeStep(void);
/* Routine to schedule a leap second. Leap second will be inserted /* Routine to set the system clock to correct itself for a leap second if
at the end of the day if argument is positive, deleted if negative, supported. Leap second will be inserted at the end of the day if the
and zero cancels scheduled leap second. */ argument is positive, deleted if negative, and zero resets the setting. */
extern void LCL_SetLeap(int leap); extern void LCL_SetSystemLeap(int leap);
/* Routine to set a frequency correction (in ppm) that should be applied /* Routine to set a frequency correction (in ppm) that should be applied
to local clock to compensate for temperature changes. A positive to local clock to compensate for temperature changes. A positive

View file

@ -98,6 +98,17 @@ static double drift_file_age;
static void update_drift_file(double, double); static void update_drift_file(double, double);
/* Leap second handling mode */
static REF_LeapMode leap_mode;
/* 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;
/* Timer for the leap second handler */
static int leap_timer_running;
static SCH_TimeoutID leap_timeout_id;
/* Name of a system timezone containing leap seconds occuring at midnight */ /* Name of a system timezone containing leap seconds occuring at midnight */
static char *leap_tzname; static char *leap_tzname;
static time_t last_tz_leap_check; static time_t last_tz_leap_check;
@ -136,6 +147,7 @@ static double last_ref_update_interval;
/* ================================================== */ /* ================================================== */
static NTP_Leap get_tz_leap(time_t when); static NTP_Leap get_tz_leap(time_t when);
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */ /* ================================================== */
@ -148,6 +160,7 @@ handle_slew(struct timeval *raw,
void *anything) void *anything)
{ {
double delta; double delta;
struct timeval now;
if (change_type == LCL_ChangeUnknownStep) { if (change_type == LCL_ChangeUnknownStep) {
last_ref_update.tv_sec = 0; last_ref_update.tv_sec = 0;
@ -155,6 +168,13 @@ handle_slew(struct timeval *raw,
} else if (last_ref_update.tv_sec) { } else if (last_ref_update.tv_sec) {
UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset); UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
} }
/* When the clock was stepped, check if that doesn't change our leap status
and also reset the leap timeout to undo the shift in the scheduler */
if (change_type != LCL_ChangeAdjust && our_leap_sec && !leap_in_progress) {
LCL_ReadRawTime(&now);
update_leap_status(our_leap_status, now.tv_sec, 1);
}
} }
/* ================================================== */ /* ================================================== */
@ -217,6 +237,10 @@ REF_Initialise(void)
enable_local_stratum = CNF_AllowLocalReference(&local_stratum); enable_local_stratum = CNF_AllowLocalReference(&local_stratum);
leap_timer_running = 0;
leap_in_progress = 0;
leap_mode = CNF_GetLeapSecMode();
leap_tzname = CNF_GetLeapSecTimezone(); leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname) { if (leap_tzname) {
/* Check that the timezone has good data for Jun 30 2008 and Dec 31 2008 */ /* Check that the timezone has good data for Jun 30 2008 and Dec 31 2008 */
@ -263,9 +287,7 @@ REF_Initialise(void)
void void
REF_Finalise(void) REF_Finalise(void)
{ {
if (our_leap_sec) { update_leap_status(LEAP_Unsynchronised, 0, 0);
LCL_SetLeap(0);
}
if (drift_file) { if (drift_file) {
update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew); update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
@ -657,7 +679,72 @@ get_tz_leap(time_t when)
/* ================================================== */ /* ================================================== */
static void static void
update_leap_status(NTP_Leap leap, time_t now) leap_end_timeout(void *arg)
{
leap_timer_running = 0;
leap_in_progress = 0;
}
/* ================================================== */
static void
leap_start_timeout(void *arg)
{
leap_in_progress = 1;
switch (leap_mode) {
case REF_LeapModeSlew:
LCL_NotifyLeap(our_leap_sec);
LCL_AccumulateOffset(our_leap_sec, 0.0);
LOG(LOGS_WARN, LOGF_Reference, "Adjusting system clock for leap second");
break;
case REF_LeapModeStep:
LCL_NotifyLeap(our_leap_sec);
LCL_ApplyStepOffset(our_leap_sec);
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped for leap second");
break;
case REF_LeapModeIgnore:
LOG(LOGS_WARN, LOGF_Reference, "Ignoring leap second");
break;
default:
break;
}
/* Wait until the leap second is over with some extra room to be safe */
leap_timeout_id = SCH_AddTimeoutByDelay(2.0, leap_end_timeout, NULL);
}
/* ================================================== */
static void
set_leap_timeout(time_t now)
{
struct timeval when;
/* Stop old timer if there is one */
if (leap_timer_running) {
SCH_RemoveTimeout(leap_timeout_id);
leap_timer_running = 0;
leap_in_progress = 0;
}
if (!our_leap_sec)
return;
/* Insert leap second at 0:00:00 UTC, delete at 23:59:59 UTC */
when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
when.tv_usec = 0;
if (our_leap_sec < 0)
when.tv_sec--;
leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL);
leap_timer_running = 1;
}
/* ================================================== */
static void
update_leap_status(NTP_Leap leap, time_t now, int reset)
{ {
int leap_sec; int leap_sec;
@ -680,9 +767,22 @@ update_leap_status(NTP_Leap leap, time_t now)
} }
} }
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) { if (reset || (leap_sec != our_leap_sec && !REF_IsLeapSecondClose())) {
LCL_SetLeap(leap_sec);
our_leap_sec = leap_sec; our_leap_sec = leap_sec;
switch (leap_mode) {
case REF_LeapModeSystem:
LCL_SetSystemLeap(our_leap_sec);
break;
case REF_LeapModeSlew:
case REF_LeapModeStep:
case REF_LeapModeIgnore:
set_leap_timeout(now);
break;
default:
assert(0);
break;
}
} }
our_leap_status = leap; our_leap_status = leap;
@ -920,7 +1020,7 @@ REF_SetReference(int stratum,
our_residual_freq = frequency; our_residual_freq = frequency;
} }
update_leap_status(leap, raw_now.tv_sec); update_leap_status(leap, raw_now.tv_sec, 0);
maybe_log_offset(our_offset, raw_now.tv_sec); maybe_log_offset(our_offset, raw_now.tv_sec);
if (step_offset != 0.0) { if (step_offset != 0.0) {
@ -1015,7 +1115,7 @@ REF_SetUnsynchronised(void)
schedule_fb_drift(&now); schedule_fb_drift(&now);
} }
update_leap_status(LEAP_Unsynchronised, 0); update_leap_status(LEAP_Unsynchronised, 0, 0);
are_we_synchronised = 0; are_we_synchronised = 0;
LCL_SetSyncStatus(0, 0.0, 0.0); LCL_SetSyncStatus(0, 0.0, 0.0);

View file

@ -35,6 +35,14 @@
#include "ntp.h" #include "ntp.h"
#include "reports.h" #include "reports.h"
/* Leap second handling modes */
typedef enum {
REF_LeapModeSystem,
REF_LeapModeSlew,
REF_LeapModeStep,
REF_LeapModeIgnore,
} REF_LeapMode;
/* Init function */ /* Init function */
extern void REF_Initialise(void); extern void REF_Initialise(void);