Compare commits

..

23 Commits

Author SHA1 Message Date
Miroslav Lichvar
5ac791665e doc: update NEWS 2017-01-24 15:03:24 +01:00
Miroslav Lichvar
a4e3f83611 update copyright years 2017-01-24 15:01:38 +01:00
Miroslav Lichvar
8a837f9c2b test: extend 119-smoothtime 2017-01-23 16:17:39 +01:00
Miroslav Lichvar
da2d33e9a8 ntp: fix time smoothing in interleaved mode
When the server's transmit timestamp was updated with a kernel/HW
timestamp, it didn't include the time smoothing offset. If the offset
was larger than one second, the update failed and clients using the
interleaved mode received less accurate timestamps. If the update
succeeded, the clients received timestamps that were not adjusted for
the time smoothing offset, which added an error of up to 0.5s/1s to
their measured offset/delay.

Fix the update to include the smoothing offset in the new timestamp.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
4b98dadae9 ntp: simplify UTI_Ntp64ToTimespec() callers
Since UTI_Ntp64ToTimespec() was modified to handle zero timestamps, some
of its callers don't need to do that anymore.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
86acea5c46 ntp: add interface index to NTP_Local_Address
This will allow us to get the interface index when sending responses to
clients.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
a60fc73e7b refclock_phc: add nocrossts option 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
50f99ec5f4 conf: add nocrossts option to hwtimestamp directive
This option disables the use of the PTP_SYS_OFFSET_PRECISE ioctl.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
31b6a14444 sys_linux: add support for PTP_SYS_OFFSET_PRECISE
This is for hardware that can precisely cross timestamp the PHC with the
system clock.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
9df4d36157 refclock_phc: use sys_linux code for reading PHC
This drops support for non-ioctl reading of PHC.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
b70f0b674f ntp: move PHC-specific code to sys_linux
This will allow sharing of the code with the PHC refclock driver.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
510784077f conf: add minpoll option to hwtimestamp directive 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
9800e397fb hwclock: make minimum sampling separation configurable 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
1436d9961f conf: add precision option to hwtimestamp directive 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
98f5d05925 ntp: include precision of PHC readings in their selection
Include a fixed non-zero precision (100 nanosecond) in the selection of
PHC readings.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
7a937c7652 conf: return hwtimestamp data in struct 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
b198d76676 ntp: include precision in maxdelay test 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
97d4203354 ntp: adapt sampling separation for short polling intervals 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
beaaaad162 ntp: allow sub-second polling intervals
Change the minimum minpoll to -4, but keep the minimum maxpoll at 0 in
order to not make it too easy to flood distant servers.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
4e78975909 ntp: use current poll when backing off on KoD RATE 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
99147ed8f2 ntp: rename maxdelay constants 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
dec0d3bfc2 ntp: reset ntpdata report on address change 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
cd84c99e70 examples: improve chronyd.service 2017-01-23 15:58:55 +01:00
26 changed files with 407 additions and 302 deletions

13
NEWS
View File

@@ -1,3 +1,16 @@
New in version 3.1
==================
Enhancements
------------
* Add support for precise cross timestamping of PHC on Linux
* Add minpoll, precision, nocrossts options to hwtimestamp directive
* Allow sub-second polling interval with NTP sources
Bug fixes
---------
* Fix time smoothing in interleaved mode
New in version 3.0
==================

View File

@@ -50,8 +50,11 @@ typedef struct {
unsigned short port;
} NTP_Remote_Address;
#define INVALID_IF_INDEX -1
typedef struct {
IPAddr ip_addr;
int if_index;
int sock_fd;
} NTP_Local_Address;

38
conf.c
View File

