Thread: [Linuxptp-devel] [PATCH RFC 1/7] rtnl: Fix rtnl_rtattr_parse() to process max attribute.
PTP IEEE 1588 stack for Linux
Brought to you by:
rcochran
From: Miroslav L. <mli...@re...> - 2022-01-13 15:55:15
|
Initialize the whole array passed to rtnl_rtattr_parse() and don't ignore the last attribute with the maximum value. This will be needed to get the ETHTOOL_A_PHC_VCLOCKS_INDEX attribute. Signed-off-by: Miroslav Lichvar <mli...@re...> Cc: Hangbin Liu <liu...@gm...> --- rtnl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtnl.c b/rtnl.c index fe625d6..88a749f 100644 --- a/rtnl.c +++ b/rtnl.c @@ -178,10 +178,10 @@ static int rtnl_rtattr_parse(struct rtattr *tb[], int max, struct rtattr *rta, i { unsigned short type; - memset(tb, 0, sizeof(struct rtattr *) * max); + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); while (RTA_OK(rta, len)) { type = rta->rta_type; - if ((type < max) && (!tb[type])) + if ((type <= max) && (!tb[type])) tb[type] = rta; rta = RTA_NEXT(rta, len); } -- 2.33.1 |
From: Miroslav L. <mli...@re...> - 2022-02-03 11:50:27
|
rfc->v1: - fixed rtnl_linkinfo_parse() to make space for the _MAX attrs - added new TLV for PHC index and flags - added support to timemaster - updated defaults.cfg With recent kernels it is possible to create virtual PHCs running on top of a free-running PHC by writing to /sys/class/ptp/ptp?/n_vclocks. If supported in linuxptp, it would allow users to run multiple ptp4l instances with hardware timestamping on one interface, e.g. in different domains for better resiliency, or separate instances using different unicast servers. The kernel support was added in 5.14, but there were some bugs fixed recently. 5.17-rc1 or later is recommended for testing. The 1st patch is a bug fix required by the second patch. The 2nd patch adds detection of virtual clocks via ethtool netlink. The 3rd-5th patch add support to ptp4l. The PHC index of the virtual clock needs to be specified in the configuration. The 6th and 7th patch add support to phc2sys. A new PORT_HWCLOCK_NP TLV is added to get the PHC index of the virtual clock in the automatic mode. The 8th patch adds support to timemaster. If enabled (by default on Linux >=5.18), a virtual clock is created for each HW-timestamping ptp4l instance. Miroslav Lichvar (8): rtnl: Fix rtnl_rtattr_parse() to process max attribute. rtnl: Add function to detect virtual clocks. Add support for binding sockets to virtual clocks. config: Add port-specific phc_index option. port: Check for virtual clocks. tlv: Add PORT_HWCLOCK_NP. phc2sys: Use PHC index from PORT_HWCLOCK_NP. timemaster: Add support for virtual clocks. clock.c | 7 +- config.c | 1 + configs/default.cfg | 1 + incdefs.sh | 4 + interface.c | 12 ++ interface.h | 14 +++ missing.h | 11 ++ phc2sys.c | 42 ++++--- pmc.c | 11 ++ pmc_agent.c | 18 ++- pmc_agent.h | 4 +- pmc_common.c | 1 + port.c | 24 +++- ptp4l.8 | 8 ++ raw.c | 3 +- rtnl.c | 91 +++++++++++++- rtnl.h | 9 ++ sk.c | 11 +- sk.h | 3 +- timemaster.8 | 14 ++- timemaster.c | 196 ++++++++++++++++++++++++++++-- tlv.c | 15 +++ tlv.h | 10 ++ udp.c | 3 +- udp6.c | 3 +- 25 files changed, 471 insertions(+), 45 deletions(-) -- 2.34.1 |
From: Miroslav L. <mli...@re...> - 2022-01-13 15:55:15
|
With recent kernels it is possible to create virtual PHCs running on top of a free-running PHC by writing to /sys/class/ptp/ptp?/n_vclocks. If supported in linuxptp, it would allow users to run multiple ptp4l instance with hardware timestamping on one interface, e.g. in different domains for better resiliency, or separate instances for different unicast servers. The kernel support was added in 5.14, but there were some bugs fixed recently, so 5.17-rc1 or later is recommended for testing. The 1st patch is a bug fix required by the second patch. The 2nd patch adds detection of virtual clocks via ethtool netlink. The 3rd-5th patch add support to ptp4l. The PHC index of the virtual clock needs to be specified in the configuration. The 6th and 7th patch add support to phc2sys. The PORT_PROPERTIES_NP message is extended with the PHC index. The command is available only on the UDS port, but I'm not sure if an incompatible change like this is acceptable. The alternative would be to add a new command specifically for the index. My plan is to add support also to timemaster to detect the kernel support, automatically create virtual clocks for interfaces specified in multiple domains (where previously some instances had to use SW timestamping), and pass the correct PHC indexes to the ptp4l instances. Miroslav Lichvar (7): rtnl: Fix rtnl_rtattr_parse() to process max attribute. rtnl: Add function to detect virtual clocks. Add support for binding sockets to virtual clocks. config: Add port-specific phc_index option. port: Check for virtual clocks. tlv: Add PHC index to PORT_PROPERTIES_NP. phc2sys: Use PHC index from PORT_PROPERTIES_NP. clock.c | 6 +++- config.c | 1 + incdefs.sh | 4 +++ interface.c | 12 ++++++++ interface.h | 14 +++++++++ missing.h | 11 +++++++ phc2sys.c | 42 +++++++++++++++----------- pmc_agent.c | 3 +- pmc_agent.h | 4 ++- port.c | 16 +++++++--- ptp4l.8 | 8 +++++ raw.c | 3 +- rtnl.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++-- rtnl.h | 9 ++++++ sk.c | 11 +++++-- sk.h | 3 +- tlv.c | 2 ++ tlv.h | 1 + udp.c | 3 +- udp6.c | 3 +- 20 files changed, 211 insertions(+), 32 deletions(-) -- 2.33.1 |
From: Miroslav L. <mli...@re...> - 2022-01-13 15:55:15
|
If the PHC specified with the phc_index or -p option is a virtual clock of the interface, bind sockets to the virtual clock instead of the real clock to get correct timestamps. Signed-off-by: Miroslav Lichvar <mli...@re...> --- port.c | 7 ++++++- ptp4l.8 | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/port.c b/port.c index a84ba66..5a5741d 100644 --- a/port.c +++ b/port.c @@ -3133,7 +3133,12 @@ struct port *port_open(const char *phc_device, pr_warning("%s: get_ts_info not supported", p->log_name); } else if (p->phc_index >= 0 && p->phc_index != interface_phc_index(interface)) { - if (p->jbod) { + if (rtnl_iface_has_vclock(interface_name(interface), + p->phc_index)) { + pr_info("%s: /dev/ptp%d is virtual clock", + p->log_name, p->phc_index); + interface_set_vclock(interface, phc_index); + } else if (p->jbod) { pr_warning("%s: just a bunch of devices", p->log_name); p->phc_index = interface_phc_index(interface); } else if (phc_device) { diff --git a/ptp4l.8 b/ptp4l.8 index 1d6f078..e53fcc4 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -377,6 +377,9 @@ The default is 0 (disabled). .TP .B phc_index Specifies the index of the PHC to be used for synchronization and timestamping. +This option is useful with Linux kernels 5.17 and later, which allow multiple +virtual clocks to be created on top of a free-running hardware clock by writing +to /sys/class/ptp/ptp*/n_vclocks. The default is -1, which means the index will be set to the PHC associated with the interface, or the device specified by the \fB-p\fP option. .TP -- 2.33.1 |
From: Miroslav L. <mli...@re...> - 2022-01-13 15:55:17
|
When running in the automatic mode, use the PHC index from PORT_PROPERTIES_NP instead of calling sk_get_ts_info(). This allows phc2sys -a to synchronize (to) a virtual clock. Signed-off-by: Miroslav Lichvar <mli...@re...> --- phc2sys.c | 42 +++++++++++++++++++++++++----------------- pmc_agent.c | 3 ++- pmc_agent.h | 4 +++- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/phc2sys.c b/phc2sys.c index 6815c3d..8fc241d 100644 --- a/phc2sys.c +++ b/phc2sys.c @@ -153,14 +153,21 @@ static struct servo *servo_add(struct phc2sys_private *priv, return servo; } -static struct clock *clock_add(struct phc2sys_private *priv, const char *device) +static struct clock *clock_add(struct phc2sys_private *priv, const char *device, + int phc_index) { struct clock *c; clockid_t clkid = CLOCK_INVALID; - int phc_index = -1; + char phc_device[19]; if (device) { - clkid = posix_clock_open(device, &phc_index); + if (phc_index >= 0) { + snprintf(phc_device, sizeof(phc_device), "/dev/ptp%d", + phc_index); + clkid = posix_clock_open(phc_device, &phc_index); + } else { + clkid = posix_clock_open(device, &phc_index); + } if (clkid == CLOCK_INVALID) return NULL; } @@ -257,7 +264,7 @@ static struct port *port_get(struct phc2sys_private *priv, unsigned int number) } static struct port *port_add(struct phc2sys_private *priv, unsigned int number, - char *device) + char *device, int phc_index) { struct port *p; struct clock *c = NULL, *tmp; @@ -274,7 +281,7 @@ static struct port *port_add(struct phc2sys_private *priv, unsigned int number, } } if (!c) { - c = clock_add(priv, device); + c = clock_add(priv, device, phc_index); if (!c) return NULL; } @@ -293,9 +300,8 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, int new_state) { int err = -1, phc_index = -1, phc_switched = 0, state, timestamping; + char iface[IFNAMSIZ], phc_device[19]; struct port *p; - struct sk_ts_info ts_info; - char iface[IFNAMSIZ]; clockid_t clkid = CLOCK_INVALID; LIST_FOREACH(p, &priv->ports, list) { @@ -304,7 +310,8 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, } err = pmc_agent_query_port_properties(priv->agent, 1000, p->number, &state, - ×tamping, iface); + ×tamping, &phc_index, + iface); if (!err) { p->state = normalize_state(state); } @@ -318,9 +325,10 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, clock->device = strdup(iface); } /* Check if phc index changed */ - if (!sk_get_ts_info(clock->device, &ts_info) && - clock->phc_index != ts_info.phc_index) { - clkid = posix_clock_open(clock->device, &phc_index); + if (clock->phc_index != phc_index) { + snprintf(phc_device, sizeof(phc_device), "/dev/ptp%d", + phc_index); + clkid = posix_clock_open(phc_device, &phc_index); if (clkid == CLOCK_INVALID) return; @@ -844,7 +852,7 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, static int auto_init_ports(struct phc2sys_private *priv, int add_rt) { - int err, number_ports, state, timestamping; + int err, number_ports, phc_index, state, timestamping; char iface[IFNAMSIZ]; struct clock *clock; struct port *port; @@ -880,7 +888,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) for (i = 1; i <= number_ports; i++) { err = pmc_agent_query_port_properties(priv->agent, 1000, i, &state, ×tamping, - iface); + &phc_index, iface); if (err == -ENODEV) { /* port does not exist, ignore the port */ continue; @@ -893,7 +901,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) /* ignore ports with software time stamping */ continue; } - port = port_add(priv, i, iface); + port = port_add(priv, i, iface, phc_index); if (!port) return -1; port->state = normalize_state(state); @@ -908,7 +916,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) priv->state_changed = 1; if (add_rt) { - clock = clock_add(priv, "CLOCK_REALTIME"); + clock = clock_add(priv, "CLOCK_REALTIME", -1); if (!clock) return -1; if (add_rt == 1) @@ -991,7 +999,7 @@ static int phc2sys_static_configuration(struct phc2sys_private *priv, { struct clock *src, *dst; - src = clock_add(priv, src_name); + src = clock_add(priv, src_name, -1); if (!src) { fprintf(stderr, "valid source clock must be selected.\n"); return -1; @@ -999,7 +1007,7 @@ static int phc2sys_static_configuration(struct phc2sys_private *priv, src->state = PS_SLAVE; priv->master = src; - dst = clock_add(priv, dst_name); + dst = clock_add(priv, dst_name, -1); if (!dst) { fprintf(stderr, "valid destination clock must be selected.\n"); return -1; diff --git a/pmc_agent.c b/pmc_agent.c index 86350d8..c8f93e5 100644 --- a/pmc_agent.c +++ b/pmc_agent.c @@ -304,7 +304,7 @@ int pmc_agent_query_dds(struct pmc_agent *node, int timeout) int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, unsigned int port, int *state, - int *tstamping, char *iface) + int *tstamping, int *phc_index, char *iface) { struct port_properties_np *ppn; struct ptp_message *msg; @@ -323,6 +323,7 @@ int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, } *state = ppn->port_state; *tstamping = ppn->timestamping; + *phc_index = ppn->phc_index; len = ppn->interface.length; if (len > IFNAMSIZ - 1) { len = IFNAMSIZ - 1; diff --git a/pmc_agent.h b/pmc_agent.h index 11b9347..ad4b771 100644 --- a/pmc_agent.h +++ b/pmc_agent.h @@ -103,12 +103,14 @@ int pmc_agent_query_dds(struct pmc_agent *agent, int timeout); * @param port The port index of interest. * @param state Buffer to hold the returned port state. * @param tstamping Buffer to hold the returned time stamping flavor. + * @param phc_index Buffer to hold the returned PHC index. * @param iface Buffer to hold the returned interface name. * @return Zero on success, negative error code otherwise. */ int pmc_agent_query_port_properties(struct pmc_agent *agent, int timeout, unsigned int port, int *state, - int *tstamping, char *iface); + int *tstamping, int *phc_index, + char *iface); /** * Queries the TAI-UTC offset and the current leap adjustment from the -- 2.33.1 |
From: Miroslav L. <mli...@re...> - 2022-01-13 15:55:18
|
With the latest kernels it is possible to create virtual PHCs on top of a free-running physical PHC. In order for the application to get timestamps captured by the clock which it is controlling, it needs to bind its sockets to the clock using a new field in the SO_TIMESTAMPING option. Extend the interface structure with the vclock index and modify the transport code to pass it to sk_timestamping_init() to bind the sockets to the clock. Signed-off-by: Miroslav Lichvar <mli...@re...> --- incdefs.sh | 4 ++++ interface.c | 12 ++++++++++++ interface.h | 14 ++++++++++++++ missing.h | 11 +++++++++++ raw.c | 3 ++- sk.c | 11 +++++++++-- sk.h | 3 ++- udp.c | 3 ++- udp6.c | 3 ++- 9 files changed, 58 insertions(+), 6 deletions(-) diff --git a/incdefs.sh b/incdefs.sh index 19e620e..d4c97be 100755 --- a/incdefs.sh +++ b/incdefs.sh @@ -86,6 +86,10 @@ kernel_flags() if grep -q HWTSTAMP_TX_ONESTEP_P2P ${prefix}${tstamp}; then printf " -DHAVE_ONESTEP_P2P" fi + + if grep -q SOF_TIMESTAMPING_BIND_PHC ${prefix}${tstamp}; then + printf " -DHAVE_BIND_PHC" + fi } flags="$(user_flags)$(kernel_flags)" diff --git a/interface.c b/interface.c index 65bdff0..445a270 100644 --- a/interface.c +++ b/interface.c @@ -12,6 +12,7 @@ struct interface { char name[MAX_IFNAME_SIZE + 1]; char ts_label[MAX_IFNAME_SIZE + 1]; struct sk_ts_info ts_info; + int vclock; }; struct interface *interface_create(const char *name) @@ -23,6 +24,7 @@ struct interface *interface_create(const char *name) return NULL; } strncpy(iface->name, name, MAX_IFNAME_SIZE); + iface->vclock = -1; return iface; } @@ -76,3 +78,13 @@ bool interface_tsmodes_supported(struct interface *iface, int modes) } return false; } + +void interface_set_vclock(struct interface *iface, int vclock) +{ + iface->vclock = vclock; +} + +int interface_get_vclock(struct interface *iface) +{ + return iface->vclock; +} diff --git a/interface.h b/interface.h index 8bf2727..752f4f1 100644 --- a/interface.h +++ b/interface.h @@ -91,4 +91,18 @@ bool interface_tsinfo_valid(struct interface *iface); */ bool interface_tsmodes_supported(struct interface *iface, int modes); +/** + * Set the vclock (virtual PHC) to be used for timestamping on an interface. + * @param iface The interface of interest. + * @param vclock The index of the vclock. + */ +void interface_set_vclock(struct interface *iface, int vclock); + +/** + * Get the vclock index set for the interface. + * @param iface The interface of interest. + * @return The index of the vclock, or -1 if not set. + */ +int interface_get_vclock(struct interface *iface); + #endif diff --git a/missing.h b/missing.h index 89cb513..64a856d 100644 --- a/missing.h +++ b/missing.h @@ -62,6 +62,17 @@ enum { }; #endif +#ifndef HAVE_BIND_PHC +enum { + SOF_TIMESTAMPING_BIND_PHC = (1 << 15), +}; + +struct so_timestamping { + int flags; + int bind_phc; +}; +#endif + #ifdef PTP_EXTTS_REQUEST2 #define PTP_EXTTS_REQUEST_FAILED "PTP_EXTTS_REQUEST2 failed: %m" #else diff --git a/raw.c b/raw.c index 0bd15b0..ce64684 100644 --- a/raw.c +++ b/raw.c @@ -243,7 +243,8 @@ static int raw_open(struct transport *t, struct interface *iface, if (gfd < 0) goto no_general; - if (sk_timestamping_init(efd, name, ts_type, TRANS_IEEE_802_3)) + if (sk_timestamping_init(efd, name, ts_type, TRANS_IEEE_802_3, + interface_get_vclock(iface))) goto no_timestamping; if (sk_general_init(gfd)) diff --git a/sk.c b/sk.c index 8be0708..b55d6b5 100644 --- a/sk.c +++ b/sk.c @@ -447,9 +447,10 @@ int sk_set_priority(int fd, int family, uint8_t dscp) } int sk_timestamping_init(int fd, const char *device, enum timestamp_type type, - enum transport_type transport) + enum transport_type transport, int vclock) { int err, filter1, filter2 = 0, flags, tx_type = HWTSTAMP_TX_ON; + struct so_timestamping timestamping; switch (type) { case TS_SOFTWARE: @@ -509,8 +510,14 @@ int sk_timestamping_init(int fd, const char *device, enum timestamp_type type, return err; } + if (vclock >= 0) + flags |= SOF_TIMESTAMPING_BIND_PHC; + + timestamping.flags = flags; + timestamping.bind_phc = vclock; + if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, - &flags, sizeof(flags)) < 0) { + ×tamping, sizeof(timestamping)) < 0) { pr_err("ioctl SO_TIMESTAMPING failed: %m"); return -1; } diff --git a/sk.h b/sk.h index 04d26ee..486dbc4 100644 --- a/sk.h +++ b/sk.h @@ -124,10 +124,11 @@ int sk_set_priority(int fd, int family, uint8_t dscp); * @param device The name of the network interface to configure. * @param type The requested flavor of time stamping. * @param transport The type of transport used. + * @param vclock Index of the virtual PHC, or -1 for the physical clock. * @return Zero on success, non-zero otherwise. */ int sk_timestamping_init(int fd, const char *device, enum timestamp_type type, - enum transport_type transport); + enum transport_type transport, int vclock); /** * Limits the time that RECVMSG(2) will poll while waiting for the tx timestamp diff --git a/udp.c b/udp.c index 826bd12..7c9402e 100644 --- a/udp.c +++ b/udp.c @@ -179,7 +179,8 @@ static int udp_open(struct transport *t, struct interface *iface, if (gfd < 0) goto no_general; - if (sk_timestamping_init(efd, interface_label(iface), ts_type, TRANS_UDP_IPV4)) + if (sk_timestamping_init(efd, interface_label(iface), ts_type, TRANS_UDP_IPV4, + interface_get_vclock(iface))) goto no_timestamping; if (sk_general_init(gfd)) diff --git a/udp6.c b/udp6.c index ba5482e..bde1710 100644 --- a/udp6.c +++ b/udp6.c @@ -196,7 +196,8 @@ static int udp6_open(struct transport *t, struct interface *iface, if (gfd < 0) goto no_general; - if (sk_timestamping_init(efd, interface_label(iface), ts_type, TRANS_UDP_IPV6)) + if (sk_timestamping_init(efd, interface_label(iface), ts_type, + TRANS_UDP_IPV6, interface_get_vclock(iface))) goto no_timestamping; if (sk_general_init(gfd)) -- 2.33.1 |
From: Miroslav L. <mli...@re...> - 2022-01-13 15:55:19
|
This will allow phc2sys -a to use the correct PHC for synchronization if ptp4l is using a virtual clock. Signed-off-by: Miroslav Lichvar <mli...@re...> --- port.c | 1 + tlv.c | 2 ++ tlv.h | 1 + 3 files changed, 4 insertions(+) diff --git a/port.c b/port.c index 5a5741d..83aa452 100644 --- a/port.c +++ b/port.c @@ -956,6 +956,7 @@ static int port_management_fill_response(struct port *target, else ppn->port_state = target->state; ppn->timestamping = target->timestamping; + ppn->phc_index = target->phc_index; ts_label = interface_label(target->iface); ptp_text_set(&ppn->interface, ts_label); datalen = sizeof(*ppn) + ppn->interface.length; diff --git a/tlv.c b/tlv.c index cc8d507..5175694 100644 --- a/tlv.c +++ b/tlv.c @@ -316,6 +316,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, goto bad_length; ppn = (struct port_properties_np *)m->data; ppn->portIdentity.portNumber = ntohs(ppn->portIdentity.portNumber); + ppn->phc_index = ntohl(ppn->phc_index); extra_len = sizeof(struct port_properties_np); extra_len += ppn->interface.length; break; @@ -438,6 +439,7 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) case MID_PORT_PROPERTIES_NP: ppn = (struct port_properties_np *)m->data; ppn->portIdentity.portNumber = htons(ppn->portIdentity.portNumber); + ppn->phc_index = htonl(ppn->phc_index); break; case MID_PORT_STATS_NP: psn = (struct port_stats_np *)m->data; diff --git a/tlv.h b/tlv.h index 97615fd..0f6cf28 100644 --- a/tlv.h +++ b/tlv.h @@ -341,6 +341,7 @@ struct port_properties_np { struct PortIdentity portIdentity; uint8_t port_state; uint8_t timestamping; + Integer32 phc_index; struct PTPText interface; } PACKED; -- 2.33.1 |
From: Miroslav L. <mli...@re...> - 2022-01-13 15:55:18
|
Allow the PHC index to be configured for each port. The default value is -1, which enables the original behavior using the PHC specified by -p or the index from ETHTOOL_GET_TS_INFO. Signed-off-by: Miroslav Lichvar <mli...@re...> --- clock.c | 6 +++++- config.c | 1 + port.c | 8 +++++--- ptp4l.8 | 5 +++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/clock.c b/clock.c index bbfd1a8..36ce4d8 100644 --- a/clock.c +++ b/clock.c @@ -898,7 +898,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, char ts_label[IF_NAMESIZE], phc[32], *tmp; enum timestamp_type timestamping; int fadj = 0, max_adj = 0, sw_ts; - int phc_index, required_modes = 0; + int phc_index, conf_phc_index, required_modes = 0; struct clock *c = &the_clock; const char *uds_ifname; struct port *p; @@ -1016,6 +1016,8 @@ struct clock *clock_create(enum clock_type type, struct config *config, iface = STAILQ_FIRST(&config->interfaces); + conf_phc_index = config_get_int(config, interface_name(iface), "phc_index"); + /* determine PHC Clock index */ if (config_get_int(config, NULL, "free_running")) { phc_index = -1; @@ -1025,6 +1027,8 @@ struct clock *clock_create(enum clock_type type, struct config *config, if (1 != sscanf(phc_device, "/dev/ptp%d", &phc_index)) { phc_index = -1; } + } else if (conf_phc_index >= 0) { + phc_index = conf_phc_index; } else if (interface_tsinfo_valid(iface)) { phc_index = interface_phc_index(iface); } else { diff --git a/config.c b/config.c index f3c52ba..7f7d9ba 100644 --- a/config.c +++ b/config.c @@ -292,6 +292,7 @@ struct config_item config_tab[] = { GLOB_ITEM_DBL("pi_proportional_exponent", -0.3, -DBL_MAX, DBL_MAX), GLOB_ITEM_DBL("pi_proportional_norm_max", 0.7, DBL_MIN, 1.0), GLOB_ITEM_DBL("pi_proportional_scale", 0.0, 0.0, DBL_MAX), + PORT_ITEM_INT("phc_index", -1, -1, INT_MAX), GLOB_ITEM_INT("priority1", 128, 0, UINT8_MAX), GLOB_ITEM_INT("priority2", 128, 0, UINT8_MAX), GLOB_ITEM_STR("productDescription", ";;"), diff --git a/port.c b/port.c index 7e51e77..a84ba66 100644 --- a/port.c +++ b/port.c @@ -3102,7 +3102,9 @@ struct port *port_open(const char *phc_device, goto err_log_name; } - p->phc_index = phc_index; + p->phc_index = config_get_int(cfg, interface_name(interface), "phc_index"); + if (p->phc_index < 0) + p->phc_index = phc_index; p->jbod = config_get_int(cfg, interface_name(interface), "boundary_clock_jbod"); p->master_only = config_get_int(cfg, interface_name(interface), "serverOnly"); p->bmca = config_get_int(cfg, interface_name(interface), "BMCA"); @@ -3129,8 +3131,8 @@ struct port *port_open(const char *phc_device, ; /* UDS cannot have a PHC. */ } else if (!interface_tsinfo_valid(interface)) { pr_warning("%s: get_ts_info not supported", p->log_name); - } else if (phc_index >= 0 && - phc_index != interface_phc_index(interface)) { + } else if (p->phc_index >= 0 && + p->phc_index != interface_phc_index(interface)) { if (p->jbod) { pr_warning("%s: just a bunch of devices", p->log_name); p->phc_index = interface_phc_index(interface); diff --git a/ptp4l.8 b/ptp4l.8 index 8c2969f..1d6f078 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -375,6 +375,11 @@ collection of clocks must be synchronized by an external program, for example phc2sys(8) in "automatic" mode. The default is 0 (disabled). .TP +.B phc_index +Specifies the index of the PHC to be used for synchronization and timestamping. +The default is -1, which means the index will be set to the PHC associated with +the interface, or the device specified by the \fB-p\fP option. +.TP .B udp_ttl Specifies the Time to live (TTL) value for IPv4 multicast messages and the hop limit for IPv6 multicast messages. This option is only relevant with the IPv4 -- 2.33.1 |
From: Miroslav L. <mli...@re...> - 2022-01-13 15:55:19
|
Add a function using ethtool netlink to check whether a PHC is a virtual clock of an interface. Signed-off-by: Miroslav Lichvar <mli...@re...> Cc: Hangbin Liu <liu...@gm...> --- rtnl.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rtnl.h | 9 +++++++ 2 files changed, 92 insertions(+) diff --git a/rtnl.c b/rtnl.c index 88a749f..2e7bcad 100644 --- a/rtnl.c +++ b/rtnl.c @@ -19,6 +19,7 @@ #include <asm/types.h> #include <sys/socket.h> /* Must come before linux/netlink.h on some systems. */ #include <linux/netlink.h> +#include <linux/ethtool_netlink.h> #include <linux/rtnetlink.h> #include <linux/genetlink.h> #include <linux/if_team.h> @@ -465,3 +466,85 @@ no_info: nl_close(fd); return index; } + +static int search_vclocks(struct rtattr *attr, int phc_index) +{ + int i, len = RTA_PAYLOAD(attr); + + for (i = 0; i < len / sizeof (__s32); i++) { + if (((__s32 *)RTA_DATA(attr))[i] == phc_index) + return 1; + } + + return 0; +} + +int rtnl_iface_has_vclock(const char *device, int phc_index) +{ + struct rtattr *tb[ETHTOOL_A_PHC_VCLOCKS_MAX + 1]; + int index, fd, gf_id, len, ret = 0; + struct genlmsghdr *gnlh; + struct nlmsghdr *nlh; + char msg[BUF_SIZE]; + struct { + struct nlattr attr; + uint32_t index; + } req; + + index = if_nametoindex(device); + + fd = nl_open(NETLINK_GENERIC); + if (fd < 0) + return fd; + + gf_id = genl_get_family_id(fd, ETHTOOL_GENL_NAME); + if (gf_id < 0) { + pr_err("get genl family failed"); + goto no_info; + } + + req.attr.nla_len = sizeof(req); + req.attr.nla_type = ETHTOOL_A_HEADER_DEV_INDEX; + req.index = index; + + len = genl_send_msg(fd, gf_id, ETHTOOL_MSG_PHC_VCLOCKS_GET, + ETHTOOL_GENL_VERSION, + NLA_F_NESTED | ETHTOOL_A_PHC_VCLOCKS_HEADER, + &req, sizeof(req)); + + if (len < 0) { + pr_err("send vclock request failed: %m"); + goto no_info; + } + + len = recv(fd, msg, sizeof(msg), 0); + if (len < 0) { + pr_err("recv vclock failed: %m"); + goto no_info; + } + + for (nlh = (struct nlmsghdr *) msg; NLMSG_OK(nlh, len); + nlh = NLMSG_NEXT(nlh, len)) { + if (nlh->nlmsg_type != gf_id) + continue; + + gnlh = (struct genlmsghdr *) NLMSG_DATA(nlh); + if (gnlh->cmd != ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY) + continue; + + if (rtnl_rtattr_parse(tb, ETHTOOL_A_PHC_VCLOCKS_MAX, + (struct rtattr *) GENLMSG_DATA(msg), + NLMSG_PAYLOAD(nlh, GENL_HDRLEN))) + continue; + + if (tb[ETHTOOL_A_PHC_VCLOCKS_INDEX]) { + ret = search_vclocks(tb[ETHTOOL_A_PHC_VCLOCKS_INDEX], + phc_index); + break; + } + } + +no_info: + nl_close(fd); + return ret; +} diff --git a/rtnl.h b/rtnl.h index 8fef4a9..96fee29 100644 --- a/rtnl.h +++ b/rtnl.h @@ -59,6 +59,15 @@ int rtnl_link_query(int fd, const char *device); */ int rtnl_link_status(int fd, const char *device, rtnl_callback cb, void *ctx); +/** + * Check if the PHC is a virtual clock of the interface (i.e. sockets bound to + * the interface also need to be bound to the clock). + * @param device Name of the interface. + * @param phc_index Index of the clock to check. + * @return 1 if true, otherwise 0. + */ +int rtnl_iface_has_vclock(const char *device, int phc_index); + /** * Open a RT netlink socket for monitoring link state. * @return A valid socket, or -1 on error. -- 2.33.1 |
From: Hangbin L. <liu...@gm...> - 2022-01-14 02:47:09
|
On Thu, Jan 13, 2022 at 04:54:55PM +0100, Miroslav Lichvar wrote: > Add a function using ethtool netlink to check whether a PHC is a virtual > clock of an interface. > > Signed-off-by: Miroslav Lichvar <mli...@re...> > Cc: Hangbin Liu <liu...@gm...> > --- > rtnl.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > rtnl.h | 9 +++++++ > 2 files changed, 92 insertions(+) Looks good to me. Thanks Hangbin |
From: Miroslav L. <mli...@re...> - 2022-01-20 11:49:54
|
On Thu, Jan 20, 2022 at 11:24:59AM +0000, Geva, Erez wrote: > Hi, > > When we would use multiple PHC per interface (or per bridge/switch). > How can we defer a real PHC from virtual PHC? I think you would need to iterate over all interfaces and find the one which has the vclock, and then get the index of its real PHC. > Would not it be easier to simply call then multiple PHC and use the index regardless if it is virtual or not. I'm not sure to what exactly you are referring to, but in the patches I sent we need to know whether it's a virtual clock in order to bind the sockets. > As for the PORT_PROPERTIES_NP. It is a port TLV not a clock TLV. With the jbod option each port can have a different PHC. > I think it is better to add a new TLV and not break backward compatible of PORT_PROPERTIES_NP. > A patch like that was rejected 2 month ago. Ok. Thanks, -- Miroslav Lichvar |
From: Erez <ere...@gm...> - 2022-01-20 17:28:04
|
On Thu, 20 Jan 2022 at 12:52, Miroslav Lichvar <mli...@re...> wrote: > On Thu, Jan 20, 2022 at 11:24:59AM +0000, Geva, Erez wrote: > > Hi, > > > > When we would use multiple PHC per interface (or per bridge/switch). > > How can we defer a real PHC from virtual PHC? > > I think you would need to iterate over all interfaces and find the one > which has the vclock, and then get the index of its real PHC. > I meant that I think we should add a flag indicating the PHC is virtual as we can use a PHC index for a real PHC of multiple PHCs, or one created for VLAN over bond. And we might have more kinds in the future. > > > Would not it be easier to simply call then multiple PHC and use the > index regardless if it is virtual or not. > > I'm not sure to what exactly you are referring to, but in the patches > I sent we need to know whether it's a virtual clock in order to bind > the sockets. > > Yap, but we should bare in mind that multiple PHC indexes could be a real PHC or other kind, so we should probably use a flag to indicate the PHC index is a virtual if we need to defar on the socket layer (sk.c). > > As for the PORT_PROPERTIES_NP. It is a port TLV not a clock TLV. > > With the jbod option each port can have a different PHC. > Yap, but it could have multiple clocks (though not on the same PTP domain). I think we need to support both. Multiple real PHC on a single interface is not here yet, but I do not see the big difference to multiple virtual PHCs. At the end it is only if the Hardware has the PHC or there is a software implementing it. > > > I think it is better to add a new TLV and not break backward compatible > of PORT_PROPERTIES_NP. > > A patch like that was rejected 2 month ago. > > Ok. > Cool :-) > > Thanks, > > -- > Miroslav Lichvar > > > Erez > > _______________________________________________ > Linuxptp-devel mailing list > Lin...@li... > https://lists.sourceforge.net/lists/listinfo/linuxptp-devel > |
From: Geva, E. <ere...@si...> - 2022-01-20 17:01:00
|
Hi, When we would use multiple PHC per interface (or per bridge/switch). How can we defer a real PHC from virtual PHC? Would not it be easier to simply call then multiple PHC and use the index regardless if it is virtual or not. As for the PORT_PROPERTIES_NP. It is a port TLV not a clock TLV. I think it is better to add a new TLV and not break backward compatible of PORT_PROPERTIES_NP. A patch like that was rejected 2 month ago. Erez -----Original Message----- From: Miroslav Lichvar <mli...@re...> Sent: Thursday, 13 January 2022 16:55 To: lin...@li... Subject: [Linuxptp-devel] [PATCH RFC 0/7] Support for virtual clocks With recent kernels it is possible to create virtual PHCs running on top of a free-running PHC by writing to /sys/class/ptp/ptp?/n_vclocks. If supported in linuxptp, it would allow users to run multiple ptp4l instance with hardware timestamping on one interface, e.g. in different domains for better resiliency, or separate instances for different unicast servers. The kernel support was added in 5.14, but there were some bugs fixed recently, so 5.17-rc1 or later is recommended for testing. The 1st patch is a bug fix required by the second patch. The 2nd patch adds detection of virtual clocks via ethtool netlink. The 3rd-5th patch add support to ptp4l. The PHC index of the virtual clock needs to be specified in the configuration. The 6th and 7th patch add support to phc2sys. The PORT_PROPERTIES_NP message is extended with the PHC index. The command is available only on the UDS port, but I'm not sure if an incompatible change like this is acceptable. The alternative would be to add a new command specifically for the index. My plan is to add support also to timemaster to detect the kernel support, automatically create virtual clocks for interfaces specified in multiple domains (where previously some instances had to use SW timestamping), and pass the correct PHC indexes to the ptp4l instances. Miroslav Lichvar (7): rtnl: Fix rtnl_rtattr_parse() to process max attribute. rtnl: Add function to detect virtual clocks. Add support for binding sockets to virtual clocks. config: Add port-specific phc_index option. port: Check for virtual clocks. tlv: Add PHC index to PORT_PROPERTIES_NP. phc2sys: Use PHC index from PORT_PROPERTIES_NP. clock.c | 6 +++- config.c | 1 + incdefs.sh | 4 +++ interface.c | 12 ++++++++ interface.h | 14 +++++++++ missing.h | 11 +++++++ phc2sys.c | 42 +++++++++++++++----------- pmc_agent.c | 3 +- pmc_agent.h | 4 ++- port.c | 16 +++++++--- ptp4l.8 | 8 +++++ raw.c | 3 +- rtnl.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++-- rtnl.h | 9 ++++++ sk.c | 11 +++++-- sk.h | 3 +- tlv.c | 2 ++ tlv.h | 1 + udp.c | 3 +- udp6.c | 3 +- 20 files changed, 211 insertions(+), 32 deletions(-) -- 2.33.1 _______________________________________________ Linuxptp-devel mailing list Lin...@li... https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Flists.sourceforge.net%2Flists%2Flistinfo%2Flinuxptp-devel&data=04%7C01%7Cerez.geva.ext%40siemens.com%7Ccf6f6ea49816434c66dd08d9d6ade80f%7C38ae3bcd95794fd4addab42e1495d55a%7C1%7C0%7C637776864684717246%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=yt2%2FX%2BRyNF2oiO80Jxy8cGQ%2Fezs3AVpMmFQUWmSEuB4%3D&reserved=0 |
From: Miroslav L. <mli...@re...> - 2022-02-03 11:50:27
|
Initialize the whole array passed to rtnl_rtattr_parse() and don't ignore the last attribute with the maximum value. This will be needed to get the ETHTOOL_A_PHC_VCLOCKS_INDEX attribute. Signed-off-by: Miroslav Lichvar <mli...@re...> Acked-by: Hangbin Liu <liu...@gm...> --- rtnl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rtnl.c b/rtnl.c index fe625d6..f8bdbe6 100644 --- a/rtnl.c +++ b/rtnl.c @@ -178,10 +178,10 @@ static int rtnl_rtattr_parse(struct rtattr *tb[], int max, struct rtattr *rta, i { unsigned short type; - memset(tb, 0, sizeof(struct rtattr *) * max); + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); while (RTA_OK(rta, len)) { type = rta->rta_type; - if ((type < max) && (!tb[type])) + if ((type <= max) && (!tb[type])) tb[type] = rta; rta = RTA_NEXT(rta, len); } @@ -200,8 +200,8 @@ static inline int rtnl_nested_rtattr_parse(struct rtattr *tb[], int max, struct static int rtnl_linkinfo_parse(int master_index, struct rtattr *rta) { - struct rtattr *linkinfo[IFLA_INFO_MAX]; - struct rtattr *bond[IFLA_BOND_MAX]; + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + struct rtattr *bond[IFLA_BOND_MAX+1]; int index = -1; char *kind; -- 2.34.1 |
From: Miroslav L. <mli...@re...> - 2022-02-03 11:50:28
|
Allow the PHC index to be configured for each port. The default value is -1, which enables the original behavior using the PHC specified by -p or the index from ETHTOOL_GET_TS_INFO. Signed-off-by: Miroslav Lichvar <mli...@re...> --- clock.c | 6 +++++- config.c | 1 + configs/default.cfg | 1 + port.c | 8 +++++--- ptp4l.8 | 5 +++++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/clock.c b/clock.c index bbfd1a8..36ce4d8 100644 --- a/clock.c +++ b/clock.c @@ -898,7 +898,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, char ts_label[IF_NAMESIZE], phc[32], *tmp; enum timestamp_type timestamping; int fadj = 0, max_adj = 0, sw_ts; - int phc_index, required_modes = 0; + int phc_index, conf_phc_index, required_modes = 0; struct clock *c = &the_clock; const char *uds_ifname; struct port *p; @@ -1016,6 +1016,8 @@ struct clock *clock_create(enum clock_type type, struct config *config, iface = STAILQ_FIRST(&config->interfaces); + conf_phc_index = config_get_int(config, interface_name(iface), "phc_index"); + /* determine PHC Clock index */ if (config_get_int(config, NULL, "free_running")) { phc_index = -1; @@ -1025,6 +1027,8 @@ struct clock *clock_create(enum clock_type type, struct config *config, if (1 != sscanf(phc_device, "/dev/ptp%d", &phc_index)) { phc_index = -1; } + } else if (conf_phc_index >= 0) { + phc_index = conf_phc_index; } else if (interface_tsinfo_valid(iface)) { phc_index = interface_phc_index(iface); } else { diff --git a/config.c b/config.c index f3c52ba..7f7d9ba 100644 --- a/config.c +++ b/config.c @@ -292,6 +292,7 @@ struct config_item config_tab[] = { GLOB_ITEM_DBL("pi_proportional_exponent", -0.3, -DBL_MAX, DBL_MAX), GLOB_ITEM_DBL("pi_proportional_norm_max", 0.7, DBL_MIN, 1.0), GLOB_ITEM_DBL("pi_proportional_scale", 0.0, 0.0, DBL_MAX), + PORT_ITEM_INT("phc_index", -1, -1, INT_MAX), GLOB_ITEM_INT("priority1", 128, 0, UINT8_MAX), GLOB_ITEM_INT("priority2", 128, 0, UINT8_MAX), GLOB_ITEM_STR("productDescription", ";;"), diff --git a/configs/default.cfg b/configs/default.cfg index cd383b5..26817de 100644 --- a/configs/default.cfg +++ b/configs/default.cfg @@ -106,6 +106,7 @@ delay_filter_length 10 egressLatency 0 ingressLatency 0 boundary_clock_jbod 0 +phc_index -1 # # Clock description # diff --git a/port.c b/port.c index 7e51e77..a84ba66 100644 --- a/port.c +++ b/port.c @@ -3102,7 +3102,9 @@ struct port *port_open(const char *phc_device, goto err_log_name; } - p->phc_index = phc_index; + p->phc_index = config_get_int(cfg, interface_name(interface), "phc_index"); + if (p->phc_index < 0) + p->phc_index = phc_index; p->jbod = config_get_int(cfg, interface_name(interface), "boundary_clock_jbod"); p->master_only = config_get_int(cfg, interface_name(interface), "serverOnly"); p->bmca = config_get_int(cfg, interface_name(interface), "BMCA"); @@ -3129,8 +3131,8 @@ struct port *port_open(const char *phc_device, ; /* UDS cannot have a PHC. */ } else if (!interface_tsinfo_valid(interface)) { pr_warning("%s: get_ts_info not supported", p->log_name); - } else if (phc_index >= 0 && - phc_index != interface_phc_index(interface)) { + } else if (p->phc_index >= 0 && + p->phc_index != interface_phc_index(interface)) { if (p->jbod) { pr_warning("%s: just a bunch of devices", p->log_name); p->phc_index = interface_phc_index(interface); diff --git a/ptp4l.8 b/ptp4l.8 index 8c2969f..5ef471b 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -375,6 +375,11 @@ collection of clocks must be synchronized by an external program, for example phc2sys(8) in "automatic" mode. The default is 0 (disabled). .TP +.B phc_index +Specifies the index of the PHC to be used for synchronization with hardware +timestamping. The default is -1, which means the index will be set to the PHC +associated with the interface, or the device specified by the \fB-p\fP option. +.TP .B udp_ttl Specifies the Time to live (TTL) value for IPv4 multicast messages and the hop limit for IPv6 multicast messages. This option is only relevant with the IPv4 -- 2.34.1 |
From: Richard C. <ric...@gm...> - 2022-02-03 19:37:26
|
On Thu, Feb 03, 2022 at 12:50:08PM +0100, Miroslav Lichvar wrote: > diff --git a/config.c b/config.c > index f3c52ba..7f7d9ba 100644 > --- a/config.c > +++ b/config.c > @@ -292,6 +292,7 @@ struct config_item config_tab[] = { > GLOB_ITEM_DBL("pi_proportional_exponent", -0.3, -DBL_MAX, DBL_MAX), > GLOB_ITEM_DBL("pi_proportional_norm_max", 0.7, DBL_MIN, 1.0), > GLOB_ITEM_DBL("pi_proportional_scale", 0.0, 0.0, DBL_MAX), > + PORT_ITEM_INT("phc_index", -1, -1, INT_MAX), Nit: alphabetical order please. > GLOB_ITEM_INT("priority1", 128, 0, UINT8_MAX), > GLOB_ITEM_INT("priority2", 128, 0, UINT8_MAX), > GLOB_ITEM_STR("productDescription", ";;"), |
From: Miroslav L. <mli...@re...> - 2022-02-03 11:50:28
|
Add a new command to get the PHC index associated with the port. This will be needed for phc2sys -a to use the correct PHC for synchronization if ptp4l is using a virtual clock. The TLV also contains a flag indicating a virtual clock. To follow the PORT_PROPERTIES_NP access policy, PORT_HWCLOCK_NP is limited to the UDS-RW port. Signed-off-by: Miroslav Lichvar <mli...@re...> --- clock.c | 1 + pmc.c | 11 +++++++++++ pmc_common.c | 1 + port.c | 9 +++++++++ tlv.c | 15 +++++++++++++++ tlv.h | 10 ++++++++++ 6 files changed, 47 insertions(+) diff --git a/clock.c b/clock.c index 36ce4d8..f808b35 100644 --- a/clock.c +++ b/clock.c @@ -1486,6 +1486,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) switch (mgt->id) { case MID_PORT_PROPERTIES_NP: + case MID_PORT_HWCLOCK_NP: if (p != c->uds_rw_port) { /* Only the UDS-RW port allowed. */ clock_management_send_error(p, msg, MID_NOT_SUPPORTED); diff --git a/pmc.c b/pmc.c index a1ee787..d1ef484 100644 --- a/pmc.c +++ b/pmc.c @@ -144,6 +144,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) struct subscribe_events_np *sen; struct management_tlv_datum *mtd; struct port_properties_np *ppn; + struct port_hwclock_np *phn; struct timePropertiesDS *tp; struct management_tlv *mgt; struct time_status_np *tsn; @@ -495,6 +496,16 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) pcp->stats.txMsgType[SIGNALING], pcp->stats.txMsgType[MANAGEMENT]); break; + case MID_PORT_HWCLOCK_NP: + phn = (struct port_hwclock_np *) mgt->data; + fprintf(fp, "PORT_HWCLOCK_NP " + IFMT "portIdentity %s" + IFMT "phcIndex %d" + IFMT "flags %hhu", + pid2str(&phn->portIdentity), + phn->phc_index, + phn->flags); + break; case MID_LOG_ANNOUNCE_INTERVAL: mtd = (struct management_tlv_datum *) mgt->data; fprintf(fp, "LOG_ANNOUNCE_INTERVAL " diff --git a/pmc_common.c b/pmc_common.c index 7a1dbb4..fbc5ae8 100644 --- a/pmc_common.c +++ b/pmc_common.c @@ -132,6 +132,7 @@ struct management_id idtab[] = { { "PORT_DATA_SET_NP", MID_PORT_DATA_SET_NP, do_set_action }, { "PORT_STATS_NP", MID_PORT_STATS_NP, do_get_action }, { "PORT_PROPERTIES_NP", MID_PORT_PROPERTIES_NP, do_get_action }, + { "PORT_HWCLOCK_NP", MID_PORT_HWCLOCK_NP, do_get_action }, }; static void do_get_action(struct pmc *pmc, int action, int index, char *str) diff --git a/port.c b/port.c index 5a5741d..5acbd9f 100644 --- a/port.c +++ b/port.c @@ -797,6 +797,7 @@ static int port_management_fill_response(struct port *target, struct management_tlv_datum *mtd; struct clock_description *desc; struct port_properties_np *ppn; + struct port_hwclock_np *phn; struct port_stats_np *psn; struct management_tlv *tlv; struct port_ds_np *pdsnp; @@ -966,6 +967,14 @@ static int port_management_fill_response(struct port *target, psn->stats = target->stats; datalen = sizeof(*psn); break; + case MID_PORT_HWCLOCK_NP: + phn = (struct port_hwclock_np *)tlv->data; + phn->portIdentity = target->portIdentity; + phn->phc_index = target->phc_index; + phn->flags = interface_get_vclock(target->iface) >= 0 ? + PORT_HWCLOCK_VCLOCK : 0; + datalen = sizeof(*phn); + break; default: /* The caller should *not* respond to this message. */ tlv_extra_recycle(extra); diff --git a/tlv.c b/tlv.c index cc8d507..f49363c 100644 --- a/tlv.c +++ b/tlv.c @@ -117,6 +117,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, struct mgmt_clock_description *cd; struct subscribe_events_np *sen; struct port_properties_np *ppn; + struct port_hwclock_np *phn; struct timePropertiesDS *tp; struct time_status_np *tsn; struct port_stats_np *psn; @@ -331,6 +332,14 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, } extra_len = sizeof(struct port_stats_np); break; + case MID_PORT_HWCLOCK_NP: + if (data_len < sizeof(struct port_hwclock_np)) + goto bad_length; + phn = (struct port_hwclock_np *)m->data; + phn->portIdentity.portNumber = ntohs(phn->portIdentity.portNumber); + phn->phc_index = ntohl(phn->phc_index); + extra_len = sizeof(struct port_hwclock_np); + break; case MID_SAVE_IN_NON_VOLATILE_STORAGE: case MID_RESET_NON_VOLATILE_STORAGE: case MID_INITIALIZE: @@ -358,6 +367,7 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) struct mgmt_clock_description *cd; struct subscribe_events_np *sen; struct port_properties_np *ppn; + struct port_hwclock_np *phn; struct timePropertiesDS *tp; struct time_status_np *tsn; struct port_stats_np *psn; @@ -448,6 +458,11 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) psn->stats.txMsgType[i] = __cpu_to_le64(psn->stats.txMsgType[i]); } break; + case MID_PORT_HWCLOCK_NP: + phn = (struct port_hwclock_np *)m->data; + phn->portIdentity.portNumber = htons(phn->portIdentity.portNumber); + phn->phc_index = htonl(phn->phc_index); + break; } } diff --git a/tlv.h b/tlv.h index 97615fd..b309686 100644 --- a/tlv.h +++ b/tlv.h @@ -125,6 +125,7 @@ enum management_action { #define MID_PORT_DATA_SET_NP 0xC002 #define MID_PORT_PROPERTIES_NP 0xC004 #define MID_PORT_STATS_NP 0xC005 +#define MID_PORT_HWCLOCK_NP 0xC007 /* Management error ID values */ #define MID_RESPONSE_TOO_BIG 0x0001 @@ -144,6 +145,9 @@ enum management_action { #define CANCEL_UNICAST_MAINTAIN_GRANT (1 << 1) #define GRANT_UNICAST_RENEWAL_INVITED (1 << 0) +/* Flags in PORT_HWCLOCK_NP */ +#define PORT_HWCLOCK_VCLOCK (1 << 0) + struct ack_cancel_unicast_xmit_tlv { Enumeration16 type; UInteger16 length; @@ -344,6 +348,12 @@ struct port_properties_np { struct PTPText interface; } PACKED; +struct port_hwclock_np { + struct PortIdentity portIdentity; + Integer32 phc_index; + UInteger8 flags; +} PACKED; + struct port_stats_np { struct PortIdentity portIdentity; struct PortStats stats; -- 2.34.1 |
From: Miroslav L. <mli...@re...> - 2022-02-03 11:50:28
|
Add a function using ethtool netlink to check whether a PHC is a virtual clock of an interface. Signed-off-by: Miroslav Lichvar <mli...@re...> Acked-by: Hangbin Liu <liu...@gm...> --- rtnl.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rtnl.h | 9 +++++++ 2 files changed, 92 insertions(+) diff --git a/rtnl.c b/rtnl.c index f8bdbe6..a0cf494 100644 --- a/rtnl.c +++ b/rtnl.c @@ -19,6 +19,7 @@ #include <asm/types.h> #include <sys/socket.h> /* Must come before linux/netlink.h on some systems. */ #include <linux/netlink.h> +#include <linux/ethtool_netlink.h> #include <linux/rtnetlink.h> #include <linux/genetlink.h> #include <linux/if_team.h> @@ -465,3 +466,85 @@ no_info: nl_close(fd); return index; } + +static int search_vclocks(struct rtattr *attr, int phc_index) +{ + int i, len = RTA_PAYLOAD(attr); + + for (i = 0; i < len / sizeof (__s32); i++) { + if (((__s32 *)RTA_DATA(attr))[i] == phc_index) + return 1; + } + + return 0; +} + +int rtnl_iface_has_vclock(const char *device, int phc_index) +{ + struct rtattr *tb[ETHTOOL_A_PHC_VCLOCKS_MAX + 1]; + int index, fd, gf_id, len, ret = 0; + struct genlmsghdr *gnlh; + struct nlmsghdr *nlh; + char msg[BUF_SIZE]; + struct { + struct nlattr attr; + uint32_t index; + } req; + + index = if_nametoindex(device); + + fd = nl_open(NETLINK_GENERIC); + if (fd < 0) + return fd; + + gf_id = genl_get_family_id(fd, ETHTOOL_GENL_NAME); + if (gf_id < 0) { + pr_err("get genl family failed"); + goto no_info; + } + + req.attr.nla_len = sizeof(req); + req.attr.nla_type = ETHTOOL_A_HEADER_DEV_INDEX; + req.index = index; + + len = genl_send_msg(fd, gf_id, ETHTOOL_MSG_PHC_VCLOCKS_GET, + ETHTOOL_GENL_VERSION, + NLA_F_NESTED | ETHTOOL_A_PHC_VCLOCKS_HEADER, + &req, sizeof(req)); + + if (len < 0) { + pr_err("send vclock request failed: %m"); + goto no_info; + } + + len = recv(fd, msg, sizeof(msg), 0); + if (len < 0) { + pr_err("recv vclock failed: %m"); + goto no_info; + } + + for (nlh = (struct nlmsghdr *) msg; NLMSG_OK(nlh, len); + nlh = NLMSG_NEXT(nlh, len)) { + if (nlh->nlmsg_type != gf_id) + continue; + + gnlh = (struct genlmsghdr *) NLMSG_DATA(nlh); + if (gnlh->cmd != ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY) + continue; + + if (rtnl_rtattr_parse(tb, ETHTOOL_A_PHC_VCLOCKS_MAX, + (struct rtattr *) GENLMSG_DATA(msg), + NLMSG_PAYLOAD(nlh, GENL_HDRLEN))) + continue; + + if (tb[ETHTOOL_A_PHC_VCLOCKS_INDEX]) { + ret = search_vclocks(tb[ETHTOOL_A_PHC_VCLOCKS_INDEX], + phc_index); + break; + } + } + +no_info: + nl_close(fd); + return ret; +} diff --git a/rtnl.h b/rtnl.h index 8fef4a9..96fee29 100644 --- a/rtnl.h +++ b/rtnl.h @@ -59,6 +59,15 @@ int rtnl_link_query(int fd, const char *device); */ int rtnl_link_status(int fd, const char *device, rtnl_callback cb, void *ctx); +/** + * Check if the PHC is a virtual clock of the interface (i.e. sockets bound to + * the interface also need to be bound to the clock). + * @param device Name of the interface. + * @param phc_index Index of the clock to check. + * @return 1 if true, otherwise 0. + */ +int rtnl_iface_has_vclock(const char *device, int phc_index); + /** * Open a RT netlink socket for monitoring link state. * @return A valid socket, or -1 on error. -- 2.34.1 |
From: Miroslav L. <mli...@re...> - 2022-02-03 11:50:28
|
With the latest kernels it is possible to create virtual PHCs on top of a free-running physical PHC. In order for the application to get timestamps captured by the clock which it is controlling, it needs to bind its sockets to the clock using a new field in the SO_TIMESTAMPING option. Extend the interface structure with the vclock index and modify the transport code to pass it to sk_timestamping_init() to bind the sockets to the clock. Signed-off-by: Miroslav Lichvar <mli...@re...> --- incdefs.sh | 4 ++++ interface.c | 12 ++++++++++++ interface.h | 14 ++++++++++++++ missing.h | 11 +++++++++++ raw.c | 3 ++- sk.c | 11 +++++++++-- sk.h | 3 ++- udp.c | 3 ++- udp6.c | 3 ++- 9 files changed, 58 insertions(+), 6 deletions(-) diff --git a/incdefs.sh b/incdefs.sh index 19e620e..d4c97be 100755 --- a/incdefs.sh +++ b/incdefs.sh @@ -86,6 +86,10 @@ kernel_flags() if grep -q HWTSTAMP_TX_ONESTEP_P2P ${prefix}${tstamp}; then printf " -DHAVE_ONESTEP_P2P" fi + + if grep -q SOF_TIMESTAMPING_BIND_PHC ${prefix}${tstamp}; then + printf " -DHAVE_BIND_PHC" + fi } flags="$(user_flags)$(kernel_flags)" diff --git a/interface.c b/interface.c index 65bdff0..445a270 100644 --- a/interface.c +++ b/interface.c @@ -12,6 +12,7 @@ struct interface { char name[MAX_IFNAME_SIZE + 1]; char ts_label[MAX_IFNAME_SIZE + 1]; struct sk_ts_info ts_info; + int vclock; }; struct interface *interface_create(const char *name) @@ -23,6 +24,7 @@ struct interface *interface_create(const char *name) return NULL; } strncpy(iface->name, name, MAX_IFNAME_SIZE); + iface->vclock = -1; return iface; } @@ -76,3 +78,13 @@ bool interface_tsmodes_supported(struct interface *iface, int modes) } return false; } + +void interface_set_vclock(struct interface *iface, int vclock) +{ + iface->vclock = vclock; +} + +int interface_get_vclock(struct interface *iface) +{ + return iface->vclock; +} diff --git a/interface.h b/interface.h index 8bf2727..752f4f1 100644 --- a/interface.h +++ b/interface.h @@ -91,4 +91,18 @@ bool interface_tsinfo_valid(struct interface *iface); */ bool interface_tsmodes_supported(struct interface *iface, int modes); +/** + * Set the vclock (virtual PHC) to be used for timestamping on an interface. + * @param iface The interface of interest. + * @param vclock The index of the vclock. + */ +void interface_set_vclock(struct interface *iface, int vclock); + +/** + * Get the vclock index set for the interface. + * @param iface The interface of interest. + * @return The index of the vclock, or -1 if not set. + */ +int interface_get_vclock(struct interface *iface); + #endif diff --git a/missing.h b/missing.h index 89cb513..64a856d 100644 --- a/missing.h +++ b/missing.h @@ -62,6 +62,17 @@ enum { }; #endif +#ifndef HAVE_BIND_PHC +enum { + SOF_TIMESTAMPING_BIND_PHC = (1 << 15), +}; + +struct so_timestamping { + int flags; + int bind_phc; +}; +#endif + #ifdef PTP_EXTTS_REQUEST2 #define PTP_EXTTS_REQUEST_FAILED "PTP_EXTTS_REQUEST2 failed: %m" #else diff --git a/raw.c b/raw.c index 0bd15b0..ce64684 100644 --- a/raw.c +++ b/raw.c @@ -243,7 +243,8 @@ static int raw_open(struct transport *t, struct interface *iface, if (gfd < 0) goto no_general; - if (sk_timestamping_init(efd, name, ts_type, TRANS_IEEE_802_3)) + if (sk_timestamping_init(efd, name, ts_type, TRANS_IEEE_802_3, + interface_get_vclock(iface))) goto no_timestamping; if (sk_general_init(gfd)) diff --git a/sk.c b/sk.c index 8be0708..b55d6b5 100644 --- a/sk.c +++ b/sk.c @@ -447,9 +447,10 @@ int sk_set_priority(int fd, int family, uint8_t dscp) } int sk_timestamping_init(int fd, const char *device, enum timestamp_type type, - enum transport_type transport) + enum transport_type transport, int vclock) { int err, filter1, filter2 = 0, flags, tx_type = HWTSTAMP_TX_ON; + struct so_timestamping timestamping; switch (type) { case TS_SOFTWARE: @@ -509,8 +510,14 @@ int sk_timestamping_init(int fd, const char *device, enum timestamp_type type, return err; } + if (vclock >= 0) + flags |= SOF_TIMESTAMPING_BIND_PHC; + + timestamping.flags = flags; + timestamping.bind_phc = vclock; + if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, - &flags, sizeof(flags)) < 0) { + ×tamping, sizeof(timestamping)) < 0) { pr_err("ioctl SO_TIMESTAMPING failed: %m"); return -1; } diff --git a/sk.h b/sk.h index 04d26ee..486dbc4 100644 --- a/sk.h +++ b/sk.h @@ -124,10 +124,11 @@ int sk_set_priority(int fd, int family, uint8_t dscp); * @param device The name of the network interface to configure. * @param type The requested flavor of time stamping. * @param transport The type of transport used. + * @param vclock Index of the virtual PHC, or -1 for the physical clock. * @return Zero on success, non-zero otherwise. */ int sk_timestamping_init(int fd, const char *device, enum timestamp_type type, - enum transport_type transport); + enum transport_type transport, int vclock); /** * Limits the time that RECVMSG(2) will poll while waiting for the tx timestamp diff --git a/udp.c b/udp.c index 826bd12..7c9402e 100644 --- a/udp.c +++ b/udp.c @@ -179,7 +179,8 @@ static int udp_open(struct transport *t, struct interface *iface, if (gfd < 0) goto no_general; - if (sk_timestamping_init(efd, interface_label(iface), ts_type, TRANS_UDP_IPV4)) + if (sk_timestamping_init(efd, interface_label(iface), ts_type, TRANS_UDP_IPV4, + interface_get_vclock(iface))) goto no_timestamping; if (sk_general_init(gfd)) diff --git a/udp6.c b/udp6.c index ba5482e..bde1710 100644 --- a/udp6.c +++ b/udp6.c @@ -196,7 +196,8 @@ static int udp6_open(struct transport *t, struct interface *iface, if (gfd < 0) goto no_general; - if (sk_timestamping_init(efd, interface_label(iface), ts_type, TRANS_UDP_IPV6)) + if (sk_timestamping_init(efd, interface_label(iface), ts_type, + TRANS_UDP_IPV6, interface_get_vclock(iface))) goto no_timestamping; if (sk_general_init(gfd)) -- 2.34.1 |
From: Miroslav L. <mli...@re...> - 2022-02-03 11:50:28
|
If the PHC specified with the phc_index or -p option is a virtual clock of the interface, bind sockets to the virtual clock instead of the real clock to get correct timestamps. Signed-off-by: Miroslav Lichvar <mli...@re...> --- port.c | 7 ++++++- ptp4l.8 | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/port.c b/port.c index a84ba66..5a5741d 100644 --- a/port.c +++ b/port.c @@ -3133,7 +3133,12 @@ struct port *port_open(const char *phc_device, pr_warning("%s: get_ts_info not supported", p->log_name); } else if (p->phc_index >= 0 && p->phc_index != interface_phc_index(interface)) { - if (p->jbod) { + if (rtnl_iface_has_vclock(interface_name(interface), + p->phc_index)) { + pr_info("%s: /dev/ptp%d is virtual clock", + p->log_name, p->phc_index); + interface_set_vclock(interface, phc_index); + } else if (p->jbod) { pr_warning("%s: just a bunch of devices", p->log_name); p->phc_index = interface_phc_index(interface); } else if (phc_device) { diff --git a/ptp4l.8 b/ptp4l.8 index 5ef471b..71a414c 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -377,8 +377,11 @@ The default is 0 (disabled). .TP .B phc_index Specifies the index of the PHC to be used for synchronization with hardware -timestamping. The default is -1, which means the index will be set to the PHC -associated with the interface, or the device specified by the \fB-p\fP option. +timestamping. This option is useful with virtual clocks running on top of a +free-running physical clock (created by writing to +/sys/class/ptp/ptp*/n_vclocks). +The default is -1, which means the index will be set to the PHC associated with +the interface, or the device specified by the \fB-p\fP option. .TP .B udp_ttl Specifies the Time to live (TTL) value for IPv4 multicast messages and the hop -- 2.34.1 |
From: Miroslav L. <mli...@re...> - 2022-02-03 11:50:29
|
When running in the automatic mode, get the PHC index of the port from PORT_HWCLOCK_NP instead of calling sk_get_ts_info(). This allows phc2sys -a to synchronize (to) a virtual clock. Signed-off-by: Miroslav Lichvar <mli...@re...> --- phc2sys.c | 42 +++++++++++++++++++++++++----------------- pmc_agent.c | 18 +++++++++++++++++- pmc_agent.h | 4 +++- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/phc2sys.c b/phc2sys.c index 6815c3d..8fc241d 100644 --- a/phc2sys.c +++ b/phc2sys.c @@ -153,14 +153,21 @@ static struct servo *servo_add(struct phc2sys_private *priv, return servo; } -static struct clock *clock_add(struct phc2sys_private *priv, const char *device) +static struct clock *clock_add(struct phc2sys_private *priv, const char *device, + int phc_index) { struct clock *c; clockid_t clkid = CLOCK_INVALID; - int phc_index = -1; + char phc_device[19]; if (device) { - clkid = posix_clock_open(device, &phc_index); + if (phc_index >= 0) { + snprintf(phc_device, sizeof(phc_device), "/dev/ptp%d", + phc_index); + clkid = posix_clock_open(phc_device, &phc_index); + } else { + clkid = posix_clock_open(device, &phc_index); + } if (clkid == CLOCK_INVALID) return NULL; } @@ -257,7 +264,7 @@ static struct port *port_get(struct phc2sys_private *priv, unsigned int number) } static struct port *port_add(struct phc2sys_private *priv, unsigned int number, - char *device) + char *device, int phc_index) { struct port *p; struct clock *c = NULL, *tmp; @@ -274,7 +281,7 @@ static struct port *port_add(struct phc2sys_private *priv, unsigned int number, } } if (!c) { - c = clock_add(priv, device); + c = clock_add(priv, device, phc_index); if (!c) return NULL; } @@ -293,9 +300,8 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, int new_state) { int err = -1, phc_index = -1, phc_switched = 0, state, timestamping; + char iface[IFNAMSIZ], phc_device[19]; struct port *p; - struct sk_ts_info ts_info; - char iface[IFNAMSIZ]; clockid_t clkid = CLOCK_INVALID; LIST_FOREACH(p, &priv->ports, list) { @@ -304,7 +310,8 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, } err = pmc_agent_query_port_properties(priv->agent, 1000, p->number, &state, - ×tamping, iface); + ×tamping, &phc_index, + iface); if (!err) { p->state = normalize_state(state); } @@ -318,9 +325,10 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, clock->device = strdup(iface); } /* Check if phc index changed */ - if (!sk_get_ts_info(clock->device, &ts_info) && - clock->phc_index != ts_info.phc_index) { - clkid = posix_clock_open(clock->device, &phc_index); + if (clock->phc_index != phc_index) { + snprintf(phc_device, sizeof(phc_device), "/dev/ptp%d", + phc_index); + clkid = posix_clock_open(phc_device, &phc_index); if (clkid == CLOCK_INVALID) return; @@ -844,7 +852,7 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, static int auto_init_ports(struct phc2sys_private *priv, int add_rt) { - int err, number_ports, state, timestamping; + int err, number_ports, phc_index, state, timestamping; char iface[IFNAMSIZ]; struct clock *clock; struct port *port; @@ -880,7 +888,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) for (i = 1; i <= number_ports; i++) { err = pmc_agent_query_port_properties(priv->agent, 1000, i, &state, ×tamping, - iface); + &phc_index, iface); if (err == -ENODEV) { /* port does not exist, ignore the port */ continue; @@ -893,7 +901,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) /* ignore ports with software time stamping */ continue; } - port = port_add(priv, i, iface); + port = port_add(priv, i, iface, phc_index); if (!port) return -1; port->state = normalize_state(state); @@ -908,7 +916,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) priv->state_changed = 1; if (add_rt) { - clock = clock_add(priv, "CLOCK_REALTIME"); + clock = clock_add(priv, "CLOCK_REALTIME", -1); if (!clock) return -1; if (add_rt == 1) @@ -991,7 +999,7 @@ static int phc2sys_static_configuration(struct phc2sys_private *priv, { struct clock *src, *dst; - src = clock_add(priv, src_name); + src = clock_add(priv, src_name, -1); if (!src) { fprintf(stderr, "valid source clock must be selected.\n"); return -1; @@ -999,7 +1007,7 @@ static int phc2sys_static_configuration(struct phc2sys_private *priv, src->state = PS_SLAVE; priv->master = src; - dst = clock_add(priv, dst_name); + dst = clock_add(priv, dst_name, -1); if (!dst) { fprintf(stderr, "valid destination clock must be selected.\n"); return -1; diff --git a/pmc_agent.c b/pmc_agent.c index 86350d8..79971e2 100644 --- a/pmc_agent.c +++ b/pmc_agent.c @@ -304,9 +304,10 @@ int pmc_agent_query_dds(struct pmc_agent *node, int timeout) int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, unsigned int port, int *state, - int *tstamping, char *iface) + int *tstamping, int *phc_index, char *iface) { struct port_properties_np *ppn; + struct port_hwclock_np *phn; struct ptp_message *msg; int res, len; @@ -330,6 +331,21 @@ int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, memcpy(iface, ppn->interface.text, len); iface[len] = '\0'; + msg_put(msg); + break; + } + while (1) { + res = run_pmc(node, timeout, MID_PORT_HWCLOCK_NP, &msg); + if (is_run_pmc_error(res)) { + goto out; + } + phn = management_tlv_data(msg); + if (phn->portIdentity.portNumber != port) { + msg_put(msg); + continue; + } + *phc_index = phn->phc_index; + msg_put(msg); res = RUN_PMC_OKAY; break; diff --git a/pmc_agent.h b/pmc_agent.h index 11b9347..ad4b771 100644 --- a/pmc_agent.h +++ b/pmc_agent.h @@ -103,12 +103,14 @@ int pmc_agent_query_dds(struct pmc_agent *agent, int timeout); * @param port The port index of interest. * @param state Buffer to hold the returned port state. * @param tstamping Buffer to hold the returned time stamping flavor. + * @param phc_index Buffer to hold the returned PHC index. * @param iface Buffer to hold the returned interface name. * @return Zero on success, negative error code otherwise. */ int pmc_agent_query_port_properties(struct pmc_agent *agent, int timeout, unsigned int port, int *state, - int *tstamping, char *iface); + int *tstamping, int *phc_index, + char *iface); /** * Queries the TAI-UTC offset and the current leap adjustment from the -- 2.34.1 |
From: Miroslav L. <mli...@re...> - 2022-02-03 11:50:30
|
Add "use_vclocks" option to enable synchronization with virtual clocks. This enables multiple ptp4l instances sharing an interface to use hardware timestamping. By default, vclocks are enabled if running on Linux 5.18 or later, which should have all features to make them work as well as physical clocks. When preparing the script, count how many vclocks are needed for each physical clock. Add a placeholder option in the form of "--phc_index %PHC0-0%" to the generated ptp4l commands that need hardware clock. The index of the virtual clock is unknown at this point. When running the script (not just printing), create the required number of virtual clocks by writing to /sys/.../n_vclocks and fix the --phc_index options to refer to the indices of the created vclocks. On exit, remove the virtual clocks to restore the original state. Signed-off-by: Miroslav Lichvar <mli...@re...> --- timemaster.8 | 14 +++- timemaster.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 199 insertions(+), 11 deletions(-) diff --git a/timemaster.8 b/timemaster.8 index a11f4dd..2891a97 100644 --- a/timemaster.8 +++ b/timemaster.8 @@ -97,6 +97,15 @@ configuration error). If a process was terminated and is not started again, \fBtimemaster\fR will kill the other processes and exit with a non-zero status. The default value is 1 (enabled). +.TP +.B use_vclocks +Enable or disable synchronization with virtual clocks. If enabled, +\fBtimemaster\fR will create virtual clocks running on top of physical clocks +needed by configured PTP domains. This enables hardware time stamping for +multiple \fBptp4l\fR instances using the same network interface. The default +value is -1, which enables the virtual clocks if running on Linux 5.18 or +later. + .SS [ntp_server address] The \fBntp_server\fR section specifies an NTP server that should be used as a @@ -140,8 +149,8 @@ Specify which network interfaces should be used for this PTP domain. A separate \fBptp4l\fR instance will be started for each group of interfaces sharing the same PHC and for each interface that supports only SW time stamping. HW time stamping is enabled automatically. If an interface with HW time stamping is -specified also in other PTP domains, only the \fBptp4l\fR instance from the -first PTP domain will be using HW time stamping. +specified also in other PTP domains and virtual clocks are disabled, only the +\fBptp4l\fR instance from the first PTP domain will be using HW time stamping. .TP .B ntp_poll @@ -333,6 +342,7 @@ ntp_program chronyd rundir /var/run/timemaster first_shm_segment 1 restart_processes 0 +use_vclocks 0 [chronyd] path /usr/sbin/chronyd diff --git a/timemaster.c b/timemaster.c index 616a99a..cafd3a3 100644 --- a/timemaster.c +++ b/timemaster.c @@ -20,6 +20,7 @@ #include <ctype.h> #include <errno.h> +#include <glob.h> #include <libgen.h> #include <limits.h> #include <time.h> @@ -33,6 +34,7 @@ #include <string.h> #include <sys/stat.h> #include <sys/types.h> +#include <sys/utsname.h> #include <sys/wait.h> #include <unistd.h> @@ -46,6 +48,7 @@ #define DEFAULT_FIRST_SHM_SEGMENT 0 #define DEFAULT_RESTART_PROCESSES 1 +#define DEFAULT_USE_VCLOCKS -1 #define DEFAULT_NTP_PROGRAM CHRONYD #define DEFAULT_NTP_MINPOLL 6 @@ -111,6 +114,7 @@ struct timemaster_config { char *rundir; int first_shm_segment; int restart_processes; + int use_vclocks; struct program_config chronyd; struct program_config ntpd; struct program_config phc2sys; @@ -122,7 +126,13 @@ struct config_file { char *content; }; +struct phc_vclocks { + int pclock_index; + int vclocks; +}; + struct script { + struct phc_vclocks **vclocks; struct config_file **configs; char ***commands; int **command_groups; @@ -393,6 +403,8 @@ static int parse_timemaster_settings(char **settings, r = parse_int(value, &config->first_shm_segment); } else if (!strcasecmp(name, "restart_processes")) { r = parse_int(value, &config->restart_processes); + } else if (!strcasecmp(name, "use_vclocks")) { + r = parse_int(value, &config->use_vclocks); } else { pr_err("unknown timemaster setting %s", name); return 1; @@ -520,6 +532,20 @@ static void config_destroy(struct timemaster_config *config) free(config); } +static int check_kernel_version(int version, int patch) +{ + struct utsname uts; + int v, p; + + if (uname(&uts) < 0) + return 1; + if (sscanf(uts.release, "%d.%d", &v, &p) < 2) + return 1; + if (version > v || (version == v && patch > p)) + return 1; + return 0; +} + static struct timemaster_config *config_parse(char *path) { struct timemaster_config *config = xcalloc(1, sizeof(*config)); @@ -533,6 +559,7 @@ static struct timemaster_config *config_parse(char *path) config->rundir = xstrdup(DEFAULT_RUNDIR); config->first_shm_segment = DEFAULT_FIRST_SHM_SEGMENT; config->restart_processes = DEFAULT_RESTART_PROCESSES; + config->use_vclocks = DEFAULT_USE_VCLOCKS; init_program_config(&config->chronyd, "chronyd", NULL, DEFAULT_CHRONYD_SETTINGS, NULL); @@ -593,6 +620,9 @@ static struct timemaster_config *config_parse(char *path) fclose(f); + if (config->use_vclocks < 0) + config->use_vclocks = !check_kernel_version(5, 18); + if (section_name) free(section_name); if (section_lines) @@ -608,7 +638,7 @@ static struct timemaster_config *config_parse(char *path) static char **get_ptp4l_command(struct program_config *config, struct config_file *file, char **interfaces, - int hw_ts) + char *phc_index, int hw_ts) { char **command = (char **)parray_new(); @@ -617,6 +647,9 @@ static char **get_ptp4l_command(struct program_config *config, parray_extend((void ***)&command, xstrdup("-f"), xstrdup(file->path), xstrdup(hw_ts ? "-H" : "-S"), NULL); + if (phc_index && phc_index[0]) + parray_extend((void ***)&command, + xstrdup("--phc_index"), xstrdup(phc_index), NULL); for (; *interfaces; interfaces++) parray_extend((void ***)&command, @@ -706,6 +739,24 @@ static int add_ntp_source(struct ntp_server *source, char **ntp_config) return 0; } +static int add_vclock(struct script *script, int pclock_index) +{ + struct phc_vclocks **vclocks, *v; + + for (vclocks = script->vclocks; *vclocks; vclocks++) { + if ((*vclocks)->pclock_index != pclock_index) + continue; + return (*vclocks)->vclocks++; + } + + v = xmalloc(sizeof(*v)); + v->pclock_index = pclock_index; + v->vclocks = 1; + parray_append((void ***)&script->vclocks, v); + + return 0; +} + static int add_ptp_source(struct ptp_domain *source, struct timemaster_config *config, int *shm_segment, int *command_group, int ***allocated_phcs, @@ -713,7 +764,7 @@ static int add_ptp_source(struct ptp_domain *source, { struct config_file *config_file; char **command, *uds_path, *uds_path2, **interfaces, *message_tag; - char ts_interface[IF_NAMESIZE]; + char ts_interface[IF_NAMESIZE], vclock_index[20]; int i, j, num_interfaces, *phc, *phcs, hw_ts, sw_ts; struct sk_ts_info ts_info; @@ -801,10 +852,18 @@ static int add_ptp_source(struct ptp_domain *source, } } - /* don't use this PHC in other sources */ - phc = xmalloc(sizeof(int)); - *phc = phcs[i]; - parray_append((void ***)allocated_phcs, phc); + if (config->use_vclocks) { + /* request new vclock for the PHC */ + int vclock = add_vclock(script, phcs[i]); + snprintf(vclock_index, sizeof(vclock_index), + "%%PHC%d-%d%%", phcs[i], vclock); + } else { + /* don't use this PHC in other sources */ + phc = xmalloc(sizeof(int)); + *phc = phcs[i]; + parray_append((void ***)allocated_phcs, phc); + vclock_index[0] = '\0'; + } } uds_path = string_newf("%s/ptp4l.%d.socket", @@ -842,7 +901,8 @@ static int add_ptp_source(struct ptp_domain *source, if (phcs[i] >= 0) { /* HW time stamping */ command = get_ptp4l_command(&config->ptp4l, config_file, - interfaces, 1); + interfaces, + vclock_index, 1); add_command(command, *command_group, script); command = get_phc2sys_command(&config->phc2sys, @@ -854,7 +914,7 @@ static int add_ptp_source(struct ptp_domain *source, } else { /* SW time stamping */ command = get_ptp4l_command(&config->ptp4l, config_file, - interfaces, 0); + interfaces, NULL, 0); add_command(command, (*command_group)++, script); string_appendf(&config_file->content, @@ -943,6 +1003,11 @@ static void script_destroy(struct script *script) char ***commands, **command; int **groups; struct config_file *config, **configs; + struct phc_vclocks **vclocks; + + for (vclocks = script->vclocks; *vclocks; vclocks++) + free(*vclocks); + free(script->vclocks); for (configs = script->configs; *configs; configs++) { config = *configs; @@ -974,6 +1039,7 @@ static struct script *script_create(struct timemaster_config *config) int **allocated_phcs = (int **)parray_new(); int ret = 0, shm_segment, command_group = 0; + script->vclocks = (struct phc_vclocks **)parray_new(); script->configs = (struct config_file **)parray_new(); script->commands = (char ***)parray_new(); script->command_groups = (int **)parray_new(); @@ -1116,6 +1182,102 @@ static int remove_config_files(struct config_file **configs) return 0; } +static int set_phc_n_vclocks(int phc_index, int n_vclocks) +{ + char path[PATH_MAX]; + FILE *f; + + snprintf(path, sizeof(path), "/sys/class/ptp/ptp%d/n_vclocks", + phc_index); + f = fopen(path, "w"); + if (!f) { + pr_err("failed to open %s: %m", path); + return 1; + } + fprintf(f, "%d\n", n_vclocks); + fclose(f); + + return 0; +} + +static int create_vclocks(struct phc_vclocks **phc_vclocks) +{ + struct phc_vclocks **vclocks; + + for (vclocks = phc_vclocks; *vclocks; vclocks++) { + if (set_phc_n_vclocks((*vclocks)->pclock_index, + (*vclocks)->vclocks)) + return 1; + } + + return 0; +} + +static int remove_vclocks(struct phc_vclocks **phc_vclocks) +{ + struct phc_vclocks **vclocks; + + for (vclocks = phc_vclocks; *vclocks; vclocks++) { + if (set_phc_n_vclocks((*vclocks)->pclock_index, 0)) + return 1; + } + + return 0; +} + +static int get_vclock_index(int pindex, int vclock) +{ + char pattern[PATH_MAX], *s; + int n, vindex; + glob_t gl; + + snprintf(pattern, sizeof(pattern), "/sys/class/ptp/ptp%d/ptp[0-9]*", + pindex); + + if (glob(pattern, 0, NULL, &gl)) { + pr_err("glob(%s) failed", pattern); + return -1; + } + + if (vclock >= gl.gl_pathc || + !(s = strrchr(gl.gl_pathv[vclock], '/')) || + sscanf(s + 1, "ptp%d%n", &vindex, &n) != 1 || + n != strlen(s + 1)) { + pr_err("missing vclock %d:%d", pindex, vclock); + globfree(&gl); + return -1; + } + + globfree(&gl); + + return vindex; +} + +static int translate_vclock_options(char ***commands) +{ + int n, pindex, vclock, vindex, blen; + char **command; + + for (; *commands; commands++) { + for (command = *commands; *command; command++) { + if (sscanf(*command, "%%PHC%d-%d%%%n", + &pindex, &vclock, &n) != 2 || + n != strlen(*command)) + continue; + vindex = get_vclock_index(pindex, vclock); + if (vindex < 0) + return 1; + + /* overwrite the string with the vclock PHC index */ + blen = strlen(*command) + 1; + if (snprintf(*command, blen, "%d", vindex) >= blen) + return 1; + } + } + + return 0; +} + static int script_run(struct script *script) { struct timespec ts_start, ts_now; @@ -1135,6 +1297,12 @@ static int script_run(struct script *script) if (create_config_files(script->configs)) return 1; + if (create_vclocks(script->vclocks)) + return 1; + + if (translate_vclock_options(script->commands)) + return 1; + sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGTERM); @@ -1278,6 +1446,9 @@ static int script_run(struct script *script) free(pids); + if (remove_vclocks(script->vclocks)) + return 1; + if (remove_config_files(script->configs)) return 1; @@ -1289,13 +1460,20 @@ static void script_print(struct script *script) char ***commands, **command; int **groups; struct config_file *config, **configs; + struct phc_vclocks **vclocks; for (configs = script->configs; *configs; configs++) { config = *configs; fprintf(stderr, "%s:\n\n%s\n", config->path, config->content); } - fprintf(stderr, "commands:\n\n"); + fprintf(stderr, "virtual clocks:\n\n"); + for (vclocks = script->vclocks; *vclocks; vclocks++) { + fprintf(stderr, "PHC%d: %d\n", + (*vclocks)->pclock_index, (*vclocks)->vclocks); + } + + fprintf(stderr, "\ncommands:\n\n"); for (commands = script->commands, groups = script->command_groups; *commands; commands++, groups++) { fprintf(stderr, "[%d] ", **groups); -- 2.34.1 |
From: Richard C. <ric...@gm...> - 2022-02-03 19:36:30
|
On Thu, Feb 03, 2022 at 12:50:06PM +0100, Miroslav Lichvar wrote: > Add a function using ethtool netlink to check whether a PHC is a virtual > clock of an interface. > > Signed-off-by: Miroslav Lichvar <mli...@re...> > Acked-by: Hangbin Liu <liu...@gm...> > --- > rtnl.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > rtnl.h | 9 +++++++ > 2 files changed, 92 insertions(+) > > diff --git a/rtnl.c b/rtnl.c > index f8bdbe6..a0cf494 100644 > --- a/rtnl.c > +++ b/rtnl.c > @@ -19,6 +19,7 @@ > #include <asm/types.h> > #include <sys/socket.h> /* Must come before linux/netlink.h on some systems. */ > #include <linux/netlink.h> > +#include <linux/ethtool_netlink.h> > #include <linux/rtnetlink.h> > #include <linux/genetlink.h> > #include <linux/if_team.h> > @@ -465,3 +466,85 @@ no_info: > nl_close(fd); > return index; > } > + > +static int search_vclocks(struct rtattr *attr, int phc_index) Nit: please prefix with rtnl_ Thanks, Richard |
From: Miroslav L. <mli...@re...> - 2022-02-10 10:57:52
|
On Thu, Feb 03, 2022 at 11:36:12AM -0800, Richard Cochran wrote: > On Thu, Feb 03, 2022 at 12:50:06PM +0100, Miroslav Lichvar wrote: > > diff --git a/rtnl.c b/rtnl.c > > @@ -465,3 +466,85 @@ no_info: > > nl_close(fd); > > return index; > > } > > + > > +static int search_vclocks(struct rtattr *attr, int phc_index) > > Nit: please prefix with rtnl_ Even when it is a static function? Most static functions in the codebase don't seems to have a matching prefix, so I'm unsure why this one. In any case, would you like me to send a v2 now or should I wait for comments on the other patches from the series? Thanks, -- Miroslav Lichvar |
From: Richard C. <ric...@gm...> - 2022-02-10 22:58:29
|
On Thu, Feb 10, 2022 at 11:57:40AM +0100, Miroslav Lichvar wrote: > On Thu, Feb 03, 2022 at 11:36:12AM -0800, Richard Cochran wrote: > > Nit: please prefix with rtnl_ > > Even when it is a static function? Most static functions in the > codebase don't seems to have a matching prefix, so I'm unsure why > this one. You are right, of course, but as the code base grows it gets more and more useful for grep/cscope. So for new code, I'd like to have the pendantic prefixes. > In any case, would you like me to send a v2 now or should I wait for > comments on the other patches from the series? Looks good otherwise, please send v2. Thanks, Richard |