dcavalca / rpms / linuxptp

Forked from rpms/linuxptp 2 years ago
Clone
Miroslav Lichvar e3c74a
commit 6d2e07353d042b845da60dc6e3a20a71932678d0
Miroslav Lichvar e3c74a
Author: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Date:   Tue Mar 8 11:46:58 2022 +0100
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
    rtnl: Fix rtnl_rtattr_parse() to process max attribute.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Initialize the whole array passed to rtnl_rtattr_parse() and don't
Miroslav Lichvar e3c74a
    ignore the last attribute with the maximum value. This will be needed to
Miroslav Lichvar e3c74a
    get the ETHTOOL_A_PHC_VCLOCKS_INDEX attribute.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
    Acked-by: Hangbin Liu <liuhangbin@gmail.com>
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
diff --git a/rtnl.c b/rtnl.c
Miroslav Lichvar e3c74a
index b7a2667..b02e07d 100644
Miroslav Lichvar e3c74a
--- a/rtnl.c
Miroslav Lichvar e3c74a
+++ b/rtnl.c
Miroslav Lichvar e3c74a
@@ -178,10 +178,10 @@ static int rtnl_rtattr_parse(struct rtattr *tb[], int max, struct rtattr *rta, i
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	unsigned short type;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
-	memset(tb, 0, sizeof(struct rtattr *) * max);
Miroslav Lichvar e3c74a
+	memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
Miroslav Lichvar e3c74a
 	while (RTA_OK(rta, len)) {
Miroslav Lichvar e3c74a
 		type = rta->rta_type;
Miroslav Lichvar e3c74a
-		if ((type < max) && (!tb[type]))
Miroslav Lichvar e3c74a
+		if ((type <= max) && (!tb[type]))
Miroslav Lichvar e3c74a
 			tb[type] = rta;
Miroslav Lichvar e3c74a
 		rta = RTA_NEXT(rta, len);
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
@@ -200,8 +200,8 @@ static inline int rtnl_nested_rtattr_parse(struct rtattr *tb[], int max, struct
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 static int rtnl_linkinfo_parse(int master_index, struct rtattr *rta)
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
-	struct rtattr *linkinfo[IFLA_INFO_MAX];
Miroslav Lichvar e3c74a
-	struct rtattr *bond[IFLA_BOND_MAX];
Miroslav Lichvar e3c74a
+	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
Miroslav Lichvar e3c74a
+	struct rtattr *bond[IFLA_BOND_MAX+1];
Miroslav Lichvar e3c74a
 	int index = -1;
Miroslav Lichvar e3c74a
 	char *kind;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
commit 8c557a7c7e5eebc6f0d7e1de44c53791fba265c1
Miroslav Lichvar e3c74a
Author: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Date:   Tue Mar 8 11:46:59 2022 +0100
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
    rtnl: Add function to detect virtual clocks.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Add a function using ethtool netlink to check whether a PHC is a virtual
Miroslav Lichvar e3c74a
    clock of an interface.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
    Acked-by: Hangbin Liu <liuhangbin@gmail.com>
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
diff --git a/incdefs.sh b/incdefs.sh
Miroslav Lichvar e3c74a
index 19e620e..21333e5 100755
Miroslav Lichvar e3c74a
--- a/incdefs.sh
Miroslav Lichvar e3c74a
+++ b/incdefs.sh
Miroslav Lichvar e3c74a
@@ -86,6 +86,10 @@ kernel_flags()
Miroslav Lichvar e3c74a
 	if grep -q HWTSTAMP_TX_ONESTEP_P2P ${prefix}${tstamp}; then
Miroslav Lichvar e3c74a
 		printf " -DHAVE_ONESTEP_P2P"
Miroslav Lichvar e3c74a
 	fi
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	if grep -q SOF_TIMESTAMPING_BIND_PHC ${prefix}${tstamp}; then
Miroslav Lichvar e3c74a
+		printf " -DHAVE_VCLOCKS"
Miroslav Lichvar e3c74a
+	fi
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 flags="$(user_flags)$(kernel_flags)"
Miroslav Lichvar e3c74a
diff --git a/missing.h b/missing.h
Miroslav Lichvar e3c74a
index 35eaf15..3df7bd1 100644
Miroslav Lichvar e3c74a
--- a/missing.h
Miroslav Lichvar e3c74a
+++ b/missing.h
Miroslav Lichvar e3c74a
@@ -251,6 +251,107 @@ enum {
Miroslav Lichvar e3c74a
 #define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
Miroslav Lichvar e3c74a
 #endif /*NLA_TYPE_MAX*/
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+#ifndef ETHTOOL_GENL_NAME
Miroslav Lichvar e3c74a
+#define ETHTOOL_GENL_NAME "ethtool"
Miroslav Lichvar e3c74a
+#define ETHTOOL_GENL_VERSION 1
Miroslav Lichvar e3c74a
+#endif
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+#ifndef HAVE_VCLOCKS
Miroslav Lichvar e3c74a
+enum {
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_USER_NONE,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_STRSET_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_LINKINFO_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_LINKINFO_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_LINKMODES_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_LINKMODES_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_LINKSTATE_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_DEBUG_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_DEBUG_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_WOL_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_WOL_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_FEATURES_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_FEATURES_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_PRIVFLAGS_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_PRIVFLAGS_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_RINGS_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_RINGS_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_CHANNELS_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_CHANNELS_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_COALESCE_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_COALESCE_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_PAUSE_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_PAUSE_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_EEE_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_EEE_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_TSINFO_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_CABLE_TEST_ACT,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_CABLE_TEST_TDR_ACT,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_TUNNEL_INFO_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_FEC_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_FEC_SET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_MODULE_EEPROM_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_STATS_GET,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_PHC_VCLOCKS_GET,
Miroslav Lichvar e3c74a
+};
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+enum {
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_KERNEL_NONE,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_STRSET_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_LINKINFO_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_LINKINFO_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_LINKMODES_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_LINKMODES_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_LINKSTATE_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_DEBUG_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_DEBUG_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_WOL_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_WOL_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_FEATURES_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_FEATURES_SET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_FEATURES_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_PRIVFLAGS_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_PRIVFLAGS_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_RINGS_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_RINGS_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_CHANNELS_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_CHANNELS_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_COALESCE_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_COALESCE_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_PAUSE_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_PAUSE_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_EEE_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_EEE_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_TSINFO_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_CABLE_TEST_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_CABLE_TEST_TDR_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_FEC_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_FEC_NTF,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_STATS_GET_REPLY,
Miroslav Lichvar e3c74a
+	ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY,
Miroslav Lichvar e3c74a
+};
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+enum {
Miroslav Lichvar e3c74a
+	ETHTOOL_A_HEADER_UNSPEC,
Miroslav Lichvar e3c74a
+	ETHTOOL_A_HEADER_DEV_INDEX,		/* u32 */
Miroslav Lichvar e3c74a
+	ETHTOOL_A_HEADER_DEV_NAME,		/* string */
Miroslav Lichvar e3c74a
+	ETHTOOL_A_HEADER_FLAGS,			/* u32 - ETHTOOL_FLAG_* */
Miroslav Lichvar e3c74a
+	__ETHTOOL_A_HEADER_CNT,
Miroslav Lichvar e3c74a
+	ETHTOOL_A_HEADER_MAX = __ETHTOOL_A_HEADER_CNT - 1
Miroslav Lichvar e3c74a
+};
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+enum {
Miroslav Lichvar e3c74a
+	ETHTOOL_A_PHC_VCLOCKS_UNSPEC,
Miroslav Lichvar e3c74a
+	ETHTOOL_A_PHC_VCLOCKS_HEADER,			/* nest - _A_HEADER_* */
Miroslav Lichvar e3c74a
+	ETHTOOL_A_PHC_VCLOCKS_NUM,			/* u32 */
Miroslav Lichvar e3c74a
+	ETHTOOL_A_PHC_VCLOCKS_INDEX,			/* array, s32 */
Miroslav Lichvar e3c74a
+	__ETHTOOL_A_PHC_VCLOCKS_CNT,
Miroslav Lichvar e3c74a
+	ETHTOOL_A_PHC_VCLOCKS_MAX = (__ETHTOOL_A_PHC_VCLOCKS_CNT - 1)
Miroslav Lichvar e3c74a
+};
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+#endif /* HAVE_VCLOCKS */
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 #ifdef __UCLIBC__
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 #if (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L) && \
Miroslav Lichvar e3c74a
diff --git a/rtnl.c b/rtnl.c
Miroslav Lichvar e3c74a
index b02e07d..a8999b2 100644
Miroslav Lichvar e3c74a
--- a/rtnl.c
Miroslav Lichvar e3c74a
+++ b/rtnl.c
Miroslav Lichvar e3c74a
@@ -19,6 +19,9 @@
Miroslav Lichvar e3c74a
 #include <asm/types.h>
Miroslav Lichvar e3c74a
 #include <sys/socket.h> /* Must come before linux/netlink.h on some systems. */
Miroslav Lichvar e3c74a
 #include <linux/netlink.h>
Miroslav Lichvar e3c74a
+#ifdef HAVE_VCLOCKS
Miroslav Lichvar e3c74a
+#include <linux/ethtool_netlink.h>
Miroslav Lichvar e3c74a
+#endif
Miroslav Lichvar e3c74a
 #include <linux/rtnetlink.h>
Miroslav Lichvar e3c74a
 #include <linux/genetlink.h>
Miroslav Lichvar e3c74a
 #include <linux/if_team.h>
Miroslav Lichvar e3c74a
@@ -465,3 +468,85 @@ no_info:
Miroslav Lichvar e3c74a
 	nl_close(fd);
Miroslav Lichvar e3c74a
 	return index;
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+static int rtnl_search_vclocks(struct rtattr *attr, int phc_index)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	int i, len = RTA_PAYLOAD(attr);
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	for (i = 0; i < len / sizeof (__s32); i++) {
Miroslav Lichvar e3c74a
+		if (((__s32 *)RTA_DATA(attr))[i] == phc_index)
Miroslav Lichvar e3c74a
+			return 1;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	return 0;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+int rtnl_iface_has_vclock(const char *device, int phc_index)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	struct rtattr *tb[ETHTOOL_A_PHC_VCLOCKS_MAX + 1];
Miroslav Lichvar e3c74a
+	int index, fd, gf_id, len, ret = 0;
Miroslav Lichvar e3c74a
+	struct genlmsghdr *gnlh;
Miroslav Lichvar e3c74a
+	struct nlmsghdr *nlh;
Miroslav Lichvar e3c74a
+	char msg[BUF_SIZE];
Miroslav Lichvar e3c74a
+	struct {
Miroslav Lichvar e3c74a
+		struct nlattr attr;
Miroslav Lichvar e3c74a
+		uint32_t index;
Miroslav Lichvar e3c74a
+	} req;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	index = if_nametoindex(device);
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	fd = nl_open(NETLINK_GENERIC);
Miroslav Lichvar e3c74a
+	if (fd < 0)
Miroslav Lichvar e3c74a
+		return 0;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	gf_id = genl_get_family_id(fd, ETHTOOL_GENL_NAME);
Miroslav Lichvar e3c74a
+	if (gf_id < 0) {
Miroslav Lichvar e3c74a
+		pr_debug("ethtool netlink not supported");
Miroslav Lichvar e3c74a
+		goto no_info;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	req.attr.nla_len = sizeof(req);
Miroslav Lichvar e3c74a
+	req.attr.nla_type = ETHTOOL_A_HEADER_DEV_INDEX;
Miroslav Lichvar e3c74a
+	req.index = index;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	len = genl_send_msg(fd, gf_id, ETHTOOL_MSG_PHC_VCLOCKS_GET,
Miroslav Lichvar e3c74a
+			    ETHTOOL_GENL_VERSION,
Miroslav Lichvar e3c74a
+			    NLA_F_NESTED | ETHTOOL_A_PHC_VCLOCKS_HEADER, 
Miroslav Lichvar e3c74a
+			    &req, sizeof(req));
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	if (len < 0) {
Miroslav Lichvar e3c74a
+		pr_err("send vclock request failed: %m");
Miroslav Lichvar e3c74a
+		goto no_info;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	len = recv(fd, msg, sizeof(msg), 0);
Miroslav Lichvar e3c74a
+	if (len < 0) {
Miroslav Lichvar e3c74a
+		pr_err("recv vclock failed: %m");
Miroslav Lichvar e3c74a
+		goto no_info;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	for (nlh = (struct nlmsghdr *) msg; NLMSG_OK(nlh, len);
Miroslav Lichvar e3c74a
+	     nlh = NLMSG_NEXT(nlh, len)) {
Miroslav Lichvar e3c74a
+		if (nlh->nlmsg_type != gf_id)
Miroslav Lichvar e3c74a
+			continue;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+		gnlh = (struct genlmsghdr *) NLMSG_DATA(nlh);
Miroslav Lichvar e3c74a
+		if (gnlh->cmd != ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY)
Miroslav Lichvar e3c74a
+			continue;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+		if (rtnl_rtattr_parse(tb, ETHTOOL_A_PHC_VCLOCKS_MAX,
Miroslav Lichvar e3c74a
+				      (struct rtattr *) GENLMSG_DATA(msg),
Miroslav Lichvar e3c74a
+				      NLMSG_PAYLOAD(nlh, GENL_HDRLEN)))
Miroslav Lichvar e3c74a
+			continue;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+		if (tb[ETHTOOL_A_PHC_VCLOCKS_INDEX]) {
Miroslav Lichvar e3c74a
+			ret = rtnl_search_vclocks(tb[ETHTOOL_A_PHC_VCLOCKS_INDEX],
Miroslav Lichvar e3c74a
+						  phc_index);
Miroslav Lichvar e3c74a
+			break;
Miroslav Lichvar e3c74a
+		}
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+no_info:
Miroslav Lichvar e3c74a
+	nl_close(fd);
Miroslav Lichvar e3c74a
+	return ret;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
diff --git a/rtnl.h b/rtnl.h
Miroslav Lichvar e3c74a
index 8fef4a9..96fee29 100644
Miroslav Lichvar e3c74a
--- a/rtnl.h
Miroslav Lichvar e3c74a
+++ b/rtnl.h
Miroslav Lichvar e3c74a
@@ -59,6 +59,15 @@ int rtnl_link_query(int fd, const char *device);
Miroslav Lichvar e3c74a
  */
Miroslav Lichvar e3c74a
 int rtnl_link_status(int fd, const char *device, rtnl_callback cb, void *ctx);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+/**
Miroslav Lichvar e3c74a
+ * Check if the PHC is a virtual clock of the interface (i.e. sockets bound to
Miroslav Lichvar e3c74a
+ * the interface also need to be bound to the clock).
Miroslav Lichvar e3c74a
+ * @param device    Name of the interface.
Miroslav Lichvar e3c74a
+ * @param phc_index Index of the clock to check.
Miroslav Lichvar e3c74a
+ * @return          1 if true, otherwise 0.
Miroslav Lichvar e3c74a
+ */
Miroslav Lichvar e3c74a
+int rtnl_iface_has_vclock(const char *device, int phc_index);
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 /**
Miroslav Lichvar e3c74a
  * Open a RT netlink socket for monitoring link state.
Miroslav Lichvar e3c74a
  * @return    A valid socket, or -1 on error.
Miroslav Lichvar e3c74a
commit 5477078bf5c9ef050c3bcb037f856b693f1247e7
Miroslav Lichvar e3c74a
Author: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Date:   Tue Mar 8 11:47:00 2022 +0100
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
    Add support for binding sockets to virtual clocks.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    With the latest kernels it is possible to create virtual PHCs on top of
Miroslav Lichvar e3c74a
    a free-running physical PHC. In order for the application to get
Miroslav Lichvar e3c74a
    timestamps captured by the clock which it is controlling, it needs to
Miroslav Lichvar e3c74a
    bind its sockets to the clock using a new field in the SO_TIMESTAMPING
Miroslav Lichvar e3c74a
    option.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Extend the interface structure with the vclock index and modify the
Miroslav Lichvar e3c74a
    transport code to pass it to sk_timestamping_init() to bind the sockets
Miroslav Lichvar e3c74a
    to the clock.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
diff --git a/interface.c b/interface.c
Miroslav Lichvar e3c74a
index 65bdff0..445a270 100644
Miroslav Lichvar e3c74a
--- a/interface.c
Miroslav Lichvar e3c74a
+++ b/interface.c
Miroslav Lichvar e3c74a
@@ -12,6 +12,7 @@ struct interface {
Miroslav Lichvar e3c74a
 	char name[MAX_IFNAME_SIZE + 1];
Miroslav Lichvar e3c74a
 	char ts_label[MAX_IFNAME_SIZE + 1];
Miroslav Lichvar e3c74a
 	struct sk_ts_info ts_info;
Miroslav Lichvar e3c74a
+	int vclock;
Miroslav Lichvar e3c74a
 };
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 struct interface *interface_create(const char *name)
Miroslav Lichvar e3c74a
@@ -23,6 +24,7 @@ struct interface *interface_create(const char *name)
Miroslav Lichvar e3c74a
 		return NULL;
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
 	strncpy(iface->name, name, MAX_IFNAME_SIZE);
Miroslav Lichvar e3c74a
+	iface->vclock = -1;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	return iface;
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
@@ -76,3 +78,13 @@ bool interface_tsmodes_supported(struct interface *iface, int modes)
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
 	return false;
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+void interface_set_vclock(struct interface *iface, int vclock)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	iface->vclock = vclock;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+int interface_get_vclock(struct interface *iface)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	return iface->vclock;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
diff --git a/interface.h b/interface.h
Miroslav Lichvar e3c74a
index 8bf2727..752f4f1 100644
Miroslav Lichvar e3c74a
--- a/interface.h
Miroslav Lichvar e3c74a
+++ b/interface.h
Miroslav Lichvar e3c74a
@@ -91,4 +91,18 @@ bool interface_tsinfo_valid(struct interface *iface);
Miroslav Lichvar e3c74a
  */
Miroslav Lichvar e3c74a
 bool interface_tsmodes_supported(struct interface *iface, int modes);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+/**
Miroslav Lichvar e3c74a
+ * Set the vclock (virtual PHC) to be used for timestamping on an interface.
Miroslav Lichvar e3c74a
+ * @param iface  The interface of interest.
Miroslav Lichvar e3c74a
+ * @param vclock The index of the vclock.
Miroslav Lichvar e3c74a
+ */
Miroslav Lichvar e3c74a
+void interface_set_vclock(struct interface *iface, int vclock);
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+/**
Miroslav Lichvar e3c74a
+ * Get the vclock index set for the interface.
Miroslav Lichvar e3c74a
+ * @param iface  The interface of interest.
Miroslav Lichvar e3c74a
+ * @return       The index of the vclock, or -1 if not set.
Miroslav Lichvar e3c74a
+ */
Miroslav Lichvar e3c74a
+int interface_get_vclock(struct interface *iface);
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 #endif
Miroslav Lichvar e3c74a
diff --git a/missing.h b/missing.h
Miroslav Lichvar e3c74a
index 3df7bd1..c5194f4 100644
Miroslav Lichvar e3c74a
--- a/missing.h
Miroslav Lichvar e3c74a
+++ b/missing.h
Miroslav Lichvar e3c74a
@@ -62,6 +62,17 @@ enum {
Miroslav Lichvar e3c74a
 };
Miroslav Lichvar e3c74a
 #endif
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+#ifndef HAVE_VCLOCKS
Miroslav Lichvar e3c74a
+enum {
Miroslav Lichvar e3c74a
+	SOF_TIMESTAMPING_BIND_PHC = (1 << 15),
Miroslav Lichvar e3c74a
+};
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+struct so_timestamping {
Miroslav Lichvar e3c74a
+	int flags;
Miroslav Lichvar e3c74a
+	int bind_phc;
Miroslav Lichvar e3c74a
+};
Miroslav Lichvar e3c74a
+#endif
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 #ifdef PTP_EXTTS_REQUEST2
Miroslav Lichvar e3c74a
 #define PTP_EXTTS_REQUEST_FAILED "PTP_EXTTS_REQUEST2 failed: %m"
Miroslav Lichvar e3c74a
 #else
Miroslav Lichvar e3c74a
diff --git a/raw.c b/raw.c
Miroslav Lichvar e3c74a
index 0bd15b0..ce64684 100644
Miroslav Lichvar e3c74a
--- a/raw.c
Miroslav Lichvar e3c74a
+++ b/raw.c
Miroslav Lichvar e3c74a
@@ -243,7 +243,8 @@ static int raw_open(struct transport *t, struct interface *iface,
Miroslav Lichvar e3c74a
 	if (gfd < 0)
Miroslav Lichvar e3c74a
 		goto no_general;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
-	if (sk_timestamping_init(efd, name, ts_type, TRANS_IEEE_802_3))
Miroslav Lichvar e3c74a
+	if (sk_timestamping_init(efd, name, ts_type, TRANS_IEEE_802_3,
Miroslav Lichvar e3c74a
+				 interface_get_vclock(iface)))
Miroslav Lichvar e3c74a
 		goto no_timestamping;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	if (sk_general_init(gfd))
Miroslav Lichvar e3c74a
diff --git a/sk.c b/sk.c
Miroslav Lichvar e3c74a
index 8be0708..b55d6b5 100644
Miroslav Lichvar e3c74a
--- a/sk.c
Miroslav Lichvar e3c74a
+++ b/sk.c
Miroslav Lichvar e3c74a
@@ -447,9 +447,10 @@ int sk_set_priority(int fd, int family, uint8_t dscp)
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 int sk_timestamping_init(int fd, const char *device, enum timestamp_type type,
Miroslav Lichvar e3c74a
-			 enum transport_type transport)
Miroslav Lichvar e3c74a
+			 enum transport_type transport, int vclock)
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	int err, filter1, filter2 = 0, flags, tx_type = HWTSTAMP_TX_ON;
Miroslav Lichvar e3c74a
+	struct so_timestamping timestamping;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	switch (type) {
Miroslav Lichvar e3c74a
 	case TS_SOFTWARE:
Miroslav Lichvar e3c74a
@@ -509,8 +510,14 @@ int sk_timestamping_init(int fd, const char *device, enum timestamp_type type,
Miroslav Lichvar e3c74a
 			return err;
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+	if (vclock >= 0)
Miroslav Lichvar e3c74a
+		flags |= SOF_TIMESTAMPING_BIND_PHC;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	timestamping.flags = flags;
Miroslav Lichvar e3c74a
+	timestamping.bind_phc = vclock;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 	if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
Miroslav Lichvar e3c74a
-		       &flags, sizeof(flags)) < 0) {
Miroslav Lichvar e3c74a
+		       &timestamping, sizeof(timestamping)) < 0) {
Miroslav Lichvar e3c74a
 		pr_err("ioctl SO_TIMESTAMPING failed: %m");
Miroslav Lichvar e3c74a
 		return -1;
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
diff --git a/sk.h b/sk.h
Miroslav Lichvar e3c74a
index 04d26ee..486dbc4 100644
Miroslav Lichvar e3c74a
--- a/sk.h
Miroslav Lichvar e3c74a
+++ b/sk.h
Miroslav Lichvar e3c74a
@@ -124,10 +124,11 @@ int sk_set_priority(int fd, int family, uint8_t dscp);
Miroslav Lichvar e3c74a
  * @param device      The name of the network interface to configure.
Miroslav Lichvar e3c74a
  * @param type        The requested flavor of time stamping.
Miroslav Lichvar e3c74a
  * @param transport   The type of transport used.
Miroslav Lichvar e3c74a
+ * @param vclock      Index of the virtual PHC, or -1 for the physical clock.
Miroslav Lichvar e3c74a
  * @return            Zero on success, non-zero otherwise.
Miroslav Lichvar e3c74a
  */
Miroslav Lichvar e3c74a
 int sk_timestamping_init(int fd, const char *device, enum timestamp_type type,
Miroslav Lichvar e3c74a
-			 enum transport_type transport);
Miroslav Lichvar e3c74a
+			 enum transport_type transport, int vclock);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 /**
Miroslav Lichvar e3c74a
  * Limits the time that RECVMSG(2) will poll while waiting for the tx timestamp
Miroslav Lichvar e3c74a
diff --git a/udp.c b/udp.c
Miroslav Lichvar e3c74a
index 826bd12..7c9402e 100644
Miroslav Lichvar e3c74a
--- a/udp.c
Miroslav Lichvar e3c74a
+++ b/udp.c
Miroslav Lichvar e3c74a
@@ -179,7 +179,8 @@ static int udp_open(struct transport *t, struct interface *iface,
Miroslav Lichvar e3c74a
 	if (gfd < 0)
Miroslav Lichvar e3c74a
 		goto no_general;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
-	if (sk_timestamping_init(efd, interface_label(iface), ts_type, TRANS_UDP_IPV4))
Miroslav Lichvar e3c74a
+	if (sk_timestamping_init(efd, interface_label(iface), ts_type, TRANS_UDP_IPV4,
Miroslav Lichvar e3c74a
+				 interface_get_vclock(iface)))
Miroslav Lichvar e3c74a
 		goto no_timestamping;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	if (sk_general_init(gfd))
Miroslav Lichvar e3c74a
diff --git a/udp6.c b/udp6.c
Miroslav Lichvar e3c74a
index ba5482e..bde1710 100644
Miroslav Lichvar e3c74a
--- a/udp6.c
Miroslav Lichvar e3c74a
+++ b/udp6.c
Miroslav Lichvar e3c74a
@@ -196,7 +196,8 @@ static int udp6_open(struct transport *t, struct interface *iface,
Miroslav Lichvar e3c74a
 	if (gfd < 0)
Miroslav Lichvar e3c74a
 		goto no_general;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
-	if (sk_timestamping_init(efd, interface_label(iface), ts_type, TRANS_UDP_IPV6))
Miroslav Lichvar e3c74a
+	if (sk_timestamping_init(efd, interface_label(iface), ts_type,
Miroslav Lichvar e3c74a
+				 TRANS_UDP_IPV6, interface_get_vclock(iface)))
Miroslav Lichvar e3c74a
 		goto no_timestamping;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	if (sk_general_init(gfd))
Miroslav Lichvar e3c74a
commit daaaff6b553290cf09284b0cc7756b9e24358ace
Miroslav Lichvar e3c74a
Author: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Date:   Tue Mar 8 11:47:01 2022 +0100
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
    config: Add port-specific phc_index option.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Allow the PHC index to be configured for each port. The default value is
Miroslav Lichvar e3c74a
    -1, which enables the original behavior using the PHC specified by -p or
Miroslav Lichvar e3c74a
    the index from ETHTOOL_GET_TS_INFO.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    (Rebased to 3.1.1)
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
diff --git a/clock.c b/clock.c
Miroslav Lichvar e3c74a
index 49bd4a9..67b3372 100644
Miroslav Lichvar e3c74a
--- a/clock.c
Miroslav Lichvar e3c74a
+++ b/clock.c
Miroslav Lichvar e3c74a
@@ -900,7 +900,7 @@ struct clock *clock_create(enum clock_type type, struct config *config,
Miroslav Lichvar e3c74a
 	char ts_label[IF_NAMESIZE], phc[32], *tmp;
Miroslav Lichvar e3c74a
 	enum timestamp_type timestamping;
Miroslav Lichvar e3c74a
 	int fadj = 0, max_adj = 0, sw_ts;
Miroslav Lichvar e3c74a
-	int phc_index, required_modes = 0;
Miroslav Lichvar e3c74a
+	int phc_index, conf_phc_index, required_modes = 0;
Miroslav Lichvar e3c74a
 	struct clock *c = &the_clock;
Miroslav Lichvar e3c74a
 	const char *uds_ifname;
Miroslav Lichvar e3c74a
 	struct port *p;
Miroslav Lichvar e3c74a
@@ -1018,6 +1018,8 @@ struct clock *clock_create(enum clock_type type, struct config *config,
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	iface = STAILQ_FIRST(&config->interfaces);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+	conf_phc_index = config_get_int(config, interface_name(iface), "phc_index");
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 	/* determine PHC Clock index */
Miroslav Lichvar e3c74a
 	if (config_get_int(config, NULL, "free_running")) {
Miroslav Lichvar e3c74a
 		phc_index = -1;
Miroslav Lichvar e3c74a
@@ -1027,6 +1029,8 @@ struct clock *clock_create(enum clock_type type, struct config *config,
Miroslav Lichvar e3c74a
 		if (1 != sscanf(phc_device, "/dev/ptp%d", &phc_index)) {
Miroslav Lichvar e3c74a
 			phc_index = -1;
Miroslav Lichvar e3c74a
 		}
Miroslav Lichvar e3c74a
+	} else if (conf_phc_index >= 0) {
Miroslav Lichvar e3c74a
+		phc_index = conf_phc_index;
Miroslav Lichvar e3c74a
 	} else if (interface_tsinfo_valid(iface)) {
Miroslav Lichvar e3c74a
 		phc_index = interface_phc_index(iface);
Miroslav Lichvar e3c74a
 	} else {
Miroslav Lichvar e3c74a
diff --git a/config.c b/config.c
Miroslav Lichvar e3c74a
index ef5e833..0613eda 100644
Miroslav Lichvar e3c74a
--- a/config.c
Miroslav Lichvar e3c74a
+++ b/config.c
Miroslav Lichvar e3c74a
@@ -282,6 +282,7 @@ struct config_item config_tab[] = {
Miroslav Lichvar e3c74a
 	PORT_ITEM_INT("operLogPdelayReqInterval", 0, INT8_MIN, INT8_MAX),
Miroslav Lichvar e3c74a
 	PORT_ITEM_INT("operLogSyncInterval", 0, INT8_MIN, INT8_MAX),
Miroslav Lichvar e3c74a
 	PORT_ITEM_INT("path_trace_enabled", 0, 0, 1),
Miroslav Lichvar e3c74a
+	PORT_ITEM_INT("phc_index", -1, -1, INT_MAX),
Miroslav Lichvar e3c74a
 	GLOB_ITEM_DBL("pi_integral_const", 0.0, 0.0, DBL_MAX),
Miroslav Lichvar e3c74a
 	GLOB_ITEM_DBL("pi_integral_exponent", 0.4, -DBL_MAX, DBL_MAX),
Miroslav Lichvar e3c74a
 	GLOB_ITEM_DBL("pi_integral_norm_max", 0.3, DBL_MIN, 2.0),
Miroslav Lichvar e3c74a
diff --git a/configs/default.cfg b/configs/default.cfg
Miroslav Lichvar e3c74a
index 8c19129..45888d5 100644
Miroslav Lichvar e3c74a
--- a/configs/default.cfg
Miroslav Lichvar e3c74a
+++ b/configs/default.cfg
Miroslav Lichvar e3c74a
@@ -103,6 +103,7 @@ delay_filter_length	10
Miroslav Lichvar e3c74a
 egressLatency		0
Miroslav Lichvar e3c74a
 ingressLatency		0
Miroslav Lichvar e3c74a
 boundary_clock_jbod	0
Miroslav Lichvar e3c74a
+phc_index		-1
Miroslav Lichvar e3c74a
 #
Miroslav Lichvar e3c74a
 # Clock description
Miroslav Lichvar e3c74a
 #
Miroslav Lichvar e3c74a
diff --git a/port.c b/port.c
Miroslav Lichvar e3c74a
index d26b87f..7912ee6 100644
Miroslav Lichvar e3c74a
--- a/port.c
Miroslav Lichvar e3c74a
+++ b/port.c
Miroslav Lichvar e3c74a
@@ -3057,7 +3057,9 @@ struct port *port_open(const char *phc_device,
Miroslav Lichvar e3c74a
 		goto err_port;
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
-	p->phc_index = phc_index;
Miroslav Lichvar e3c74a
+	p->phc_index = config_get_int(cfg, interface_name(interface), "phc_index");
Miroslav Lichvar e3c74a
+	if (p->phc_index < 0)
Miroslav Lichvar e3c74a
+		p->phc_index = phc_index;
Miroslav Lichvar e3c74a
 	p->jbod = config_get_int(cfg, interface_name(interface), "boundary_clock_jbod");
Miroslav Lichvar e3c74a
 	transport = config_get_int(cfg, interface_name(interface), "network_transport");
Miroslav Lichvar e3c74a
 	p->master_only = config_get_int(cfg, interface_name(interface), "masterOnly");
Miroslav Lichvar e3c74a
@@ -3080,8 +3082,8 @@ struct port *port_open(const char *phc_device,
Miroslav Lichvar e3c74a
 		; /* UDS cannot have a PHC. */
Miroslav Lichvar e3c74a
 	} else if (!interface_tsinfo_valid(interface)) {
Miroslav Lichvar e3c74a
 		pr_warning("port %d: get_ts_info not supported", number);
Miroslav Lichvar e3c74a
-	} else if (phc_index >= 0 &&
Miroslav Lichvar e3c74a
-		   phc_index != interface_phc_index(interface)) {
Miroslav Lichvar e3c74a
+	} else if (p->phc_index >= 0 &&
Miroslav Lichvar e3c74a
+		   p->phc_index != interface_phc_index(interface)) {
Miroslav Lichvar e3c74a
 		if (p->jbod) {
Miroslav Lichvar e3c74a
 			pr_warning("port %d: just a bunch of devices", number);
Miroslav Lichvar e3c74a
 			p->phc_index = interface_phc_index(interface);
Miroslav Lichvar e3c74a
diff --git a/ptp4l.8 b/ptp4l.8
Miroslav Lichvar e3c74a
index b179b81..fc73e84 100644
Miroslav Lichvar e3c74a
--- a/ptp4l.8
Miroslav Lichvar e3c74a
+++ b/ptp4l.8
Miroslav Lichvar e3c74a
@@ -365,6 +365,11 @@ collection of clocks must be synchronized by an external program, for
Miroslav Lichvar e3c74a
 example phc2sys(8) in "automatic" mode.
Miroslav Lichvar e3c74a
 The default is 0 (disabled).
Miroslav Lichvar e3c74a
 .TP
Miroslav Lichvar e3c74a
+.B phc_index
Miroslav Lichvar e3c74a
+Specifies the index of the PHC to be used for synchronization with hardware
Miroslav Lichvar e3c74a
+timestamping. The default is -1, which means the index will be set to the PHC
Miroslav Lichvar e3c74a
+associated with the interface, or the device specified by the \fB-p\fP option.
Miroslav Lichvar e3c74a
+.TP
Miroslav Lichvar e3c74a
 .B udp_ttl
Miroslav Lichvar e3c74a
 Specifies the Time to live (TTL) value for IPv4 multicast messages and the hop
Miroslav Lichvar e3c74a
 limit for IPv6 multicast messages. This option is only relevant with the IPv4
Miroslav Lichvar e3c74a
commit bb50991e8b9ecbcea53abbd0164a51e3e0bfe246
Miroslav Lichvar e3c74a
Author: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Date:   Tue Mar 8 11:47:02 2022 +0100
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
    port: Check for virtual clocks.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    If the PHC specified with the phc_index or -p option is a virtual clock
Miroslav Lichvar e3c74a
    of the interface, bind sockets to the virtual clock instead of the real
Miroslav Lichvar e3c74a
    clock to get correct timestamps.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    (Rebased to 3.1.1)
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
diff --git a/port.c b/port.c
Miroslav Lichvar e3c74a
index 7912ee6..b4dcd1b 100644
Miroslav Lichvar e3c74a
--- a/port.c
Miroslav Lichvar e3c74a
+++ b/port.c
Miroslav Lichvar e3c74a
@@ -3084,7 +3084,12 @@ struct port *port_open(const char *phc_device,
Miroslav Lichvar e3c74a
 		pr_warning("port %d: get_ts_info not supported", number);
Miroslav Lichvar e3c74a
 	} else if (p->phc_index >= 0 &&
Miroslav Lichvar e3c74a
 		   p->phc_index != interface_phc_index(interface)) {
Miroslav Lichvar e3c74a
-		if (p->jbod) {
Miroslav Lichvar e3c74a
+		if (rtnl_iface_has_vclock(interface_name(interface),
Miroslav Lichvar e3c74a
+					  p->phc_index)) {
Miroslav Lichvar e3c74a
+			pr_info("port %d: /dev/ptp%d is virtual clock",
Miroslav Lichvar e3c74a
+				number, p->phc_index);
Miroslav Lichvar e3c74a
+			interface_set_vclock(interface, p->phc_index);
Miroslav Lichvar e3c74a
+		} else if (p->jbod) {
Miroslav Lichvar e3c74a
 			pr_warning("port %d: just a bunch of devices", number);
Miroslav Lichvar e3c74a
 			p->phc_index = interface_phc_index(interface);
Miroslav Lichvar e3c74a
 		} else if (phc_device) {
Miroslav Lichvar e3c74a
diff --git a/ptp4l.8 b/ptp4l.8
Miroslav Lichvar e3c74a
index fc73e84..d0446d5 100644
Miroslav Lichvar e3c74a
--- a/ptp4l.8
Miroslav Lichvar e3c74a
+++ b/ptp4l.8
Miroslav Lichvar e3c74a
@@ -367,8 +367,11 @@ The default is 0 (disabled).
Miroslav Lichvar e3c74a
 .TP
Miroslav Lichvar e3c74a
 .B phc_index
Miroslav Lichvar e3c74a
 Specifies the index of the PHC to be used for synchronization with hardware
Miroslav Lichvar e3c74a
-timestamping. The default is -1, which means the index will be set to the PHC
Miroslav Lichvar e3c74a
-associated with the interface, or the device specified by the \fB-p\fP option.
Miroslav Lichvar e3c74a
+timestamping. This option is useful with virtual clocks running on top of a
Miroslav Lichvar e3c74a
+free-running physical clock (created by writing to
Miroslav Lichvar e3c74a
+/sys/class/ptp/ptp*/n_vclocks).
Miroslav Lichvar e3c74a
+The default is -1, which means the index will be set to the PHC associated with
Miroslav Lichvar e3c74a
+the interface, or the device specified by the \fB-p\fP option.
Miroslav Lichvar e3c74a
 .TP
Miroslav Lichvar e3c74a
 .B udp_ttl
Miroslav Lichvar e3c74a
 Specifies the Time to live (TTL) value for IPv4 multicast messages and the hop
Miroslav Lichvar e3c74a
commit 2b1657a65c0f3c880a0b9982401d419108560a1f
Miroslav Lichvar e3c74a
Author: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Date:   Tue Mar 8 11:47:03 2022 +0100
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
    tlv: Add PORT_HWCLOCK_NP.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Add a new command to get the PHC index associated with the port. This
Miroslav Lichvar e3c74a
    will be needed for phc2sys -a to use the correct PHC for synchronization
Miroslav Lichvar e3c74a
    if ptp4l is using a virtual clock.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    The TLV also contains a flag indicating a virtual clock.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    To follow the PORT_PROPERTIES_NP access policy, PORT_HWCLOCK_NP is
Miroslav Lichvar e3c74a
    limited to the UDS-RW port.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    (Rebased to 3.1.1)
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
diff --git a/clock.c b/clock.c
Miroslav Lichvar e3c74a
index 67b3372..39df135 100644
Miroslav Lichvar e3c74a
--- a/clock.c
Miroslav Lichvar e3c74a
+++ b/clock.c
Miroslav Lichvar e3c74a
@@ -1482,6 +1482,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	switch (mgt->id) {
Miroslav Lichvar e3c74a
 	case TLV_PORT_PROPERTIES_NP:
Miroslav Lichvar e3c74a
+	case TLV_PORT_HWCLOCK_NP:
Miroslav Lichvar e3c74a
 		if (p != c->uds_rw_port) {
Miroslav Lichvar e3c74a
 			/* Only the UDS-RW port allowed. */
Miroslav Lichvar e3c74a
 			clock_management_send_error(p, msg, TLV_NOT_SUPPORTED);
Miroslav Lichvar e3c74a
diff --git a/pmc.c b/pmc.c
Miroslav Lichvar e3c74a
index 65d1d61..3832f0d 100644
Miroslav Lichvar e3c74a
--- a/pmc.c
Miroslav Lichvar e3c74a
+++ b/pmc.c
Miroslav Lichvar e3c74a
@@ -144,6 +144,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
Miroslav Lichvar e3c74a
 	struct subscribe_events_np *sen;
Miroslav Lichvar e3c74a
 	struct management_tlv_datum *mtd;
Miroslav Lichvar e3c74a
 	struct port_properties_np *ppn;
Miroslav Lichvar e3c74a
+	struct port_hwclock_np *phn;
Miroslav Lichvar e3c74a
 	struct timePropertiesDS *tp;
Miroslav Lichvar e3c74a
 	struct management_tlv *mgt;
Miroslav Lichvar e3c74a
 	struct time_status_np *tsn;
Miroslav Lichvar e3c74a
@@ -487,6 +488,16 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
Miroslav Lichvar e3c74a
 			pcp->stats.txMsgType[SIGNALING],
Miroslav Lichvar e3c74a
 			pcp->stats.txMsgType[MANAGEMENT]);
Miroslav Lichvar e3c74a
 		break;
Miroslav Lichvar e3c74a
+	case TLV_PORT_HWCLOCK_NP:
Miroslav Lichvar e3c74a
+		phn = (struct port_hwclock_np *) mgt->data;
Miroslav Lichvar e3c74a
+		fprintf(fp, "PORT_HWCLOCK_NP "
Miroslav Lichvar e3c74a
+			IFMT "portIdentity            %s"
Miroslav Lichvar e3c74a
+			IFMT "phcIndex                %d"
Miroslav Lichvar e3c74a
+			IFMT "flags                   %hhu",
Miroslav Lichvar e3c74a
+			pid2str(&phn->portIdentity),
Miroslav Lichvar e3c74a
+			phn->phc_index,
Miroslav Lichvar e3c74a
+			phn->flags);
Miroslav Lichvar e3c74a
+		break;
Miroslav Lichvar e3c74a
 	case TLV_LOG_ANNOUNCE_INTERVAL:
Miroslav Lichvar e3c74a
 		mtd = (struct management_tlv_datum *) mgt->data;
Miroslav Lichvar e3c74a
 		fprintf(fp, "LOG_ANNOUNCE_INTERVAL "
Miroslav Lichvar e3c74a
diff --git a/pmc_common.c b/pmc_common.c
Miroslav Lichvar e3c74a
index f07f6f6..756edf5 100644
Miroslav Lichvar e3c74a
--- a/pmc_common.c
Miroslav Lichvar e3c74a
+++ b/pmc_common.c
Miroslav Lichvar e3c74a
@@ -132,6 +132,7 @@ struct management_id idtab[] = {
Miroslav Lichvar e3c74a
 	{ "PORT_DATA_SET_NP", TLV_PORT_DATA_SET_NP, do_set_action },
Miroslav Lichvar e3c74a
 	{ "PORT_STATS_NP", TLV_PORT_STATS_NP, do_get_action },
Miroslav Lichvar e3c74a
 	{ "PORT_PROPERTIES_NP", TLV_PORT_PROPERTIES_NP, do_get_action },
Miroslav Lichvar e3c74a
+	{ "PORT_HWCLOCK_NP", TLV_PORT_HWCLOCK_NP, do_get_action },
Miroslav Lichvar e3c74a
 };
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 static void do_get_action(struct pmc *pmc, int action, int index, char *str)
Miroslav Lichvar e3c74a
diff --git a/port.c b/port.c
Miroslav Lichvar e3c74a
index b4dcd1b..e309b98 100644
Miroslav Lichvar e3c74a
--- a/port.c
Miroslav Lichvar e3c74a
+++ b/port.c
Miroslav Lichvar e3c74a
@@ -797,6 +797,7 @@ static int port_management_fill_response(struct port *target,
Miroslav Lichvar e3c74a
 	struct management_tlv_datum *mtd;
Miroslav Lichvar e3c74a
 	struct clock_description *desc;
Miroslav Lichvar e3c74a
 	struct port_properties_np *ppn;
Miroslav Lichvar e3c74a
+	struct port_hwclock_np *phn;
Miroslav Lichvar e3c74a
 	struct port_stats_np *psn;
Miroslav Lichvar e3c74a
 	struct management_tlv *tlv;
Miroslav Lichvar e3c74a
 	struct port_ds_np *pdsnp;
Miroslav Lichvar e3c74a
@@ -961,6 +962,14 @@ static int port_management_fill_response(struct port *target,
Miroslav Lichvar e3c74a
 		psn->stats = target->stats;
Miroslav Lichvar e3c74a
 		datalen = sizeof(*psn);
Miroslav Lichvar e3c74a
 		break;
Miroslav Lichvar e3c74a
+	case TLV_PORT_HWCLOCK_NP:
Miroslav Lichvar e3c74a
+		phn = (struct port_hwclock_np *)tlv->data;
Miroslav Lichvar e3c74a
+		phn->portIdentity = target->portIdentity;
Miroslav Lichvar e3c74a
+		phn->phc_index = target->phc_index;
Miroslav Lichvar e3c74a
+		phn->flags = interface_get_vclock(target->iface) >= 0 ?
Miroslav Lichvar e3c74a
+			PORT_HWCLOCK_VCLOCK : 0;
Miroslav Lichvar e3c74a
+		datalen = sizeof(*phn);
Miroslav Lichvar e3c74a
+		break;
Miroslav Lichvar e3c74a
 	default:
Miroslav Lichvar e3c74a
 		/* The caller should *not* respond to this message. */
Miroslav Lichvar e3c74a
 		tlv_extra_recycle(extra);
Miroslav Lichvar e3c74a
diff --git a/tlv.c b/tlv.c
Miroslav Lichvar e3c74a
index 738e404..38aeb80 100644
Miroslav Lichvar e3c74a
--- a/tlv.c
Miroslav Lichvar e3c74a
+++ b/tlv.c
Miroslav Lichvar e3c74a
@@ -123,6 +123,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
Miroslav Lichvar e3c74a
 	struct grandmaster_settings_np *gsn;
Miroslav Lichvar e3c74a
 	struct subscribe_events_np *sen;
Miroslav Lichvar e3c74a
 	struct port_properties_np *ppn;
Miroslav Lichvar e3c74a
+	struct port_hwclock_np *phn;
Miroslav Lichvar e3c74a
 	struct port_stats_np *psn;
Miroslav Lichvar e3c74a
 	struct mgmt_clock_description *cd;
Miroslav Lichvar e3c74a
 	int extra_len = 0, len;
Miroslav Lichvar e3c74a
@@ -326,6 +327,14 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
Miroslav Lichvar e3c74a
 			ntohs(psn->portIdentity.portNumber);
Miroslav Lichvar e3c74a
 		extra_len = sizeof(struct port_stats_np);
Miroslav Lichvar e3c74a
 		break;
Miroslav Lichvar e3c74a
+	case TLV_PORT_HWCLOCK_NP:
Miroslav Lichvar e3c74a
+		if (data_len < sizeof(struct port_hwclock_np))
Miroslav Lichvar e3c74a
+			goto bad_length;
Miroslav Lichvar e3c74a
+		phn = (struct port_hwclock_np *)m->data;
Miroslav Lichvar e3c74a
+		phn->portIdentity.portNumber = ntohs(phn->portIdentity.portNumber);
Miroslav Lichvar e3c74a
+		phn->phc_index = ntohl(phn->phc_index);
Miroslav Lichvar e3c74a
+		extra_len = sizeof(struct port_hwclock_np);
Miroslav Lichvar e3c74a
+		break;
Miroslav Lichvar e3c74a
 	case TLV_SAVE_IN_NON_VOLATILE_STORAGE:
Miroslav Lichvar e3c74a
 	case TLV_RESET_NON_VOLATILE_STORAGE:
Miroslav Lichvar e3c74a
 	case TLV_INITIALIZE:
Miroslav Lichvar e3c74a
@@ -352,6 +361,7 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
Miroslav Lichvar e3c74a
 	struct defaultDS *dds;
Miroslav Lichvar e3c74a
 	struct currentDS *cds;
Miroslav Lichvar e3c74a
 	struct parentDS *pds;
Miroslav Lichvar e3c74a
+	struct port_hwclock_np *phn;
Miroslav Lichvar e3c74a
 	struct timePropertiesDS *tp;
Miroslav Lichvar e3c74a
 	struct portDS *p;
Miroslav Lichvar e3c74a
 	struct port_ds_np *pdsnp;
Miroslav Lichvar e3c74a
@@ -437,6 +447,11 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
Miroslav Lichvar e3c74a
 		psn->portIdentity.portNumber =
Miroslav Lichvar e3c74a
 			htons(psn->portIdentity.portNumber);
Miroslav Lichvar e3c74a
 		break;
Miroslav Lichvar e3c74a
+	case TLV_PORT_HWCLOCK_NP:
Miroslav Lichvar e3c74a
+		phn = (struct port_hwclock_np *)m->data;
Miroslav Lichvar e3c74a
+		phn->portIdentity.portNumber = htons(phn->portIdentity.portNumber);
Miroslav Lichvar e3c74a
+		phn->phc_index = htonl(phn->phc_index);
Miroslav Lichvar e3c74a
+		break;
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
diff --git a/tlv.h b/tlv.h
Miroslav Lichvar e3c74a
index a205119..5ac3d7c 100644
Miroslav Lichvar e3c74a
--- a/tlv.h
Miroslav Lichvar e3c74a
+++ b/tlv.h
Miroslav Lichvar e3c74a
@@ -125,6 +125,7 @@ enum management_action {
Miroslav Lichvar e3c74a
 #define TLV_PORT_DATA_SET_NP				0xC002
Miroslav Lichvar e3c74a
 #define TLV_PORT_PROPERTIES_NP				0xC004
Miroslav Lichvar e3c74a
 #define TLV_PORT_STATS_NP				0xC005
Miroslav Lichvar e3c74a
+#define TLV_PORT_HWCLOCK_NP				0xC009
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 /* Management error ID values */
Miroslav Lichvar e3c74a
 #define TLV_RESPONSE_TOO_BIG				0x0001
Miroslav Lichvar e3c74a
@@ -144,6 +145,9 @@ enum management_action {
Miroslav Lichvar e3c74a
 #define CANCEL_UNICAST_MAINTAIN_GRANT	(1 << 1)
Miroslav Lichvar e3c74a
 #define GRANT_UNICAST_RENEWAL_INVITED	(1 << 0)
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+/* Flags in PORT_HWCLOCK_NP */
Miroslav Lichvar e3c74a
+#define PORT_HWCLOCK_VCLOCK		(1 << 0)
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 struct ack_cancel_unicast_xmit_tlv {
Miroslav Lichvar e3c74a
 	Enumeration16   type;
Miroslav Lichvar e3c74a
 	UInteger16      length;
Miroslav Lichvar e3c74a
@@ -344,6 +348,12 @@ struct port_properties_np {
Miroslav Lichvar e3c74a
 	struct PTPText interface;
Miroslav Lichvar e3c74a
 } PACKED;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+struct port_hwclock_np {
Miroslav Lichvar e3c74a
+	struct PortIdentity portIdentity;
Miroslav Lichvar e3c74a
+	Integer32 phc_index;
Miroslav Lichvar e3c74a
+	UInteger8 flags;
Miroslav Lichvar e3c74a
+} PACKED;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 struct port_stats_np {
Miroslav Lichvar e3c74a
 	struct PortIdentity portIdentity;
Miroslav Lichvar e3c74a
 	struct PortStats stats;
Miroslav Lichvar e3c74a
commit a64a45a0eedec82376fd9dab4d960b6fa652513e
Miroslav Lichvar e3c74a
Author: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Date:   Tue Mar 8 11:47:04 2022 +0100
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
    phc2sys: Use PHC index from PORT_HWCLOCK_NP.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    When running in the automatic mode, get the PHC index of the port
Miroslav Lichvar e3c74a
    from PORT_HWCLOCK_NP instead of calling sk_get_ts_info(). This allows
Miroslav Lichvar e3c74a
    phc2sys -a to synchronize (to) a virtual clock.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    (Rebased to 3.1.1)
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
diff --git a/phc2sys.c b/phc2sys.c
Miroslav Lichvar e3c74a
index a36cbe0..adbe37d 100644
Miroslav Lichvar e3c74a
--- a/phc2sys.c
Miroslav Lichvar e3c74a
+++ b/phc2sys.c
Miroslav Lichvar e3c74a
@@ -135,7 +135,8 @@ static void run_pmc_events(struct phc2sys_private *priv);
Miroslav Lichvar e3c74a
 static int normalize_state(int state);
Miroslav Lichvar e3c74a
 static int run_pmc_port_properties(struct phc2sys_private *priv,
Miroslav Lichvar e3c74a
 				   int timeout, unsigned int port,
Miroslav Lichvar e3c74a
-				   int *state, int *tstamping, char *iface);
Miroslav Lichvar e3c74a
+				   int *state, int *tstamping, int *phc_index,
Miroslav Lichvar e3c74a
+				   char *iface);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 static struct servo *servo_add(struct phc2sys_private *priv,
Miroslav Lichvar e3c74a
 			       struct clock *clock)
Miroslav Lichvar e3c74a
@@ -172,14 +173,21 @@ static struct servo *servo_add(struct phc2sys_private *priv,
Miroslav Lichvar e3c74a
 	return servo;
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
-static struct clock *clock_add(struct phc2sys_private *priv, char *device)
Miroslav Lichvar e3c74a
+static struct clock *clock_add(struct phc2sys_private *priv, char *device,
Miroslav Lichvar e3c74a
+			       int phc_index)
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	struct clock *c;
Miroslav Lichvar e3c74a
 	clockid_t clkid = CLOCK_INVALID;
Miroslav Lichvar e3c74a
-	int phc_index = -1;
Miroslav Lichvar e3c74a
+	char phc_device[19];
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	if (device) {
Miroslav Lichvar e3c74a
-		clkid = posix_clock_open(device, &phc_index);
Miroslav Lichvar e3c74a
+		if (phc_index >= 0) {
Miroslav Lichvar e3c74a
+			snprintf(phc_device, sizeof(phc_device), "/dev/ptp%d",
Miroslav Lichvar e3c74a
+				 phc_index);
Miroslav Lichvar e3c74a
+			clkid = posix_clock_open(phc_device, &phc_index);
Miroslav Lichvar e3c74a
+		} else {
Miroslav Lichvar e3c74a
+			clkid = posix_clock_open(device, &phc_index);
Miroslav Lichvar e3c74a
+		}
Miroslav Lichvar e3c74a
 		if (clkid == CLOCK_INVALID)
Miroslav Lichvar e3c74a
 			return NULL;
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
@@ -279,7 +287,7 @@ static struct port *port_get(struct phc2sys_private *priv, unsigned int number)
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 static struct port *port_add(struct phc2sys_private *priv, unsigned int number,
Miroslav Lichvar e3c74a
-			     char *device)
Miroslav Lichvar e3c74a
+			     char *device, int phc_index)
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	struct port *p;
Miroslav Lichvar e3c74a
 	struct clock *c = NULL, *tmp;
Miroslav Lichvar e3c74a
@@ -296,7 +304,7 @@ static struct port *port_add(struct phc2sys_private *priv, unsigned int number,
Miroslav Lichvar e3c74a
 		}
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
 	if (!c) {
Miroslav Lichvar e3c74a
-		c = clock_add(priv, device);
Miroslav Lichvar e3c74a
+		c = clock_add(priv, device, phc_index);
Miroslav Lichvar e3c74a
 		if (!c)
Miroslav Lichvar e3c74a
 			return NULL;
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
@@ -316,17 +324,16 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock,
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	int phc_index = -1, phc_switched = 0;
Miroslav Lichvar e3c74a
 	int state, timestamping, ret = -1;
Miroslav Lichvar e3c74a
+	char iface[IFNAMSIZ], phc_device[19];
Miroslav Lichvar e3c74a
 	struct port *p;
Miroslav Lichvar e3c74a
 	struct servo *servo;
Miroslav Lichvar e3c74a
-	struct sk_ts_info ts_info;
Miroslav Lichvar e3c74a
-	char iface[IFNAMSIZ];
Miroslav Lichvar e3c74a
 	clockid_t clkid = CLOCK_INVALID;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	LIST_FOREACH(p, &priv->ports, list) {
Miroslav Lichvar e3c74a
 		if (p->clock == clock) {
Miroslav Lichvar e3c74a
 			ret = run_pmc_port_properties(priv, 1000, p->number,
Miroslav Lichvar e3c74a
 					              &state, &timestamping,
Miroslav Lichvar e3c74a
-						      iface);
Miroslav Lichvar e3c74a
+						      &phc_index, iface);
Miroslav Lichvar e3c74a
 			if (ret > 0)
Miroslav Lichvar e3c74a
 				p->state = normalize_state(state);
Miroslav Lichvar e3c74a
 		}
Miroslav Lichvar e3c74a
@@ -339,9 +346,10 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock,
Miroslav Lichvar e3c74a
 			clock->device = strdup(iface);
Miroslav Lichvar e3c74a
 		}
Miroslav Lichvar e3c74a
 		/* Check if phc index changed */
Miroslav Lichvar e3c74a
-		if (!sk_get_ts_info(clock->device, &ts_info) &&
Miroslav Lichvar e3c74a
-		    clock->phc_index != ts_info.phc_index) {
Miroslav Lichvar e3c74a
-			clkid = posix_clock_open(clock->device, &phc_index);
Miroslav Lichvar e3c74a
+		if (clock->phc_index != phc_index) {
Miroslav Lichvar e3c74a
+			snprintf(phc_device, sizeof(phc_device), "/dev/ptp%d",
Miroslav Lichvar e3c74a
+				 phc_index);
Miroslav Lichvar e3c74a
+			clkid = posix_clock_open(phc_device, &phc_index);
Miroslav Lichvar e3c74a
 			if (clkid == CLOCK_INVALID)
Miroslav Lichvar e3c74a
 				return;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
@@ -1099,11 +1107,13 @@ static void run_pmc_events(struct phc2sys_private *priv)
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 static int run_pmc_port_properties(struct phc2sys_private *priv, int timeout,
Miroslav Lichvar e3c74a
 				   unsigned int port,
Miroslav Lichvar e3c74a
-				   int *state, int *tstamping, char *iface)
Miroslav Lichvar e3c74a
+				   int *state, int *tstamping, int *phc_index,
Miroslav Lichvar e3c74a
+				   char *iface)
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	struct ptp_message *msg;
Miroslav Lichvar e3c74a
 	int res, len;
Miroslav Lichvar e3c74a
 	struct port_properties_np *ppn;
Miroslav Lichvar e3c74a
+	struct port_hwclock_np *phn;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	pmc_target_port(priv->pmc, port);
Miroslav Lichvar e3c74a
 	while (1) {
Miroslav Lichvar e3c74a
@@ -1125,6 +1135,21 @@ static int run_pmc_port_properties(struct phc2sys_private *priv, int timeout,
Miroslav Lichvar e3c74a
 		memcpy(iface, ppn->interface.text, len);
Miroslav Lichvar e3c74a
 		iface[len] = '\0';
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+		msg_put(msg);
Miroslav Lichvar e3c74a
+		break;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+	while (1) {
Miroslav Lichvar e3c74a
+		res = run_pmc(priv, timeout, TLV_PORT_HWCLOCK_NP, &msg;;
Miroslav Lichvar e3c74a
+		if (res <= 0)
Miroslav Lichvar e3c74a
+			goto out;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+		phn = get_mgt_data(msg);
Miroslav Lichvar e3c74a
+		if (ppn->portIdentity.portNumber != port) {
Miroslav Lichvar e3c74a
+			msg_put(msg);
Miroslav Lichvar e3c74a
+			continue;
Miroslav Lichvar e3c74a
+		}
Miroslav Lichvar e3c74a
+		*phc_index = phn->phc_index;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 		msg_put(msg);
Miroslav Lichvar e3c74a
 		res = 1;
Miroslav Lichvar e3c74a
 		break;
Miroslav Lichvar e3c74a
@@ -1164,7 +1189,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt)
Miroslav Lichvar e3c74a
 	struct clock *clock;
Miroslav Lichvar e3c74a
 	int number_ports, res;
Miroslav Lichvar e3c74a
 	unsigned int i;
Miroslav Lichvar e3c74a
-	int state, timestamping;
Miroslav Lichvar e3c74a
+	int state, timestamping, phc_index;
Miroslav Lichvar e3c74a
 	char iface[IFNAMSIZ];
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	while (1) {
Miroslav Lichvar e3c74a
@@ -1193,7 +1218,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt)
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	for (i = 1; i <= number_ports; i++) {
Miroslav Lichvar e3c74a
 		res = run_pmc_port_properties(priv, 1000, i, &state,
Miroslav Lichvar e3c74a
-					      &timestamping, iface);
Miroslav Lichvar e3c74a
+					      &timestamping, &phc_index, iface);
Miroslav Lichvar e3c74a
 		if (res == -1) {
Miroslav Lichvar e3c74a
 			/* port does not exist, ignore the port */
Miroslav Lichvar e3c74a
 			continue;
Miroslav Lichvar e3c74a
@@ -1206,7 +1231,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt)
Miroslav Lichvar e3c74a
 			/* ignore ports with software time stamping */
Miroslav Lichvar e3c74a
 			continue;
Miroslav Lichvar e3c74a
 		}
Miroslav Lichvar e3c74a
-		port = port_add(priv, i, iface);
Miroslav Lichvar e3c74a
+		port = port_add(priv, i, iface, phc_index);
Miroslav Lichvar e3c74a
 		if (!port)
Miroslav Lichvar e3c74a
 			return -1;
Miroslav Lichvar e3c74a
 		port->state = normalize_state(state);
Miroslav Lichvar e3c74a
@@ -1221,7 +1246,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt)
Miroslav Lichvar e3c74a
 	priv->state_changed = 1;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	if (add_rt) {
Miroslav Lichvar e3c74a
-		clock = clock_add(priv, "CLOCK_REALTIME");
Miroslav Lichvar e3c74a
+		clock = clock_add(priv, "CLOCK_REALTIME", -1);
Miroslav Lichvar e3c74a
 		if (!clock)
Miroslav Lichvar e3c74a
 			return -1;
Miroslav Lichvar e3c74a
 		if (add_rt == 1)
Miroslav Lichvar e3c74a
@@ -1598,7 +1623,7 @@ int main(int argc, char *argv[])
Miroslav Lichvar e3c74a
 		goto end;
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
-	src = clock_add(&priv, src_name);
Miroslav Lichvar e3c74a
+	src = clock_add(&priv, src_name, -1);
Miroslav Lichvar e3c74a
 	free(src_name);
Miroslav Lichvar e3c74a
 	if (!src) {
Miroslav Lichvar e3c74a
 		fprintf(stderr,
Miroslav Lichvar e3c74a
@@ -1608,7 +1633,7 @@ int main(int argc, char *argv[])
Miroslav Lichvar e3c74a
 	src->state = PS_SLAVE;
Miroslav Lichvar e3c74a
 	priv.master = src;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
-	dst = clock_add(&priv, dst_name ? dst_name : "CLOCK_REALTIME");
Miroslav Lichvar e3c74a
+	dst = clock_add(&priv, dst_name ? dst_name : "CLOCK_REALTIME", -1);
Miroslav Lichvar e3c74a
 	free(dst_name);
Miroslav Lichvar e3c74a
 	if (!dst) {
Miroslav Lichvar e3c74a
 		fprintf(stderr,
Miroslav Lichvar e3c74a
commit 3238beafd5aca017a29f335e94b1ff05f4596fe3
Miroslav Lichvar e3c74a
Author: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Date:   Tue Mar 8 11:47:05 2022 +0100
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
    timemaster: Add support for virtual clocks.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Add "use_vclocks" option to enable synchronization with virtual clocks.
Miroslav Lichvar e3c74a
    This enables multiple ptp4l instances sharing an interface to use
Miroslav Lichvar e3c74a
    hardware timestamping. By default, vclocks are enabled if running on
Miroslav Lichvar e3c74a
    Linux 5.18 or later, which should have all features to make them work as
Miroslav Lichvar e3c74a
    well as physical clocks.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    When preparing the script, count how many vclocks are needed for each
Miroslav Lichvar e3c74a
    physical clock. Add a placeholder option in the form of "--phc_index
Miroslav Lichvar e3c74a
    %PHC0-0%" to the generated ptp4l commands that need hardware clock. The
Miroslav Lichvar e3c74a
    index of the virtual clock is unknown at this point.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    When running the script (not just printing), create the required number
Miroslav Lichvar e3c74a
    of virtual clocks by writing to /sys/.../n_vclocks and fix the
Miroslav Lichvar e3c74a
    --phc_index options to refer to the indices of the created vclocks. On
Miroslav Lichvar e3c74a
    exit, remove the virtual clocks to restore the original state.
Miroslav Lichvar e3c74a
    
Miroslav Lichvar e3c74a
    Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
diff --git a/timemaster.8 b/timemaster.8
Miroslav Lichvar e3c74a
index 2f92976..102768c 100644
Miroslav Lichvar e3c74a
--- a/timemaster.8
Miroslav Lichvar e3c74a
+++ b/timemaster.8
Miroslav Lichvar e3c74a
@@ -97,6 +97,15 @@ configuration error). If a process was terminated and is not started again,
Miroslav Lichvar e3c74a
 \fBtimemaster\fR will kill the other processes and exit with a non-zero status.
Miroslav Lichvar e3c74a
 The default value is 1 (enabled).
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+.TP
Miroslav Lichvar e3c74a
+.B use_vclocks
Miroslav Lichvar e3c74a
+Enable or disable synchronization with virtual clocks. If enabled,
Miroslav Lichvar e3c74a
+\fBtimemaster\fR will create virtual clocks running on top of physical clocks
Miroslav Lichvar e3c74a
+needed by configured PTP domains. This enables hardware time stamping for
Miroslav Lichvar e3c74a
+multiple \fBptp4l\fR instances using the same network interface. The default
Miroslav Lichvar e3c74a
+value is -1, which enables the virtual clocks if running on Linux 5.18 or
Miroslav Lichvar e3c74a
+later.
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 .SS [ntp_server address]
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 The \fBntp_server\fR section specifies an NTP server that should be used as a
Miroslav Lichvar e3c74a
@@ -140,8 +149,8 @@ Specify which network interfaces should be used for this PTP domain. A separate
Miroslav Lichvar e3c74a
 \fBptp4l\fR instance will be started for each group of interfaces sharing the
Miroslav Lichvar e3c74a
 same PHC and for each interface that supports only SW time stamping. HW time
Miroslav Lichvar e3c74a
 stamping is enabled automatically. If an interface with HW time stamping is
Miroslav Lichvar e3c74a
-specified also in other PTP domains, only the \fBptp4l\fR instance from the
Miroslav Lichvar e3c74a
-first PTP domain will be using HW time stamping.
Miroslav Lichvar e3c74a
+specified also in other PTP domains and virtual clocks are disabled, only the
Miroslav Lichvar e3c74a
+\fBptp4l\fR instance from the first PTP domain will be using HW time stamping.
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 .TP
Miroslav Lichvar e3c74a
 .B ntp_poll
Miroslav Lichvar e3c74a
@@ -333,6 +342,7 @@ ntp_program chronyd
Miroslav Lichvar e3c74a
 rundir /var/run/timemaster
Miroslav Lichvar e3c74a
 first_shm_segment 1
Miroslav Lichvar e3c74a
 restart_processes 0
Miroslav Lichvar e3c74a
+use_vclocks 0
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 [chronyd]
Miroslav Lichvar e3c74a
 path /usr/sbin/chronyd
Miroslav Lichvar e3c74a
diff --git a/timemaster.c b/timemaster.c
Miroslav Lichvar e3c74a
index 02408d6..1fbadcb 100644
Miroslav Lichvar e3c74a
--- a/timemaster.c
Miroslav Lichvar e3c74a
+++ b/timemaster.c
Miroslav Lichvar e3c74a
@@ -20,6 +20,7 @@
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 #include <ctype.h>
Miroslav Lichvar e3c74a
 #include <errno.h>
Miroslav Lichvar e3c74a
+#include <glob.h>
Miroslav Lichvar e3c74a
 #include <libgen.h>
Miroslav Lichvar e3c74a
 #include <limits.h>
Miroslav Lichvar e3c74a
 #include <time.h>
Miroslav Lichvar e3c74a
@@ -33,6 +34,7 @@
Miroslav Lichvar e3c74a
 #include <string.h>
Miroslav Lichvar e3c74a
 #include <sys/stat.h>
Miroslav Lichvar e3c74a
 #include <sys/types.h>
Miroslav Lichvar e3c74a
+#include <sys/utsname.h>
Miroslav Lichvar e3c74a
 #include <sys/wait.h>
Miroslav Lichvar e3c74a
 #include <unistd.h>
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
@@ -46,6 +48,7 @@
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 #define DEFAULT_FIRST_SHM_SEGMENT 0
Miroslav Lichvar e3c74a
 #define DEFAULT_RESTART_PROCESSES 1
Miroslav Lichvar e3c74a
+#define DEFAULT_USE_VCLOCKS -1
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 #define DEFAULT_NTP_PROGRAM CHRONYD
Miroslav Lichvar e3c74a
 #define DEFAULT_NTP_MINPOLL 6
Miroslav Lichvar e3c74a
@@ -111,6 +114,7 @@ struct timemaster_config {
Miroslav Lichvar e3c74a
 	char *rundir;
Miroslav Lichvar e3c74a
 	int first_shm_segment;
Miroslav Lichvar e3c74a
 	int restart_processes;
Miroslav Lichvar e3c74a
+	int use_vclocks;
Miroslav Lichvar e3c74a
 	struct program_config chronyd;
Miroslav Lichvar e3c74a
 	struct program_config ntpd;
Miroslav Lichvar e3c74a
 	struct program_config phc2sys;
Miroslav Lichvar e3c74a
@@ -122,7 +126,13 @@ struct config_file {
Miroslav Lichvar e3c74a
 	char *content;
Miroslav Lichvar e3c74a
 };
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+struct phc_vclocks {
Miroslav Lichvar e3c74a
+	int pclock_index;
Miroslav Lichvar e3c74a
+	int vclocks;
Miroslav Lichvar e3c74a
+};
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 struct script {
Miroslav Lichvar e3c74a
+	struct phc_vclocks **vclocks;
Miroslav Lichvar e3c74a
 	struct config_file **configs;
Miroslav Lichvar e3c74a
 	char ***commands;
Miroslav Lichvar e3c74a
 	int **command_groups;
Miroslav Lichvar e3c74a
@@ -393,6 +403,8 @@ static int parse_timemaster_settings(char **settings,
Miroslav Lichvar e3c74a
 			r = parse_int(value, &config->first_shm_segment);
Miroslav Lichvar e3c74a
 		} else if (!strcasecmp(name, "restart_processes")) {
Miroslav Lichvar e3c74a
 			r = parse_int(value, &config->restart_processes);
Miroslav Lichvar e3c74a
+		} else if (!strcasecmp(name, "use_vclocks")) {
Miroslav Lichvar e3c74a
+			r = parse_int(value, &config->use_vclocks);
Miroslav Lichvar e3c74a
 		} else {
Miroslav Lichvar e3c74a
 			pr_err("unknown timemaster setting %s", name);
Miroslav Lichvar e3c74a
 			return 1;
Miroslav Lichvar e3c74a
@@ -520,6 +532,20 @@ static void config_destroy(struct timemaster_config *config)
Miroslav Lichvar e3c74a
 	free(config);
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+static int check_kernel_version(int version, int patch)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	struct utsname uts;
Miroslav Lichvar e3c74a
+	int v, p;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	if (uname(&uts) < 0)
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+	if (sscanf(uts.release, "%d.%d", &v, &p) < 2)
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+	if (version > v || (version == v && patch > p))
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+	return 0;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 static struct timemaster_config *config_parse(char *path)
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	struct timemaster_config *config = xcalloc(1, sizeof(*config));
Miroslav Lichvar e3c74a
@@ -533,6 +559,7 @@ static struct timemaster_config *config_parse(char *path)
Miroslav Lichvar e3c74a
 	config->rundir = xstrdup(DEFAULT_RUNDIR);
Miroslav Lichvar e3c74a
 	config->first_shm_segment = DEFAULT_FIRST_SHM_SEGMENT;
Miroslav Lichvar e3c74a
 	config->restart_processes = DEFAULT_RESTART_PROCESSES;
Miroslav Lichvar e3c74a
+	config->use_vclocks = DEFAULT_USE_VCLOCKS;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	init_program_config(&config->chronyd, "chronyd",
Miroslav Lichvar e3c74a
 			    NULL, DEFAULT_CHRONYD_SETTINGS, NULL);
Miroslav Lichvar e3c74a
@@ -593,6 +620,9 @@ static struct timemaster_config *config_parse(char *path)
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	fclose(f);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+	if (config->use_vclocks < 0)
Miroslav Lichvar e3c74a
+		config->use_vclocks = !check_kernel_version(5, 18);
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 	if (section_name)
Miroslav Lichvar e3c74a
 		free(section_name);
Miroslav Lichvar e3c74a
 	if (section_lines)
Miroslav Lichvar e3c74a
@@ -608,7 +638,7 @@ static struct timemaster_config *config_parse(char *path)
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 static char **get_ptp4l_command(struct program_config *config,
Miroslav Lichvar e3c74a
 				struct config_file *file, char **interfaces,
Miroslav Lichvar e3c74a
-				int hw_ts)
Miroslav Lichvar e3c74a
+				char *phc_index, int hw_ts)
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	char **command = (char **)parray_new();
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
@@ -617,6 +647,9 @@ static char **get_ptp4l_command(struct program_config *config,
Miroslav Lichvar e3c74a
 	parray_extend((void ***)&command,
Miroslav Lichvar e3c74a
 		      xstrdup("-f"), xstrdup(file->path),
Miroslav Lichvar e3c74a
 		      xstrdup(hw_ts ? "-H" : "-S"), NULL);
Miroslav Lichvar e3c74a
+	if (phc_index && phc_index[0])
Miroslav Lichvar e3c74a
+		parray_extend((void ***)&command,
Miroslav Lichvar e3c74a
+			      xstrdup("--phc_index"), xstrdup(phc_index), NULL);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	for (; *interfaces; interfaces++)
Miroslav Lichvar e3c74a
 		parray_extend((void ***)&command,
Miroslav Lichvar e3c74a
@@ -706,6 +739,24 @@ static int add_ntp_source(struct ntp_server *source, char **ntp_config)
Miroslav Lichvar e3c74a
 	return 0;
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+static int add_vclock(struct script *script, int pclock_index)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	struct phc_vclocks **vclocks, *v;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	for (vclocks = script->vclocks; *vclocks; vclocks++) {
Miroslav Lichvar e3c74a
+		if ((*vclocks)->pclock_index != pclock_index)
Miroslav Lichvar e3c74a
+			continue;
Miroslav Lichvar e3c74a
+		return (*vclocks)->vclocks++;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	v = xmalloc(sizeof(*v));
Miroslav Lichvar e3c74a
+	v->pclock_index = pclock_index;
Miroslav Lichvar e3c74a
+	v->vclocks = 1;
Miroslav Lichvar e3c74a
+	parray_append((void ***)&script->vclocks, v);
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	return 0;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 static int add_ptp_source(struct ptp_domain *source,
Miroslav Lichvar e3c74a
 			  struct timemaster_config *config, int *shm_segment,
Miroslav Lichvar e3c74a
 			  int *command_group, int ***allocated_phcs,
Miroslav Lichvar e3c74a
@@ -713,7 +764,7 @@ static int add_ptp_source(struct ptp_domain *source,
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	struct config_file *config_file;
Miroslav Lichvar e3c74a
 	char **command, *uds_path, *uds_path2, **interfaces, *message_tag;
Miroslav Lichvar e3c74a
-	char ts_interface[IF_NAMESIZE];
Miroslav Lichvar e3c74a
+	char ts_interface[IF_NAMESIZE], vclock_index[20];
Miroslav Lichvar e3c74a
 	int i, j, num_interfaces, *phc, *phcs, hw_ts, sw_ts;
Miroslav Lichvar e3c74a
 	struct sk_ts_info ts_info;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
@@ -801,10 +852,18 @@ static int add_ptp_source(struct ptp_domain *source,
Miroslav Lichvar e3c74a
 				}
Miroslav Lichvar e3c74a
 			}
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
-			/* don't use this PHC in other sources */
Miroslav Lichvar e3c74a
-			phc = xmalloc(sizeof(int));
Miroslav Lichvar e3c74a
-			*phc = phcs[i];
Miroslav Lichvar e3c74a
-			parray_append((void ***)allocated_phcs, phc);
Miroslav Lichvar e3c74a
+			if (config->use_vclocks) {
Miroslav Lichvar e3c74a
+				/* request new vclock for the PHC */
Miroslav Lichvar e3c74a
+				int vclock = add_vclock(script, phcs[i]);
Miroslav Lichvar e3c74a
+				snprintf(vclock_index, sizeof(vclock_index),
Miroslav Lichvar e3c74a
+					 "%%PHC%d-%d%%", phcs[i], vclock);
Miroslav Lichvar e3c74a
+			} else {
Miroslav Lichvar e3c74a
+				/* don't use this PHC in other sources */
Miroslav Lichvar e3c74a
+				phc = xmalloc(sizeof(int));
Miroslav Lichvar e3c74a
+				*phc = phcs[i];
Miroslav Lichvar e3c74a
+				parray_append((void ***)allocated_phcs, phc);
Miroslav Lichvar e3c74a
+				vclock_index[0] = '\0';
Miroslav Lichvar e3c74a
+			}
Miroslav Lichvar e3c74a
 		}
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 		uds_path = string_newf("%s/ptp4l.%d.socket",
Miroslav Lichvar e3c74a
@@ -842,7 +901,8 @@ static int add_ptp_source(struct ptp_domain *source,
Miroslav Lichvar e3c74a
 		if (phcs[i] >= 0) {
Miroslav Lichvar e3c74a
 			/* HW time stamping */
Miroslav Lichvar e3c74a
 			command = get_ptp4l_command(&config->ptp4l, config_file,
Miroslav Lichvar e3c74a
-						    interfaces, 1);
Miroslav Lichvar e3c74a
+						    interfaces,
Miroslav Lichvar e3c74a
+						    vclock_index, 1);
Miroslav Lichvar e3c74a
 			add_command(command, *command_group, script);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 			command = get_phc2sys_command(&config->phc2sys,
Miroslav Lichvar e3c74a
@@ -854,7 +914,7 @@ static int add_ptp_source(struct ptp_domain *source,
Miroslav Lichvar e3c74a
 		} else {
Miroslav Lichvar e3c74a
 			/* SW time stamping */
Miroslav Lichvar e3c74a
 			command = get_ptp4l_command(&config->ptp4l, config_file,
Miroslav Lichvar e3c74a
-						    interfaces, 0);
Miroslav Lichvar e3c74a
+						    interfaces, NULL, 0);
Miroslav Lichvar e3c74a
 			add_command(command, (*command_group)++, script);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 			string_appendf(&config_file->content,
Miroslav Lichvar e3c74a
@@ -943,6 +1003,11 @@ static void script_destroy(struct script *script)
Miroslav Lichvar e3c74a
 	char ***commands, **command;
Miroslav Lichvar e3c74a
 	int **groups;
Miroslav Lichvar e3c74a
 	struct config_file *config, **configs;
Miroslav Lichvar e3c74a
+	struct phc_vclocks **vclocks;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	for (vclocks = script->vclocks; *vclocks; vclocks++)
Miroslav Lichvar e3c74a
+		free(*vclocks);
Miroslav Lichvar e3c74a
+	free(script->vclocks);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	for (configs = script->configs; *configs; configs++) {
Miroslav Lichvar e3c74a
 		config = *configs;
Miroslav Lichvar e3c74a
@@ -974,6 +1039,7 @@ static struct script *script_create(struct timemaster_config *config)
Miroslav Lichvar e3c74a
 	int **allocated_phcs = (int **)parray_new();
Miroslav Lichvar e3c74a
 	int ret = 0, shm_segment, command_group = 0;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+	script->vclocks = (struct phc_vclocks **)parray_new();
Miroslav Lichvar e3c74a
 	script->configs = (struct config_file **)parray_new();
Miroslav Lichvar e3c74a
 	script->commands = (char ***)parray_new();
Miroslav Lichvar e3c74a
 	script->command_groups = (int **)parray_new();
Miroslav Lichvar e3c74a
@@ -1116,6 +1182,102 @@ static int remove_config_files(struct config_file **configs)
Miroslav Lichvar e3c74a
 	return 0;
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+static int set_phc_n_vclocks(int phc_index, int n_vclocks)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	char path[PATH_MAX];
Miroslav Lichvar e3c74a
+	FILE *f;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	snprintf(path, sizeof(path), "/sys/class/ptp/ptp%d/n_vclocks",
Miroslav Lichvar e3c74a
+		 phc_index);
Miroslav Lichvar e3c74a
+	f = fopen(path, "w");
Miroslav Lichvar e3c74a
+	if (!f) {
Miroslav Lichvar e3c74a
+		pr_err("failed to open %s: %m", path);
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+	fprintf(f, "%d\n", n_vclocks);
Miroslav Lichvar e3c74a
+	fclose(f);
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	return 0;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+static int create_vclocks(struct phc_vclocks **phc_vclocks)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	struct phc_vclocks **vclocks;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	for (vclocks = phc_vclocks; *vclocks; vclocks++) {
Miroslav Lichvar e3c74a
+		if (set_phc_n_vclocks((*vclocks)->pclock_index,
Miroslav Lichvar e3c74a
+				      (*vclocks)->vclocks))
Miroslav Lichvar e3c74a
+			return 1;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	return 0;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+static int remove_vclocks(struct phc_vclocks **phc_vclocks)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	struct phc_vclocks **vclocks;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	for (vclocks = phc_vclocks; *vclocks; vclocks++) {
Miroslav Lichvar e3c74a
+		if (set_phc_n_vclocks((*vclocks)->pclock_index, 0))
Miroslav Lichvar e3c74a
+			return 1;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	return 0;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+static int get_vclock_index(int pindex, int vclock)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	char pattern[PATH_MAX], *s;
Miroslav Lichvar e3c74a
+	int n, vindex;
Miroslav Lichvar e3c74a
+	glob_t gl;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	snprintf(pattern, sizeof(pattern), "/sys/class/ptp/ptp%d/ptp[0-9]*",
Miroslav Lichvar e3c74a
+		 pindex);
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	if (glob(pattern, 0, NULL, &gl)) {
Miroslav Lichvar e3c74a
+		pr_err("glob(%s) failed", pattern);
Miroslav Lichvar e3c74a
+		return -1;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	if (vclock >= gl.gl_pathc ||
Miroslav Lichvar e3c74a
+	    !(s = strrchr(gl.gl_pathv[vclock], '/')) ||
Miroslav Lichvar e3c74a
+	    sscanf(s + 1, "ptp%d%n", &vindex, &n) != 1 ||
Miroslav Lichvar e3c74a
+	    n != strlen(s + 1)) {
Miroslav Lichvar e3c74a
+		pr_err("missing vclock %d:%d", pindex, vclock);
Miroslav Lichvar e3c74a
+		globfree(&gl);
Miroslav Lichvar e3c74a
+		return -1;
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	globfree(&gl);
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	return vindex;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+static int translate_vclock_options(char ***commands)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	int n, pindex, vclock, vindex, blen;
Miroslav Lichvar e3c74a
+	char **command;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	for (; *commands; commands++) {
Miroslav Lichvar e3c74a
+		for (command = *commands; *command; command++) {
Miroslav Lichvar e3c74a
+			if (sscanf(*command, "%%PHC%d-%d%%%n",
Miroslav Lichvar e3c74a
+				   &pindex, &vclock, &n) != 2 ||
Miroslav Lichvar e3c74a
+			    n != strlen(*command))
Miroslav Lichvar e3c74a
+				continue;
Miroslav Lichvar e3c74a
+			vindex = get_vclock_index(pindex, vclock);
Miroslav Lichvar e3c74a
+			if (vindex < 0)
Miroslav Lichvar e3c74a
+				return 1;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+			/* overwrite the string with the vclock PHC index */
Miroslav Lichvar e3c74a
+			blen = strlen(*command) + 1;
Miroslav Lichvar e3c74a
+			if (snprintf(*command, blen, "%d", vindex) >= blen)
Miroslav Lichvar e3c74a
+				return 1;
Miroslav Lichvar e3c74a
+		}
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	return 0;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 static int script_run(struct script *script)
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	struct timespec ts_start, ts_now;
Miroslav Lichvar e3c74a
@@ -1135,6 +1297,12 @@ static int script_run(struct script *script)
Miroslav Lichvar e3c74a
 	if (create_config_files(script->configs))
Miroslav Lichvar e3c74a
 		return 1;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+	if (create_vclocks(script->vclocks))
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	if (translate_vclock_options(script->commands))
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 	sigemptyset(&mask);
Miroslav Lichvar e3c74a
 	sigaddset(&mask, SIGCHLD);
Miroslav Lichvar e3c74a
 	sigaddset(&mask, SIGTERM);
Miroslav Lichvar e3c74a
@@ -1278,6 +1446,9 @@ static int script_run(struct script *script)
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	free(pids);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+	if (remove_vclocks(script->vclocks))
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 	if (remove_config_files(script->configs))
Miroslav Lichvar e3c74a
 		return 1;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
@@ -1289,13 +1460,20 @@ static void script_print(struct script *script)
Miroslav Lichvar e3c74a
 	char ***commands, **command;
Miroslav Lichvar e3c74a
 	int **groups;
Miroslav Lichvar e3c74a
 	struct config_file *config, **configs;
Miroslav Lichvar e3c74a
+	struct phc_vclocks **vclocks;
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	for (configs = script->configs; *configs; configs++) {
Miroslav Lichvar e3c74a
 		config = *configs;
Miroslav Lichvar e3c74a
 		fprintf(stderr, "%s:\n\n%s\n", config->path, config->content);
Miroslav Lichvar e3c74a
 	}
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
-	fprintf(stderr, "commands:\n\n");
Miroslav Lichvar e3c74a
+	fprintf(stderr, "virtual clocks:\n\n");
Miroslav Lichvar e3c74a
+	for (vclocks = script->vclocks; *vclocks; vclocks++) {
Miroslav Lichvar e3c74a
+		fprintf(stderr, "PHC%d: %d\n",
Miroslav Lichvar e3c74a
+			(*vclocks)->pclock_index, (*vclocks)->vclocks);
Miroslav Lichvar e3c74a
+	}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	fprintf(stderr, "\ncommands:\n\n");
Miroslav Lichvar e3c74a
 	for (commands = script->commands, groups = script->command_groups;
Miroslav Lichvar e3c74a
 	     *commands; commands++, groups++) {
Miroslav Lichvar e3c74a
 		fprintf(stderr, "[%d] ", **groups);
Miroslav Lichvar e3c74a
commit e09b9fda7435799afad45c96b56ac020e7f7b3d3
Miroslav Lichvar e3c74a
Author: Miroslav Lichvar <mlichvar@redhat.com>
Miroslav Lichvar e3c74a
Date:   Thu Apr 28 14:23:57 2022 +0200
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
    timemaster: Check for RH-specific kernel with vclock support.
Miroslav Lichvar e3c74a
Miroslav Lichvar e3c74a
diff --git a/timemaster.8 b/timemaster.8
Miroslav Lichvar e3c74a
index 102768c..edf5818 100644
Miroslav Lichvar e3c74a
--- a/timemaster.8
Miroslav Lichvar e3c74a
+++ b/timemaster.8
Miroslav Lichvar e3c74a
@@ -104,7 +104,7 @@ Enable or disable synchronization with virtual clocks. If enabled,
Miroslav Lichvar e3c74a
 needed by configured PTP domains. This enables hardware time stamping for
Miroslav Lichvar e3c74a
 multiple \fBptp4l\fR instances using the same network interface. The default
Miroslav Lichvar e3c74a
 value is -1, which enables the virtual clocks if running on Linux 5.18 or
Miroslav Lichvar e3c74a
-later.
Miroslav Lichvar e3c74a
+later, or the EL9-specific kernel-5.14.0-106 or later release.
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 .SS [ntp_server address]
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
diff --git a/timemaster.c b/timemaster.c
Miroslav Lichvar e3c74a
index 1fbadcb..287d77c 100644
Miroslav Lichvar e3c74a
--- a/timemaster.c
Miroslav Lichvar e3c74a
+++ b/timemaster.c
Miroslav Lichvar e3c74a
@@ -546,6 +546,23 @@ static int check_kernel_version(int version, int patch)
Miroslav Lichvar e3c74a
 	return 0;
Miroslav Lichvar e3c74a
 }
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
+static int check_rh_kernel_version(const char *el, int version, int patch,
Miroslav Lichvar e3c74a
+				   int sub, int release)
Miroslav Lichvar e3c74a
+{
Miroslav Lichvar e3c74a
+	struct utsname uts;
Miroslav Lichvar e3c74a
+	int v, p, sp, r;
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
+	if (uname(&uts) < 0)
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+	if (!strstr(uts.release, el))
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+	if (sscanf(uts.release, "%d.%d.%d-%d", &v, &p, &sp, &r) < 4)
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+	if (version != v || patch != p || sub != sp || release > r)
Miroslav Lichvar e3c74a
+		return 1;
Miroslav Lichvar e3c74a
+	return 0;
Miroslav Lichvar e3c74a
+}
Miroslav Lichvar e3c74a
+
Miroslav Lichvar e3c74a
 static struct timemaster_config *config_parse(char *path)
Miroslav Lichvar e3c74a
 {
Miroslav Lichvar e3c74a
 	struct timemaster_config *config = xcalloc(1, sizeof(*config));
Miroslav Lichvar e3c74a
@@ -621,7 +638,8 @@ static struct timemaster_config *config_parse(char *path)
Miroslav Lichvar e3c74a
 	fclose(f);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	if (config->use_vclocks < 0)
Miroslav Lichvar e3c74a
-		config->use_vclocks = !check_kernel_version(5, 18);
Miroslav Lichvar e3c74a
+		config->use_vclocks = !check_kernel_version(5, 18) ||
Miroslav Lichvar e3c74a
+			!check_rh_kernel_version(".el9.", 5, 14, 0, 106);
Miroslav Lichvar e3c74a
 
Miroslav Lichvar e3c74a
 	if (section_name)
Miroslav Lichvar e3c74a
 		free(section_name);