@@ -223,13 +223,7 @@ static char *leapsec_tz = NULL;
/* Name of the user to which will be dropped root privileges. */
static char *user;
typedef struct {
char *name;
double tx_comp;
double rx_comp;
} HwTs_Interface;
/* Array of HwTs_Interface */
/* Array of CNF_HwTsInterface */
static ARR_Instance hwts_interfaces;
typedef struct {
@@ -333,7 +327,7 @@ CNF_Initialise(int r)
{
restarted = r;
hwts_interfaces = ARR_CreateInstance(sizeof (HwTs_Interface));
hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
@@ -360,7 +354,7 @@ CNF_Finalise(void)
unsigned int i;
for (i = 0; i < ARR_GetSize(hwts_interfaces); i++)
Free(((HwTs_Interface *)ARR_GetElement(hwts_interfaces, i))->name);
Free(((CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, i))->name);
ARR_DestroyInstance(hwts_interfaces);
for (i = 0; i < ARR_GetSize(ntp_sources); i++)
@@ -1251,7 +1245,7 @@ parse_tempcomp(char *line)
static void
parse_hwtimestamp(char *line)
{
HwTs_Interface *iface;
CNF_HwTsInterface *iface;
char *p;
int n;
@@ -1265,18 +1259,30 @@ parse_hwtimestamp(char *line)
iface = ARR_GetNewElement(hwts_interfaces);
iface->name = Strdup(p);
iface->minpoll = 0;
iface->nocrossts = 0;
iface->precision = 100.0e-9;
iface->tx_comp = 0.0;
iface->rx_comp = 0.0;
for (p = line; *p; line += n, p = line) {
line = CPS_SplitWord(line);
if (!strcasecmp(p, "rxcomp")) {
if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
break;
} else if (!strcasecmp(p, "precision")) {
if (sscanf(line, "%lf%n", &iface->precision, &n) != 1)
break;
} else if (!strcasecmp(p, "rxcomp")) {
if (sscanf(line, "%lf%n", &iface->rx_comp, &n) != 1)
break;
} else if (!strcasecmp(p, "txcomp")) {
if (sscanf(line, "%lf%n", &iface->tx_comp, &n) != 1)
break;
} else if (!strcasecmp(p, "nocrossts")) {
n = 0;
iface->nocrossts = 1;
} else {
break;
}
@@ -1968,17 +1974,11 @@ CNF_GetInitStepThreshold(void)
/* ================================================== */
int
CNF_GetHwTsInterface(unsigned int index, char **name, double *tx_comp, double *rx_comp)
CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
{
HwTs_Interface *iface;
if (index >= ARR_GetSize(hwts_interfaces))
return 0;
iface = ARR_GetElement(hwts_interfaces, index);
*name = iface->name;
*tx_comp = iface->tx_comp;
*rx_comp = iface->rx_comp;
*iface = (CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, index);
return 1;
}

11
conf.h
View File

@@ -119,6 +119,15 @@ extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void);
extern int CNF_GetHwTsInterface(unsigned int index, char **name, double *tx_comp, double *rx_comp);
typedef struct {
char *name;
int minpoll;
int nocrossts;
double precision;
double tx_comp;
double rx_comp;
} CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
#endif /* GOT_CONF_H */

2
configure vendored
View File

@@ -732,7 +732,7 @@ fi
if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
grep '#define HAVE_CLOCK_GETTIME' config.h > /dev/null && \
test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \
'ioctl(1, PTP_CLOCK_GETCAPS, 0);'
'ioctl(1, PTP_CLOCK_GETCAPS + PTP_SYS_OFFSET, 0);'
then
add_def FEAT_PHC
fi

View File

@@ -68,11 +68,15 @@ options:
Although *chronyd* will trim the rate at which it samples the server during
normal operation, the user might want to constrain the minimum polling interval.
This is always defined as a power of 2, so *minpoll 5* would mean that the
polling interval cannot drop below 32 seconds. The default is 6 (64 seconds).
polling interval cannot drop below 32 seconds. The default is 6 (64 seconds),
the minimum is -4 (1/16th of a second), and the maximum is 24 (6 months). Note
that intervals shorter than 6 (64 seconds) should generally not be used with
public servers on the Internet as their administrators may consider it abuse.
*maxpoll* _poll_:::
In a similar way, the user might want to constrain the maximum polling interval.
Again this is specified as a power of 2, *maxpoll 9* indicates that the polling
interval must stay at or below 512 seconds. The default is 10 (1024 seconds).
interval must stay at or below 512 seconds. The default is 10 (1024 seconds),
the minimum is 0 (1 second), and the maximum is 24 (6 months).
*iburst*:::
If this option is set, the interval between the first four polls will be 2
seconds instead of _minpoll_. This is useful to quickly get the first update of
@@ -398,9 +402,10 @@ refclock SOCK /var/run/chrony.ttyS0.sock
*PHC*:::
PTP hardware clock (PHC) driver. The parameter is the path to the device of
the PTP clock, which for example can be synchronised by *ptp4l* from
http://linuxptp.sourceforge.net[*linuxptp*]. PTP clocks are typically kept in
TAI instead of UTC, so the *offset* option should be used to compensate for the
current UTC-TAI offset. For example:
http://linuxptp.sourceforge.net[*linuxptp*]. String *:nocrossts* can be
appended to the path to disable use of precise cross timestamping. PTP clocks
are typically kept in TAI instead of UTC, so the *offset* option should be used
to compensate for the current UTC-TAI offset. For example:
+
----
refclock PHC /dev/ptp0 poll 3 dpoll -2 offset -36
@@ -1814,6 +1819,15 @@ on all available interfaces.
+
The *hwtimestamp* directive has the following options:
+
*minpoll* _poll_:::
This option specifies the minimum interval between readings of the NIC clock.
It's defined as a power of two. It should correspond to the minimum polling
interval of all NTP sources and the minimum expected polling interval of NTP
clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
of a second).
*precision* _precision_:::
This option specifies the assumed precision of reading of the NIC clock. The
default value is 100e-9 (100 nanoseconds).
*txcomp* _compensation_:::
This option specifies the difference in seconds between the actual transmission
time at the physical layer and the reported transmit timestamp. This value will
@@ -1823,6 +1837,9 @@ This option specifies the difference in seconds between the reported receive
timestamp and the actual reception time at the physical layer. This value will
be subtracted from receive timestamps obtained from the NIC. The default value
is 0.
*nocrossts*:::
Some hardware can precisely cross timestamp the NIC clock with the system
clock. This option disables the use of the cross timestamping.
::
+
Examples of the directive are:

View File

@@ -1,7 +1,9 @@
[Unit]
Description=NTP client/server
Documentation=man:chronyd(8) man:chrony.conf(5)
After=ntpdate.service sntp.service ntpd.service
Conflicts=ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking

View File

@@ -39,9 +39,6 @@
/* Maximum number of samples per clock */
#define MAX_SAMPLES 16
/* Minimum interval between samples (in seconds) */
#define MIN_SAMPLE_SEPARATION 1.0
struct HCL_Instance_Record {
/* HW and local reference timestamp */
struct timespec hw_ref;
@@ -58,6 +55,9 @@ struct HCL_Instance_Record {
/* Maximum error of the last sample */
double last_err;
/* Minimum interval between samples */
double min_separation;
/* Flag indicating the offset and frequency values are valid */
int valid_coefs;
@@ -86,7 +86,7 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
/* ================================================== */
HCL_Instance
HCL_CreateInstance(void)
HCL_CreateInstance(double min_separation)
{
HCL_Instance clock;
@@ -95,6 +95,7 @@ HCL_CreateInstance(void)
clock->y_data[MAX_SAMPLES - 1] = 0.0;
clock->n_samples = 0;
clock->valid_coefs = 0;
clock->min_separation = min_separation;
LCL_AddParameterChangeHandler(handle_slew, clock);
@@ -115,7 +116,7 @@ int
HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
{
if (!clock->n_samples ||
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= MIN_SAMPLE_SEPARATION)
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= clock->min_separation)
return 1;
return 0;
@@ -140,7 +141,7 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
local_delta = UTI_DiffTimespecsToDouble(local_ts, &clock->local_ref) / local_freq;
if (hw_delta <= 0.0 || local_delta < MIN_SAMPLE_SEPARATION / 2.0) {
if (hw_delta <= 0.0 || local_delta < clock->min_separation / 2.0) {
clock->n_samples = 0;
DEBUG_LOG(LOGF_HwClocks, "HW clock reset interval=%f", local_delta);
}

View File

@@ -29,7 +29,7 @@
typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */
extern HCL_Instance HCL_CreateInstance(void);
extern HCL_Instance HCL_CreateInstance(double min_separation);
/* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock);

2
main.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2012-2015
* Copyright (C) Miroslav Lichvar 2012-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as

View File

@@ -205,7 +205,8 @@ static ARR_Instance broadcasts;
/* Spacing required between samples for any two servers/peers (to
minimise risk of network collisions) (in seconds) */
#define SAMPLING_SEPARATION 0.2
#define MIN_SAMPLING_SEPARATION 0.02
#define MAX_SAMPLING_SEPARATION 0.2
/* Randomness added to spacing between samples for one server/peer */
#define SAMPLING_RANDOMNESS 0.02
@@ -244,12 +245,13 @@ static ARR_Instance broadcasts;
#define MAX_TX_DELAY 1.0
/* Maximum allowed values of maxdelay parameters */
#define MAX_MAX_DELAY 1.0e3
#define MAX_MAX_DELAY_RATIO 1.0e6
#define MAX_MAX_DELAY_DEV_RATIO 1.0e6
#define MAX_MAXDELAY 1.0e3
#define MAX_MAXDELAYRATIO 1.0e6
#define MAX_MAXDELAYDEVRATIO 1.0e6
/* Minimum and maximum allowed poll interval */
#define MIN_POLL 0
#define MIN_MINPOLL -4
#define MIN_MAXPOLL 0
#define MAX_POLL 24
/* Kiss-o'-Death codes */
@@ -278,6 +280,7 @@ static const char tss_chars[3] = {'D', 'K', 'H'};
static void transmit_timeout(void *arg);
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
static double get_separation(int poll);
/* ================================================== */
@@ -398,7 +401,7 @@ restart_timeout(NCR_Instance inst, double delay)
SCH_RemoveTimeout(inst->tx_timeout_id);
/* Start new timer for transmission */
inst->tx_timeout_id = SCH_AddTimeoutInClass(delay, SAMPLING_SEPARATION,
inst->tx_timeout_id = SCH_AddTimeoutInClass(delay, get_separation(inst->local_poll),
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
@@ -479,6 +482,7 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->remote_addr = *remote_addr;
result->local_addr.ip_addr.family = IPADDR_UNSPEC;
result->local_addr.if_index = INVALID_IF_INDEX;
switch (type) {
case NTP_SERVER:
@@ -497,12 +501,12 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->interleaved = params->interleaved;
result->minpoll = params->minpoll;
if (result->minpoll < MIN_POLL)
if (result->minpoll < MIN_MINPOLL)
result->minpoll = SRC_DEFAULT_MINPOLL;
else if (result->minpoll > MAX_POLL)
result->minpoll = MAX_POLL;
result->maxpoll = params->maxpoll;
if (result->maxpoll < MIN_POLL)
if (result->maxpoll < MIN_MAXPOLL)
result->maxpoll = SRC_DEFAULT_MAXPOLL;
else if (result->maxpoll > MAX_POLL)
result->maxpoll = MAX_POLL;
@@ -518,9 +522,9 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
if (result->presend_minpoll <= MAX_POLL && result->mode != MODE_CLIENT)
result->presend_minpoll = MAX_POLL + 1;
result->max_delay = CLAMP(0.0, params->max_delay, MAX_MAX_DELAY);
result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAX_DELAY_RATIO);
result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAX_DELAY_DEV_RATIO);
result->max_delay = CLAMP(0.0, params->max_delay, MAX_MAXDELAY);
result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAXDELAYRATIO);
result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
result->offset_correction = params->offset;
result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target;
@@ -653,6 +657,7 @@ NCR_ResetPoll(NCR_Instance instance)
void
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr)
{
memset(&inst->report, 0, sizeof (inst->report));
NCR_ResetInstance(inst);
inst->remote_addr = *remote_addr;
@@ -661,6 +666,7 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr)
else {
NIO_CloseServerSocket(inst->local_addr.sock_fd);
inst->local_addr.ip_addr.family = IPADDR_UNSPEC;
inst->local_addr.if_index = INVALID_IF_INDEX;
inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr);
}
@@ -764,7 +770,7 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
approx the poll interval away */
poll_to_use = inst->local_poll;
delay_time = (double) (1UL<<poll_to_use);
delay_time = UTI_Log2ToDouble(poll_to_use);
if (inst->presend_done)
delay_time = WARM_UP_DELAY;
@@ -782,7 +788,7 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
if (poll_to_use < inst->minpoll)
poll_to_use = inst->minpoll;
delay_time = (double) (1UL<<poll_to_use);
delay_time = UTI_Log2ToDouble(poll_to_use);
/* If the remote stratum is higher than ours, try to lock on the
peer's polling to minimize our response time by slightly extending
@@ -821,6 +827,21 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
return delay_time;
}
/* ================================================== */
/* Calculate sampling separation for given polling interval */
static double
get_separation(int poll)
{
double separation;
/* Allow up to 8 sources using the same short interval to not be limited
by the separation */
separation = UTI_Log2ToDouble(poll - 3);
return CLAMP(MIN_SAMPLING_SEPARATION, separation, MAX_SAMPLING_SEPARATION);
}
/* ================================================== */
/* Timeout handler for closing the client socket when no acceptable
reply can be received from the server */
@@ -1075,6 +1096,7 @@ transmit_timeout(void *arg)
/* Don't require the packet to be sent from the same address as before */
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = inst->local_addr.sock_fd;
/* Check whether we need to 'warm up' the link to the other end by
@@ -1456,7 +1478,7 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
processing time is sane, and in the interleaved symmetric mode that
the delay is not longer than half of the remote polling interval to
detect missed packets */
testA = delay - dispersion <= inst->max_delay &&
testA = delay - dispersion <= inst->max_delay && precision <= inst->max_delay &&
!(inst->mode == MODE_CLIENT && server_interval > MAX_SERVER_INTERVAL) &&
!(inst->mode == MODE_ACTIVE && interleaved_packet &&
delay > UTI_Log2ToDouble(message->poll - 1));
@@ -1608,8 +1630,9 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
server and the socket can be closed */
close_client_socket(inst);
/* Update the local address */
/* Update the local address and interface */
inst->local_addr.ip_addr = local_addr->ip_addr;
inst->local_addr.if_index = local_addr->if_index;
/* And now, requeue the timer */
if (inst->opmode != MD_OFFLINE) {
@@ -1621,7 +1644,7 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
UTI_IPToString(&inst->remote_addr.ip_addr));
/* Back off for a while and stop ongoing burst */
delay_time += 4 * (1UL << inst->minpoll);
delay_time += 4 * UTI_Log2ToDouble(inst->local_poll);
if (inst->opmode == MD_BURST_WAS_OFFLINE || inst->opmode == MD_BURST_WAS_ONLINE) {
inst->burst_good_samples_to_go = 0;
@@ -1920,10 +1943,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
!UTI_CompareNtp64(&message->originate_ts, local_ntp_rx);
if (interleaved) {
if (!UTI_IsZeroNtp64(local_ntp_tx))
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
else
interleaved = 0;
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
tx_ts = &local_tx;
} else {
UTI_ZeroNtp64(local_ntp_tx);
@@ -2016,10 +2036,10 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
if (log_index < 0)
return;
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
if (SMT_IsEnabled() && NTP_LVM_TO_MODE(message->lvm) == MODE_SERVER)
UTI_AddDoubleToTimespec(&tx_ts->ts, SMT_GetOffset(&tx_ts->ts), &tx_ts->ts);
if (UTI_IsZeroNtp64(local_ntp_tx))
return;
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
update_tx_timestamp(&local_tx, tx_ts, local_ntp_rx, NULL, message);
@@ -2091,7 +2111,7 @@ NCR_TakeSourceOffline(NCR_Instance inst)
void
NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
{
if (new_minpoll < MIN_POLL || new_minpoll > MAX_POLL)
if (new_minpoll < MIN_MINPOLL || new_minpoll > MAX_POLL)
return;
inst->minpoll = new_minpoll;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll);
@@ -2104,7 +2124,7 @@ NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
void
NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
{
if (new_maxpoll < MIN_POLL || new_maxpoll > MAX_POLL)
if (new_maxpoll < MIN_MAXPOLL || new_maxpoll > MAX_POLL)
return;
inst->maxpoll = new_maxpoll;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll);
@@ -2117,7 +2137,7 @@ NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
void
NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
{
inst->max_delay = CLAMP(0.0, new_max_delay, MAX_MAX_DELAY);
inst->max_delay = CLAMP(0.0, new_max_delay, MAX_MAXDELAY);
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay %f",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay);
}
@@ -2127,7 +2147,7 @@ NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
void
NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
{
inst->max_delay_ratio = CLAMP(0.0, new_max_delay_ratio, MAX_MAX_DELAY_RATIO);
inst->max_delay_ratio = CLAMP(0.0, new_max_delay_ratio, MAX_MAXDELAYRATIO);
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay ratio %f",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_ratio);
}
@@ -2137,7 +2157,7 @@ NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
void
NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_dev_ratio)
{
inst->max_delay_dev_ratio = CLAMP(0.0, new_max_delay_dev_ratio, MAX_MAX_DELAY_DEV_RATIO);
inst->max_delay_dev_ratio = CLAMP(0.0, new_max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay dev ratio %f",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_dev_ratio);
}
@@ -2343,20 +2363,21 @@ broadcast_timeout(void *arg)
BroadcastDestination *destination;
NTP_int64 orig_ts;
NTP_Local_Timestamp recv_ts;
int poll;
destination = ARR_GetElement(broadcasts, (long)arg);
poll = log(destination->interval) / log(2.0) + 0.5;
UTI_ZeroNtp64(&orig_ts);
UTI_ZeroTimespec(&recv_ts.ts);
recv_ts.source = NTP_TS_DAEMON;
recv_ts.err = 0.0;
transmit_packet(MODE_BROADCAST, 0, log(destination->interval) / log(2.0) + 0.5,
NTP_VERSION, 0, 0, &orig_ts, &orig_ts, &recv_ts, NULL, NULL, NULL,
&destination->addr, &destination->local_addr);
transmit_packet(MODE_BROADCAST, 0, poll, NTP_VERSION, 0, 0, &orig_ts, &orig_ts, &recv_ts,
NULL, NULL, NULL, &destination->addr, &destination->local_addr);
/* Requeue timeout. We don't care if interval drifts gradually. */
SCH_AddTimeoutInClass(destination->interval, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS,
SCH_AddTimeoutInClass(destination->interval, get_separation(poll), SAMPLING_RANDOMNESS,
SCH_NtpBroadcastClass, broadcast_timeout, arg);
}
@@ -2372,10 +2393,11 @@ NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval)
destination->addr.ip_addr = *addr;
destination->addr.port = port;
destination->local_addr.ip_addr.family = IPADDR_UNSPEC;
destination->local_addr.if_index = INVALID_IF_INDEX;
destination->local_addr.sock_fd = NIO_OpenServerSocket(&destination->addr);
destination->interval = CLAMP(1 << MIN_POLL, interval, 1 << MAX_POLL);
destination->interval = CLAMP(1, interval, 1 << MAX_POLL);
SCH_AddTimeoutInClass(destination->interval, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS,
SCH_AddTimeoutInClass(destination->interval, MAX_SAMPLING_SEPARATION, SAMPLING_RANDOMNESS,
SCH_NtpBroadcastClass, broadcast_timeout,
(void *)(long)(ARR_GetSize(broadcasts) - 1));
}

View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2015
* Copyright (C) Miroslav Lichvar 2009, 2013-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -365,9 +365,8 @@ NIO_Initialise(int family)
NIO_Linux_Initialise();
#else
if (1) {
double tx_comp, rx_comp;
char *name;
if (CNF_GetHwTsInterface(0, &name, &tx_comp, &rx_comp))
CNF_HwTsInterface *conf_iface;
if (CNF_GetHwTsInterface(0, &conf_iface))
LOG_FATAL(LOGF_NtpIO, "HW timestamping not supported");
}
#endif
@@ -579,7 +578,6 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
NTP_Local_Timestamp local_ts;
struct timespec sched_ts;
struct cmsghdr *cmsg;
int if_index;
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
local_ts.source = NTP_TS_DAEMON;
@@ -599,8 +597,8 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
}
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = sock_fd;
if_index = -1;
if (hdr->msg_flags & MSG_TRUNC) {
DEBUG_LOG(LOGF_NtpIO, "Received truncated message from %s:%d",
@@ -621,7 +619,7 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
local_addr.ip_addr.family = IPADDR_INET4;
if_index = ipi.ipi_ifindex;
local_addr.if_index = ipi.ipi_ifindex;
}
#endif
@@ -633,7 +631,7 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (local_addr.ip_addr.addr.in6));
local_addr.ip_addr.family = IPADDR_INET6;
if_index = ipi.ipi6_ifindex;
local_addr.if_index = ipi.ipi6_ifindex;
}
#endif
@@ -661,14 +659,13 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
}
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts,
hdr, length, sock_fd, if_index))
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, hdr, length))
return;
#endif
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f",
length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, if_index,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index,
local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
/* Just ignore the packet if it's not of a recognized length */

