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
not support hardware reference clocks, leap seconds or broadcast
modes.
not support hardware reference clocks or broadcast modes.
Where are new versions announced?
=================================

View file

@ -213,8 +213,8 @@ interfaced at a later date.
@item
@code{xntpd} supports effectively all of RFC1305, including broadcast /
multicast clients, leap seconds, and extra encryption schemes for
authenticating data packets.
multicast clients and extra encryption schemes for authenticating
data packets.
@item
@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_OffsetCorrectionDriver drv_offset_convert;
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_ApplyStepOffsetDriver apply_step_offset,
lcl_OffsetCorrectionDriver offset_convert,
lcl_ImmediateStepDriver immediate_step)
lcl_ImmediateStepDriver immediate_step,
lcl_SetLeapDriver set_leap)
{
drv_read_freq = read_freq;
drv_set_freq = set_freq;
@ -543,6 +545,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
drv_apply_step_offset = apply_step_offset;
drv_offset_convert = offset_convert;
drv_immediate_step = immediate_step;
drv_set_leap = set_leap;
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. */
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 */

View file

@ -60,6 +60,9 @@ typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr);
as an immediate step instead */
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
@ -68,6 +71,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
lcl_AccrueOffsetDriver accrue_offset,
lcl_ApplyStepOffsetDriver apply_step_offset,
lcl_OffsetCorrectionDriver offset_convert,
lcl_ImmediateStepDriver immediate_step_driver);
lcl_ImmediateStepDriver immediate_step_driver,
lcl_SetLeapDriver set_leap);
#endif /* GOT_LOCALP_H */

View file

@ -44,6 +44,7 @@ static int are_we_synchronised;
static int enable_local_stratum;
static int local_stratum;
static NTP_Leap our_leap_status;
static int our_leap_sec;
static int our_stratum;
static unsigned long our_ref_id;
struct timeval our_ref_time; /* Stored relative to reference, NOT local time */
@ -102,7 +103,8 @@ REF_Initialise(void)
double our_frequency_ppm;
are_we_synchronised = 0;
our_leap_status = LEAP_Normal;
our_leap_status = LEAP_Unsynchronised;
our_leap_sec = 0;
initialised = 1;
our_root_dispersion = 1.0;
our_root_delay = 1.0;
@ -176,6 +178,10 @@ REF_Initialise(void)
void
REF_Finalise(void)
{
if (our_leap_sec) {
LCL_SetLeap(0);
}
if (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
REF_SetReference(int stratum,
@ -356,13 +400,14 @@ REF_SetReference(int stratum,
are_we_synchronised = 1;
our_stratum = stratum + 1;
our_leap_status = leap;
our_ref_id = ref_id;
our_ref_time = *ref_time;
our_offset = offset;
our_root_delay = root_delay;
our_root_dispersion = root_dispersion;
update_leap_status(leap);
/* Eliminate updates that are based on totally unreliable frequency
information */
@ -517,6 +562,8 @@ REF_SetUnsynchronised(void)
}
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 +
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) ||
(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
* 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
@ -813,7 +828,7 @@ SYS_Linux_Initialise(void)
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
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,
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,
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
with the non-volatile clock */

View file

@ -395,7 +395,8 @@ SYS_SunOS_Initialise(void)
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
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
with the non-volatile clock */

View file

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

View file

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