From 8f9c2370104261bb506908383a31eddfe0378697 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Wed, 31 Dec 2008 09:59:20 +0100 Subject: [PATCH] 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. --- README | 3 +-- chrony.texi | 4 ++-- local.c | 17 ++++++++++++++++- local.h | 5 +++++ localp.h | 6 +++++- reference.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++-- sources.c | 17 +++++++++++++++++ sys_linux.c | 17 ++++++++++++++++- sys_netbsd.c | 3 ++- sys_solaris.c | 3 ++- sys_sunos.c | 3 ++- wrap_adjtimex.c | 23 ++++++++++++++++++++++ wrap_adjtimex.h | 1 + 13 files changed, 141 insertions(+), 12 deletions(-) diff --git a/README b/README index 44649c3..40a51a2 100644 --- a/README +++ b/README @@ -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? ================================= diff --git a/chrony.texi b/chrony.texi index 045f02c..ce29f7a 100644 --- a/chrony.texi +++ b/chrony.texi @@ -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 diff --git a/local.c b/local.c index 905ce3f..5e63a0e 100644 --- a/local.c +++ b/local.c @@ -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; +} + +/* ================================================== */ diff --git a/local.h b/local.h index 857d8b7..cc0f4a2 100644 --- a/local.h +++ b/local.h @@ -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 */ diff --git a/localp.h b/localp.h index 3e01a33..0ccdd0c 100644 --- a/localp.h +++ b/localp.h @@ -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 */ diff --git a/reference.c b/reference.c index 829fa80..b7313e9 100644 --- a/reference.c +++ b/reference.c @@ -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); } /* ================================================== */ diff --git a/sources.c b/sources.c index 40cb7d2..d90eb99 100644 --- a/sources.c +++ b/sources.c @@ -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; ileap_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)) { diff --git a/sys_linux.c b/sys_linux.c index 65eb563..d734b5e 100644 --- a/sys_linux.c +++ b/sys_linux.c @@ -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); } /* ================================================== */ diff --git a/sys_netbsd.c b/sys_netbsd.c index 3b275f3..54ea024 100644 --- a/sys_netbsd.c +++ b/sys_netbsd.c @@ -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 */); } diff --git a/sys_solaris.c b/sys_solaris.c index e9192aa..33b45c1 100644 --- a/sys_solaris.c +++ b/sys_solaris.c @@ -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 */ diff --git a/sys_sunos.c b/sys_sunos.c index 49ebf67..ac3b25b 100644 --- a/sys_sunos.c +++ b/sys_sunos.c @@ -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 */ diff --git a/wrap_adjtimex.c b/wrap_adjtimex.c index 9571ec4..3481e3c 100644 --- a/wrap_adjtimex.c +++ b/wrap_adjtimex.c @@ -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 diff --git a/wrap_adjtimex.h b/wrap_adjtimex.h index 7e44c6a..d7d59b1 100644 --- a/wrap_adjtimex.h +++ b/wrap_adjtimex.h @@ -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 */