Leap second support

Leap second status is accepted and forwarded to clients if majority
of selectable sources agree. The actual insertion/deletion is supported
only on Linux now.
This commit is contained in:
Miroslav Lichvar 2008-12-31 09:59:20 +01:00
parent 0148ecaea0
commit 8f9c237010
13 changed files with 141 additions and 12 deletions

3
README
View file

@ -90,8 +90,7 @@ What can chrony not do?
======================= =======================
Compared to the `reference' RFC1305 implementation xntpd, chronyd does Compared to the `reference' RFC1305 implementation xntpd, chronyd does
not support hardware reference clocks, leap seconds or broadcast not support hardware reference clocks or broadcast modes.
modes.
Where are new versions announced? Where are new versions announced?
================================= =================================

View file

@ -213,8 +213,8 @@ interfaced at a later date.
@item @item
@code{xntpd} supports effectively all of RFC1305, including broadcast / @code{xntpd} supports effectively all of RFC1305, including broadcast /
multicast clients, leap seconds, and extra encryption schemes for multicast clients and extra encryption schemes for authenticating
authenticating data packets. data packets.
@item @item
@code{xntpd} has been ported to more types of computer / operating @code{xntpd} has been ported to more types of computer / operating

17
local.c
View file

@ -54,6 +54,7 @@ static lcl_AccrueOffsetDriver drv_accrue_offset;
static lcl_ApplyStepOffsetDriver drv_apply_step_offset; static lcl_ApplyStepOffsetDriver drv_apply_step_offset;
static lcl_OffsetCorrectionDriver drv_offset_convert; static lcl_OffsetCorrectionDriver drv_offset_convert;
static lcl_ImmediateStepDriver drv_immediate_step; static lcl_ImmediateStepDriver drv_immediate_step;
static lcl_SetLeapDriver drv_set_leap;
/* ================================================== */ /* ================================================== */
@ -535,7 +536,8 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
lcl_AccrueOffsetDriver accrue_offset, lcl_AccrueOffsetDriver accrue_offset,
lcl_ApplyStepOffsetDriver apply_step_offset, lcl_ApplyStepOffsetDriver apply_step_offset,
lcl_OffsetCorrectionDriver offset_convert, lcl_OffsetCorrectionDriver offset_convert,
lcl_ImmediateStepDriver immediate_step) lcl_ImmediateStepDriver immediate_step,
lcl_SetLeapDriver set_leap)
{ {
drv_read_freq = read_freq; drv_read_freq = read_freq;
drv_set_freq = set_freq; drv_set_freq = set_freq;
@ -543,6 +545,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
drv_apply_step_offset = apply_step_offset; drv_apply_step_offset = apply_step_offset;
drv_offset_convert = offset_convert; drv_offset_convert = offset_convert;
drv_immediate_step = immediate_step; drv_immediate_step = immediate_step;
drv_set_leap = set_leap;
current_freq_ppm = (*drv_read_freq)(); current_freq_ppm = (*drv_read_freq)();
@ -572,3 +575,15 @@ LCL_MakeStep(void)
} }
/* ================================================== */ /* ================================================== */
void
LCL_SetLeap(int leap)
{
if (drv_set_leap) {
(drv_set_leap)(leap);
}
return;
}
/* ================================================== */

View file

@ -181,4 +181,9 @@ 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
at the end of the day if argument is positive, deleted if negative,
and zero cancels scheduled leap second. */
extern void LCL_SetLeap(int leap);
#endif /* GOT_LOCAL_H */ #endif /* GOT_LOCAL_H */

View file

@ -60,6 +60,9 @@ typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr);
as an immediate step instead */ as an immediate step instead */
typedef void (*lcl_ImmediateStepDriver)(void); typedef void (*lcl_ImmediateStepDriver)(void);
/* System driver to schedule leap second */
typedef void (*lcl_SetLeapDriver)(int leap);
extern void lcl_InvokeDispersionNotifyHandlers(double dispersion); extern void lcl_InvokeDispersionNotifyHandlers(double dispersion);
extern void extern void
@ -68,6 +71,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
lcl_AccrueOffsetDriver accrue_offset, lcl_AccrueOffsetDriver accrue_offset,
lcl_ApplyStepOffsetDriver apply_step_offset, lcl_ApplyStepOffsetDriver apply_step_offset,
lcl_OffsetCorrectionDriver offset_convert, lcl_OffsetCorrectionDriver offset_convert,
lcl_ImmediateStepDriver immediate_step_driver); lcl_ImmediateStepDriver immediate_step_driver,
lcl_SetLeapDriver set_leap);
#endif /* GOT_LOCALP_H */ #endif /* GOT_LOCALP_H */

