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:
parent
0148ecaea0
commit
8f9c237010
13 changed files with 141 additions and 12 deletions
3
README
3
README
|
@ -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?
|
||||||
=================================
|
=================================
|
||||||
|
|
|
@ -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
17
local.c
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
5
local.h
5
local.h
|
@ -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 */
|
||||||
|
|
6
localp.h
6
localp.h
|
@ -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 */
|
||||||
|
|
51
reference.c
51
reference.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
17
sources.c
17
sources.c
|
@ -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)) {
|
||||||
|
|
||||||
|
|
17
sys_linux.c
17
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
|
/* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
|
@ -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 */);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue