diff -up chrony-3.2/configure.timestamping chrony-3.2/configure --- chrony-3.2/configure.timestamping 2017-09-19 13:58:21.093194558 +0200 +++ chrony-3.2/configure 2017-09-19 13:57:56.109116276 +0200 @@ -683,15 +683,15 @@ if [ $feat_timestamping = "1" ] && [ $tr test_code 'SW/HW timestamping' 'sys/types.h sys/socket.h linux/net_tstamp.h linux/errqueue.h linux/ptp_clock.h' '' '' ' int val = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_OPT_CMSG; - return sizeof (struct scm_timestamping) + SCM_TSTAMP_SND + PTP_SYS_OFFSET + + SOF_TIMESTAMPING_RAW_HARDWARE | 1; + return 3 * sizeof (struct timespec) + 0 + PTP_SYS_OFFSET + setsockopt(0, SOL_SOCKET, SO_SELECT_ERR_QUEUE + SO_TIMESTAMPING, &val, sizeof (val));' then add_def HAVE_LINUX_TIMESTAMPING EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o" - if test_code 'other timestamping options' \ + if true || test_code 'other timestamping options' \ 'sys/types.h sys/socket.h linux/net_tstamp.h' '' '' ' struct scm_ts_pktinfo pktinfo; pktinfo.if_index = pktinfo.pkt_length = 0; diff -up chrony-3.2/doc/chrony.conf.man.in.timestamping chrony-3.2/doc/chrony.conf.man.in --- chrony-3.2/doc/chrony.conf.man.in.timestamping 2017-09-15 10:17:41.000000000 +0200 +++ chrony-3.2/doc/chrony.conf.man.in 2017-09-19 13:52:59.544187046 +0200 @@ -3201,13 +3201,12 @@ timestamping. If the server or peer supp be enabled by the \fBxleave\fP option in the \fBserver\fP or the \fBpeer\fP directive. .sp -This directive is supported on Linux 3.19 and newer. The NIC must support HW +This directive is supported on Linux. The NIC must support HW timestamping, which can be verified with the \fBethtool \-T\fP command. The list of capabilities should include \fISOF_TIMESTAMPING_RAW_HARDWARE\fP, \fISOF_TIMESTAMPING_TX_HARDWARE\fP, and \fISOF_TIMESTAMPING_RX_HARDWARE\fP. Receive filter \fIHWTSTAMP_FILTER_ALL\fP, or \fIHWTSTAMP_FILTER_NTP_ALL\fP, is necessary for -timestamping of received packets. Timestamping of packets received from bridged -and bonded interfaces is supported on Linux 4.13 and newer. When \fBchronyd\fP is +timestamping of received packets. When \fBchronyd\fP is running, no other process (e.g. a PTP daemon) should be working with the NIC clock. .sp diff -up chrony-3.2/ntp_io_linux.c.timestamping chrony-3.2/ntp_io_linux.c --- chrony-3.2/ntp_io_linux.c.timestamping 2017-09-15 08:32:09.000000000 +0200 +++ chrony-3.2/ntp_io_linux.c 2017-09-19 13:52:59.544187046 +0200 @@ -35,6 +35,16 @@ #include #include +/* Missing in older kernel headers */ +#define SOF_TIMESTAMPING_OPT_CMSG (1<<10) +#define SOF_TIMESTAMPING_OPT_PKTINFO (1<<13) +#define SOF_TIMESTAMPING_OPT_TX_SWHW (1<<14) +#define SCM_TSTAMP_SND 0 +#define HWTSTAMP_FILTER_NTP_ALL 15 +#ifndef SCM_TIMESTAMPING_PKTINFO +#define SCM_TIMESTAMPING_PKTINFO 58 +#endif + #include "array.h" #include "conf.h" #include "hwclock.h" @@ -94,6 +104,10 @@ static int ts_tx_flags; /* Flag indicating the socket options can't be changed in control messages */ static int permanent_ts_options; +/* Index of a HW-timestamping interface, but only if the machine has not more + than one */ +static int single_hwts_if_index; + /* ================================================== */ static int @@ -278,7 +292,7 @@ update_interface_speed(struct Interface /* ================================================== */ -#if defined(HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO) || defined(HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW) +#if 1 static int check_timestamping_option(int option) { @@ -301,6 +315,61 @@ check_timestamping_option(int option) /* ================================================== */ +static int +get_single_hwts_index() +{ + struct ifaddrs *ifaddr, *ifa; + struct ethtool_ts_info ts_info; + struct ifreq req; + int sock_fd, if_index, hwts_if_index = INVALID_IF_INDEX; + + sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (sock_fd < 0) + return INVALID_IF_INDEX; + + if (getifaddrs(&ifaddr)) { + DEBUG_LOG("getifaddrs() failed : %s", strerror(errno)); + close(sock_fd); + return INVALID_IF_INDEX; + } + + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + memset(&req, 0, sizeof (req)); + memset(&ts_info, 0, sizeof (ts_info)); + + if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", ifa->ifa_name) >= + sizeof (req.ifr_name)) + break; + + if (ioctl(sock_fd, SIOCGIFINDEX, &req)) + break; + + if_index = req.ifr_ifindex; + ts_info.cmd = ETHTOOL_GET_TS_INFO; + req.ifr_data = (char *)&ts_info; + + if (ioctl(sock_fd, SIOCETHTOOL, &req)) + break; + + if (ts_info.phc_index < 0) + continue; + + if (hwts_if_index != INVALID_IF_INDEX && hwts_if_index != if_index) + break; + + hwts_if_index = if_index; + } + + close(sock_fd); + freeifaddrs(ifaddr); + + if (ifa) + return INVALID_IF_INDEX; + + return hwts_if_index; +} + +/* ================================================== */ void NIO_Linux_Initialise(void) { @@ -345,8 +414,20 @@ NIO_Linux_Initialise(void) #endif } - /* Enable IP_PKTINFO in messages looped back to the error queue */ - ts_flags |= SOF_TIMESTAMPING_OPT_CMSG; + single_hwts_if_index = INVALID_IF_INDEX; + + /* Enable IP_PKTINFO in messages looped back to the error queue if possible. + If not, HW timestamping of IPv4 packets can be supported only with one + interface capable of HW timestamping. */ + if (check_timestamping_option(SOF_TIMESTAMPING_OPT_CMSG)) { + ts_flags |= SOF_TIMESTAMPING_OPT_CMSG; + } else if (ARR_GetSize(interfaces) > 0) { + single_hwts_if_index = get_single_hwts_index(); + if (single_hwts_if_index == INVALID_IF_INDEX) + LOG(LOGS_WARN, "Missing SOF_TIMESTAMPING_OPT_CMSG option for HW timestamping with multiple HW-timestamping interfaces"); + else + LOG(LOGS_INFO, "Enabled single-interface HW-timestamping mode"); + } /* Kernels before 4.7 ignore timestamping flags set in control messages */ permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7); @@ -590,7 +671,11 @@ NIO_Linux_ProcessMessage(NTP_Remote_Addr for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) { #ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) { - struct scm_ts_pktinfo ts_pktinfo; + struct { + __u32 if_index; + __u32 pkt_length; + __u32 reserved[2]; + } ts_pktinfo; memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo)); @@ -602,11 +687,16 @@ NIO_Linux_ProcessMessage(NTP_Remote_Addr #endif if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) { - struct scm_timestamping ts3; + struct { + struct timespec ts[3]; + } ts3; memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3)); if (!UTI_IsZeroTimespec(&ts3.ts[2])) { + if (ts_if_index == INVALID_IF_INDEX) + ts_if_index = single_hwts_if_index; + iface = get_interface(ts_if_index); if (iface) { process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,