diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8069dde --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +SOURCES/clknetsim-ce89a1.tar.gz +SOURCES/linuxptp-1.8.tgz +SOURCES/linuxptp-testsuite-502e82.tar.gz diff --git a/.linuxptp.metadata b/.linuxptp.metadata new file mode 100644 index 0000000..bb51355 --- /dev/null +++ b/.linuxptp.metadata @@ -0,0 +1,3 @@ +922f475728383b56b03eeadaf990c9231a51a9e3 SOURCES/clknetsim-ce89a1.tar.gz +449cf190347d24846440bd757570e391dd7dff96 SOURCES/linuxptp-1.8.tgz +1b6395c22a47a4af60db6963d063697ef6daae08 SOURCES/linuxptp-testsuite-502e82.tar.gz diff --git a/README.md b/README.md deleted file mode 100644 index 0e7897f..0000000 --- a/README.md +++ /dev/null @@ -1,5 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 - -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/linuxptp-bonding.patch b/SOURCES/linuxptp-bonding.patch new file mode 100644 index 0000000..b3737bb --- /dev/null +++ b/SOURCES/linuxptp-bonding.patch @@ -0,0 +1,1671 @@ +commit 17aa750a49ebaecfc7b063c770aa8d36f5078b2c +Author: Hangbin Liu +Date: Mon Jul 10 15:39:28 2017 +0800 + + rtnl: replace obsolete RTMGRP_LINK with RTNLGRP_LINK for nl groups + + Signed-off-by: Hangbin Liu + +diff --git a/rtnl.c b/rtnl.c +index 5cddc4b..d7a430d 100644 +--- a/rtnl.c ++++ b/rtnl.c +@@ -151,7 +151,7 @@ int rtnl_open(void) + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; +- sa.nl_groups = RTMGRP_LINK; ++ sa.nl_groups = RTNLGRP_LINK; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) { +commit c149a3dbc1b38e833de783ae863038562baca0fe +Author: Hangbin Liu +Date: Mon Jul 10 15:39:29 2017 +0800 + + port: add FD_RTNL event to track per-port status + + With rtnl socket we can track link status per port(except UDS port). + + We can make sure we get the correct interface and latest status with function + port_link_status(). + + At the same time we need to set clock sde after link down. But we return + EV_FAULT_DETECTED in port_event(), which will not set clock sde. So we need + to set it in port_link_status(). + + Signed-off-by: Hangbin Liu + +diff --git a/clock.c b/clock.c +index b6afba9..4c5c4e3 100644 +--- a/clock.c ++++ b/clock.c +@@ -1469,6 +1469,11 @@ struct PortIdentity clock_parent_identity(struct clock *c) + return c->dad.pds.parentPortIdentity; + } + ++void clock_set_sde(struct clock *c, int sde) ++{ ++ c->sde = sde; ++} ++ + int clock_poll(struct clock *c) + { + int cnt, i; +diff --git a/clock.h b/clock.h +index fcd9328..49ecb76 100644 +--- a/clock.h ++++ b/clock.h +@@ -205,6 +205,13 @@ void clock_peer_delay(struct clock *c, tmv_t ppd, tmv_t req, tmv_t rx, + double nrr); + + /** ++ * Set clock sde ++ * @param c A pointer to a clock instance obtained with clock_create(). ++ * @param sde Pass one (1) if need a decision event and zero if not. ++ */ ++void clock_set_sde(struct clock *c, int sde); ++ ++/** + * Poll for events and dispatch them. + * @param c A pointer to a clock instance obtained with clock_create(). + * @return Zero on success, non-zero otherwise. +diff --git a/fd.h b/fd.h +index e328e98..23401f4 100644 +--- a/fd.h ++++ b/fd.h +@@ -31,6 +31,7 @@ enum { + FD_QUALIFICATION_TIMER, + FD_MANNO_TIMER, + FD_SYNC_TX_TIMER, ++ FD_RTNL, + N_POLLFD, + }; + +diff --git a/port.c b/port.c +index ec02825..2896d1a 100644 +--- a/port.c ++++ b/port.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #include "bmc.h" + #include "clock.h" +@@ -32,6 +33,7 @@ + #include "phc.h" + #include "port.h" + #include "print.h" ++#include "rtnl.h" + #include "sk.h" + #include "tlv.h" + #include "tmv.h" +@@ -1458,7 +1460,9 @@ static void port_disable(struct port *p) + for (i = 0; i < N_TIMER_FDS; i++) { + close(p->fda.fd[FD_ANNOUNCE_TIMER + i]); + } +- port_clear_fda(p, N_POLLFD); ++ ++ /* Keep rtnl socket to get link status info. */ ++ port_clear_fda(p, FD_RTNL); + clock_fda_changed(p->clock); + } + +@@ -1502,6 +1506,14 @@ static int port_initialize(struct port *p) + if (port_set_announce_tmo(p)) + goto no_tmo; + ++ /* No need to open rtnl socket on UDS port. */ ++ if (transport_type(p->trp) != TRANS_UDS) { ++ if (p->fda.fd[FD_RTNL] == -1) ++ p->fda.fd[FD_RTNL] = rtnl_open(); ++ if (p->fda.fd[FD_RTNL] >= 0) ++ rtnl_link_query(p->fda.fd[FD_RTNL]); ++ } ++ + port_nrate_initialize(p); + + clock_fda_changed(p->clock); +@@ -2025,6 +2037,10 @@ void port_close(struct port *p) + if (port_is_enabled(p)) { + port_disable(p); + } ++ ++ if (p->fda.fd[FD_RTNL] >= 0) ++ rtnl_close(p->fda.fd[FD_RTNL]); ++ + transport_destroy(p->trp); + tsproc_destroy(p->tsproc); + if (p->fault_fd >= 0) +@@ -2204,6 +2220,24 @@ void port_dispatch(struct port *p, enum fsm_event event, int mdiff) + } + } + ++static void port_link_status(void *ctx, int index, int linkup) ++{ ++ struct port *p = ctx; ++ ++ if (index != if_nametoindex(p->name) || p->link_status == linkup) ++ return; ++ ++ p->link_status = linkup; ++ pr_notice("port %hu: link %s", portnum(p), linkup ? "up" : "down"); ++ ++ /* ++ * A port going down can affect the BMCA result. ++ * Force a state decision event. ++ */ ++ if (!p->link_status) ++ clock_set_sde(p->clock, 1); ++} ++ + enum fsm_event port_event(struct port *p, int fd_index) + { + enum fsm_event event = EV_NONE; +@@ -2242,6 +2276,11 @@ enum fsm_event port_event(struct port *p, int fd_index) + pr_debug("port %hu: master sync timeout", portnum(p)); + port_set_sync_tx_tmo(p); + return port_tx_sync(p) ? EV_FAULT_DETECTED : EV_NONE; ++ ++ case FD_RTNL: ++ pr_debug("port %hu: received link status notification", portnum(p)); ++ rtnl_link_status(fd, port_link_status, p); ++ return port_link_status_get(p) ? EV_FAULT_CLEARED : EV_FAULT_DETECTED; + } + + msg = msg_allocate(); +commit 25ec8a3b4e2222b394794ff830bd3583e9cf6c71 +Author: Hangbin Liu +Date: Mon Jul 10 15:39:30 2017 +0800 + + clock: remove rtnl fd on clock + + Remove rtnl fd on clock since we track the link status per port now. + + Signed-off-by: Hangbin Liu + +diff --git a/clock.c b/clock.c +index 4c5c4e3..354f038 100644 +--- a/clock.c ++++ b/clock.c +@@ -39,7 +39,6 @@ + #include "servo.h" + #include "stats.h" + #include "print.h" +-#include "rtnl.h" + #include "sk.h" + #include "tlv.h" + #include "tsproc.h" +@@ -274,9 +273,6 @@ void clock_destroy(struct clock *c) + LIST_FOREACH_SAFE(p, &c->ports, list, tmp) { + clock_remove_port(c, p); + } +- if (c->pollfd[0].fd >= 0) { +- rtnl_close(c->pollfd[0].fd); +- } + port_close(c->uds_port); + free(c->pollfd); + hash_destroy(c->index2port, NULL); +@@ -326,30 +322,6 @@ static void clock_freq_est_reset(struct clock *c) + c->fest.count = 0; + } + +-static void clock_link_status(void *ctx, int index, int linkup) +-{ +- struct clock *c = ctx; +- struct port *p; +- char key[16]; +- +- snprintf(key, sizeof(key), "%d", index); +- p = hash_lookup(c->index2port, key); +- if (!p) { +- return; +- } +- port_link_status_set(p, linkup); +- if (linkup) { +- port_dispatch(p, EV_FAULT_CLEARED, 0); +- } else { +- port_dispatch(p, EV_FAULT_DETECTED, 0); +- /* +- * A port going down can affect the BMCA result. +- * Force a state decision event. +- */ +- c->sde = 1; +- } +-} +- + static void clock_management_send_error(struct port *p, + struct ptp_message *msg, int error_id) + { +@@ -1133,10 +1105,6 @@ struct clock *clock_create(enum clock_type type, struct config *config, + return NULL; + } + +- /* Open a RT netlink socket. */ +- c->pollfd[0].fd = rtnl_open(); +- c->pollfd[0].events = POLLIN|POLLPRI; +- + /* Create the UDS interface. */ + c->uds_port = port_open(phc_index, timestamping, 0, udsif, c); + if (!c->uds_port) { +@@ -1164,9 +1132,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, + port_dispatch(p, EV_INITIALIZE, 0); + } + port_dispatch(c->uds_port, EV_INITIALIZE, 0); +- if (c->pollfd[0].fd >= 0) { +- rtnl_link_query(c->pollfd[0].fd); +- } ++ + return c; + } + +@@ -1231,12 +1197,9 @@ static int clock_resize_pollfd(struct clock *c, int new_nports) + { + struct pollfd *new_pollfd; + +- /* +- * Need to allocate one descriptor for RT netlink and one +- * whole extra block of fds for UDS. +- */ ++ /* Need to allocate one whole extra block of fds for UDS. */ + new_pollfd = realloc(c->pollfd, +- (1 + (new_nports + 1) * N_CLOCK_PFD) * ++ (new_nports + 1) * N_CLOCK_PFD * + sizeof(struct pollfd)); + if (!new_pollfd) + return -1; +@@ -1261,7 +1224,7 @@ static void clock_fill_pollfd(struct pollfd *dest, struct port *p) + static void clock_check_pollfd(struct clock *c) + { + struct port *p; +- struct pollfd *dest = c->pollfd + 1; ++ struct pollfd *dest = c->pollfd; + + if (c->pollfd_valid) + return; +@@ -1482,7 +1445,7 @@ int clock_poll(struct clock *c) + struct port *p; + + clock_check_pollfd(c); +- cnt = poll(c->pollfd, 1 + (c->nports + 1) * N_CLOCK_PFD, -1); ++ cnt = poll(c->pollfd, (c->nports + 1) * N_CLOCK_PFD, -1); + if (cnt < 0) { + if (EINTR == errno) { + return 0; +@@ -1494,13 +1457,8 @@ int clock_poll(struct clock *c) + return 0; + } + +- /* Check the RT netlink. */ + cur = c->pollfd; +- if (cur->revents & (POLLIN|POLLPRI)) { +- rtnl_link_status(cur->fd, clock_link_status, c); +- } + +- cur++; + LIST_FOREACH(p, &c->ports, list) { + /* Let the ports handle their events. */ + for (i = 0; i < N_POLLFD; i++) { +diff --git a/port.c b/port.c +index 2896d1a..34837cc 100644 +--- a/port.c ++++ b/port.c +@@ -2411,12 +2411,6 @@ int port_link_status_get(struct port *p) + return p->link_status; + } + +-void port_link_status_set(struct port *p, int up) +-{ +- p->link_status = up ? 1 : 0; +- pr_notice("port %hu: link %s", portnum(p), up ? "up" : "down"); +-} +- + int port_manage(struct port *p, struct port *ingress, struct ptp_message *msg) + { + struct management_tlv *mgt; +diff --git a/port.h b/port.h +index b00bc64..60fd0a4 100644 +--- a/port.h ++++ b/port.h +@@ -132,13 +132,6 @@ int port_number(struct port *p); + int port_link_status_get(struct port *p); + + /** +- * Sets the link status for a port. +- * @param p A port instance. +- * @param up Pass one (1) if the link is up and zero if down. +- */ +-void port_link_status_set(struct port *p, int up); +- +-/** + * Manage a port according to a given message. + * @param p A pointer previously obtained via port_open(). + * @param ingress The port on which 'msg' was received. +commit 7c3f9579f0c230ee798644a26d664ebd3efc612f +Author: Hangbin Liu +Date: Mon Jul 10 15:39:31 2017 +0800 + + clock: remove hash table index2port + + Remove index2port since we don't need it now. + + Signed-off-by: Hangbin Liu + +diff --git a/clock.c b/clock.c +index 354f038..da15882 100644 +--- a/clock.c ++++ b/clock.c +@@ -31,7 +31,6 @@ + #include "clockcheck.h" + #include "foreign.h" + #include "filter.h" +-#include "hash.h" + #include "missing.h" + #include "msg.h" + #include "phc.h" +@@ -39,7 +38,6 @@ + #include "servo.h" + #include "stats.h" + #include "print.h" +-#include "sk.h" + #include "tlv.h" + #include "tsproc.h" + #include "uds.h" +@@ -96,7 +94,6 @@ struct clock { + int nports; /* does not include the UDS port */ + int last_port_number; + int sde; +- struct hash *index2port; + int free_running; + int freq_est_interval; + int grand_master_capable; /* for 802.1AS only */ +@@ -275,7 +272,6 @@ void clock_destroy(struct clock *c) + } + port_close(c->uds_port); + free(c->pollfd); +- hash_destroy(c->index2port, NULL); + if (c->clkid != CLOCK_REALTIME) { + phc_close(c->clkid); + } +@@ -773,8 +769,6 @@ static int clock_add_port(struct clock *c, int phc_index, + struct interface *iface) + { + struct port *p, *piter, *lastp = NULL; +- int fd, index; +- char key[16]; + + if (clock_resize_pollfd(c, c->nports + 1)) { + return -1; +@@ -795,24 +789,6 @@ static int clock_add_port(struct clock *c, int phc_index, + c->nports++; + clock_fda_changed(c); + +- /* Remember the index to port mapping, for link status tracking. */ +- fd = sk_interface_fd(); +- if (fd < 0) { +- return -1; +- } +- index = sk_interface_index(fd, iface->name); +- if (index < 0) { +- close(fd); +- return -1; +- } +- snprintf(key, sizeof(key), "%d", index); +- if (hash_insert(c->index2port, key, p)) { +- pr_err("failed to add port with index %d twice!", index); +- close(fd); +- return -1; +- } +- close(fd); +- + return 0; + } + +@@ -1113,11 +1089,6 @@ struct clock *clock_create(enum clock_type type, struct config *config, + } + clock_fda_changed(c); + +- c->index2port = hash_create(); +- if (!c->index2port) { +- pr_err("failed create index-to-port hash table"); +- return NULL; +- } + /* Create the ports. */ + STAILQ_FOREACH(iface, &config->interfaces, list) { + if (clock_add_port(c, phc_index, timestamping, iface)) { +commit 9e744d9e8a2dab6ec0ea5ece5ac25e7384264575 +Author: Hangbin Liu +Date: Mon Oct 9 22:31:39 2017 +0800 + + config: add new element ts_label in struct interface + + Add new element ts_label in struct interface to track real ts interface. + + Signed-off-by: Hangbin Liu + +diff --git a/config.h b/config.h +index 1cc7051..c79855e 100644 +--- a/config.h ++++ b/config.h +@@ -36,6 +36,7 @@ + struct interface { + STAILQ_ENTRY(interface) list; + char name[MAX_IFNAME_SIZE + 1]; ++ char ts_label[MAX_IFNAME_SIZE + 1]; + struct sk_ts_info ts_info; + }; + +commit 7e294a4d047654f746c3fcdff7bce512be149e37 +Author: Hangbin Liu +Date: Mon Oct 9 22:31:40 2017 +0800 + + port: track interface info in port + + Signed-off-by: Hangbin Liu + +diff --git a/port.c b/port.c +index 34837cc..849a7c1 100644 +--- a/port.c ++++ b/port.c +@@ -68,6 +68,7 @@ struct nrate_estimator { + struct port { + LIST_ENTRY(port) list; + char *name; ++ struct interface *iface; + struct clock *clock; + struct transport *trp; + enum timestamp_type timestamping; +@@ -2619,6 +2620,7 @@ struct port *port_open(int phc_index, + } + + p->name = interface->name; ++ p->iface = interface; + p->asymmetry = config_get_int(cfg, p->name, "delayAsymmetry"); + p->asymmetry <<= 16; + p->announce_span = transport == TRANS_UDS ? 0 : ANNOUNCE_SPAN; +commit 05bba46198cfc4ccfe0aff2e67e76d78898bbd96 +Author: Hangbin Liu +Date: Mon Oct 9 22:31:41 2017 +0800 + + rtnl: update rtgenmsg to ifinfomsg when request link info + + The previous function use general message and will dump all interfaces' + information. Now update with ifinfomsg so we could get specific interface's + information. + + We still could get all interfaces' info if set device to NULL. + + Signed-off-by: Hangbin Liu + +diff --git a/port.c b/port.c +index 849a7c1..5b85d87 100644 +--- a/port.c ++++ b/port.c +@@ -1512,7 +1512,7 @@ static int port_initialize(struct port *p) + if (p->fda.fd[FD_RTNL] == -1) + p->fda.fd[FD_RTNL] = rtnl_open(); + if (p->fda.fd[FD_RTNL] >= 0) +- rtnl_link_query(p->fda.fd[FD_RTNL]); ++ rtnl_link_query(p->fda.fd[FD_RTNL], p->iface->name); + } + + port_nrate_initialize(p); +diff --git a/rtnl.c b/rtnl.c +index d7a430d..8ecf6fe 100644 +--- a/rtnl.c ++++ b/rtnl.c +@@ -42,7 +42,7 @@ int rtnl_close(int fd) + return close(fd); + } + +-int rtnl_link_query(int fd) ++int rtnl_link_query(int fd, char *device) + { + struct sockaddr_nl sa; + struct msghdr msg; +@@ -51,19 +51,21 @@ int rtnl_link_query(int fd) + + struct { + struct nlmsghdr hdr; +- struct rtgenmsg gen; ++ struct ifinfomsg ifm; + } __attribute__((packed)) request; + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + + memset(&request, 0, sizeof(request)); +- request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.gen)); ++ request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.ifm)); + request.hdr.nlmsg_type = RTM_GETLINK; +- request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; ++ request.hdr.nlmsg_flags = NLM_F_REQUEST; + request.hdr.nlmsg_seq = 1; + request.hdr.nlmsg_pid = 0; +- request.gen.rtgen_family = AF_UNSPEC; ++ request.ifm.ifi_family = AF_UNSPEC; ++ request.ifm.ifi_index = if_nametoindex(device ? device : ""); ++ request.ifm.ifi_change = 0xffffffff; + + iov.iov_base = &request; + iov.iov_len = sizeof(request); +diff --git a/rtnl.h b/rtnl.h +index f1871f2..5c93eec 100644 +--- a/rtnl.h ++++ b/rtnl.h +@@ -31,10 +31,11 @@ int rtnl_close(int fd); + + /** + * Request the link status from the kernel. +- * @param fd A socket obtained via rtnl_open(). +- * @return Zero on success, non-zero otherwise. ++ * @param fd A socket obtained via rtnl_open(). ++ * @param device Interface name. Request all iface's status if set NULL. ++ * @return Zero on success, non-zero otherwise. + */ +-int rtnl_link_query(int fd); ++int rtnl_link_query(int fd, char *device); + + /** + * Read kernel messages looking for a link up/down events. +Backport of commit 80bc4d4c2f4d1c3c246091aa6f103bb7943202ec +Author: Hangbin Liu +Date: Mon Oct 9 22:31:42 2017 +0800 + + rtnl: update function rtnl_link_status to get bond slave info + + Update function rtnl_link_status to get bond slave info. Pass the slave index + to call back functions. i.e. port_link_status. + + Also check the interface index of rtnl message in function rtnl_link_status. + Then we don't need to check it in port_link_status. + + [BACKPORT: not included] Add ifndef IFLA_BOND_MAX in case we build linuxptp + on kernel before v3.13-rc1. + + Signed-off-by: Hangbin Liu + +diff --git a/port.c b/port.c +index 5b85d87..05fc321 100644 +--- a/port.c ++++ b/port.c +@@ -2221,11 +2221,11 @@ void port_dispatch(struct port *p, enum fsm_event event, int mdiff) + } + } + +-static void port_link_status(void *ctx, int index, int linkup) ++static void port_link_status(void *ctx, int linkup, int ts_index) + { + struct port *p = ctx; + +- if (index != if_nametoindex(p->name) || p->link_status == linkup) ++ if (p->link_status == linkup) + return; + + p->link_status = linkup; +@@ -2280,7 +2280,7 @@ enum fsm_event port_event(struct port *p, int fd_index) + + case FD_RTNL: + pr_debug("port %hu: received link status notification", portnum(p)); +- rtnl_link_status(fd, port_link_status, p); ++ rtnl_link_status(fd, p->name, port_link_status, p); + return port_link_status_get(p) ? EV_FAULT_CLEARED : EV_FAULT_DETECTED; + } + +diff --git a/rtnl.c b/rtnl.c +index 8ecf6fe..3419873 100644 +--- a/rtnl.c ++++ b/rtnl.c +@@ -84,15 +85,79 @@ int rtnl_link_query(int fd, char *device) + return 0; + } + +-int rtnl_link_status(int fd, rtnl_callback cb, void *ctx) ++static inline __u32 rta_getattr_u32(const struct rtattr *rta) + { +- int index, len; ++ return *(__u32 *)RTA_DATA(rta); ++} ++ ++static inline const char *rta_getattr_str(const struct rtattr *rta) ++{ ++ return (const char *)RTA_DATA(rta); ++} ++ ++static int rtnl_rtattr_parse(struct rtattr *tb[], int max, struct rtattr *rta, int len) ++{ ++ unsigned short type; ++ ++ memset(tb, 0, sizeof(struct rtattr *) * max); ++ while (RTA_OK(rta, len)) { ++ type = rta->rta_type; ++ if ((type < max) && (!tb[type])) ++ tb[type] = rta; ++ rta = RTA_NEXT(rta, len); ++ } ++ if (len) { ++ pr_err("Length mismatch: len %d, rta_len=%d\n", len, rta->rta_len); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static inline int rtnl_nested_rtattr_parse(struct rtattr *tb[], int max, struct rtattr *rta) ++{ ++ return rtnl_rtattr_parse(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta)); ++} ++ ++static int rtnl_linkinfo_parse(struct rtattr *rta) ++{ ++ int index = -1; ++ const char *kind; ++ struct rtattr *linkinfo[IFLA_INFO_MAX]; ++ struct rtattr *bond[IFLA_BOND_MAX]; ++ ++ if (rtnl_nested_rtattr_parse(linkinfo, IFLA_INFO_MAX, rta) < 0) ++ return -1; ++ ++ if (linkinfo[IFLA_INFO_KIND]) { ++ kind = rta_getattr_str(linkinfo[IFLA_INFO_KIND]); ++ ++ if (kind && !strncmp(kind, "bond", 4) && ++ linkinfo[IFLA_INFO_DATA]) { ++ if (rtnl_nested_rtattr_parse(bond, IFLA_BOND_MAX, ++ linkinfo[IFLA_INFO_DATA]) < 0) ++ return -1; ++ ++ if (bond[IFLA_BOND_ACTIVE_SLAVE]) { ++ index = rta_getattr_u32(bond[IFLA_BOND_ACTIVE_SLAVE]); ++ } ++ } ++ } ++ return index; ++} ++ ++int rtnl_link_status(int fd, char *device, rtnl_callback cb, void *ctx) ++{ ++ int index, len, link_up; ++ int slave_index = -1; + struct iovec iov; + struct sockaddr_nl sa; + struct msghdr msg; + struct nlmsghdr *nh; + struct ifinfomsg *info = NULL; ++ struct rtattr *tb[IFLA_MAX+1]; + ++ index = if_nametoindex(device); + if (!rtnl_buf) { + rtnl_len = 4096; + rtnl_buf = malloc(rtnl_len); +@@ -135,14 +200,27 @@ int rtnl_link_status(int fd, rtnl_callback cb, void *ctx) + nh = (struct nlmsghdr *) rtnl_buf; + + for ( ; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { +- if (nh->nlmsg_type == RTM_NEWLINK) { +- info = NLMSG_DATA(nh); +- index = info->ifi_index; +- pr_debug("interface index %d is %s", index, +- info->ifi_flags & IFF_RUNNING ? "up" : "down"); +- cb(ctx, index, info->ifi_flags & IFF_RUNNING ? 1 : 0); +- } ++ if (nh->nlmsg_type != RTM_NEWLINK) ++ continue; ++ ++ info = NLMSG_DATA(nh); ++ if (index != info->ifi_index) ++ continue; ++ ++ link_up = info->ifi_flags & IFF_RUNNING ? 1 : 0; ++ pr_debug("interface index %d is %s", index, ++ link_up ? "up" : "down"); ++ ++ rtnl_rtattr_parse(tb, IFLA_MAX, IFLA_RTA(info), ++ IFLA_PAYLOAD(nh)); ++ ++ if (tb[IFLA_LINKINFO]) ++ slave_index = rtnl_linkinfo_parse(tb[IFLA_LINKINFO]); ++ ++ if (cb) ++ cb(ctx, link_up, slave_index); + } ++ + return 0; + } + +diff --git a/rtnl.h b/rtnl.h +index 5c93eec..6eced2d 100644 +--- a/rtnl.h ++++ b/rtnl.h +@@ -20,7 +20,7 @@ + #ifndef HAVE_RTNL_H + #define HAVE_RTNL_H + +-typedef void (*rtnl_callback)(void *ctx, int index, int linkup); ++typedef void (*rtnl_callback)(void *ctx, int linkup, int ts_index); + + /** + * Close a RT netlink socket. +@@ -39,12 +39,13 @@ int rtnl_link_query(int fd, char *device); + + /** + * Read kernel messages looking for a link up/down events. +- * @param fd Readable socket obtained via rtnl_open(). +- * @param cb Callback function to be invoked on each event. +- * @param ctx Private context passed to the callback. +- * @return Zero on success, non-zero otherwise. ++ * @param fd Readable socket obtained via rtnl_open(). ++ * @param device The device which we need to get link info. ++ * @param cb Callback function to be invoked on each event. ++ * @param ctx Private context passed to the callback. ++ * @return Zero on success, non-zero otherwise. + */ +-int rtnl_link_status(int fd, rtnl_callback cb, void *ctx); ++int rtnl_link_status(int fd, char *device, rtnl_callback cb, void *ctx); + + /** + * Open a RT netlink socket for monitoring link state. +commit 6d1e2a62bdb4f6353e3a9006f2d603031f4648e6 +Author: Hangbin Liu +Date: Mon Oct 9 22:31:43 2017 +0800 + + rtnl: add function rtnl_get_ts_label to get interface ts_label info + + Signed-off-by: Hangbin Liu + +diff --git a/rtnl.c b/rtnl.c +index 3419873..cea936b 100644 +--- a/rtnl.c ++++ b/rtnl.c +@@ -43,6 +43,37 @@ int rtnl_close(int fd) + return close(fd); + } + ++static void rtnl_get_ts_label_callback(void *ctx, int linkup, int ts_index) ++{ ++ int *dst = ctx; ++ *dst = ts_index; ++} ++ ++int rtnl_get_ts_label(struct interface *iface) ++{ ++ int err, fd; ++ int ts_index = -1; ++ ++ fd = rtnl_open(); ++ if (fd < 0) ++ return fd; ++ ++ err = rtnl_link_query(fd, iface->name); ++ if (err) { ++ goto no_info; ++ } ++ ++ rtnl_link_status(fd, iface->name, rtnl_get_ts_label_callback, &ts_index); ++ if (ts_index > 0 && if_indextoname(ts_index, iface->ts_label)) ++ err = 0; ++ else ++ err = -1; ++ ++no_info: ++ rtnl_close(fd); ++ return err; ++} ++ + int rtnl_link_query(int fd, char *device) + { + struct sockaddr_nl sa; +diff --git a/rtnl.h b/rtnl.h +index 6eced2d..2906d74 100644 +--- a/rtnl.h ++++ b/rtnl.h +@@ -20,6 +20,8 @@ + #ifndef HAVE_RTNL_H + #define HAVE_RTNL_H + ++#include "config.h" ++ + typedef void (*rtnl_callback)(void *ctx, int linkup, int ts_index); + + /** +@@ -30,6 +32,13 @@ typedef void (*rtnl_callback)(void *ctx, int linkup, int ts_index); + int rtnl_close(int fd); + + /** ++ * Get interface ts_label information ++ * @param iface struct interface. ++ * @return Zero on success, or -1 on error. ++ */ ++int rtnl_get_ts_label(struct interface *iface); ++ ++/** + * Request the link status from the kernel. + * @param fd A socket obtained via rtnl_open(). + * @param device Interface name. Request all iface's status if set NULL. +commit b65b1d5f3b5d8e85e47e9cf19c2e5aa6dc2ebd77 +Author: Hangbin Liu +Date: Mon Oct 9 22:31:44 2017 +0800 + + port: update port link_status to enum + + Besides link up and down, we may also receive other rtnl messages, like + bond slave changed info, which link state keeps the same. + + So we should return EV_FAULT_CLEARED only when both LINK_UP and + LINK_STATE_CHANGED. + + When the link state keep the same, we should return EV_NONE. + + Signed-off-by: Hangbin Liu + +diff --git a/port.c b/port.c +index 05fc321..81d52ff 100644 +--- a/port.c ++++ b/port.c +@@ -56,6 +56,12 @@ enum syfu_event { + FUP_MATCH, + }; + ++enum link_state { ++ LINK_DOWN = (1<<0), ++ LINK_UP = (1<<1), ++ LINK_STATE_CHANGED = (1<<3), ++}; ++ + struct nrate_estimator { + double ratio; + tmv_t origin1; +@@ -122,7 +128,7 @@ struct port { + int path_trace_enabled; + int rx_timestamp_offset; + int tx_timestamp_offset; +- int link_status; ++ enum link_state link_status; + struct fault_interval flt_interval_pertype[FT_CNT]; + enum fault_type last_fault_type; + unsigned int versionNumber; /*UInteger4*/ +@@ -2224,18 +2230,21 @@ void port_dispatch(struct port *p, enum fsm_event event, int mdiff) + static void port_link_status(void *ctx, int linkup, int ts_index) + { + struct port *p = ctx; ++ int link_state; + +- if (p->link_status == linkup) +- return; +- +- p->link_status = linkup; +- pr_notice("port %hu: link %s", portnum(p), linkup ? "up" : "down"); ++ link_state = linkup ? LINK_UP : LINK_DOWN; ++ if (p->link_status & link_state) { ++ p->link_status = link_state; ++ } else { ++ p->link_status = link_state | LINK_STATE_CHANGED; ++ pr_notice("port %hu: link %s", portnum(p), linkup ? "up" : "down"); ++ } + + /* + * A port going down can affect the BMCA result. + * Force a state decision event. + */ +- if (!p->link_status) ++ if (p->link_status & LINK_DOWN) + clock_set_sde(p->clock, 1); + } + +@@ -2281,7 +2290,12 @@ enum fsm_event port_event(struct port *p, int fd_index) + case FD_RTNL: + pr_debug("port %hu: received link status notification", portnum(p)); + rtnl_link_status(fd, p->name, port_link_status, p); +- return port_link_status_get(p) ? EV_FAULT_CLEARED : EV_FAULT_DETECTED; ++ if (p->link_status == (LINK_UP | LINK_STATE_CHANGED)) ++ return EV_FAULT_CLEARED; ++ else if (p->link_status == (LINK_DOWN | LINK_STATE_CHANGED)) ++ return EV_FAULT_DETECTED; ++ else ++ return EV_NONE; + } + + msg = msg_allocate(); +@@ -2409,7 +2423,7 @@ int port_number(struct port *p) + + int port_link_status_get(struct port *p) + { +- return p->link_status; ++ return !!(p->link_status & LINK_UP); + } + + int port_manage(struct port *p, struct port *ingress, struct ptp_message *msg) +@@ -2630,7 +2644,7 @@ struct port *port_open(int phc_index, + p->path_trace_enabled = config_get_int(cfg, p->name, "path_trace_enabled"); + p->rx_timestamp_offset = config_get_int(cfg, p->name, "ingressLatency"); + p->tx_timestamp_offset = config_get_int(cfg, p->name, "egressLatency"); +- p->link_status = 1; ++ p->link_status = LINK_UP; + p->clock = clock; + p->trp = transport_create(cfg, transport); + if (!p->trp) +commit 1440f093847a79d0e80ea6b4bca011ddd87090d0 +Author: Hangbin Liu +Date: Mon Oct 9 22:31:45 2017 +0800 + + clock: add clock_required_modes to obtain the required time stamping mode + + Separate required_modes setting from clock_create so we can obtain the + required time stamping flags from other place. + + Add enum timestamping in struct clock to store the time stamping mode. + + Signed-off-by: Hangbin Liu + +diff --git a/clock.c b/clock.c +index 9d224c9..5926a3c 100644 +--- a/clock.c ++++ b/clock.c +@@ -105,6 +105,7 @@ struct clock { + int time_flags; /* grand master role */ + int time_source; /* grand master role */ + enum servo_state servo_state; ++ enum timestamp_type timestamping; + tmv_t master_offset; + tmv_t path_delay; + tmv_t ingress_ts; +@@ -803,6 +804,34 @@ static void clock_remove_port(struct clock *c, struct port *p) + port_close(p); + } + ++int clock_required_modes(struct clock *c) ++{ ++ int required_modes = 0; ++ ++ switch (c->timestamping) { ++ case TS_SOFTWARE: ++ required_modes |= SOF_TIMESTAMPING_TX_SOFTWARE | ++ SOF_TIMESTAMPING_RX_SOFTWARE | ++ SOF_TIMESTAMPING_SOFTWARE; ++ break; ++ case TS_LEGACY_HW: ++ required_modes |= SOF_TIMESTAMPING_TX_HARDWARE | ++ SOF_TIMESTAMPING_RX_HARDWARE | ++ SOF_TIMESTAMPING_SYS_HARDWARE; ++ break; ++ case TS_HARDWARE: ++ case TS_ONESTEP: ++ required_modes |= SOF_TIMESTAMPING_TX_HARDWARE | ++ SOF_TIMESTAMPING_RX_HARDWARE | ++ SOF_TIMESTAMPING_RAW_HARDWARE; ++ break; ++ default: ++ break; ++ } ++ ++ return required_modes; ++} ++ + struct clock *clock_create(enum clock_type type, struct config *config, + const char *phc_device) + { +@@ -911,24 +940,8 @@ struct clock *clock_create(enum clock_type type, struct config *config, + } + + /* Check the time stamping mode on each interface. */ +- switch (timestamping) { +- case TS_SOFTWARE: +- required_modes |= SOF_TIMESTAMPING_TX_SOFTWARE | +- SOF_TIMESTAMPING_RX_SOFTWARE | +- SOF_TIMESTAMPING_SOFTWARE; +- break; +- case TS_LEGACY_HW: +- required_modes |= SOF_TIMESTAMPING_TX_HARDWARE | +- SOF_TIMESTAMPING_RX_HARDWARE | +- SOF_TIMESTAMPING_SYS_HARDWARE; +- break; +- case TS_HARDWARE: +- case TS_ONESTEP: +- required_modes |= SOF_TIMESTAMPING_TX_HARDWARE | +- SOF_TIMESTAMPING_RX_HARDWARE | +- SOF_TIMESTAMPING_RAW_HARDWARE; +- break; +- } ++ c->timestamping = timestamping; ++ required_modes = clock_required_modes(c); + STAILQ_FOREACH(iface, &config->interfaces, list) { + if (iface->ts_info.valid && + ((iface->ts_info.so_timestamping & required_modes) != required_modes)) { +diff --git a/clock.h b/clock.h +index 49ecb76..986d363 100644 +--- a/clock.h ++++ b/clock.h +@@ -73,6 +73,14 @@ UInteger8 clock_class(struct clock *c); + struct config *clock_config(struct clock *c); + + /** ++ * Obtains the required time stamping mode. ++ * @param c The clock instance. ++ * @return The value of required time stamping mode, which is a bit mask ++ * of SOF_TIMESTAMPING_ flags. ++ */ ++int clock_required_modes(struct clock *c); ++ ++/** + * Create a clock instance. There can only be one clock in any system, + * so subsequent calls will destroy the previous clock instance. + * +commit 536a71031d5c7689fd186ff550dc11cf743e02cb +Author: Hangbin Liu +Date: Mon Oct 9 22:31:46 2017 +0800 + + ptp4l: use ts label to get ts info + + Now the ts label will be either the bond active slave or the interface + name, which is the exactly interface we need to get ts info. + + When the link down/up or there is a fail over and ts_label changed, the + phc index may also changed. So we need to check get new ts info and check + clock_required_modes. We will set the link to LINK_DOWN by force if + the new ts_label's timestamp do not support required mode. + + If all good, then we set phc index to new one. Also sync clock interval + after switch phc. + + Signed-off-by: Hangbin Liu + +diff --git a/clock.c b/clock.c +index 5926a3c..41c8f81 100644 +--- a/clock.c ++++ b/clock.c +@@ -38,6 +38,7 @@ + #include "servo.h" + #include "stats.h" + #include "print.h" ++#include "rtnl.h" + #include "tlv.h" + #include "tsproc.h" + #include "uds.h" +@@ -832,6 +833,16 @@ int clock_required_modes(struct clock *c) + return required_modes; + } + ++/* ++ * If we do not have a slave or the rtnl query failed, then use our ++ * own interface name as the time stamping interface name. ++ */ ++static void ensure_ts_label(struct interface *iface) ++{ ++ if (iface->ts_label[0] == '\0') ++ strncpy(iface->ts_label, iface->name, MAX_IFNAME_SIZE); ++} ++ + struct clock *clock_create(enum clock_type type, struct config *config, + const char *phc_device) + { +@@ -943,6 +954,9 @@ struct clock *clock_create(enum clock_type type, struct config *config, + c->timestamping = timestamping; + required_modes = clock_required_modes(c); + STAILQ_FOREACH(iface, &config->interfaces, list) { ++ rtnl_get_ts_label(iface); ++ ensure_ts_label(iface); ++ sk_get_ts_info(iface->ts_label, &iface->ts_info); + if (iface->ts_info.valid && + ((iface->ts_info.so_timestamping & required_modes) != required_modes)) { + pr_err("interface '%s' does not support " +diff --git a/config.c b/config.c +index e6fe676..bbaf36e 100644 +--- a/config.c ++++ b/config.c +@@ -633,7 +633,6 @@ struct interface *config_create_interface(char *name, struct config *cfg) + } + + strncpy(iface->name, name, MAX_IFNAME_SIZE); +- sk_get_ts_info(iface->name, &iface->ts_info); + STAILQ_INSERT_TAIL(&cfg->interfaces, iface, list); + cfg->n_interfaces++; + +diff --git a/port.c b/port.c +index 81d52ff..615c800 100644 +--- a/port.c ++++ b/port.c +@@ -60,6 +60,7 @@ enum link_state { + LINK_DOWN = (1<<0), + LINK_UP = (1<<1), + LINK_STATE_CHANGED = (1<<3), ++ TS_LABEL_CHANGED = (1<<4), + }; + + struct nrate_estimator { +@@ -2231,6 +2232,8 @@ static void port_link_status(void *ctx, int linkup, int ts_index) + { + struct port *p = ctx; + int link_state; ++ char ts_label[MAX_IFNAME_SIZE + 1] = {0}; ++ int required_modes; + + link_state = linkup ? LINK_UP : LINK_DOWN; + if (p->link_status & link_state) { +@@ -2240,6 +2243,39 @@ static void port_link_status(void *ctx, int linkup, int ts_index) + pr_notice("port %hu: link %s", portnum(p), linkup ? "up" : "down"); + } + ++ /* ts_label changed */ ++ if (if_indextoname(ts_index, ts_label) && strcmp(p->iface->ts_label, ts_label)) { ++ strncpy(p->iface->ts_label, ts_label, MAX_IFNAME_SIZE); ++ p->link_status |= TS_LABEL_CHANGED; ++ pr_notice("port %hu: ts label changed to %s", portnum(p), ts_label); ++ } ++ ++ /* Both link down/up and change ts_label may change phc index. */ ++ if (p->link_status & LINK_UP && ++ (p->link_status & LINK_STATE_CHANGED || p->link_status & TS_LABEL_CHANGED)) { ++ sk_get_ts_info(p->iface->ts_label, &p->iface->ts_info); ++ ++ /* Only switch phc with HW time stamping mode */ ++ if (p->phc_index >= 0 && p->iface->ts_info.valid) { ++ required_modes = clock_required_modes(p->clock); ++ if ((p->iface->ts_info.so_timestamping & required_modes) != required_modes) { ++ pr_err("interface '%s' does not support requested " ++ "timestamping mode, set link status down by force.", ++ p->iface->ts_label); ++ p->link_status = LINK_DOWN | LINK_STATE_CHANGED; ++ } else if (p->phc_index != p->iface->ts_info.phc_index) { ++ p->phc_index = p->iface->ts_info.phc_index; ++ ++ if (clock_switch_phc(p->clock, p->phc_index)) { ++ p->last_fault_type = FT_SWITCH_PHC; ++ port_dispatch(p, EV_FAULT_DETECTED, 0); ++ return; ++ } ++ clock_sync_interval(p->clock, p->log_sync_interval); ++ } ++ } ++ } ++ + /* + * A port going down can affect the BMCA result. + * Force a state decision event. +@@ -2292,7 +2328,8 @@ enum fsm_event port_event(struct port *p, int fd_index) + rtnl_link_status(fd, p->name, port_link_status, p); + if (p->link_status == (LINK_UP | LINK_STATE_CHANGED)) + return EV_FAULT_CLEARED; +- else if (p->link_status == (LINK_DOWN | LINK_STATE_CHANGED)) ++ else if ((p->link_status == (LINK_DOWN | LINK_STATE_CHANGED)) || ++ (p->link_status & TS_LABEL_CHANGED)) + return EV_FAULT_DETECTED; + else + return EV_NONE; +commit 8923bcdf64c0c95bac7af977b867393bae69e7a1 +Author: Hangbin Liu +Date: Mon Oct 9 22:31:47 2017 +0800 + + transport: pass struct interface to transport_open + + Pass struct interface so we can use ts_iface in HW filter. + + Signed-off-by: Hangbin Liu + +diff --git a/pmc_common.c b/pmc_common.c +index d92b0cd..447cf99 100644 +--- a/pmc_common.c ++++ b/pmc_common.c +@@ -67,6 +67,7 @@ struct pmc *pmc_create(struct config *cfg, enum transport_type transport_type, + int zero_datalen) + { + struct pmc *pmc; ++ struct interface iface; + + pmc = calloc(1, sizeof *pmc); + if (!pmc) +@@ -90,7 +91,9 @@ struct pmc *pmc_create(struct config *cfg, enum transport_type transport_type, + pr_err("failed to create transport"); + goto failed; + } +- if (transport_open(pmc->transport, iface_name, ++ ++ strncpy(iface.name, iface_name, MAX_IFNAME_SIZE); ++ if (transport_open(pmc->transport, &iface, + &pmc->fdarray, TS_SOFTWARE)) { + pr_err("failed to open transport"); + goto failed; +diff --git a/port.c b/port.c +index 615c800..1e3f474 100644 +--- a/port.c ++++ b/port.c +@@ -1504,7 +1504,7 @@ static int port_initialize(struct port *p) + goto no_timers; + } + } +- if (transport_open(p->trp, p->name, &p->fda, p->timestamping)) ++ if (transport_open(p->trp, p->iface, &p->fda, p->timestamping)) + goto no_tropen; + + for (i = 0; i < N_TIMER_FDS; i++) { +@@ -1547,7 +1547,7 @@ static int port_renew_transport(struct port *p) + } + transport_close(p->trp, &p->fda); + port_clear_fda(p, FD_ANNOUNCE_TIMER); +- res = transport_open(p->trp, p->name, &p->fda, p->timestamping); ++ res = transport_open(p->trp, p->iface, &p->fda, p->timestamping); + /* Need to call clock_fda_changed even if transport_open failed in + * order to update clock to the now closed descriptors. */ + clock_fda_changed(p->clock); +diff --git a/raw.c b/raw.c +index 73e45b4..8b7bcf1 100644 +--- a/raw.c ++++ b/raw.c +@@ -198,15 +198,16 @@ static void addr_to_mac(void *mac, struct address *addr) + memcpy(mac, &addr->sll.sll_addr, MAC_LEN); + } + +-static int raw_open(struct transport *t, const char *name, ++static int raw_open(struct transport *t, struct interface *iface, + struct fdarray *fda, enum timestamp_type ts_type) + { + struct raw *raw = container_of(t, struct raw, t); + unsigned char ptp_dst_mac[MAC_LEN]; + unsigned char p2p_dst_mac[MAC_LEN]; + int efd, gfd; +- char *str; ++ char *str, *name; + ++ name = iface->ts_label; + str = config_get_string(t->cfg, name, "ptp_dst_mac"); + if (str2mac(str, ptp_dst_mac)) { + pr_err("invalid ptp_dst_mac %s", str); +diff --git a/transport.c b/transport.c +index d24c05b..3541394 100644 +--- a/transport.c ++++ b/transport.c +@@ -31,10 +31,10 @@ int transport_close(struct transport *t, struct fdarray *fda) + return t->close(t, fda); + } + +-int transport_open(struct transport *t, const char *name, ++int transport_open(struct transport *t, struct interface *iface, + struct fdarray *fda, enum timestamp_type tt) + { +- return t->open(t, name, fda, tt); ++ return t->open(t, iface, fda, tt); + } + + int transport_recv(struct transport *t, int fd, struct ptp_message *msg) +diff --git a/transport.h b/transport.h +index 5d6ba98..15616bb 100644 +--- a/transport.h ++++ b/transport.h +@@ -27,6 +27,7 @@ + #include "msg.h" + + struct config; ++struct interface; + + /* Values from networkProtocol enumeration 7.4.1 Table 3 */ + enum transport_type { +@@ -54,7 +55,7 @@ struct transport; + + int transport_close(struct transport *t, struct fdarray *fda); + +-int transport_open(struct transport *t, const char *name, ++int transport_open(struct transport *t, struct interface *iface, + struct fdarray *fda, enum timestamp_type tt); + + int transport_recv(struct transport *t, int fd, struct ptp_message *msg); +diff --git a/transport_private.h b/transport_private.h +index b54f32a..7530896 100644 +--- a/transport_private.h ++++ b/transport_private.h +@@ -32,8 +32,8 @@ struct transport { + + int (*close)(struct transport *t, struct fdarray *fda); + +- int (*open)(struct transport *t, const char *name, struct fdarray *fda, +- enum timestamp_type tt); ++ int (*open)(struct transport *t, struct interface *iface, ++ struct fdarray *fda, enum timestamp_type tt); + + int (*recv)(struct transport *t, int fd, void *buf, int buflen, + struct address *addr, struct hw_timestamp *hwts); +diff --git a/udp.c b/udp.c +index 530a2ee..05c2ba0 100644 +--- a/udp.c ++++ b/udp.c +@@ -152,12 +152,13 @@ enum { MC_PRIMARY, MC_PDELAY }; + + static struct in_addr mcast_addr[2]; + +-static int udp_open(struct transport *t, const char *name, struct fdarray *fda, +- enum timestamp_type ts_type) ++static int udp_open(struct transport *t, struct interface *iface, ++ struct fdarray *fda, enum timestamp_type ts_type) + { + struct udp *udp = container_of(t, struct udp, t); + uint8_t event_dscp, general_dscp; + int efd, gfd, ttl; ++ char *name = iface->name; + + ttl = config_get_int(t->cfg, name, "udp_ttl"); + udp->mac.len = 0; +@@ -180,7 +181,7 @@ static int udp_open(struct transport *t, const char *name, struct fdarray *fda, + if (gfd < 0) + goto no_general; + +- if (sk_timestamping_init(efd, name, ts_type, TRANS_UDP_IPV4)) ++ if (sk_timestamping_init(efd, iface->ts_label, ts_type, TRANS_UDP_IPV4)) + goto no_timestamping; + + if (sk_general_init(gfd)) +diff --git a/udp6.c b/udp6.c +index 89e27bf..7551e3f 100644 +--- a/udp6.c ++++ b/udp6.c +@@ -160,12 +160,13 @@ enum { MC_PRIMARY, MC_PDELAY }; + + static struct in6_addr mc6_addr[2]; + +-static int udp6_open(struct transport *t, const char *name, struct fdarray *fda, +- enum timestamp_type ts_type) ++static int udp6_open(struct transport *t, struct interface *iface, ++ struct fdarray *fda, enum timestamp_type ts_type) + { + struct udp6 *udp6 = container_of(t, struct udp6, t); + uint8_t event_dscp, general_dscp; + int efd, gfd, hop_limit; ++ char *name = iface->name; + + hop_limit = config_get_int(t->cfg, name, "udp_ttl"); + udp6->mac.len = 0; +@@ -190,7 +191,7 @@ static int udp6_open(struct transport *t, const char *name, struct fdarray *fda, + if (gfd < 0) + goto no_general; + +- if (sk_timestamping_init(efd, name, ts_type, TRANS_UDP_IPV6)) ++ if (sk_timestamping_init(efd, iface->ts_label, ts_type, TRANS_UDP_IPV6)) + goto no_timestamping; + + if (sk_general_init(gfd)) +diff --git a/uds.c b/uds.c +index d5e8f50..7e11f63 100644 +--- a/uds.c ++++ b/uds.c +@@ -52,13 +52,14 @@ static int uds_close(struct transport *t, struct fdarray *fda) + return 0; + } + +-static int uds_open(struct transport *t, const char *name, struct fdarray *fda, ++static int uds_open(struct transport *t, struct interface *iface, struct fdarray *fda, + enum timestamp_type tt) + { + int fd, err; + struct sockaddr_un sa; + struct uds *uds = container_of(t, struct uds, t); + char *uds_path = config_get_string(t->cfg, NULL, "uds_address"); ++ char *name = iface->name; + + fd = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (fd < 0) { +commit cb53238d5d1a1b9319536b4df1edf237e428d931 +Author: Hangbin Liu +Date: Mon Oct 9 22:31:48 2017 +0800 + + phc2sys: split servo_add from function clock_add + + We also need this part in clock_reinit() later. So split it out of + function clock_add(). + + Signed-off-by: Hangbin Liu + +diff --git a/phc2sys.c b/phc2sys.c +index e2b5c47..0472c68 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -162,12 +162,45 @@ static clockid_t clock_open(char *device, int *phc_index) + return clkid; + } + ++static struct servo *servo_add(struct node *node, struct clock *clock) ++{ ++ double ppb; ++ int max_ppb; ++ struct servo *servo; ++ ++ clockadj_init(clock->clkid); ++ ppb = clockadj_get_freq(clock->clkid); ++ /* The reading may silently fail and return 0, reset the frequency to ++ make sure ppb is the actual frequency of the clock. */ ++ clockadj_set_freq(clock->clkid, ppb); ++ if (clock->clkid == CLOCK_REALTIME) { ++ sysclk_set_leap(0); ++ max_ppb = sysclk_max_freq(); ++ } else { ++ max_ppb = phc_max_adj(clock->clkid); ++ if (!max_ppb) { ++ pr_err("clock is not adjustable"); ++ return NULL; ++ } ++ } ++ ++ servo = servo_create(phc2sys_config, node->servo_type, ++ -ppb, max_ppb, 0); ++ if (!servo) { ++ pr_err("Failed to create servo"); ++ return NULL; ++ } ++ ++ servo_sync_interval(servo, node->phc_interval); ++ ++ return servo; ++} ++ + static struct clock *clock_add(struct node *node, char *device) + { + struct clock *c; + clockid_t clkid = CLOCK_INVALID; +- int max_ppb, phc_index = -1; +- double ppb; ++ int phc_index = -1; + + if (device) { + clkid = clock_open(device, &phc_index); +@@ -211,25 +244,7 @@ static struct clock *clock_add(struct node *node, char *device) + } + } + +- clockadj_init(c->clkid); +- ppb = clockadj_get_freq(c->clkid); +- /* The reading may silently fail and return 0, reset the frequency to +- make sure ppb is the actual frequency of the clock. */ +- clockadj_set_freq(c->clkid, ppb); +- if (c->clkid == CLOCK_REALTIME) { +- sysclk_set_leap(0); +- max_ppb = sysclk_max_freq(); +- } else { +- max_ppb = phc_max_adj(c->clkid); +- if (!max_ppb) { +- pr_err("clock is not adjustable"); +- return NULL; +- } +- } +- +- c->servo = servo_create(phc2sys_config, node->servo_type, +- -ppb, max_ppb, 0); +- servo_sync_interval(c->servo, node->phc_interval); ++ c->servo = servo_add(node, c); + + if (clkid != CLOCK_REALTIME) + c->sysoff_supported = (SYSOFF_SUPPORTED == +commit 8a349e557c653d24b04b0545e2f179080e10731f +Author: Hangbin Liu +Date: Mon Oct 9 22:31:49 2017 +0800 + + phc2sys: re-create clock clkid and servo when phc index changed + + When the clock device or phc index changed, the new phc device may have + different maximum adjustment. So we need to create a new clkid and servo + in clock_reinit(). + + At the same time, we should not only do clock_reinit() when the new state + is PS_MASTER. We also need to reinit clock after a failover in slave mode( + the new state is PS_SLAVE). We can do clock_reinit() even in PS_FAULTY so + we can finish adjust offset before come back to PS_LISTENING. So I removed + the check and let's do clock_reinit() whenever there is a new state. + + And for servo reset, as Miroslav suggested, we will do it in these cases: + - the system clock is the new destination (master state) + - a PHC is the new destination (master state) + - a PHC is switched (in any state) + + Signed-off-by: Hangbin Liu + +diff --git a/phc2sys.c b/phc2sys.c +index 0472c68..5c54055 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -128,6 +128,11 @@ static int clock_handle_leap(struct node *node, struct clock *clock, + static int run_pmc_get_utc_offset(struct node *node, int timeout); + static void run_pmc_events(struct node *node); + ++static int normalize_state(int state); ++static int run_pmc_port_properties(struct node *node, int timeout, ++ unsigned int port, ++ int *state, int *tstamping, char *iface); ++ + static clockid_t clock_open(char *device, int *phc_index) + { + struct sk_ts_info ts_info; +@@ -309,15 +314,62 @@ static struct port *port_add(struct node *node, unsigned int number, + return p; + } + +-static void clock_reinit(struct clock *clock) ++static void clock_reinit(struct node *node, struct clock *clock, int new_state) + { +- servo_reset(clock->servo); +- clock->servo_state = SERVO_UNLOCKED; ++ int phc_index = -1, phc_switched = 0; ++ int state, timestamping, ret = -1; ++ struct port *p; ++ struct servo *servo; ++ struct sk_ts_info ts_info; ++ char iface[IFNAMSIZ]; ++ clockid_t clkid = CLOCK_INVALID; + +- if (clock->offset_stats) { +- stats_reset(clock->offset_stats); +- stats_reset(clock->freq_stats); +- stats_reset(clock->delay_stats); ++ LIST_FOREACH(p, &node->ports, list) { ++ if (p->clock == clock) { ++ ret = run_pmc_port_properties(node, 1000, p->number, ++ &state, ×tamping, ++ iface); ++ if (ret > 0) ++ p->state = normalize_state(state); ++ } ++ } ++ ++ if (ret > 0 && timestamping != TS_SOFTWARE) { ++ /* Check if device changed */ ++ if (strcmp(clock->device, iface)) { ++ free(clock->device); ++ 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 = clock_open(clock->device, &phc_index); ++ if (clkid == CLOCK_INVALID) ++ return; ++ ++ phc_close(clock->clkid); ++ clock->clkid = clkid; ++ clock->phc_index = phc_index; ++ ++ servo = servo_add(node, clock); ++ if (servo) { ++ servo_destroy(clock->servo); ++ clock->servo = servo; ++ } ++ ++ phc_switched = 1; ++ } ++ } ++ ++ if (new_state == PS_MASTER || phc_switched) { ++ servo_reset(clock->servo); ++ clock->servo_state = SERVO_UNLOCKED; ++ ++ if (clock->offset_stats) { ++ stats_reset(clock->offset_stats); ++ stats_reset(clock->freq_stats); ++ stats_reset(clock->delay_stats); ++ } + } + } + +@@ -336,9 +388,7 @@ static void reconfigure(struct node *node) + } + + if (c->new_state) { +- if (c->new_state == PS_MASTER) +- clock_reinit(c); +- ++ clock_reinit(node, c, c->new_state); + c->state = c->new_state; + c->new_state = 0; + } +@@ -403,7 +453,7 @@ static void reconfigure(struct node *node) + } else if (rt) { + if (rt->state != PS_MASTER) { + rt->state = PS_MASTER; +- clock_reinit(rt); ++ clock_reinit(node, rt, rt->state); + } + pr_info("selecting %s for synchronization", rt->device); + } +commit 7092db303028d84574138c8199375dfde0b4e232 +Author: Hangbin Liu +Date: Mon Oct 9 22:31:50 2017 +0800 + + port: return timestamping iface in port properties + + Signed-off-by: Hangbin Liu + +diff --git a/port.c b/port.c +index 1e3f474..d8e29d5 100644 +--- a/port.c ++++ b/port.c +@@ -861,7 +861,7 @@ static int port_management_fill_response(struct port *target, + else + ppn->port_state = target->state; + ppn->timestamping = target->timestamping; +- ptp_text_set(&ppn->interface, target->name); ++ ptp_text_set(&ppn->interface, target->iface->ts_label); + datalen = sizeof(*ppn) + ppn->interface.length; + respond = 1; + break; +commit 5ce04f9bf366c6d62db5344a6553f87a99ea52ff +Author: Hangbin Liu +Date: Wed Oct 18 20:31:38 2017 +0800 + + phc2sys: update '-s' option + + Add description about bond interface. + + Signed-off-by: Hangbin Liu + +diff --git a/phc2sys.8 b/phc2sys.8 +index 2559c74..4fc4fa3 100644 +--- a/phc2sys.8 ++++ b/phc2sys.8 +@@ -94,7 +94,11 @@ together with the + option, the master clock is used only to correct the offset by whole number of + seconds, which cannot be fixed with PPS alone. Not compatible with the + .B \-a +-option. ++option. This option does not support bonded interface (e.g. bond0). If ++.B ptp4l ++has a port on an active-backup bond interface, the ++.B \-a ++option can be used to track the active interface. + .TP + .BI \-i " interface" + Performs the exact same function as diff --git a/SOURCES/linuxptp-closesocket.patch b/SOURCES/linuxptp-closesocket.patch new file mode 100644 index 0000000..6b27b99 --- /dev/null +++ b/SOURCES/linuxptp-closesocket.patch @@ -0,0 +1,38 @@ +commit a3500e14ca4af38034d5ad2115ca7ac271c8c5b4 +Author: Miroslav Lichvar +Date: Mon Feb 6 15:43:00 2017 +0100 + + Fix leaks of sockets on errors. + + Signed-off-by: Miroslav Lichvar + +diff --git a/clock.c b/clock.c +index a6a1a1a..9481606 100644 +--- a/clock.c ++++ b/clock.c +@@ -824,11 +824,13 @@ static int clock_add_port(struct clock *c, int phc_index, + } + index = sk_interface_index(fd, iface->name); + if (index < 0) { ++ close(fd); + return -1; + } + snprintf(key, sizeof(key), "%d", index); + if (hash_insert(c->index2port, key, p)) { + pr_err("failed to add port with index %d twice!", index); ++ close(fd); + return -1; + } + close(fd); +diff --git a/rtnl.c b/rtnl.c +index 7f5dc45..251b5f3 100644 +--- a/rtnl.c ++++ b/rtnl.c +@@ -160,6 +160,7 @@ int rtnl_open(void) + } + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa))) { + pr_err("failed to bind netlink socket: %m"); ++ close(fd); + return -1; + } + return fd; diff --git a/SOURCES/linuxptp-ipoib.patch b/SOURCES/linuxptp-ipoib.patch new file mode 100644 index 0000000..1c62978 --- /dev/null +++ b/SOURCES/linuxptp-ipoib.patch @@ -0,0 +1,225 @@ +commit 7546434030d456b13b2eda6138f183ac3bf29751 +Author: Feras Daoud +Date: Wed Aug 9 18:27:32 2017 +0300 + + ptp4l: Add IPoIB interface support for ptp4l + + The current implementation of ptp4l always assumes 6 octets MAC + address, which is correct for Ethernet interfaces but not for IPoIB + interfaces (that have 20 octets MAC), therefore running ptp4l over + IPoIB interface does not function correctly. + + In Infiniband, every interface has three identifiers: + GUID, GID, and LID. + The GUID is similar in concept to a MAC address. From RFC4392: + The EUI-64 portion of a GID is referred to as the Global Unique + Identifier (GUID) and is the only persistent identifier of a port. + + Therefore, to support IPoIB interfaces, the GUID of the port should + be used instead of the MAC. + This patch checks the interface type before creating the clock identity, + for Infiniband ports, it retrieves the GUID of the port using sysfs + and use it to create the clock identity. + + sysfs method was chosen since the GUID is the 6 lsb bytes of + the 20 byte device address, and SIOCGIFHWADDR ioctl call returns + the 14 msb bytes of the device address, so it is not possible to + get the GUID using SIOCGIFHWADDR ioctl call. + + [ RC: fixed trivial coding style error, space after switch keyword. ] + + Signed-off-by: Feras Daoud + Reviewed-by: Alex Vesker + +diff --git a/address.h b/address.h +index 7578f91..35ef05f 100644 +--- a/address.h ++++ b/address.h +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + struct address { + socklen_t len; +diff --git a/ether.h b/ether.h +index ce3d663..8ec9669 100644 +--- a/ether.h ++++ b/ether.h +@@ -22,7 +22,13 @@ + + #include + +-#define MAC_LEN 6 ++#define EUI48 6 ++#define EUI64 8 ++ ++#define MAC_LEN EUI48 ++#define GUID_LEN EUI64 ++ ++#define GUID_OFFSET 36 + + typedef uint8_t eth_addr[MAC_LEN]; + +diff --git a/sk.c b/sk.c +index 63ec206..0cf55c5 100644 +--- a/sk.c ++++ b/sk.c +@@ -159,10 +159,55 @@ failed: + return -1; + } + ++static int sk_interface_guidaddr(const char *name, unsigned char *guid) ++{ ++ char file_name[64], buf[64], addr[8]; ++ FILE *f; ++ char *err; ++ int res; ++ ++ snprintf(file_name, sizeof buf, "/sys/class/net/%s/address", name); ++ f = fopen(file_name, "r"); ++ if (!f) { ++ pr_err("failed to open %s: %m", buf); ++ return -1; ++ } ++ ++ /* Set the file position to the beginning of the GUID */ ++ res = fseek(f, GUID_OFFSET, SEEK_SET); ++ if (res) { ++ pr_err("fseek failed: %m"); ++ goto error; ++ } ++ ++ err = fgets(buf, sizeof buf, f); ++ if (err == NULL) { ++ pr_err("fseek failed: %m"); ++ goto error; ++ } ++ ++ res = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", ++ &addr[0], &addr[1], &addr[2], &addr[3], ++ &addr[4], &addr[5], &addr[6], &addr[7]); ++ if (res != GUID_LEN) { ++ pr_err("sscanf failed: %m"); ++ goto error; ++ } ++ ++ memcpy(guid, addr, GUID_LEN); ++ fclose(f); ++ ++ return 0; ++ ++error: ++ fclose(f); ++ return -1; ++} ++ + int sk_interface_macaddr(const char *name, struct address *mac) + { + struct ifreq ifreq; +- int err, fd; ++ int err, fd, type; + + memset(&ifreq, 0, sizeof(ifreq)); + strncpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name) - 1); +@@ -180,9 +225,23 @@ int sk_interface_macaddr(const char *name, struct address *mac) + return -1; + } + ++ /* Get interface type */ ++ type = ifreq.ifr_hwaddr.sa_family; ++ switch (type) { ++ case ARPHRD_INFINIBAND: ++ err = sk_interface_guidaddr(name, mac->sll.sll_addr); ++ if (err) { ++ pr_err("fail to get address using sysfs: %m"); ++ return -1; ++ } ++ mac->sll.sll_halen = EUI64; ++ break; ++ default: ++ memcpy(mac->sll.sll_addr, &ifreq.ifr_hwaddr.sa_data, MAC_LEN); ++ mac->sll.sll_halen = EUI48; ++ } ++ + mac->sll.sll_family = AF_PACKET; +- mac->sll.sll_halen = MAC_LEN; +- memcpy(mac->sll.sll_addr, &ifreq.ifr_hwaddr.sa_data, MAC_LEN); + mac->len = sizeof(mac->sll); + close(fd); + return 0; +diff --git a/util.c b/util.c +index 2b880ff..62f2638 100644 +--- a/util.c ++++ b/util.c +@@ -135,14 +135,32 @@ int generate_clock_identity(struct ClockIdentity *ci, const char *name) + + if (sk_interface_macaddr(name, &addr)) + return -1; +- ci->id[0] = addr.sll.sll_addr[0]; +- ci->id[1] = addr.sll.sll_addr[1]; +- ci->id[2] = addr.sll.sll_addr[2]; +- ci->id[3] = 0xFF; +- ci->id[4] = 0xFE; +- ci->id[5] = addr.sll.sll_addr[3]; +- ci->id[6] = addr.sll.sll_addr[4]; +- ci->id[7] = addr.sll.sll_addr[5]; ++ ++ switch (addr.sll.sll_halen) { ++ case EUI48: ++ ci->id[0] = addr.sll.sll_addr[0]; ++ ci->id[1] = addr.sll.sll_addr[1]; ++ ci->id[2] = addr.sll.sll_addr[2]; ++ ci->id[3] = 0xFF; ++ ci->id[4] = 0xFE; ++ ci->id[5] = addr.sll.sll_addr[3]; ++ ci->id[6] = addr.sll.sll_addr[4]; ++ ci->id[7] = addr.sll.sll_addr[5]; ++ break; ++ case EUI64: ++ ci->id[0] = addr.sll.sll_addr[0]; ++ ci->id[1] = addr.sll.sll_addr[1]; ++ ci->id[2] = addr.sll.sll_addr[2]; ++ ci->id[3] = addr.sll.sll_addr[3]; ++ ci->id[4] = addr.sll.sll_addr[4]; ++ ci->id[5] = addr.sll.sll_addr[5]; ++ ci->id[6] = addr.sll.sll_addr[6]; ++ ci->id[7] = addr.sll.sll_addr[7]; ++ break; ++ default: ++ return -1; ++ } ++ + return 0; + } + +commit fd2646263f0fb1a31dde53e67ac24971ab1f90f4 +Author: Miroslav Lichvar +Date: Mon Oct 16 11:29:48 2017 +0200 + + sk: don't leak socket when reading of IB GUID fails. + + Signed-off-by: Miroslav Lichvar + +diff --git a/sk.c b/sk.c +index 0cf55c5..2c7593b 100644 +--- a/sk.c ++++ b/sk.c +@@ -225,6 +225,8 @@ int sk_interface_macaddr(const char *name, struct address *mac) + return -1; + } + ++ close(fd); ++ + /* Get interface type */ + type = ifreq.ifr_hwaddr.sa_family; + switch (type) { +@@ -243,7 +245,6 @@ int sk_interface_macaddr(const char *name, struct address *mac) + + mac->sll.sll_family = AF_PACKET; + mac->len = sizeof(mac->sll); +- close(fd); + return 0; + } + diff --git a/SOURCES/linuxptp-linkdown.patch b/SOURCES/linuxptp-linkdown.patch new file mode 100644 index 0000000..41c3918 --- /dev/null +++ b/SOURCES/linuxptp-linkdown.patch @@ -0,0 +1,76 @@ +commit 73318c5b99571c8da01474ad665e2e2e06ab12bb +Author: Richard Cochran +Date: Sun Feb 5 18:31:27 2017 +0100 + + clock: Force a BMC election when a port link goes down. + + Having one fewer port may affect the result of the BMCA. This patch + changes the main loop so that a link down event also causes a state + decision event. + + Signed-off-by: Richard Cochran + Reported-by: Henry Jesuiter + +diff --git a/clock.c b/clock.c +index f027305..6ec3e72 100644 +--- a/clock.c ++++ b/clock.c +@@ -96,6 +96,7 @@ struct clock { + int pollfd_valid; + int nports; /* does not include the UDS port */ + int last_port_number; ++ int sde; + struct hash *index2port; + int free_running; + int freq_est_interval; +@@ -341,6 +342,11 @@ static void clock_link_status(void *ctx, int index, int linkup) + port_dispatch(p, EV_FAULT_CLEARED, 0); + } else { + port_dispatch(p, EV_FAULT_DETECTED, 0); ++ /* ++ * A port going down can affect the BMCA result. ++ * Force a state decision event. ++ */ ++ c->sde = 1; + } + } + +@@ -1463,7 +1469,7 @@ struct PortIdentity clock_parent_identity(struct clock *c) + + int clock_poll(struct clock *c) + { +- int cnt, i, sde = 0; ++ int cnt, i; + enum fsm_event event; + struct pollfd *cur; + struct port *p; +@@ -1494,9 +1500,9 @@ int clock_poll(struct clock *c) + if (cur[i].revents & (POLLIN|POLLPRI)) { + event = port_event(p, i); + if (EV_STATE_DECISION_EVENT == event) +- sde = 1; ++ c->sde = 1; + if (EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES == event) +- sde = 1; ++ c->sde = 1; + port_dispatch(p, event, 0); + /* Clear any fault after a little while. */ + if (PS_FAULTY == port_state(p)) { +@@ -1525,13 +1531,14 @@ int clock_poll(struct clock *c) + if (cur[i].revents & (POLLIN|POLLPRI)) { + event = port_event(c->uds_port, i); + if (EV_STATE_DECISION_EVENT == event) +- sde = 1; ++ c->sde = 1; + } + } + +- if (sde) ++ if (c->sde) { + handle_state_decision_event(c); +- ++ c->sde = 0; ++ } + clock_prune_subscriptions(c); + return 0; + } diff --git a/SOURCES/linuxptp-messagetag.patch b/SOURCES/linuxptp-messagetag.patch new file mode 100644 index 0000000..dc9caa8 --- /dev/null +++ b/SOURCES/linuxptp-messagetag.patch @@ -0,0 +1,553 @@ +commit 4e8dbd84925d36f1193a7339a542677d884d3ba1 +Author: Richard Cochran +Date: Tue Dec 6 19:40:36 2016 +0100 + + ptp4l: Accept any configuration option as a command line argument. + + This patch provides a way to use the entire table of configuration options + as "long" command line switches. + + Signed-off-by: Richard Cochran + +diff --git a/config.c b/config.c +index 5da5ecc..384b437 100644 +--- a/config.c ++++ b/config.c +@@ -329,6 +329,7 @@ static enum parser_result parse_section_line(char *s, enum config_section *secti + } + + static enum parser_result parse_item(struct config *cfg, ++ int commandline, + const char *section, + const char *option, + const char *value) +@@ -387,7 +388,7 @@ static enum parser_result parse_item(struct config *cfg, + return NOT_PARSED; + } + } +- } else if (cgi->flags & CFG_ITEM_LOCKED) { ++ } else if (!commandline && cgi->flags & CFG_ITEM_LOCKED) { + /* This global option was set on the command line. */ + return PARSED_OK; + } else { +@@ -415,6 +416,10 @@ static enum parser_result parse_item(struct config *cfg, + dst->flags |= CFG_ITEM_DYNSTR; + break; + } ++ ++ if (commandline) { ++ dst->flags &= CFG_ITEM_LOCKED; ++ } + return PARSED_OK; + } + +@@ -490,6 +495,25 @@ static void check_deprecated_options(const char **option) + } + } + ++static struct option *config_alloc_longopts(struct config *cfg) ++{ ++ struct config_item *ci; ++ struct option *opts; ++ int i; ++ ++ opts = calloc(1, (1 + N_CONFIG_ITEMS) * sizeof(*opts)); ++ if (!opts) { ++ return NULL; ++ } ++ for (i = 0; i < N_CONFIG_ITEMS; i++) { ++ ci = &config_tab[i]; ++ opts[i].name = ci->label; ++ opts[i].has_arg = required_argument; ++ } ++ ++ return opts; ++} ++ + int config_read(char *name, struct config *cfg) + { + enum config_section current_section = UNKNOWN_SECTION; +@@ -554,7 +578,7 @@ int config_read(char *name, struct config *cfg) + + check_deprecated_options(&option); + +- parser_res = parse_item(cfg, current_section == GLOBAL_SECTION ? ++ parser_res = parse_item(cfg, 0, current_section == GLOBAL_SECTION ? + NULL : current_port->name, option, value); + + switch (parser_res) { +@@ -627,8 +651,15 @@ struct config *config_create(void) + } + STAILQ_INIT(&cfg->interfaces); + ++ cfg->opts = config_alloc_longopts(cfg); ++ if (!cfg->opts) { ++ free(cfg); ++ return NULL; ++ } ++ + cfg->htab = hash_create(); + if (!cfg->htab) { ++ free(cfg->opts); + free(cfg); + return NULL; + } +@@ -657,6 +688,7 @@ struct config *config_create(void) + return cfg; + fail: + hash_destroy(cfg->htab, NULL); ++ free(cfg->opts); + free(cfg); + return NULL; + } +@@ -670,6 +702,7 @@ void config_destroy(struct config *cfg) + free(iface); + } + hash_destroy(cfg->htab, config_item_free); ++ free(cfg->opts); + free(cfg); + } + +@@ -720,6 +753,33 @@ char *config_get_string(struct config *cfg, const char *section, + return ci->val.s; + } + ++int config_parse_option(struct config *cfg, const char *opt, const char *val) ++{ ++ enum parser_result result; ++ ++ result = parse_item(cfg, 1, NULL, opt, val); ++ ++ switch (result) { ++ case PARSED_OK: ++ return 0; ++ case NOT_PARSED: ++ fprintf(stderr, "unknown option %s\n", opt); ++ break; ++ case BAD_VALUE: ++ fprintf(stderr, "%s is a bad value for option %s\n", val, opt); ++ break; ++ case MALFORMED: ++ fprintf(stderr, "%s is a malformed value for option %s\n", ++ val, opt); ++ break; ++ case OUT_OF_RANGE: ++ fprintf(stderr, "%s is an out of range value for option %s\n", ++ val, opt); ++ break; ++ } ++ return -1; ++} ++ + int config_set_double(struct config *cfg, const char *option, double val) + { + struct config_item *ci = config_find_item(cfg, NULL, option); +diff --git a/config.h b/config.h +index b02bde6..1cc7051 100644 +--- a/config.h ++++ b/config.h +@@ -20,6 +20,7 @@ + #ifndef HAVE_CONFIG_H + #define HAVE_CONFIG_H + ++#include + #include + + #include "ds.h" +@@ -43,6 +44,9 @@ struct config { + STAILQ_HEAD(interfaces_head, interface) interfaces; + int n_interfaces; + ++ /* for parsing command line options */ ++ struct option *opts; ++ + /* hash of all non-legacy items */ + struct hash *htab; + }; +@@ -64,6 +68,13 @@ int config_get_int(struct config *cfg, const char *section, + char *config_get_string(struct config *cfg, const char *section, + const char *option); + ++static inline struct option *config_long_options(struct config *cfg) ++{ ++ return cfg->opts; ++} ++ ++int config_parse_option(struct config *cfg, const char *opt, const char *val); ++ + int config_set_double(struct config *cfg, const char *option, double val); + + int config_set_section_int(struct config *cfg, const char *section, +diff --git a/ptp4l.c b/ptp4l.c +index a87e7e6..e90fcb2 100644 +--- a/ptp4l.c ++++ b/ptp4l.c +@@ -73,8 +73,9 @@ static void usage(char *progname) + int main(int argc, char *argv[]) + { + char *config = NULL, *req_phc = NULL, *progname; +- int c, err = -1, print_level; ++ int c, err = -1, index, print_level; + struct clock *clock = NULL; ++ struct option *opts; + struct config *cfg; + + if (handle_term_signals()) +@@ -84,12 +85,18 @@ int main(int argc, char *argv[]) + if (!cfg) { + return -1; + } ++ opts = config_long_options(cfg); + + /* Process the command line arguments. */ + progname = strrchr(argv[0], '/'); + progname = progname ? 1+progname : argv[0]; +- while (EOF != (c = getopt(argc, argv, "AEP246HSLf:i:p:sl:mqvh"))) { ++ while (EOF != (c = getopt_long(argc, argv, "AEP246HSLf:i:p:sl:mqvh", ++ opts, &index))) { + switch (c) { ++ case 0: ++ if (config_parse_option(cfg, opts[index].name, optarg)) ++ goto out; ++ break; + case 'A': + if (config_set_int(cfg, "delay_mechanism", DM_AUTO)) + goto out; + +commit e658982624bfd5cd998066fdf35b93cdcf8797ca +Author: Richard Cochran +Date: Tue Dec 13 19:55:39 2016 +0100 + + config: Fix bitwise copy-and-pasto for command line items. + + The recent change allowing every configuration option to appear on the + command line wrongly used bitwise AND to set a flag. This patch fixes + the bug by using the proper bitwise OR idiom. + + Signed-off-by: Richard Cochran + Reported-by: Miroslav Lichvar + Fixes: 4e8dbd8 ("ptp4l: Accept any configuration option as a command line argument.") + +diff --git a/config.c b/config.c +index 384b437..b19f3ad 100644 +--- a/config.c ++++ b/config.c +@@ -418,7 +418,7 @@ static enum parser_result parse_item(struct config *cfg, + } + + if (commandline) { +- dst->flags &= CFG_ITEM_LOCKED; ++ dst->flags |= CFG_ITEM_LOCKED; + } + return PARSED_OK; + } + +commit 4e966536c6253d73e1a3202e8b562ab0f518e33b +Author: Richard Cochran +Date: Tue Dec 13 20:49:22 2016 +0100 + + ptp4l: Document the "long" command line options in the man page. + + Signed-off-by: Richard Cochran + +diff --git a/ptp4l.8 b/ptp4l.8 +index 63e9abd..f53fc6e 100644 +--- a/ptp4l.8 ++++ b/ptp4l.8 +@@ -1,4 +1,4 @@ +-.TH PTP4l 8 "July 2016" "linuxptp" ++.TH PTP4l 8 "December 2016" "linuxptp" + .SH NAME + ptp4l - PTP Boundary/Ordinary Clock + +@@ -15,6 +15,8 @@ ptp4l - PTP Boundary/Ordinary Clock + ] + [ + .BI \-i " interface" ++] [ ++.I long-options + ] + .I .\|.\|. + +@@ -94,6 +96,19 @@ Prints the software version and exits. + .BI \-h + Display a help message. + ++.SH LONG OPTIONS ++ ++Each and every configuration file option (see below) may also appear ++as a "long" style command line argument. For example, the slaveOnly ++option may be set using either of these two forms. ++ ++.RS ++\f(CW\-\-slaveOnly 1 \-\-slaveOnly=1\fP ++.RE ++ ++Option values given on the command line override values in the global ++section of the configuration file. ++ + .SH CONFIGURATION FILE + + The configuration file is divided into sections. Each section starts with a + +commit 0f6c6972c791813e0b9618e9158da3951a099737 +Author: Miroslav Lichvar +Date: Tue Jan 17 14:17:39 2017 +0100 + + Add options to tag ptp4l and phc2sys log messages. + + When running multiple instances of ptp4l or phc2sys, it's difficult to + tell which log message belongs to which instance. Add new options to + ptp4l and phc2sys which can specify a tag for all messages printed to + the standard output or system log, so messages from different instances + can have different tags. + + Signed-off-by: Miroslav Lichvar + +diff --git a/config.c b/config.c +index 7bb949d..e6fe676 100644 +--- a/config.c ++++ b/config.c +@@ -199,6 +199,7 @@ struct config_item config_tab[] = { + PORT_ITEM_INT("logMinPdelayReqInterval", 0, INT8_MIN, INT8_MAX), + PORT_ITEM_INT("logSyncInterval", 0, INT8_MIN, INT8_MAX), + GLOB_ITEM_INT("logging_level", LOG_INFO, PRINT_LEVEL_MIN, PRINT_LEVEL_MAX), ++ GLOB_ITEM_STR("message_tag", NULL), + GLOB_ITEM_STR("manufacturerIdentity", "00:00:00"), + GLOB_ITEM_INT("max_frequency", 900000000, 0, INT_MAX), + PORT_ITEM_INT("min_neighbor_prop_delay", -20000000, INT_MIN, -1), +diff --git a/phc2sys.8 b/phc2sys.8 +index 22d02c2..2559c74 100644 +--- a/phc2sys.8 ++++ b/phc2sys.8 +@@ -206,6 +206,10 @@ The default is /var/run/ptp4l. + Set the maximum syslog level of messages which should be printed or sent to + the system logger. The default is 6 (LOG_INFO). + .TP ++.BI \-t " message-tag" ++Specify the tag which is added to all messages printed to the standard output ++or system log. The default is an empty string. ++.TP + .B \-m + Print messages to the standard output. + .TP +diff --git a/phc2sys.c b/phc2sys.c +index 35cf6fa..aa4186b 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -1209,6 +1209,7 @@ static void usage(char *progname) + " -x apply leap seconds by servo instead of kernel\n" + " -z [path] server address for UDS (/var/run/ptp4l)\n" + " -l [num] set the logging level to 'num' (6)\n" ++ " -t [tag] add tag to log messages\n" + " -m print messages to stdout\n" + " -q do not print messages to the syslog\n" + " -v prints the software version and exits\n" +@@ -1219,7 +1220,7 @@ static void usage(char *progname) + + int main(int argc, char *argv[]) + { +- char *progname; ++ char *progname, *message_tag = NULL; + char *src_name = NULL, *dst_name = NULL; + struct clock *src, *dst; + struct config *cfg; +@@ -1251,7 +1252,7 @@ int main(int argc, char *argv[]) + progname = strrchr(argv[0], '/'); + progname = progname ? 1+progname : argv[0]; + while (EOF != (c = getopt(argc, argv, +- "arc:d:s:E:P:I:S:F:R:N:O:L:M:i:u:wn:xz:l:mqvh"))) { ++ "arc:d:s:E:P:I:S:F:R:N:O:L:M:i:u:wn:xz:l:t:mqvh"))) { + switch (c) { + case 'a': + autocfg = 1; +@@ -1363,6 +1364,9 @@ int main(int argc, char *argv[]) + PRINT_LEVEL_MIN, PRINT_LEVEL_MAX)) + goto end; + break; ++ case 't': ++ message_tag = optarg; ++ break; + case 'm': + verbose = 1; + break; +@@ -1405,6 +1409,7 @@ int main(int argc, char *argv[]) + } + + print_set_progname(progname); ++ print_set_tag(message_tag); + print_set_verbose(verbose); + print_set_syslog(use_syslog); + print_set_level(print_level); +diff --git a/print.c b/print.c +index a82d0e7..6c48e1e 100644 +--- a/print.c ++++ b/print.c +@@ -28,12 +28,18 @@ static int verbose = 0; + static int print_level = LOG_INFO; + static int use_syslog = 1; + static const char *progname; ++static const char *message_tag; + + void print_set_progname(const char *name) + { + progname = name; + } + ++void print_set_tag(const char *tag) ++{ ++ message_tag = tag; ++} ++ + void print_set_syslog(int value) + { + use_syslog = value ? 1 : 0; +@@ -67,13 +73,17 @@ void print(int level, char const *format, ...) + + if (verbose) { + f = level >= LOG_NOTICE ? stdout : stderr; +- fprintf(f, "%s[%ld.%03ld]: %s\n", ++ fprintf(f, "%s[%ld.%03ld]: %s%s%s\n", + progname ? progname : "", +- ts.tv_sec, ts.tv_nsec / 1000000, buf); ++ ts.tv_sec, ts.tv_nsec / 1000000, ++ message_tag ? message_tag : "", message_tag ? " " : "", ++ buf); + fflush(f); + } + if (use_syslog) { +- syslog(level, "[%ld.%03ld] %s", +- ts.tv_sec, ts.tv_nsec / 1000000, buf); ++ syslog(level, "[%ld.%03ld] %s%s%s", ++ ts.tv_sec, ts.tv_nsec / 1000000, ++ message_tag ? message_tag : "", message_tag ? " " : "", ++ buf); + } + } +diff --git a/print.h b/print.h +index e8f2c8e..1723d8a 100644 +--- a/print.h ++++ b/print.h +@@ -33,6 +33,7 @@ __attribute__ ((format (printf, 2, 3))) + void print(int level, char const *format, ...); + + void print_set_progname(const char *name); ++void print_set_tag(const char *tag); + void print_set_syslog(int value); + void print_set_level(int level); + void print_set_verbose(int value); +diff --git a/ptp4l.8 b/ptp4l.8 +index 53d5f28..a724151 100644 +--- a/ptp4l.8 ++++ b/ptp4l.8 +@@ -485,6 +485,12 @@ is 0. + The maximum logging level of messages which should be printed. + The default is 6 (LOG_INFO). + .TP ++.B message_tag ++The tag which is added to all messages printed to the standard output or system ++log. ++The default is an empty string (which cannot be set in the configuration file ++as the option requires an argument). ++.TP + .B verbose + Print messages to the standard output if enabled. + The default is 0 (disabled). +diff --git a/ptp4l.c b/ptp4l.c +index e90fcb2..f01ff6f 100644 +--- a/ptp4l.c ++++ b/ptp4l.c +@@ -183,6 +183,7 @@ int main(int argc, char *argv[]) + } + + print_set_progname(progname); ++ print_set_tag(config_get_string(cfg, NULL, "message_tag")); + print_set_verbose(config_get_int(cfg, NULL, "verbose")); + print_set_syslog(config_get_int(cfg, NULL, "use_syslog")); + print_set_level(config_get_int(cfg, NULL, "logging_level")); + +commit e54158195b1eadfdb6275646bc3dfb7611dba5b6 +Author: Miroslav Lichvar +Date: Tue Jan 17 14:17:40 2017 +0100 + + timemaster: tag ptp4l and phc2sys messages. + + Use the new options of ptp4l and phc2sys to tag their log messages with + the PTP domain number and name(s) of interface(s) in the domain. + + Signed-off-by: Miroslav Lichvar + +diff --git a/timemaster.c b/timemaster.c +index 66ac521..880b2ab 100644 +--- a/timemaster.c ++++ b/timemaster.c +@@ -599,7 +599,8 @@ static char **get_ptp4l_command(struct program_config *config, + } + + static char **get_phc2sys_command(struct program_config *config, int domain, +- int poll, int shm_segment, char *uds_path) ++ int poll, int shm_segment, char *uds_path, ++ char *message_tag) + { + char **command = (char **)parray_new(); + +@@ -610,6 +611,7 @@ static char **get_phc2sys_command(struct program_config *config, int domain, + xstrdup("-R"), string_newf("%.2f", poll > 0 ? + 1.0 / (1 << poll) : 1 << -poll), + xstrdup("-z"), xstrdup(uds_path), ++ xstrdup("-t"), xstrdup(message_tag), + xstrdup("-n"), string_newf("%d", domain), + xstrdup("-E"), xstrdup("ntpshm"), + xstrdup("-M"), string_newf("%d", shm_segment), NULL); +@@ -671,7 +673,7 @@ static int add_ptp_source(struct ptp_domain *source, + struct script *script) + { + struct config_file *config_file; +- char **command, *uds_path, **interfaces; ++ char **command, *uds_path, **interfaces, *message_tag; + int i, j, num_interfaces, *phc, *phcs, hw_ts; + struct sk_ts_info ts_info; + +@@ -749,6 +751,12 @@ static int add_ptp_source(struct ptp_domain *source, + uds_path = string_newf("%s/ptp4l.%d.socket", + config->rundir, *shm_segment); + ++ message_tag = string_newf("[%d", source->domain); ++ for (j = 0; interfaces[j]; j++) ++ string_appendf(&message_tag, "%s%s", j ? "+" : ":", ++ interfaces[j]); ++ string_appendf(&message_tag, "]"); ++ + config_file = xmalloc(sizeof(*config_file)); + config_file->path = string_newf("%s/ptp4l.%d.conf", + config->rundir, *shm_segment); +@@ -760,8 +768,9 @@ static int add_ptp_source(struct ptp_domain *source, + string_appendf(&config_file->content, + "slaveOnly 1\n" + "domainNumber %d\n" +- "uds_address %s\n", +- source->domain, uds_path); ++ "uds_address %s\n" ++ "message_tag %s\n", ++ source->domain, uds_path, message_tag); + + if (phcs[i] >= 0) { + /* HW time stamping */ +@@ -772,7 +781,8 @@ static int add_ptp_source(struct ptp_domain *source, + command = get_phc2sys_command(&config->phc2sys, + source->domain, + source->phc2sys_poll, +- *shm_segment, uds_path); ++ *shm_segment, uds_path, ++ message_tag); + parray_append((void ***)&script->commands, command); + } else { + /* SW time stamping */ +@@ -793,6 +803,7 @@ static int add_ptp_source(struct ptp_domain *source, + + (*shm_segment)++; + ++ free(message_tag); + free(uds_path); + free(interfaces); + } diff --git a/SOURCES/linuxptp-mgttlv.patch b/SOURCES/linuxptp-mgttlv.patch new file mode 100644 index 0000000..646bf43 --- /dev/null +++ b/SOURCES/linuxptp-mgttlv.patch @@ -0,0 +1,50 @@ +commit 43b2f5d1207a010f1df67e101b129b09502371e2 +Author: Hangbin Liu +Date: Fri May 12 15:36:45 2017 +0800 + + msg: use last_tlv if there is not enough room for another tlv + + If the len is not enought for another tlv process. e.g. one more bytes + padding at the end of message. And we set extra to NULL instead of + msg->last_tlv in tlv_post_recv(). Then the msg->last_tlv will not be + initialised. And program will crash if we read msg->last_tlv. e.g. in + function pmc_show(). + + Signed-off-by: Hangbin Liu + +diff --git a/msg.c b/msg.c +index a38b815..4b3d926 100644 +--- a/msg.c ++++ b/msg.c +@@ -140,7 +140,7 @@ static int suffix_post_recv(uint8_t *ptr, int len, struct tlv_extra *last) + } + len -= tlv->length; + ptr += tlv->length; +- err = tlv_post_recv(tlv, len ? NULL : last); ++ err = tlv_post_recv(tlv, len > sizeof(struct TLV) ? NULL : last); + if (err) + return err; + } +commit 95b5a13cb2787b6a436ad395bb4931d1661e59a7 +Author: Hangbin Liu +Date: Tue May 23 14:49:55 2017 +0800 + + pmc: goto out when get unknown management tlv + + If handle unknown management tlv. The management message id and format are + also unknown, thus we may crash due to access unknown area. + + Signed-off-by: Hangbin Liu + +diff --git a/pmc.c b/pmc.c +index cefa771..af9cc63 100644 +--- a/pmc.c ++++ b/pmc.c +@@ -217,6 +217,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + goto out; + } else { + fprintf(fp, "unknown-tlv "); ++ goto out; + } + mgt = (struct management_tlv *) msg->management.suffix; + if (mgt->length == 2 && mgt->id != TLV_NULL_MANAGEMENT) { diff --git a/SOURCES/linuxptp-multiport.patch b/SOURCES/linuxptp-multiport.patch new file mode 100644 index 0000000..8f9568c --- /dev/null +++ b/SOURCES/linuxptp-multiport.patch @@ -0,0 +1,77 @@ +commit ac92a711614c413b75a4963eab95cd1aa2de8293 +Author: Miroslav Lichvar +Date: Mon Mar 27 17:43:06 2017 +0200 + + phc2sys: don't synchronize clock to itself. + + When ptp4l is using multiple interfaces sharing the same clock, phc2sys + in the automatic mode should not try to synchronize them to each other. + + Signed-off-by: Miroslav Lichvar + Reported-by: Stefan Lange + +diff --git a/phc2sys.c b/phc2sys.c +index aa4186b..4c8b552 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -72,6 +72,7 @@ + struct clock { + LIST_ENTRY(clock) list; + clockid_t clkid; ++ int phc_index; + int sysoff_supported; + int is_utc; + int dest_only; +@@ -127,7 +128,7 @@ static int clock_handle_leap(struct node *node, struct clock *clock, + static int run_pmc_get_utc_offset(struct node *node, int timeout); + static void run_pmc_events(struct node *node); + +-static clockid_t clock_open(char *device) ++static clockid_t clock_open(char *device, int *phc_index) + { + struct sk_ts_info ts_info; + char phc_device[16]; +@@ -157,6 +158,7 @@ static clockid_t clock_open(char *device) + clkid = phc_open(phc_device); + if (clkid == CLOCK_INVALID) + fprintf(stderr, "cannot open %s: %m\n", device); ++ *phc_index = ts_info.phc_index; + return clkid; + } + +@@ -164,11 +166,11 @@ static struct clock *clock_add(struct node *node, char *device) + { + struct clock *c; + clockid_t clkid = CLOCK_INVALID; +- int max_ppb; ++ int max_ppb, phc_index = -1; + double ppb; + + if (device) { +- clkid = clock_open(device); ++ clkid = clock_open(device, &phc_index); + if (clkid == CLOCK_INVALID) + return NULL; + } +@@ -179,6 +181,7 @@ static struct clock *clock_add(struct node *node, char *device) + return NULL; + } + c->clkid = clkid; ++ c->phc_index = phc_index; + c->servo_state = SERVO_UNLOCKED; + c->device = strdup(device); + +@@ -638,6 +641,13 @@ static int do_loop(struct node *node, int subscriptions) + if (!update_needed(clock)) + continue; + ++ /* don't try to synchronize the clock to itself */ ++ if (clock->clkid == node->master->clkid || ++ (clock->phc_index >= 0 && ++ clock->phc_index == node->master->phc_index) || ++ !strcmp(clock->device, node->master->device)) ++ continue; ++ + if (clock->clkid == CLOCK_REALTIME && + node->master->sysoff_supported) { + /* use sysoff */ diff --git a/SOURCES/linuxptp-portdispatch.patch b/SOURCES/linuxptp-portdispatch.patch new file mode 100644 index 0000000..2bb09c1 --- /dev/null +++ b/SOURCES/linuxptp-portdispatch.patch @@ -0,0 +1,434 @@ +commit 10d4e7f8b0525bbf72981e3e5fcf0c10833a7631 +Author: Richard Cochran +Date: Tue Jan 3 20:55:18 2017 +0100 + + port: Make the finite state machine into a function variable. + + This allows adding new FSM flavors in the future. + + Signed-off-by: Richard Cochran + +diff --git a/port.c b/port.c +index a1ad6f6..afe0057 100644 +--- a/port.c ++++ b/port.c +@@ -94,6 +94,8 @@ struct port { + unsigned int pdr_missing; + unsigned int multiple_seq_pdr_count; + unsigned int multiple_pdr_detected; ++ enum port_state (*state_machine)(enum port_state state, ++ enum fsm_event event, int mdiff); + /* portDS */ + struct PortIdentity portIdentity; + enum port_state state; /*portState*/ +@@ -2142,10 +2144,8 @@ int port_dispatch(struct port *p, enum fsm_event event, int mdiff) + if (event == EV_RS_MASTER || event == EV_RS_GRAND_MASTER) { + port_slave_priority_warning(p); + } +- next = ptp_slave_fsm(p->state, event, mdiff); +- } else { +- next = ptp_fsm(p->state, event, mdiff); + } ++ next = p->state_machine(p->state, event, mdiff); + + if (!fault_interval(p, last_fault_type(p), &i) && + ((i.val == FRI_ASAP && i.type == FTMO_LOG2_SECONDS) || +@@ -2555,6 +2555,7 @@ struct port *port_open(int phc_index, + + memset(p, 0, sizeof(*p)); + ++ p->state_machine = clock_slave_only(clock) ? ptp_slave_fsm : ptp_fsm; + p->phc_index = phc_index; + p->jbod = config_get_int(cfg, interface->name, "boundary_clock_jbod"); + transport = config_get_int(cfg, interface->name, "network_transport"); + +commit 80a28a9dc322f28b991effc44ec1c5362a598281 +Author: Richard Cochran +Date: Tue Jan 3 20:55:33 2017 +0100 + + Change a misleading fault handling function signature. + + Looking at the fault logic in port_dispatch(), you might think that + the function, fault_interval(), checks whether a fault is active, but + you would be wrong, since that function always returns zero. + + This patch removes the superfluous input error checking inside of + fault_interval() and changes the return type to void, making the + actual behavior explicit. Dropping the input check is safe because + that function has exactly two callers, both of whom always provide + valid inputs. + + Signed-off-by: Richard Cochran + +diff --git a/port.c b/port.c +index afe0057..6c9aa72 100644 +--- a/port.c ++++ b/port.c +@@ -205,16 +205,11 @@ enum fault_type last_fault_type(struct port *port) + return port->last_fault_type; + } + +-int fault_interval(struct port *port, enum fault_type ft, +- struct fault_interval *i) ++void fault_interval(struct port *port, enum fault_type ft, ++ struct fault_interval *i) + { +- if (!port || !i) +- return -EINVAL; +- if (ft < 0 || ft >= FT_CNT) +- return -EINVAL; + i->type = port->flt_interval_pertype[ft].type; + i->val = port->flt_interval_pertype[ft].val; +- return 0; + } + + int port_fault_fd(struct port *port) +@@ -2147,9 +2142,9 @@ int port_dispatch(struct port *p, enum fsm_event event, int mdiff) + } + next = p->state_machine(p->state, event, mdiff); + +- if (!fault_interval(p, last_fault_type(p), &i) && +- ((i.val == FRI_ASAP && i.type == FTMO_LOG2_SECONDS) || +- (i.val == 0 && i.type == FTMO_LINEAR_SECONDS))) ++ fault_interval(p, last_fault_type(p), &i); ++ if ((i.val == FRI_ASAP && i.type == FTMO_LOG2_SECONDS) || ++ (i.val == 0 && i.type == FTMO_LINEAR_SECONDS)) + fri_asap = 1; + if (PS_INITIALIZING == next || (PS_FAULTY == next && fri_asap)) { + /* +diff --git a/port.h b/port.h +index 19dec4a..d2e0865 100644 +--- a/port.h ++++ b/port.h +@@ -318,9 +318,8 @@ enum fault_type last_fault_type(struct port *port); + * @param port A port instance. + * @param ft Fault type. + * @param i Pointer to the struct which will be filled in. +- * @return Zero on success, non-zero otherwise. + */ +-int fault_interval(struct port *port, enum fault_type ft, +- struct fault_interval *i); ++void fault_interval(struct port *port, enum fault_type ft, ++ struct fault_interval *i); + + #endif + +commit 1f66948d3853c8b08c7f52c9c8daecb361164a43 +Author: Richard Cochran +Date: Tue Jan 3 20:55:42 2017 +0100 + + Make the fault handling code more readable. + + The code that decides whether a fault qualifies for ASAP treatment is + a tangle of logical operators. This patch replaces the open coded + logic with a helper function whose name makes the intent clear. This + is a cosmetic change only. + + Signed-off-by: Richard Cochran + +diff --git a/port.c b/port.c +index 6c9aa72..02dbabb 100644 +--- a/port.c ++++ b/port.c +@@ -161,6 +161,19 @@ static void announce_to_dataset(struct ptp_message *m, struct port *p, + out->receiver = p->portIdentity; + } + ++static int clear_fault_asap(struct fault_interval *faint) ++{ ++ switch (faint->type) { ++ case FTMO_LINEAR_SECONDS: ++ return faint->val == 0 ? 1 : 0; ++ case FTMO_LOG2_SECONDS: ++ return faint->val == FRI_ASAP ? 1 : 0; ++ case FTMO_CNT: ++ return 0; ++ } ++ return 0; ++} ++ + static int msg_current(struct ptp_message *m, struct timespec now) + { + int64_t t1, t2, tmo; +@@ -2143,9 +2156,9 @@ int port_dispatch(struct port *p, enum fsm_event event, int mdiff) + next = p->state_machine(p->state, event, mdiff); + + fault_interval(p, last_fault_type(p), &i); +- if ((i.val == FRI_ASAP && i.type == FTMO_LOG2_SECONDS) || +- (i.val == 0 && i.type == FTMO_LINEAR_SECONDS)) ++ if (clear_fault_asap(&i)) { + fri_asap = 1; ++ } + if (PS_INITIALIZING == next || (PS_FAULTY == next && fri_asap)) { + /* + * This is a special case. Since we initialize the + +commit 01ee947457813078f63d606c310818d6dedb4ca3 +Author: Richard Cochran +Date: Tue Jan 3 20:55:50 2017 +0100 + + Disentangle initialization from fault clearing. + + Although leaving the INITIALIZING state and clearing the FAULTY state + ASAP both result in a port entering the LISTENING state, still there + is no benefit from conflating the two. In the FAULTY case, the + current code actually skips the INITIALIZING state altogether. + + This patch separates the two cases resulting in two benefits. First, + the check for ASAP fault status is only made when a fault is actually + present, unlike the present unconditional check. Second, this change + will allow us to cleanly support alternative state machines later on. + + Signed-off-by: Richard Cochran + +diff --git a/port.c b/port.c +index 02dbabb..ebe7342 100644 +--- a/port.c ++++ b/port.c +@@ -2145,8 +2145,6 @@ static void port_p2p_transition(struct port *p, enum port_state next) + int port_dispatch(struct port *p, enum fsm_event event, int mdiff) + { + enum port_state next; +- struct fault_interval i; +- int fri_asap = 0; + + if (clock_slave_only(p->clock)) { + if (event == EV_RS_MASTER || event == EV_RS_GRAND_MASTER) { +@@ -2155,11 +2153,15 @@ int port_dispatch(struct port *p, enum fsm_event event, int mdiff) + } + next = p->state_machine(p->state, event, mdiff); + +- fault_interval(p, last_fault_type(p), &i); +- if (clear_fault_asap(&i)) { +- fri_asap = 1; ++ if (PS_FAULTY == next) { ++ struct fault_interval i; ++ fault_interval(p, last_fault_type(p), &i); ++ if (clear_fault_asap(&i)) { ++ pr_notice("port %hu: clearing fault immediately", portnum(p)); ++ next = PS_INITIALIZING; ++ } + } +- if (PS_INITIALIZING == next || (PS_FAULTY == next && fri_asap)) { ++ if (PS_INITIALIZING == next) { + /* + * This is a special case. Since we initialize the + * port immediately, we can skip right to listening + +commit b738afb6044a8c59310fc13f646c8f5c4dcf2963 +Author: Richard Cochran +Date: Thu Oct 27 15:20:36 2016 +0200 + + fsm: Make the transition out of INITIALIZING part of the FSM code. + + The state machines in 1588 do not specify an event that causes a transition + out of the initializing state. This was left as a local issue. For this + transition, the current code assigns the next state outside of the FSM. But + doing so prevents an alternative FSM to handle this transition differently. + + By introducing a new event, this patch places this transition where it + belongs, namely under the control of the FSM code, + + Signed-off-by: Richard Cochran + +diff --git a/fsm.c b/fsm.c +index d1423e7..ce6efad 100644 +--- a/fsm.c ++++ b/fsm.c +@@ -27,7 +27,16 @@ enum port_state ptp_fsm(enum port_state state, enum fsm_event event, int mdiff) + + switch (state) { + case PS_INITIALIZING: +- next = PS_LISTENING; ++ switch (event) { ++ case EV_FAULT_DETECTED: ++ next = PS_FAULTY; ++ break; ++ case EV_INIT_COMPLETE: ++ next = PS_LISTENING; ++ break; ++ default: ++ break; ++ } + break; + + case PS_FAULTY: +@@ -220,7 +229,16 @@ enum port_state ptp_slave_fsm(enum port_state state, enum fsm_event event, + + switch (state) { + case PS_INITIALIZING: +- next = PS_LISTENING; ++ switch (event) { ++ case EV_FAULT_DETECTED: ++ next = PS_FAULTY; ++ break; ++ case EV_INIT_COMPLETE: ++ next = PS_LISTENING; ++ break; ++ default: ++ break; ++ } + break; + + case PS_FAULTY: +diff --git a/fsm.h b/fsm.h +index 5d4ae91..0616daa 100644 +--- a/fsm.h ++++ b/fsm.h +@@ -48,6 +48,7 @@ enum fsm_event { + EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES, + EV_SYNCHRONIZATION_FAULT, + EV_MASTER_CLOCK_SELECTED, ++ EV_INIT_COMPLETE, + EV_RS_MASTER, + EV_RS_GRAND_MASTER, + EV_RS_SLAVE, +diff --git a/port.c b/port.c +index ebe7342..5f1646b 100644 +--- a/port.c ++++ b/port.c +@@ -2120,6 +2120,7 @@ static void port_p2p_transition(struct port *p, enum port_state next) + break; + case PS_LISTENING: + port_set_announce_tmo(p); ++ port_set_delay_tmo(p); + break; + case PS_PRE_MASTER: + port_set_qualification_tmo(p); +@@ -2158,7 +2159,7 @@ int port_dispatch(struct port *p, enum fsm_event event, int mdiff) + fault_interval(p, last_fault_type(p), &i); + if (clear_fault_asap(&i)) { + pr_notice("port %hu: clearing fault immediately", portnum(p)); +- next = PS_INITIALIZING; ++ next = p->state_machine(next, EV_FAULT_CLEARED, 0); + } + } + if (PS_INITIALIZING == next) { +@@ -2170,14 +2171,12 @@ int port_dispatch(struct port *p, enum fsm_event event, int mdiff) + if (port_is_enabled(p)) { + port_disable(p); + } +- next = port_initialize(p) ? PS_FAULTY : PS_LISTENING; +- port_show_transition(p, next, event); +- p->state = next; +- if (next == PS_LISTENING && p->delayMechanism == DM_P2P) { +- port_set_delay_tmo(p); ++ if (port_initialize(p)) { ++ event = EV_FAULT_DETECTED; ++ } else { ++ event = EV_INIT_COMPLETE; + } +- port_notify_event(p, NOTIFY_PORT_STATE); +- return 1; ++ next = p->state_machine(next, event, 0); + } + + if (next == p->state) +diff --git a/util.c b/util.c +index 594b49f..2b880ff 100644 +--- a/util.c ++++ b/util.c +@@ -61,6 +61,7 @@ const char *ev_str[] = { + "ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES", + "SYNCHRONIZATION_FAULT", + "MASTER_CLOCK_SELECTED", ++ "INIT_COMPLETE", + "RS_MASTER", + "RS_GRAND_MASTER", + "RS_SLAVE", + +commit 6b471d45edfdfa835a9115ce4be96d92f100e2ac +Author: Richard Cochran +Date: Sun Feb 5 18:25:18 2017 +0100 + + port: Change port_dispatch() into a void function. + + This global function used to return an error code, but now it always + returns zero. This patch converts the function signature to return void + and simplifies the main clock loop by removing the useless test. + + Signed-off-by: Richard Cochran + +diff --git a/clock.c b/clock.c +index a6a1a1a..f027305 100644 +--- a/clock.c ++++ b/clock.c +@@ -1463,7 +1463,7 @@ struct PortIdentity clock_parent_identity(struct clock *c) + + int clock_poll(struct clock *c) + { +- int cnt, err, i, sde = 0; ++ int cnt, i, sde = 0; + enum fsm_event event; + struct pollfd *cur; + struct port *p; +@@ -1490,14 +1490,14 @@ int clock_poll(struct clock *c) + cur++; + LIST_FOREACH(p, &c->ports, list) { + /* Let the ports handle their events. */ +- for (i = err = 0; i < N_POLLFD && !err; i++) { ++ for (i = 0; i < N_POLLFD; i++) { + if (cur[i].revents & (POLLIN|POLLPRI)) { + event = port_event(p, i); + if (EV_STATE_DECISION_EVENT == event) + sde = 1; + if (EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES == event) + sde = 1; +- err = port_dispatch(p, event, 0); ++ port_dispatch(p, event, 0); + /* Clear any fault after a little while. */ + if (PS_FAULTY == port_state(p)) { + clock_fault_timeout(p, 1); +diff --git a/port.c b/port.c +index 5f1646b..0f99b1b 100644 +--- a/port.c ++++ b/port.c +@@ -2143,7 +2143,7 @@ static void port_p2p_transition(struct port *p, enum port_state next) + }; + } + +-int port_dispatch(struct port *p, enum fsm_event event, int mdiff) ++void port_dispatch(struct port *p, enum fsm_event event, int mdiff) + { + enum port_state next; + +@@ -2180,7 +2180,7 @@ int port_dispatch(struct port *p, enum fsm_event event, int mdiff) + } + + if (next == p->state) +- return 0; ++ return; + + port_show_transition(p, next, event); + +@@ -2196,12 +2196,11 @@ int port_dispatch(struct port *p, enum fsm_event event, int mdiff) + if (p->jbod && next == PS_UNCALIBRATED) { + if (clock_switch_phc(p->clock, p->phc_index)) { + p->last_fault_type = FT_SWITCH_PHC; +- return port_dispatch(p, EV_FAULT_DETECTED, 0); ++ port_dispatch(p, EV_FAULT_DETECTED, 0); ++ return; + } + clock_sync_interval(p->clock, p->log_sync_interval); + } +- +- return 0; + } + + enum fsm_event port_event(struct port *p, int fd_index) +diff --git a/port.h b/port.h +index d2e0865..b00bc64 100644 +--- a/port.h ++++ b/port.h +@@ -69,10 +69,8 @@ struct foreign_clock *port_compute_best(struct port *port); + * @param port A pointer previously obtained via port_open(). + * @param event One of the @a fsm_event codes. + * @param mdiff Whether a new master has been selected. +- * @return Zero if the port's file descriptor array is still valid, +- * and non-zero if it has become invalid. + */ +-int port_dispatch(struct port *p, enum fsm_event event, int mdiff); ++void port_dispatch(struct port *p, enum fsm_event event, int mdiff); + + /** + * Generates state machine events based on activity on a port's file diff --git a/SOURCES/linuxptp-statechange.patch b/SOURCES/linuxptp-statechange.patch new file mode 100644 index 0000000..26c0ad9 --- /dev/null +++ b/SOURCES/linuxptp-statechange.patch @@ -0,0 +1,30 @@ +commit 17c9787b1d6891636b5be9e4e5a08278b44e9a7a +Author: Miroslav Lichvar +Date: Fri Sep 01 11:42:26 2017 +0100 + + phc2sys: fix handling of multiple state changes. + + When the master clock changed its state and then changed it back to the + original state before phc2sys could process the first change, e.g. SLAVE + -> UNCALIBRATED -> SLAVE after a clockcheck failure, the second change + was ignored because the new value was the same as the original state, + which wasn't updated for the first change yet. This caused phc2sys to be + stuck with a wrong state. + + Fix phc2sys to check both the state and new_state variables of the clock. + + Signed-off-by: Miroslav Lichvar + +diff --git a/phc2sys.c b/phc2sys.c +index b6f6719..e2b5c47 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -786,7 +786,7 @@ static int recv_subscribed(struct node *node, struct ptp_message *msg, + port->state = state; + clock = port->clock; + state = clock_compute_state(node, clock); +- if (clock->state != state) { ++ if (clock->state != state || clock->new_state) { + clock->new_state = state; + node->state_changed = 1; + } diff --git a/SOURCES/linuxptp-swtscheck.patch b/SOURCES/linuxptp-swtscheck.patch new file mode 100644 index 0000000..e448dbd --- /dev/null +++ b/SOURCES/linuxptp-swtscheck.patch @@ -0,0 +1,50 @@ +commit 117ed5c2d0f865894dd930d877f0ec28bc23d951 +Author: Miroslav Lichvar +Date: Tue Jan 17 14:17:41 2017 +0100 + + timemaster: check support for SW time stamping. + + When an interface doesn't support HW time stamping, before falling back + to SW time stamping, check if it's actually supported and exit with an + error message if not. + + Signed-off-by: Miroslav Lichvar + +diff --git a/timemaster.c b/timemaster.c +index 880b2ab..cda2d90 100644 +--- a/timemaster.c ++++ b/timemaster.c +@@ -674,13 +674,15 @@ static int add_ptp_source(struct ptp_domain *source, + { + struct config_file *config_file; + char **command, *uds_path, **interfaces, *message_tag; +- int i, j, num_interfaces, *phc, *phcs, hw_ts; ++ int i, j, num_interfaces, *phc, *phcs, hw_ts, sw_ts; + struct sk_ts_info ts_info; + + pr_debug("adding PTP domain %d", source->domain); + + hw_ts = SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; ++ sw_ts = SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE | ++ SOF_TIMESTAMPING_SOFTWARE; + + for (num_interfaces = 0; + source->interfaces[num_interfaces]; num_interfaces++) +@@ -702,9 +704,14 @@ static int add_ptp_source(struct ptp_domain *source, + return 1; + } + +- if (!ts_info.valid || +- ((ts_info.so_timestamping & hw_ts) != hw_ts)) { ++ if (((ts_info.so_timestamping & hw_ts) != hw_ts)) { + pr_debug("interface %s: no PHC", source->interfaces[i]); ++ if ((ts_info.so_timestamping & sw_ts) != sw_ts) { ++ pr_err("time stamping not supported on %s", ++ source->interfaces[i]); ++ free(phcs); ++ return 1; ++ } + continue; + } + diff --git a/SOURCES/linuxptp-utcoffset.patch b/SOURCES/linuxptp-utcoffset.patch new file mode 100644 index 0000000..b54f800 --- /dev/null +++ b/SOURCES/linuxptp-utcoffset.patch @@ -0,0 +1,130 @@ +commit 0309a880fb4389e869ae0ba431d3d17b1ec5a272 +Author: Miroslav Lichvar +Date: Thu Jan 5 10:15:53 2017 +0100 + + Update TAI-UTC offset + + A leap second was applied to UTC on 2016-12-31 and the offset between + TAI and UTC is now 37 seconds. + + Signed-off-by: Miroslav Lichvar + +diff --git a/ds.h b/ds.h +index b36862d..0e48d05 100644 +--- a/ds.h ++++ b/ds.h +@@ -84,7 +84,7 @@ struct parent_ds { + unsigned int path_length; + }; + +-#define CURRENT_UTC_OFFSET 36 /* 1 Jul 2015 */ ++#define CURRENT_UTC_OFFSET 37 /* 1 Jan 2017 */ + #define INTERNAL_OSCILLATOR 0xA0 + + struct timePropertiesDS { +commit 33e62f992542ac5ce6bdbb8ae6c34dec7011b543 +Author: Viliam Lejcik +Date: Tue Jan 17 18:25:26 2017 +0100 + + ptp4l: Make UTC offset configurable. + + Currently UTC offset is defined as a constant - CURRENT_UTC_OFFSET, and if + a leap second is added, that constant is no longer valid. Ptp4l was + updated to read the UTC offset from configuration instead. + + Signed-off-by: Viliam Lejcik + +diff --git a/clock.c b/clock.c +index c716f01..a6a1a1a 100644 +--- a/clock.c ++++ b/clock.c +@@ -105,6 +105,7 @@ struct clock { + int leap_set; + int kernel_leap; + int utc_offset; /* grand master role */ ++ int current_utc_offset; /* UTC offset fallback */ + int time_flags; /* grand master role */ + int time_source; /* grand master role */ + enum servo_state servo_state; +@@ -681,7 +682,7 @@ static void clock_update_slave(struct clock *c) + if (!(c->tds.flags & PTP_TIMESCALE)) { + pr_warning("foreign master not using PTP timescale"); + } +- if (c->tds.currentUtcOffset < CURRENT_UTC_OFFSET) { ++ if (c->tds.currentUtcOffset < c->current_utc_offset) { + pr_warning("running in a temporal vortex"); + } + } +@@ -697,10 +698,10 @@ static int clock_utc_correct(struct clock *c, tmv_t ingress) + + if (c->tds.flags & UTC_OFF_VALID && c->tds.flags & TIME_TRACEABLE) { + utc_offset = c->tds.currentUtcOffset; +- } else if (c->tds.currentUtcOffset > CURRENT_UTC_OFFSET) { ++ } else if (c->tds.currentUtcOffset > c->current_utc_offset) { + utc_offset = c->tds.currentUtcOffset; + } else { +- utc_offset = CURRENT_UTC_OFFSET; ++ utc_offset = c->current_utc_offset; + } + + if (c->tds.flags & LEAP_61) { +@@ -1035,7 +1036,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, + c->freq_est_interval = config_get_int(config, NULL, "freq_est_interval"); + c->grand_master_capable = config_get_int(config, NULL, "gmCapable"); + c->kernel_leap = config_get_int(config, NULL, "kernel_leap"); +- c->utc_offset = CURRENT_UTC_OFFSET; ++ c->utc_offset = c->current_utc_offset = config_get_int(config, NULL, "utc_offset"); + c->time_source = config_get_int(config, NULL, "timeSource"); + + if (c->free_running) { +diff --git a/config.c b/config.c +index b19f3ad..7bb949d 100644 +--- a/config.c ++++ b/config.c +@@ -237,6 +237,7 @@ struct config_item config_tab[] = { + GLOB_ITEM_STR("uds_address", "/var/run/ptp4l"), + GLOB_ITEM_INT("use_syslog", 1, 0, 1), + GLOB_ITEM_STR("userDescription", ""), ++ GLOB_ITEM_INT("utc_offset", CURRENT_UTC_OFFSET, 0, INT_MAX), + GLOB_ITEM_INT("verbose", 0, 0, 1), + }; + +diff --git a/default.cfg b/default.cfg +index 12542c0..ebb263a 100644 +--- a/default.cfg ++++ b/default.cfg +@@ -7,6 +7,7 @@ slaveOnly 0 + priority1 128 + priority2 128 + domainNumber 0 ++#utc_offset 37 + clockClass 248 + clockAccuracy 0xFE + offsetScaledLogVariance 0xFFFF +diff --git a/gPTP.cfg b/gPTP.cfg +index 75e996c..142996a 100644 +--- a/gPTP.cfg ++++ b/gPTP.cfg +@@ -7,6 +7,7 @@ gmCapable 1 + priority1 248 + priority2 248 + domainNumber 0 ++#utc_offset 37 + clockClass 248 + clockAccuracy 0xFE + offsetScaledLogVariance 0xFFFF +diff --git a/ptp4l.8 b/ptp4l.8 +index f53fc6e..53d5f28 100644 +--- a/ptp4l.8 ++++ b/ptp4l.8 +@@ -327,6 +327,10 @@ The default is 0xFFFF. + The domain attribute of the local clock. + The default is 0. + .TP ++.B utc_offset ++The current offset between TAI and UTC. ++The default is 37. ++.TP + .B free_running + Don't adjust the local clock if enabled. + The default is 0 (disabled). diff --git a/SOURCES/phc2sys.service b/SOURCES/phc2sys.service new file mode 100644 index 0000000..78ed68a --- /dev/null +++ b/SOURCES/phc2sys.service @@ -0,0 +1,11 @@ +[Unit] +Description=Synchronize system clock or PTP hardware clock (PHC) +After=ntpdate.service + +[Service] +Type=simple +EnvironmentFile=-/etc/sysconfig/phc2sys +ExecStart=/usr/sbin/phc2sys $OPTIONS + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/ptp4l.service b/SOURCES/ptp4l.service new file mode 100644 index 0000000..428394f --- /dev/null +++ b/SOURCES/ptp4l.service @@ -0,0 +1,10 @@ +[Unit] +Description=Precision Time Protocol (PTP) service + +[Service] +Type=simple +EnvironmentFile=-/etc/sysconfig/ptp4l +ExecStart=/usr/sbin/ptp4l $OPTIONS + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/timemaster.conf b/SOURCES/timemaster.conf new file mode 100644 index 0000000..d47a305 --- /dev/null +++ b/SOURCES/timemaster.conf @@ -0,0 +1,33 @@ +# Configuration file for timemaster + +#[ntp_server ntp-server.local] +#minpoll 4 +#maxpoll 4 + +#[ptp_domain 0] +#interfaces eth0 + +[timemaster] +ntp_program chronyd + +[chrony.conf] +include /etc/chrony.conf + +[ntp.conf] +includefile /etc/ntp.conf + +[ptp4l.conf] + +[chronyd] +path /usr/sbin/chronyd +options -u chrony + +[ntpd] +path /usr/sbin/ntpd +options -u ntp:ntp -g + +[phc2sys] +path /usr/sbin/phc2sys + +[ptp4l] +path /usr/sbin/ptp4l diff --git a/SOURCES/timemaster.service b/SOURCES/timemaster.service new file mode 100644 index 0000000..7505387 --- /dev/null +++ b/SOURCES/timemaster.service @@ -0,0 +1,11 @@ +[Unit] +Description=Synchronize system clock to NTP and PTP time sources +After=chronyd.service ntpd.service ntpdate.service sntp.service +Conflicts=chronyd.service ntpd.service phc2sys.service ptp4l.service + +[Service] +Type=simple +ExecStart=/usr/sbin/timemaster -f /etc/timemaster.conf + +[Install] +WantedBy=multi-user.target diff --git a/SPECS/linuxptp.spec b/SPECS/linuxptp.spec new file mode 100644 index 0000000..44ed08c --- /dev/null +++ b/SPECS/linuxptp.spec @@ -0,0 +1,203 @@ +%global _hardened_build 1 +%global testsuite_ver 502e82 +%global clknetsim_ver ce89a1 +Name: linuxptp +Version: 1.8 +Release: 5%{?dist} +Summary: PTP implementation for Linux + +Group: System Environment/Base +License: GPLv2+ +URL: http://linuxptp.sourceforge.net/ + +Source0: https://downloads.sourceforge.net/%{name}/%{name}-%{version}.tgz +Source1: phc2sys.service +Source2: ptp4l.service +Source3: timemaster.service +Source4: timemaster.conf +# external test suite +Source10: https://github.com/mlichvar/linuxptp-testsuite/archive/%{testsuite_ver}/linuxptp-testsuite-%{testsuite_ver}.tar.gz +# simulator for test suite +Source11: https://github.com/mlichvar/clknetsim/archive/%{clknetsim_ver}/clknetsim-%{clknetsim_ver}.tar.gz + +# update default TAI-UTC offset and make it configurable +Patch1: linuxptp-utcoffset.patch +# add options to tag ptp4l/phc2sys log messages and use them in timemaster +Patch2: linuxptp-messagetag.patch +# check support for SW timestamping in timemaster +Patch3: linuxptp-swtscheck.patch +# fix leaks of sockets in error handling +Patch4: linuxptp-closesocket.patch +# update port dispatch code (needed by other patches) +Patch5: linuxptp-portdispatch.patch +# Fix phc2sys to check both the state and new_state variables of the clock +Patch6: linuxptp-statechange.patch +# fix handling of unknown/invalid management TLVs in pmc +Patch7: linuxptp-mgttlv.patch +# add support for IP over InfiniBand +Patch8: linuxptp-ipoib.patch +# force BMC election when link goes down +Patch9: linuxptp-linkdown.patch +# fix phc2sys to not synchronize clock to itself +Patch10: linuxptp-multiport.patch +# add support for active-backup bonding +Patch11: linuxptp-bonding.patch + +BuildRequires: systemd-units + +Requires(post): systemd-units +Requires(preun): systemd-units +Requires(postun): systemd-units + +%description +This software is an implementation of the Precision Time Protocol (PTP) +according to IEEE standard 1588 for Linux. The dual design goals are to provide +a robust implementation of the standard and to use the most relevant and modern +Application Programming Interfaces (API) offered by the Linux kernel. +Supporting legacy APIs and other platforms is not a goal. + +%prep +%setup -q -a 10 -a 11 +%patch1 -p1 -b .utcoffset +%patch2 -p1 -b .messagetag +%patch3 -p1 -b .swtscheck +%patch4 -p1 -b .closesocket +%patch5 -p1 -b .portdispatch +%patch6 -p1 -b .statechange +%patch7 -p1 -b .mgttlv +%patch8 -p1 -b .ipoib +%patch9 -p1 -b .linkdown +%patch10 -p1 -b .multiport +%patch11 -p1 -b .bonding +mv linuxptp-testsuite-%{testsuite_ver}* testsuite +mv clknetsim-%{clknetsim_ver}* testsuite/clknetsim + +%build +make %{?_smp_mflags} \ + EXTRA_CFLAGS="$RPM_OPT_FLAGS" \ + EXTRA_LDFLAGS="$RPM_LD_FLAGS" + +%install +%makeinstall + +mkdir -p $RPM_BUILD_ROOT{%{_sysconfdir}/sysconfig,%{_unitdir},%{_mandir}/man5} +install -m 644 -p default.cfg $RPM_BUILD_ROOT%{_sysconfdir}/ptp4l.conf +install -m 644 -p %{SOURCE1} %{SOURCE2} %{SOURCE3} $RPM_BUILD_ROOT%{_unitdir} +install -m 644 -p %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir} + +echo 'OPTIONS="-f /etc/ptp4l.conf -i eth0"' > \ + $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/ptp4l +echo 'OPTIONS="-a -r"' > $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/phc2sys + +echo '.so man8/ptp4l.8' > $RPM_BUILD_ROOT%{_mandir}/man5/ptp4l.conf.5 +echo '.so man8/timemaster.8' > $RPM_BUILD_ROOT%{_mandir}/man5/timemaster.conf.5 + +%check +cd testsuite +# set random seed to get deterministic results +export CLKNETSIM_RANDOM_SEED=26743 +make %{?_smp_mflags} -C clknetsim +PATH=..:$PATH ./run + +%post +%systemd_post phc2sys.service ptp4l.service timemaster.service + +%preun +%systemd_preun phc2sys.service ptp4l.service timemaster.service + +%postun +%systemd_postun_with_restart phc2sys.service ptp4l.service timemaster.service + +%files +%doc COPYING README.org default.cfg gPTP.cfg +%config(noreplace) %{_sysconfdir}/ptp4l.conf +%config(noreplace) %{_sysconfdir}/sysconfig/phc2sys +%config(noreplace) %{_sysconfdir}/sysconfig/ptp4l +%config(noreplace) %{_sysconfdir}/timemaster.conf +%{_unitdir}/phc2sys.service +%{_unitdir}/ptp4l.service +%{_unitdir}/timemaster.service +%{_sbindir}/hwstamp_ctl +%{_sbindir}/phc2sys +%{_sbindir}/phc_ctl +%{_sbindir}/pmc +%{_sbindir}/ptp4l +%{_sbindir}/timemaster +%{_mandir}/man5/*.5* +%{_mandir}/man8/*.8* + +%changelog +* Tue Oct 24 2017 Miroslav Lichvar 1.8-5 +- add support for active-backup bonding (#1002657) +- add support for IP over InfiniBand (#1472880) +- fix handling of unknown/invalid management TLVs in pmc (#1459446 #1459449) + +* Thu Sep 07 2017 Michal Ruprich - 1.8-4 +- Resolves: #1487522 - Race condition in phc2sys + +* Wed Mar 15 2017 Miroslav Lichvar 1.8-3 +- fix backport of linkdown patch + +* Tue Mar 14 2017 Miroslav Lichvar 1.8-2 +- force BMC election when link goes down + +* Tue Feb 07 2017 Miroslav Lichvar 1.8-1 +- update to 1.8 (#1359311 #1353336) + +* Tue Nov 25 2014 Miroslav Lichvar 1.4-3.20140718gitbdb6a3 +- fix resetting of linreg servo (#1165045) +- fix phc2sys automatic mode with multiple interfaces (#1108795) + +* Tue Oct 14 2014 Miroslav Lichvar 1.4-2.20140718gitbdb6a3 +- add timemaster (#1085580) +- send peer messages to correct address +- make NTP SHM segment number configurable +- update UDS handling to allow running multiple ptp4l/phc2sys instances +- fix warnings from static analysis + +* Wed Sep 03 2014 Miroslav Lichvar 1.4-1.20140718gitbdb6a3 +- update to 20140718gitbdb6a3 (#1108795, #1059039) +- fix PIE linking (#1092537) +- replace hardening build flags with _hardened_build +- include simulation test suite + +* Fri Jan 24 2014 Daniel Mach - 1.3-3 +- Mass rebuild 2014-01-24 + +* Fri Dec 27 2013 Daniel Mach - 1.3-2 +- Mass rebuild 2013-12-27 + +* Fri Aug 02 2013 Miroslav Lichvar 1.3-1 +- update to 1.3 + +* Tue Jul 30 2013 Miroslav Lichvar 1.2-3.20130730git7789f0 +- update to 20130730git7789f0 + +* Fri Jul 19 2013 Miroslav Lichvar 1.2-2.20130719git46db40 +- update to 20130719git46db40 +- drop old systemd scriptlets +- add man page link for ptp4l.conf + +* Mon Apr 22 2013 Miroslav Lichvar 1.2-1 +- update to 1.2 + +* Mon Feb 18 2013 Miroslav Lichvar 1.1-1 +- update to 1.1 +- log phc2sys output + +* Thu Feb 14 2013 Fedora Release Engineering - 1.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Thu Dec 13 2012 Miroslav Lichvar 1.0-1 +- update to 1.0 + +* Fri Nov 09 2012 Miroslav Lichvar 0-0.3.20121109git4e8107 +- update to 20121109git4e8107 +- install unchanged default.cfg as ptp4l.conf +- drop conflicts from phc2sys service + +* Fri Sep 21 2012 Miroslav Lichvar 0-0.2.20120920git6ce135 +- fix issues found in package review (#859193) + +* Thu Sep 20 2012 Miroslav Lichvar 0-0.1.20120920git6ce135 +- initial release