From: Shachar Raindel Date: Wed, 23 Apr 2025 20:46:44 +0000 (-0700) Subject: refclock_phc: support ethernet ifname as specifier X-Git-Tag: 4.7-pre1~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6e541afbe9ed7bb36527d5d1ec57a19bc259c183;p=thirdparty%2Fchrony.git refclock_phc: support ethernet ifname as specifier This commit allows the user to select a PHC refclock associated with an Ethernet interface by specifying the interface name. This allows the user to handle situations where multiple NICs are exposing PHC devices (or non-NIC PHC device files exist in the system) in a more streamline manner. --- diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc index fd294de7..711f7c44 100644 --- a/doc/chrony.conf.adoc +++ b/doc/chrony.conf.adoc @@ -557,8 +557,9 @@ refclock SHM 1:perm=0644 refid GPS2 ---- + *PHC*::: -PTP hardware clock (PHC) driver. The parameter is the path to the device of -the PTP clock which should be used as a time source. If the clock is kept in +PTP hardware clock (PHC) driver. The parameter is the absolute path to the +device of the PTP clock which should be used as a time source, or the name of +the network interface whose PTP clock should be used. If the clock is kept in TAI instead of UTC (e.g. it is synchronised by a PTP daemon), the current UTC-TAI offset needs to be specified by the *offset* option. Alternatively, the *pps* refclock option can be enabled to treat the PHC as a PPS refclock, using diff --git a/ntp_io_linux.c b/ntp_io_linux.c index 3b6874e3..3a273aad 100644 --- a/ntp_io_linux.c +++ b/ntp_io_linux.c @@ -220,7 +220,7 @@ add_interface(CNF_HwTsInterface *conf_iface) SCK_CloseSocket(sock_fd); - phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index); + phc_fd = SYS_Linux_OpenPHC(req.ifr_name); if (phc_fd < 0) return 0; diff --git a/refclock_phc.c b/refclock_phc.c index 6c0914f6..cff75d88 100644 --- a/refclock_phc.c +++ b/refclock_phc.c @@ -74,7 +74,7 @@ static int phc_initialise(RCL_Instance instance) path = RCL_GetDriverParameter(instance); - phc_fd = SYS_Linux_OpenPHC(path, 0); + phc_fd = SYS_Linux_OpenPHC(path); if (phc_fd < 0) LOG_FATAL("Could not open PHC"); diff --git a/sys_linux.c b/sys_linux.c index 5fe9c0ec..17c1374d 100644 --- a/sys_linux.c +++ b/sys_linux.c @@ -38,6 +38,12 @@ #include #endif +#ifdef HAVE_LINUX_TIMESTAMPING +#include +#include +#include +#endif + #ifdef FEAT_SCFILTER #include #include @@ -48,9 +54,6 @@ #ifdef FEAT_RTC #include #endif -#ifdef HAVE_LINUX_TIMESTAMPING -#include -#endif #endif #ifdef FEAT_PRIVDROP @@ -64,6 +67,7 @@ #include "local.h" #include "logging.h" #include "privops.h" +#include "socket.h" #include "util.h" /* Frequency scale to convert from ppm to the timex freq */ @@ -902,33 +906,100 @@ get_precise_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3]) /* ================================================== */ -int -SYS_Linux_OpenPHC(const char *path, int phc_index) +/* Make sure an FD is a PHC. Return the FD if it is, or close the FD + and return -1 if it is not. */ + +static int +verify_fd_is_phc(int phc_fd) { 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; + if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) { + LOG(LOGS_ERR, "ioctl(%s) failed : %s", "PTP_CLOCK_GETCAPS", strerror(errno)); + close(phc_fd); + return -1; } - phc_fd = open(path, O_RDONLY); - if (phc_fd < 0) { - LOG(LOGS_ERR, "Could not open %s : %s", path, strerror(errno)); + return phc_fd; +} + +/* ================================================== */ + +static int +open_phc_by_iface_name(const char *iface) +{ +#ifdef HAVE_LINUX_TIMESTAMPING + struct ethtool_ts_info ts_info; + char phc_device[PATH_MAX]; + struct ifreq req; + int sock_fd; + + sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0); + if (sock_fd < 0) + return -1; + + memset(&req, 0, sizeof (req)); + memset(&ts_info, 0, sizeof (ts_info)); + + if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", iface) >= + sizeof (req.ifr_name)) { + SCK_CloseSocket(sock_fd); return -1; } - /* Make sure it is a PHC */ - if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) { - LOG(LOGS_ERR, "ioctl(%s) failed : %s", "PTP_CLOCK_GETCAPS", strerror(errno)); - close(phc_fd); + ts_info.cmd = ETHTOOL_GET_TS_INFO; + req.ifr_data = (char *)&ts_info; + + if (ioctl(sock_fd, SIOCETHTOOL, &req)) { + DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno)); + SCK_CloseSocket(sock_fd); + return -1; + } + + /* Simplify failure paths by closing the socket as early as possible */ + SCK_CloseSocket(sock_fd); + sock_fd = -1; + + if (ts_info.phc_index < 0) { + DEBUG_LOG("PHC missing on %s", req.ifr_name); return -1; } - UTI_FdSetCloexec(phc_fd); + if (snprintf(phc_device, sizeof (phc_device), + "/dev/ptp%d", ts_info.phc_index) >= sizeof (phc_device)) + return -1; + + return open(phc_device, O_RDONLY); +#else + return -1; +#endif +} + +/* ================================================== */ + +int +SYS_Linux_OpenPHC(const char *device) +{ + int phc_fd = -1; + + if (device[0] == '/') { + phc_fd = open(device, O_RDONLY); + if (phc_fd >= 0) + phc_fd = verify_fd_is_phc(phc_fd); + } + + if (phc_fd < 0) { + phc_fd = open_phc_by_iface_name(device); + if (phc_fd < 0) { + LOG(LOGS_ERR, "Could not open PHC of iface %s : %s", + device, strerror(errno)); + return -1; + } + phc_fd = verify_fd_is_phc(phc_fd); + } + + if (phc_fd >= 0) + UTI_FdSetCloexec(phc_fd); return phc_fd; } diff --git a/sys_linux.h b/sys_linux.h index 4741624c..b7b3eadb 100644 --- a/sys_linux.h +++ b/sys_linux.h @@ -39,7 +39,7 @@ extern void SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext conte 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_OpenPHC(const char *device); extern int SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings, struct timespec tss[][3]);