View File

@@ -61,11 +61,15 @@ struct Interface {
char name[IF_NAMESIZE];
int if_index;
int phc_fd;
int phc_mode;
int phc_nocrossts;
/* Link speed in mbit/s */
int link_speed;
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Precision of PHC readings */
double precision;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
@@ -75,6 +79,9 @@ struct Interface {
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10
/* Minimum interval between PHC readings */
#define MIN_PHC_POLL -6
/* Maximum acceptable offset between HW and daemon/kernel timestamp */
#define MAX_TS_DELAY 1.0
@@ -91,19 +98,18 @@ static int permanent_ts_options;
/* ================================================== */
static int
add_interface(const char *name, double tx_comp, double rx_comp)
add_interface(CNF_HwTsInterface *conf_iface)
{
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
int sock_fd, if_index, phc_index, phc_fd;
int sock_fd, if_index, phc_fd;
unsigned int i;
struct Interface *iface;
char phc_path[64];
/* Check if the interface was not already added */
for (i = 0; i < ARR_GetSize(interfaces); i++) {
if (!strcmp(name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
if (!strcmp(conf_iface->name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
return 1;
}
@@ -114,7 +120,8 @@ add_interface(const char *name, double tx_comp, double rx_comp)
memset(&req, 0, sizeof (req));
memset(&ts_info, 0, sizeof (ts_info));
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", name) >= sizeof (req.ifr_name)) {
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
sizeof (req.ifr_name)) {
close(sock_fd);
return 0;
}
@@ -148,36 +155,31 @@ add_interface(const char *name, double tx_comp, double rx_comp)
}
close(sock_fd);
phc_index = ts_info.phc_index;
if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path))
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
if (phc_fd < 0)
return 0;
phc_fd = open(phc_path, O_RDONLY);
if (phc_fd < 0) {
LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not open %s : %s", phc_path, strerror(errno));
return 0;
}
UTI_FdSetCloexec(phc_fd);
iface = ARR_GetNewElement(interfaces);
snprintf(iface->name, sizeof (iface->name), "%s", name);
snprintf(iface->name, sizeof (iface->name), "%s", conf_iface->name);
iface->if_index = if_index;
iface->phc_fd = phc_fd;
iface->phc_mode = 0;
iface->phc_nocrossts = conf_iface->nocrossts;
/* Start with 1 gbit and no VLANs or IPv4/IPv6 options */
iface->link_speed = 1000;
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->tx_comp = tx_comp;
iface->rx_comp = rx_comp;
iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
iface->clock = HCL_CreateInstance();
iface->clock = HCL_CreateInstance(UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
DEBUG_LOG(LOGF_NtpIOLinux, "Enabled HW timestamping on %s", name);
DEBUG_LOG(LOGF_NtpIOLinux, "Enabled HW timestamping on %s", iface->name);
return 1;
}
@@ -185,18 +187,22 @@ add_interface(const char *name, double tx_comp, double rx_comp)
/* ================================================== */
static int
add_all_interfaces(double tx_comp, double rx_comp)
add_all_interfaces(CNF_HwTsInterface *conf_iface_all)
{
CNF_HwTsInterface conf_iface;
struct ifaddrs *ifaddr, *ifa;
int r;
conf_iface = *conf_iface_all;
if (getifaddrs(&ifaddr)) {
DEBUG_LOG(LOGF_NtpIOLinux, "getifaddrs() failed : %s", strerror(errno));
return 0;
}
for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
if (add_interface(ifa->ifa_name, tx_comp, rx_comp))
conf_iface.name = ifa->ifa_name;
if (add_interface(&conf_iface))
r = 1;
}
@@ -242,8 +248,7 @@ update_interface_speed(struct Interface *iface)
void
NIO_Linux_Initialise(void)
{
double tx_comp, rx_comp;
char *name;
CNF_HwTsInterface *conf_iface;
unsigned int i;
int hwts;
@@ -252,18 +257,18 @@ NIO_Linux_Initialise(void)
/* Enable HW timestamping on specified interfaces. If "*" was specified, try
all interfaces. If no interface was specified, enable SW timestamping. */
for (i = hwts = 0; CNF_GetHwTsInterface(i, &name, &tx_comp, &rx_comp); i++) {
if (!strcmp("*", name))
for (i = hwts = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (!strcmp("*", conf_iface->name))
continue;
if (!add_interface(name, tx_comp, rx_comp))
LOG_FATAL(LOGF_NtpIO, "Could not enable HW timestamping on %s", name);
if (!add_interface(conf_iface))
LOG_FATAL(LOGF_NtpIO, "Could not enable HW timestamping on %s", conf_iface->name);
hwts = 1;
}
for (i = 0; CNF_GetHwTsInterface(i, &name, &tx_comp, &rx_comp); i++) {
if (strcmp("*", name))
for (i = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (strcmp("*", conf_iface->name))
continue;
if (add_all_interfaces(tx_comp, rx_comp))
if (add_all_interfaces(conf_iface))
hwts = 1;
break;
}
@@ -337,68 +342,6 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
/* ================================================== */
static int
get_phc_sample(int phc_fd, struct timespec *phc_ts, struct timespec *local_ts, double *p_delay)
{
struct ptp_sys_offset sys_off;
struct timespec ts1, ts2, ts3, phc_tss[PHC_READINGS], sys_tss[PHC_READINGS];
double min_delay = 0.0, delays[PHC_READINGS], phc_sum, local_sum, local_prec;
int i, n;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = PHC_READINGS;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
return 0;
}
for (i = 0; i < PHC_READINGS; i++) {
ts1.tv_sec = sys_off.ts[i * 2].sec;
ts1.tv_nsec = sys_off.ts[i * 2].nsec;
ts2.tv_sec = sys_off.ts[i * 2 + 1].sec;
ts2.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
ts3.tv_sec = sys_off.ts[i * 2 + 2].sec;
ts3.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
sys_tss[i] = ts1;
phc_tss[i] = ts2;
delays[i] = UTI_DiffTimespecsToDouble(&ts3, &ts1);
if (delays[i] <= 0.0)
/* Step in the middle of a PHC reading? */
return 0;
if (!i || delays[i] < min_delay)
min_delay = delays[i];
}
local_prec = LCL_GetSysPrecisionAsQuantum();
/* Combine best readings */
for (i = n = 0, phc_sum = local_sum = 0.0; i < PHC_READINGS; i++) {
if (delays[i] > min_delay + local_prec)
continue;
phc_sum += UTI_DiffTimespecsToDouble(&phc_tss[i], &phc_tss[0]);
local_sum += UTI_DiffTimespecsToDouble(&sys_tss[i], &sys_tss[0]) + delays[i] / 2.0;
n++;
}
assert(n);
UTI_AddDoubleToTimespec(&phc_tss[0], phc_sum / n, phc_ts);
UTI_AddDoubleToTimespec(&sys_tss[0], local_sum / n, &ts1);
LCL_CookTime(&ts1, local_ts, NULL);
*p_delay = min_delay;
return 1;
}
/* ================================================== */
static struct Interface *
get_interface(int if_index)
{
@@ -422,16 +365,17 @@ static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family)
{
struct timespec sample_phc_ts, sample_local_ts, ts;
double sample_delay, rx_correction, ts_delay, err;
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
double rx_correction, ts_delay, err;
int l2_length;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!get_phc_sample(iface->phc_fd, &sample_phc_ts, &sample_local_ts, &sample_delay))
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts, &err))
return;
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
sample_delay / 2.0);
LCL_CookTime(&sample_sys_ts, &sample_local_ts, NULL);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts, err);
update_interface_speed(iface);
}
@@ -536,8 +480,7 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
int
NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr,
int length, int sock_fd, int if_index)
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length)
{
struct Interface *iface;
struct cmsghdr *cmsg;
@@ -556,12 +499,13 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL;
} else if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
iface = get_interface(if_index);
iface = get_interface(local_addr->if_index);
if (iface) {
process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,
remote_addr->ip_addr.family);
} else {
DEBUG_LOG(LOGF_NtpIOLinux, "HW clock not found for interface %d", if_index);
DEBUG_LOG(LOGF_NtpIOLinux, "HW clock not found for interface %d",
local_addr->if_index);
}
}
}
@@ -593,7 +537,7 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
DEBUG_LOG(LOGF_NtpIOLinux, "Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%d",
l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
sock_fd, if_index, local_ts->source);
local_addr->sock_fd, local_addr->if_index, local_ts->source);
/* Update assumed position of UDP data at layer 2 for next received packet */
if (iface && length) {

View File

@@ -31,7 +31,6 @@ extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length,
int sock_fd, int if_index);
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -505,6 +505,12 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
return 1;
}
double
RCL_GetPrecision(RCL_Instance instance)
{
return instance->precision;
}
static int
valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked)
{

View File

@@ -71,5 +71,6 @@ extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
extern double RCL_GetPrecision(RCL_Instance instance);
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2013
* Copyright (C) Miroslav Lichvar 2013, 2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,144 +33,67 @@
#include "sysincl.h"
#include <linux/ptp_clock.h>
#include "refclock.h"
#include "logging.h"
#include "memory.h"
#include "util.h"
#include "sys_linux.h"
/* From linux/include/linux/posix-timers.h */
#define CPUCLOCK_MAX 3
#define CLOCKFD CPUCLOCK_MAX
#define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK)
#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
#define NUM_READINGS 10
static int no_sys_offset_ioctl = 0;
struct phc_reading {
struct timespec sys_ts1;
struct timespec phc_ts;;
struct timespec sys_ts2;
struct phc_instance {
int fd;
int mode;
int nocrossts;
};
static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n)
{
#if defined(PTP_SYS_OFFSET) && NUM_READINGS <= PTP_MAX_SAMPLES
struct ptp_sys_offset sys_off;
int i;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = n;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
LOG(LOGS_ERR, LOGF_Refclock, "ioctl(PTP_SYS_OFFSET) failed : %s", strerror(errno));
return 0;
}
for (i = 0; i < n; i++) {
readings[i].sys_ts1.tv_sec = sys_off.ts[i * 2].sec;
readings[i].sys_ts1.tv_nsec = sys_off.ts[i * 2].nsec;
readings[i].phc_ts.tv_sec = sys_off.ts[i * 2 + 1].sec;
readings[i].phc_ts.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
readings[i].sys_ts2.tv_sec = sys_off.ts[i * 2 + 2].sec;
readings[i].sys_ts2.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
}
return 1;
#else
/* Not available */
return 0;
#endif
}
static int read_phc_user(struct phc_reading *readings, int phc_fd, int n)
{
clockid_t phc_id;
int i;
phc_id = FD_TO_CLOCKID(phc_fd);
for (i = 0; i < n; i++) {
if (clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts1) ||
clock_gettime(phc_id, &readings[i].phc_ts) ||
clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts2)) {
LOG(LOGS_ERR, LOGF_Refclock, "clock_gettime() failed : %s", strerror(errno));
return 0;
}
}
return 1;
}
static int phc_initialise(RCL_Instance instance)
{
struct ptp_clock_caps caps;
struct phc_instance *phc;
int phc_fd;
char *path;
path = RCL_GetDriverParameter(instance);
phc_fd = open(path, O_RDONLY);
phc_fd = SYS_Linux_OpenPHC(path, 0);
if (phc_fd < 0) {
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
LOG_FATAL(LOGF_Refclock, "Could not open PHC");
return 0;
}
/* Make sure it is a PHC */
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
LOG_FATAL(LOGF_Refclock, "ioctl(PTP_CLOCK_GETCAPS) failed : %s", strerror(errno));
return 0;
}
phc = MallocNew(struct phc_instance);
phc->fd = phc_fd;
phc->mode = 0;
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
UTI_FdSetCloexec(phc_fd);
RCL_SetDriverData(instance, (void *)(long)phc_fd);
RCL_SetDriverData(instance, phc);
return 1;
}
static void phc_finalise(RCL_Instance instance)
{
close((long)RCL_GetDriverData(instance));
struct phc_instance *phc;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
close(phc->fd);
Free(phc);
}
static int phc_poll(RCL_Instance instance)
{
struct phc_reading readings[NUM_READINGS];
double offset = 0.0, delay, best_delay = 0.0;
int i, phc_fd, best;
struct phc_instance *phc;
struct timespec phc_ts, sys_ts;
double offset, err;
phc_fd = (long)RCL_GetDriverData(instance);
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (!no_sys_offset_ioctl) {
if (!read_phc_ioctl(readings, phc_fd, NUM_READINGS)) {
no_sys_offset_ioctl = 1;
return 0;
}
} else {
if (!read_phc_user(readings, phc_fd, NUM_READINGS))
return 0;
}
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
&phc->mode, &phc_ts, &sys_ts, &err))
return 0;
/* Find the fastest reading */
for (i = 0; i < NUM_READINGS; i++) {
delay = UTI_DiffTimespecsToDouble(&readings[i].sys_ts2, &readings[i].sys_ts1);
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
if (!i || best_delay > delay) {
best = i;
best_delay = delay;
}
}
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f err: %.9f", offset, err);
offset = UTI_DiffTimespecsToDouble(&readings[best].phc_ts, &readings[best].sys_ts2) +
best_delay / 2.0;
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay);
return RCL_AddSample(instance, &readings[best].sys_ts2, offset, LEAP_Normal);
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
}
RefclockDriver RCL_PHC_driver = {

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011
* Copyright (C) Miroslav Lichvar 2011, 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011, 2013-2015
* Copyright (C) Miroslav Lichvar 2011, 2013-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2014
* Copyright (C) Miroslav Lichvar 2011-2014, 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014-2015
* Copyright (C) Miroslav Lichvar 2014-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as

View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -47,13 +47,14 @@
#include <sys/capability.h>
#endif
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#endif
#ifdef FEAT_SCFILTER
#include <sys/prctl.h>
#include <seccomp.h>
#include <termios.h>
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#endif
#ifdef FEAT_PPS
#include <linux/pps.h>
#endif
@@ -68,6 +69,7 @@
#include "sys_linux.h"
#include "sys_timex.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "privops.h"
#include "util.h"
@@ -515,6 +517,9 @@ SYS_Linux_EnableSystemCallFilter(int level)
FIONREAD, TCGETS,
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
PTP_SYS_OFFSET,
#ifdef PTP_SYS_OFFSET_PRECISE
PTP_SYS_OFFSET_PRECISE,
#endif
#endif
#ifdef FEAT_PPS
PPS_FETCH,
@@ -661,3 +666,151 @@ SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
return kernelvercmp(req_major, req_minor, 0, major, minor, patch) <= 0;
}
/* ================================================== */
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#define PHC_READINGS 10
static int
get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
{
struct ptp_sys_offset sys_off;
struct timespec ts1, ts2, ts3, phc_tss[PHC_READINGS], sys_tss[PHC_READINGS];
double min_delay = 0.0, delays[PHC_READINGS], phc_sum, sys_sum, sys_prec;
int i, n;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = PHC_READINGS;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
DEBUG_LOG(LOGF_SysLinux, "ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
return 0;
}
for (i = 0; i < PHC_READINGS; i++) {
ts1.tv_sec = sys_off.ts[i * 2].sec;
ts1.tv_nsec = sys_off.ts[i * 2].nsec;
ts2.tv_sec = sys_off.ts[i * 2 + 1].sec;
ts2.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
ts3.tv_sec = sys_off.ts[i * 2 + 2].sec;
ts3.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
sys_tss[i] = ts1;
phc_tss[i] = ts2;
delays[i] = UTI_DiffTimespecsToDouble(&ts3, &ts1);
if (delays[i] <= 0.0)
/* Step in the middle of a PHC reading? */
return 0;
if (!i || delays[i] < min_delay)
min_delay = delays[i];
}
sys_prec = LCL_GetSysPrecisionAsQuantum();
/* Combine best readings */
for (i = n = 0, phc_sum = sys_sum = 0.0; i < PHC_READINGS; i++) {
if (delays[i] > min_delay + MAX(sys_prec, precision))
continue;
phc_sum += UTI_DiffTimespecsToDouble(&phc_tss[i], &phc_tss[0]);
sys_sum += UTI_DiffTimespecsToDouble(&sys_tss[i], &sys_tss[0]) + delays[i] / 2.0;
n++;
}
assert(n);
UTI_AddDoubleToTimespec(&phc_tss[0], phc_sum / n, phc_ts);
UTI_AddDoubleToTimespec(&sys_tss[0], sys_sum / n, sys_ts);
*err = MAX(min_delay / 2.0, precision);
return 1;
}
/* ================================================== */
static int
get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
{
#ifdef PTP_SYS_OFFSET_PRECISE
struct ptp_sys_offset_precise sys_off;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
if (ioctl(phc_fd, PTP_SYS_OFFSET_PRECISE, &sys_off)) {
DEBUG_LOG(LOGF_SysLinux, "ioctl(%s) failed : %s", "PTP_SYS_OFFSET_PRECISE",
strerror(errno));
return 0;
}
phc_ts->tv_sec = sys_off.device.sec;
phc_ts->tv_nsec = sys_off.device.nsec;
sys_ts->tv_sec = sys_off.sys_realtime.sec;
sys_ts->tv_nsec = sys_off.sys_realtime.nsec;
*err = MAX(LCL_GetSysPrecisionAsQuantum(), precision);
return 1;
#else
return 0;
#endif
}
/* ================================================== */
int
SYS_Linux_OpenPHC(const char *path, int phc_index)
{
struct ptp_clock_caps caps;
char phc_path[64];
int phc_fd;
if (!path) {
if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path))
return -1;
path = phc_path;
}
phc_fd = open(path, O_RDONLY);
if (phc_fd < 0) {
LOG(LOGS_ERR, LOGF_SysLinux, "Could not open %s : %s", path, strerror(errno));
return -1;
}
/* Make sure it is a PHC */
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
LOG(LOGS_ERR, LOGF_SysLinux, "ioctl(%s) failed : %s", "PTP_CLOCK_GETCAPS", strerror(errno));
close(phc_fd);
return -1;
}
UTI_FdSetCloexec(phc_fd);
return phc_fd;
}
/* ================================================== */
int
SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
{
if ((*reading_mode == 2 || !*reading_mode) && !nocrossts &&
get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
*reading_mode = 2;
return 1;
} else if ((*reading_mode == 1 || !*reading_mode) &&
get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
*reading_mode = 1;
return 1;
}
return 0;
}
#endif