View file

@ -44,6 +44,7 @@ static int are_we_synchronised;
static int enable_local_stratum; static int enable_local_stratum;
static int local_stratum; static int local_stratum;
static NTP_Leap our_leap_status; static NTP_Leap our_leap_status;
static int our_leap_sec;
static int our_stratum; static int our_stratum;
static unsigned long our_ref_id; static unsigned long our_ref_id;
struct timeval our_ref_time; /* Stored relative to reference, NOT local time */ struct timeval our_ref_time; /* Stored relative to reference, NOT local time */
@ -102,7 +103,8 @@ REF_Initialise(void)
double our_frequency_ppm; double our_frequency_ppm;
are_we_synchronised = 0; are_we_synchronised = 0;
our_leap_status = LEAP_Normal; our_leap_status = LEAP_Unsynchronised;
our_leap_sec = 0;
initialised = 1; initialised = 1;
our_root_dispersion = 1.0; our_root_dispersion = 1.0;
our_root_delay = 1.0; our_root_delay = 1.0;
@ -176,6 +178,10 @@ REF_Initialise(void)
void void
REF_Finalise(void) REF_Finalise(void)
{ {
if (our_leap_sec) {
LCL_SetLeap(0);
}
if (logfile) { if (logfile) {
fclose(logfile); fclose(logfile);
} }
@ -311,6 +317,44 @@ maybe_log_offset(double offset)
/* ================================================== */ /* ================================================== */
static void
update_leap_status(NTP_Leap leap)
{
time_t now;
struct tm stm;
int leap_sec;
leap_sec = 0;
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
/* Insert/delete leap second only on June 30 or December 31
and in other months ignore the leap status completely */
now = time(NULL);
stm = *gmtime(&now);
if (stm.tm_mon != 5 && stm.tm_mon != 11) {
leap = LEAP_Normal;
} else if ((stm.tm_mon == 5 && stm.tm_mday == 30) ||
(stm.tm_mon == 11 && stm.tm_mday == 31)) {
if (leap == LEAP_InsertSecond) {
leap_sec = 1;
} else {
leap_sec = -1;
}
}
}
if (leap_sec != our_leap_sec) {
LCL_SetLeap(leap_sec);
our_leap_sec = leap_sec;
}
our_leap_status = leap;
}
/* ================================================== */
void void
REF_SetReference(int stratum, REF_SetReference(int stratum,
@ -356,13 +400,14 @@ REF_SetReference(int stratum,
are_we_synchronised = 1; are_we_synchronised = 1;
our_stratum = stratum + 1; our_stratum = stratum + 1;
our_leap_status = leap;
our_ref_id = ref_id; our_ref_id = ref_id;
our_ref_time = *ref_time; our_ref_time = *ref_time;
our_offset = offset; our_offset = offset;
our_root_delay = root_delay; our_root_delay = root_delay;
our_root_dispersion = root_dispersion; our_root_dispersion = root_dispersion;
update_leap_status(leap);
/* Eliminate updates that are based on totally unreliable frequency /* Eliminate updates that are based on totally unreliable frequency
information */ information */
@ -517,6 +562,8 @@ REF_SetUnsynchronised(void)
} }
are_we_synchronised = 0; are_we_synchronised = 0;
update_leap_status(LEAP_Unsynchronised);
} }
/* ================================================== */ /* ================================================== */

View file

