ntp: add options for compensating HW timestamping errors

This commit is contained in:
Miroslav Lichvar 2017-01-05 16:37:28 +01:00
parent 8efec1d640
commit cacbe9976f
5 changed files with 108 additions and 36 deletions

63
conf.c
View file

@ -223,7 +223,13 @@ static char *leapsec_tz = NULL;
/* Name of the user to which will be dropped root privileges. */
static char *user;
/* Array of strings for interfaces with HW timestamping */
typedef struct {
char *name;
double tx_comp;
double rx_comp;
} HwTs_Interface;
/* Array of HwTs_Interface */
static ARR_Instance hwts_interfaces;
typedef struct {
@ -327,7 +333,7 @@ CNF_Initialise(int r)
{
restarted = r;
hwts_interfaces = ARR_CreateInstance(sizeof (char *));
hwts_interfaces = ARR_CreateInstance(sizeof (HwTs_Interface));
init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
@ -354,7 +360,7 @@ CNF_Finalise(void)
unsigned int i;
for (i = 0; i < ARR_GetSize(hwts_interfaces); i++)
Free(*(char **)ARR_GetElement(hwts_interfaces, i));
Free(((HwTs_Interface *)ARR_GetElement(hwts_interfaces, i))->name);
ARR_DestroyInstance(hwts_interfaces);
for (i = 0; i < ARR_GetSize(ntp_sources); i++)
@ -1245,8 +1251,39 @@ parse_tempcomp(char *line)
static void
parse_hwtimestamp(char *line)
{
check_number_of_args(line, 1);
*(char **)ARR_GetNewElement(hwts_interfaces) = Strdup(line);
HwTs_Interface *iface;
char *p;
int n;
if (!*line) {
command_parse_error();
return;
}
p = line;
line = CPS_SplitWord(line);
iface = ARR_GetNewElement(hwts_interfaces);
iface->name = Strdup(p);
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 (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 {
break;
}
}
if (*p)
command_parse_error();
}
/* ================================================== */
@ -1930,8 +1967,18 @@ CNF_GetInitStepThreshold(void)
/* ================================================== */
ARR_Instance
CNF_GetHwTsInterfaces(void)
int
CNF_GetHwTsInterface(unsigned int index, char **name, double *tx_comp, double *rx_comp)
{
return hwts_interfaces;
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;
return 1;
}

3
conf.h
View file

@ -29,7 +29,6 @@
#define GOT_CONF_H
#include "addressing.h"
#include "array.h"
#include "reference.h"
extern void CNF_Initialise(int restarted);
@ -120,6 +119,6 @@ extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void);
extern ARR_Instance CNF_GetHwTsInterfaces(void);
extern int CNF_GetHwTsInterface(unsigned int index, char **name, double *tx_comp, double *rx_comp);
#endif /* GOT_CONF_H */

View file

@ -1776,7 +1776,7 @@ sendmail binary.
=== Miscellaneous
[[hwtimestamp]]*hwtimestamp* _interface_::
[[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...::
This directive enables hardware timestamping of NTP packets sent to and
received from the specified network interface. The network interface controller
(NIC) uses its own clock to accurately timestamp the actual transmissions and
@ -1807,10 +1807,25 @@ and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in *chronyc*.
If the specified interface is _*_, *chronyd* will try to enable HW timestamping
on all available interfaces.
+
An example of the directive is:
The *hwtimestamp* directive has the following options:
+
*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
be added to transmit timestamps obtained from the NIC. The default value is 0.
*rxcomp* _compensation_:::
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.
::
+
Examples of the directive are:
+
----
hwtimestamp eth0
hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp *
----
[[include]]*include* _pattern_::

View file

@ -364,8 +364,12 @@ NIO_Initialise(int family)
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Initialise();
#else
if (ARR_GetSize(CNF_GetHwTsInterfaces()))
LOG_FATAL(LOGF_NtpIO, "HW timestamping not supported");
if (1) {
double tx_comp, rx_comp;
char *name;
if (CNF_GetHwTsInterface(0, &name, &tx_comp, &rx_comp))
LOG_FATAL(LOGF_NtpIO, "HW timestamping not supported");
}
#endif
recv_messages = ARR_CreateInstance(sizeof (struct Message));

View file

@ -66,6 +66,9 @@ struct Interface {
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
HCL_Instance clock;
};
@ -88,7 +91,7 @@ static int permanent_ts_options;
/* ================================================== */
static int
add_interface(const char *name)
add_interface(const char *name, double tx_comp, double rx_comp)
{
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
@ -169,6 +172,9 @@ add_interface(const char *name)
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->tx_comp = tx_comp;
iface->rx_comp = rx_comp;
iface->clock = HCL_CreateInstance();
DEBUG_LOG(LOGF_NtpIOLinux, "Enabled HW timestamping on %s", name);
@ -179,7 +185,7 @@ add_interface(const char *name)
/* ================================================== */
static int
add_all_interfaces(void)
add_all_interfaces(double tx_comp, double rx_comp)
{
struct ifaddrs *ifaddr, *ifa;
int r;
@ -190,7 +196,7 @@ add_all_interfaces(void)
}
for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
if (add_interface(ifa->ifa_name))
if (add_interface(ifa->ifa_name, tx_comp, rx_comp))
r = 1;
}
@ -236,34 +242,30 @@ update_interface_speed(struct Interface *iface)
void
NIO_Linux_Initialise(void)
{
ARR_Instance config_hwts_ifaces;
char *if_name;
double tx_comp, rx_comp;
char *name;
unsigned int i;
int wildcard, hwts;
int hwts;
interfaces = ARR_CreateInstance(sizeof (struct Interface));
config_hwts_ifaces = CNF_GetHwTsInterfaces();
/* Enable HW timestamping on specified interfaces. If "*" was specified, try
all interfaces. If no interface was specified, enable SW timestamping. */
for (i = wildcard = 0; i < ARR_GetSize(config_hwts_ifaces); i++) {
if (!strcmp("*", *(char **)ARR_GetElement(config_hwts_ifaces, i)))
wildcard = 1;
for (i = hwts = 0; CNF_GetHwTsInterface(i, &name, &tx_comp, &rx_comp); i++) {
if (!strcmp("*", name))
continue;
if (!add_interface(name, tx_comp, rx_comp))
LOG_FATAL(LOGF_NtpIO, "Could not enable HW timestamping on %s", name);
hwts = 1;
}
if (!wildcard && ARR_GetSize(config_hwts_ifaces)) {
for (i = 0; i < ARR_GetSize(config_hwts_ifaces); i++) {
if_name = *(char **)ARR_GetElement(config_hwts_ifaces, i);
if (!add_interface(if_name))
LOG_FATAL(LOGF_NtpIO, "Could not enable HW timestamping on %s", if_name);
}
hwts = 1;
} else if (wildcard && add_all_interfaces()) {
hwts = 1;
} else {
hwts = 0;
for (i = 0; CNF_GetHwTsInterface(i, &name, &tx_comp, &rx_comp); i++) {
if (strcmp("*", name))
continue;
if (add_all_interfaces(tx_comp, rx_comp))
hwts = 1;
break;
}
if (hwts) {
@ -447,6 +449,11 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
}
if (!rx_ntp_length && iface->tx_comp)
UTI_AddDoubleToTimespec(hw_ts, iface->tx_comp, hw_ts);
else if (rx_ntp_length && iface->rx_comp)
UTI_AddDoubleToTimespec(hw_ts, -iface->rx_comp, hw_ts);
if (!HCL_CookTime(iface->clock, hw_ts, &ts, &err))
return;