]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
refclock_phc: support ethernet ifname as specifier
authorShachar Raindel <shacharr@google.com>
Wed, 23 Apr 2025 20:46:44 +0000 (13:46 -0700)
committerMiroslav Lichvar <mlichvar@redhat.com>
Mon, 5 May 2025 10:19:17 +0000 (12:19 +0200)
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.

doc/chrony.conf.adoc
ntp_io_linux.c
refclock_phc.c
sys_linux.c
sys_linux.h

index fd294de71d13f5baa0bc722405b444082207e9b5..711f7c441aeda279f8e355f3b814d23ed756f1c7 100644 (file)
@@ -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
index 3b6874e3391325f01ea9a8c773fd8fe018b12ed7..3a273aad786c93900fb65ca5d0f5a08501878ce8 100644 (file)
@@ -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;
 
index 6c0914f623124652f970503f66187392bfcbcdc5..cff75d88b451f7eb802a77c7a61ccbf8c213b01b 100644 (file)
@@ -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");
 
index 5fe9c0ec4fd9f376c2a47ad8abc0d0f437e5e793..17c1374d1ebd01e8a4762a35c5e95611bcfc0703 100644 (file)
 #include <poll.h>
 #endif
 
+#ifdef HAVE_LINUX_TIMESTAMPING
+#include <linux/sockios.h>
+#include <linux/ethtool.h>
+#include <net/if.h>
+#endif
+
 #ifdef FEAT_SCFILTER
 #include <sys/prctl.h>
 #include <seccomp.h>
@@ -48,9 +54,6 @@
 #ifdef FEAT_RTC
 #include <linux/rtc.h>
 #endif
-#ifdef HAVE_LINUX_TIMESTAMPING
-#include <linux/sockios.h>
-#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;
 }
index 4741624cd62b384cb1cb9c1220f94b8ce7e2196a..b7b3eadb394d4c58c8c5d656f90262f4f88d0f82 100644 (file)
@@ -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]);