View File

@@ -41,4 +41,9 @@ extern void SYS_Linux_SetScheduler(int SchedPriority);
extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
extern int SYS_Linux_OpenPHC(const char *path, int phc_index);
extern int SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
struct timespec *phc_ts, struct timespec *sys_ts, double *err);
#endif /* GOT_SYS_LINUX_H */

View File

@@ -32,6 +32,14 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
client_server_options="minpoll 6 maxpoll 6 xleave maxdelay 1e-1"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
client_server_options="minpoll 6 maxpoll 6"
min_sync_time=$default_min_sync_time
max_sync_time=$default_max_sync_time
time_max_limit=11

View File

@@ -31,7 +31,7 @@ test_unit(void)
LCL_Initialise();
clock = HCL_CreateInstance();
clock = HCL_CreateInstance(1.0);
for (i = 0; i < 2000; i++) {
UTI_ZeroTimespec(&start_hw_ts);
@@ -43,7 +43,7 @@ test_unit(void)
freq = TST_GetRandomDouble(0.9, 1.1);
jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9);
interval = TST_GetRandomDouble(MIN_SAMPLE_SEPARATION / 10, MIN_SAMPLE_SEPARATION * 10.0);
interval = TST_GetRandomDouble(0.1, 10.0);
clock->n_samples = 0;
clock->valid_coefs = 0;

View File

@@ -80,6 +80,7 @@ send_request(void)
advance_time(1e-4);
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = 101;
local_ts.ts = current_time;
local_ts.err = 0.0;
@@ -176,6 +177,7 @@ process_response(int valid, int updated)
res = &res_buffer.ntp_pkt;
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) == MODE_ACTIVE ? 100 : 101;
local_ts.ts = current_time;
local_ts.err = 0.0;