@ -687,6 +687,23 @@ SRC_SelectSource(unsigned long match_addr)
total_root_dispersion = (src_accrued_dispersion + total_root_dispersion = (src_accrued_dispersion +
sources[selected_source_index]->sel_info.root_dispersion); sources[selected_source_index]->sel_info.root_dispersion);
/* Accept leap second status if more than half of selectable sources agree */
for (i=j1=j2=0; i<n_sel_sources; i++) {
index = sel_sources[i];
if (sources[index]->leap_status == LEAP_InsertSecond) {
j1++;
} else if (sources[index]->leap_status == LEAP_DeleteSecond) {
j2++;
}
}
if (j1 > n_sel_sources / 2) {
leap_status = LEAP_InsertSecond;
} else if (j2 > n_sel_sources / 2) {
leap_status = LEAP_DeleteSecond;
}
if ((match_addr == 0) || if ((match_addr == 0) ||
(match_addr == sources[selected_source_index]->ref_id)) { (match_addr == sources[selected_source_index]->ref_id)) {

View file

@ -592,6 +592,21 @@ immediate_step(void)
/* ================================================== */ /* ================================================== */
static void
set_leap(int leap)
{
if (TMX_SetLeap(leap) < 0) {
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed in set_leap");
}
LOG(LOGS_INFO, LOGF_SysLinux, "System clock status set to %s leap second",
leap ? (leap > 0 ? "insert" : "delete") : "not insert/delete");
return;
}
/* ================================================== */
/* Estimate the value of HZ given the value of txc.tick that chronyd finds when /* Estimate the value of HZ given the value of txc.tick that chronyd finds when
* it starts. The only credible values are 100 (Linux/x86) or powers of 2. * it starts. The only credible values are 100 (Linux/x86) or powers of 2.
* Also, the bounds checking inside the kernel's adjtimex system call enforces * Also, the bounds checking inside the kernel's adjtimex system call enforces
@ -813,7 +828,7 @@ SYS_Linux_Initialise(void)
lcl_RegisterSystemDrivers(read_frequency, set_frequency, lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, apply_step_offset, accrue_offset, apply_step_offset,
get_offset_correction, immediate_step); get_offset_correction, immediate_step, set_leap);
} }
/* ================================================== */ /* ================================================== */

View file

@ -308,7 +308,8 @@ SYS_NetBSD_Initialise(void)
lcl_RegisterSystemDrivers(read_frequency, set_frequency, lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, apply_step_offset, accrue_offset, apply_step_offset,
get_offset_correction, NULL /* immediate_step */); get_offset_correction, NULL /* immediate_step */,
NULL /* set_leap */);
} }

View file

@ -444,7 +444,8 @@ SYS_Solaris_Initialise(void)
lcl_RegisterSystemDrivers(read_frequency, set_frequency, lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, apply_step_offset, accrue_offset, apply_step_offset,
get_offset_correction, NULL /* immediate_step */); get_offset_correction, NULL /* immediate_step */,
NULL /* set_leap */);
/* Turn off the kernel switch that keeps the system clock in step /* Turn off the kernel switch that keeps the system clock in step
with the non-volatile clock */ with the non-volatile clock */

View file

@ -395,7 +395,8 @@ SYS_SunOS_Initialise(void)
lcl_RegisterSystemDrivers(read_frequency, set_frequency, lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, apply_step_offset, accrue_offset, apply_step_offset,
get_offset_correction, NULL /* immediate_step */); get_offset_correction, NULL /* immediate_step */,
NULL /* set_leap */);
/* Turn off the kernel switch that keeps the system clock in step /* Turn off the kernel switch that keeps the system clock in step
with the non-volatile clock */ with the non-volatile clock */

View file

@ -39,6 +39,9 @@
#include "chrony_timex.h" #include "chrony_timex.h"
#include "wrap_adjtimex.h" #include "wrap_adjtimex.h"
/* Save leap status between calls */
static int leap_status = 0;
int int
TMX_SetTick(long tick) TMX_SetTick(long tick)
{ {
@ -73,6 +76,7 @@ TMX_SetFrequency(double freq, long tick)
txc.tick = tick; txc.tick = tick;
txc.status = STA_UNSYNC; /* Prevent any of the FLL/PLL stuff coming txc.status = STA_UNSYNC; /* Prevent any of the FLL/PLL stuff coming
up */ up */
txc.status |= leap_status; /* Preserve leap bits */
return adjtimex(&txc); return adjtimex(&txc);
} }
@ -144,5 +148,24 @@ TMX_ReadCurrentParams(struct tmx_params *params)
return result; return result;
} }
int
TMX_SetLeap(int leap)
{
struct timex txc;
if (leap > 0) {
leap_status = STA_INS;
} else if (leap < 0) {
leap_status = STA_DEL;
} else {
leap_status = 0;
}
txc.modes = ADJ_STATUS;
txc.status = STA_UNSYNC | leap_status;
return adjtimex(&txc);
}
#endif #endif

View file

@ -74,6 +74,7 @@ int TMX_SetFrequency(double freq, long tick);
int TMX_GetFrequency(double *freq); int TMX_GetFrequency(double *freq);
int TMX_GetOffsetLeft(long *offset); int TMX_GetOffsetLeft(long *offset);
int TMX_ReadCurrentParams(struct tmx_params *params); int TMX_ReadCurrentParams(struct tmx_params *params);
int TMX_SetLeap(int leap);
#endif /* GOT_WRAP_ADJTIMEX_H */ #endif /* GOT_WRAP_ADJTIMEX_H */