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.
This commit is contained in:
Shachar Raindel
2025-04-23 13:46:44 -07:00
committed by Miroslav Lichvar
parent 81362fa201
commit 6e541afbe9
5 changed files with 98 additions and 26 deletions

View 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

View 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;

View 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");

View File

@@ -38,6 +38,12 @@
#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;
}
phc_fd = open(path, O_RDONLY);
if (phc_fd < 0) {
LOG(LOGS_ERR, "Could not open %s : %s", path, strerror(errno));
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);
return -1;
}
UTI_FdSetCloexec(phc_fd);
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;
}
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;
}
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;
}

View 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]);