diff --git a/conf.c b/conf.c index 5de7f42..346fce1 100644 --- a/conf.c +++ b/conf.c @@ -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; } diff --git a/conf.h b/conf.h index d241665..a24b8d9 100644 --- a/conf.h +++ b/conf.h @@ -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 */ diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc index f1b5f0e..ca454da 100644 --- a/doc/chrony.conf.adoc +++ b/doc/chrony.conf.adoc @@ -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 <> 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_:: diff --git a/ntp_io.c b/ntp_io.c index 380e2f1..8843449 100644 --- a/ntp_io.c +++ b/ntp_io.c @@ -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)); diff --git a/ntp_io_linux.c b/ntp_io_linux.c index 7ea15f1..cbfe6ab 100644 --- a/ntp_io_linux.c +++ b/ntp_io_linux.c @@ -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;