From: Richard C. <ric...@gm...> - 2018-04-21 05:22:31
|
This series add support for running a TC over multiple ports. This applies on top of my recently posted telecom series. Comments, testing, and review are most welcome. Changed in V2: ~~~~~~~~~~~~~~ - Detect and drop frames that arrive with time stamps. - Add an option to enforce a spanning tree to avoid PTP message loops. - Set priority1 really low in the example TC configuration files Thanks, Richard Richard Cochran (8): port: Share another helper function. tc: Add the transparent clock implementation. p2p_tc: Implement a peer to peer transparent clock. e2e_tc: Implement an end to end transparent clock. config: Add a configuration option for TC mode. config: Add a configuration option for preventing loops in TC mode. Add an example configuration for a peer to peer transparent clock. Add an example configuration for an end to end transparent clock. E2E-TC.cfg | 92 +++++++++++ P2P-TC.cfg | 92 +++++++++++ clock.c | 5 +- config.c | 11 ++ default.cfg | 2 + e2e_tc.c | 198 +++++++++++++++++++++++ gPTP.cfg | 1 + makefile | 6 +- p2p_tc.c | 218 ++++++++++++++++++++++++++ port.c | 20 ++- port.h | 5 + port_private.h | 17 ++ ptp4l.8 | 21 ++- ptp4l.c | 44 +++++- tc.c | 486 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tc.h | 94 +++++++++++ 16 files changed, 1299 insertions(+), 13 deletions(-) create mode 100644 E2E-TC.cfg create mode 100644 P2P-TC.cfg create mode 100644 e2e_tc.c create mode 100644 p2p_tc.c create mode 100644 tc.c create mode 100644 tc.h -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-05-01 15:08:35
|
This series adds support for running a TC over multiple ports. Comments, testing, and review are most welcome. Changed in V3: ~~~~~~~~~~~~~~ - Correct the behavior of the E2E TC with respect to the free_running option. Now the E2E TC will synchronize when not free running, just like the P2P TC and the BC. In this case, the TC will generate delay request messages. - Pay attention to the domain field when synchronizing the local clock. Changed in V2: ~~~~~~~~~~~~~~ - Detect and drop frames that arrive with time stamps. - Add an option to enforce a spanning tree to avoid PTP message loops. - Set priority1 really low in the example TC configuration files Thanks, Richard Richard Cochran (9): clock: Add a method to determine whether a clock is free running. port: Share more helper functions. tc: Add the transparent clock implementation. p2p_tc: Implement a peer to peer transparent clock. e2e_tc: Implement an end to end transparent clock. config: Add a configuration option for TC mode. config: Add a configuration option for preventing loops in TC mode. Add an example configuration for a peer to peer transparent clock. Add an example configuration for an end to end transparent clock. E2E-TC.cfg | 13 ++ P2P-TC.cfg | 14 ++ clock.c | 10 +- clock.h | 7 + config.c | 11 ++ default.cfg | 2 + e2e_tc.c | 226 +++++++++++++++++++++++++ makefile | 6 +- p2p_tc.c | 229 +++++++++++++++++++++++++ port.c | 29 +++- port.h | 5 + port_private.h | 21 +++ ptp4l.8 | 21 ++- ptp4l.c | 44 ++++- tc.c | 514 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tc.h | 103 ++++++++++++ 16 files changed, 1237 insertions(+), 18 deletions(-) create mode 100644 E2E-TC.cfg create mode 100644 P2P-TC.cfg create mode 100644 e2e_tc.c create mode 100644 p2p_tc.c create mode 100644 tc.c create mode 100644 tc.h -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-05-01 15:08:37
|
This will be needed by the end-to-end transparent clock code. Signed-off-by: Richard Cochran <ric...@gm...> --- clock.c | 5 +++++ clock.h | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/clock.c b/clock.c index 08f0c42..30348dd 100644 --- a/clock.c +++ b/clock.c @@ -1187,6 +1187,11 @@ void clock_follow_up_info(struct clock *c, struct follow_up_info_tlv *f) sizeof(c->status.lastGmPhaseChange)); } +int clock_free_running(struct clock *c) +{ + return c->free_running ? 1 : 0; +} + int clock_gm_capable(struct clock *c) { return c->grand_master_capable; diff --git a/clock.h b/clock.h index d6a79bd..efde27f 100644 --- a/clock.h +++ b/clock.h @@ -142,6 +142,13 @@ struct port *clock_first_port(struct clock *c); void clock_follow_up_info(struct clock *c, struct follow_up_info_tlv *f); /** + * Determine if a clock is free running or not. + * @param c The clock instance. + * @return One if the clock is free running or zero otherwise. + */ +int clock_free_running(struct clock *c); + +/** * Obtain the gmCapable flag from a clock's default data set. * This function is specific to the 802.1AS standard. * @param c The clock instance. -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-05-01 15:08:38
|
The transparent clock code will want to set qualification timeouts and perform end to end delay measurements. This patch exposes the needed methods. Signed-off-by: Richard Cochran <ric...@gm...> --- port.c | 11 +++++------ port_private.h | 5 +++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/port.c b/port.c index bc630b5..d4352b0 100644 --- a/port.c +++ b/port.c @@ -51,7 +51,6 @@ enum syfu_event { FUP_MATCH, }; -static void flush_delay_req(struct port *p); static int port_capable(struct port *p); static int port_is_ieee8021as(struct port *p); static void port_nrate_initialize(struct port *p); @@ -297,7 +296,7 @@ static int delay_req_current(struct ptp_message *m, struct timespec now) return t2 - t1 < tmo; } -static void delay_req_prune(struct port *p) +void delay_req_prune(struct port *p) { struct timespec now; struct ptp_message *m; @@ -1048,7 +1047,7 @@ static int port_set_manno_tmo(struct port *p) return set_tmo_log(p->fda.fd[FD_MANNO_TIMER], 1, p->logAnnounceInterval); } -static int port_set_qualification_tmo(struct port *p) +int port_set_qualification_tmo(struct port *p) { return set_tmo_log(p->fda.fd[FD_QUALIFICATION_TIMER], 1+clock_steps_removed(p->clock), p->logAnnounceInterval); @@ -1490,7 +1489,7 @@ int port_is_enabled(struct port *p) return 1; } -static void flush_last_sync(struct port *p) +void flush_last_sync(struct port *p) { if (p->syfu != SF_EMPTY) { msg_put(p->last_syncfup); @@ -1498,7 +1497,7 @@ static void flush_last_sync(struct port *p) } } -static void flush_delay_req(struct port *p) +void flush_delay_req(struct port *p) { struct ptp_message *m; while ((m = TAILQ_FIRST(&p->delay_req)) != NULL) { @@ -1777,7 +1776,7 @@ out: return err; } -static void process_delay_resp(struct port *p, struct ptp_message *m) +void process_delay_resp(struct port *p, struct ptp_message *m) { struct delay_resp_msg *rsp = &m->delay_resp; struct PortIdentity master; diff --git a/port_private.h b/port_private.h index 806329d..df6e39e 100644 --- a/port_private.h +++ b/port_private.h @@ -126,7 +126,10 @@ struct port { #define portnum(p) (p->portIdentity.portNumber) int clear_fault_asap(struct fault_interval *faint); +void delay_req_prune(struct port *p); void fc_clear(struct foreign_clock *fc); +void flush_delay_req(struct port *p); +void flush_last_sync(struct port *p); int port_clr_tmo(int fd); int port_delay_request(struct port *p); void port_disable(struct port *p); @@ -135,9 +138,11 @@ int port_is_enabled(struct port *p); void port_link_status(void *ctx, int index, int linkup); int port_set_announce_tmo(struct port *p); int port_set_delay_tmo(struct port *p); +int port_set_qualification_tmo(struct port *p); void port_show_transition(struct port *p, enum port_state next, enum fsm_event event); int process_announce(struct port *p, struct ptp_message *m); +void process_delay_resp(struct port *p, struct ptp_message *m); void process_follow_up(struct port *p, struct ptp_message *m); int process_pdelay_req(struct port *p, struct ptp_message *m); int process_pdelay_resp(struct port *p, struct ptp_message *m); -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-05-01 15:08:40
|
The P2P TC forwards Announce, Management, Signaling, and Sync messages, consumes P2P Delay messages, and drops E2E Delay messages. This implementation tracks the GM using the BMCA in order to syntonize (or possibly even synchronize) with it. Signed-off-by: Richard Cochran <ric...@gm...> --- clock.c | 2 +- makefile | 4 +- p2p_tc.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ port.c | 7 ++ port_private.h | 3 + tc.c | 4 + 6 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 p2p_tc.c diff --git a/clock.c b/clock.c index bec23c3..7731968 100644 --- a/clock.c +++ b/clock.c @@ -874,9 +874,9 @@ struct clock *clock_create(enum clock_type type, struct config *config, switch (type) { case CLOCK_TYPE_ORDINARY: case CLOCK_TYPE_BOUNDARY: + case CLOCK_TYPE_P2P: c->type = type; break; - case CLOCK_TYPE_P2P: case CLOCK_TYPE_E2E: case CLOCK_TYPE_MANAGEMENT: return NULL; diff --git a/makefile b/makefile index 707c5d4..c5c0cf8 100644 --- a/makefile +++ b/makefile @@ -25,8 +25,8 @@ LDLIBS = -lm -lrt $(EXTRA_LDFLAGS) PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o fault.o \ filter.o fsm.o hash.o linreg.o mave.o mmedian.o msg.o ntpshm.o nullf.o phc.o \ - pi.o port.o print.o ptp4l.o raw.o rtnl.o servo.o sk.o stats.o tc.o telecom.o \ - tlv.o transport.o tsproc.o udp.o udp6.o uds.o util.o version.o + pi.o port.o print.o ptp4l.o p2p_tc.o raw.o rtnl.o servo.o sk.o stats.o tc.o \ + telecom.o tlv.o transport.o tsproc.o udp.o udp6.o uds.o util.o version.o OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \ sysoff.o timemaster.o diff --git a/p2p_tc.c b/p2p_tc.c new file mode 100644 index 0000000..b2a444e --- /dev/null +++ b/p2p_tc.c @@ -0,0 +1,229 @@ +/** + * @file p2p_tc.c + * @note Copyright (C) 2018 Richard Cochran <ric...@gm...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. + */ +#include <errno.h> + +#include "port.h" +#include "port_private.h" +#include "print.h" +#include "rtnl.h" +#include "tc.h" + +static int p2p_delay_request(struct port *p) +{ + switch (p->state) { + case PS_INITIALIZING: + case PS_FAULTY: + case PS_DISABLED: + return 0; + case PS_LISTENING: + case PS_PRE_MASTER: + case PS_MASTER: + case PS_PASSIVE: + case PS_UNCALIBRATED: + case PS_SLAVE: + case PS_GRAND_MASTER: + break; + } + return port_delay_request(p); +} + +void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff) +{ + if (!port_state_update(p, event, mdiff)) { + return; + } + if (!portnum(p)) { + /* UDS needs no timers. */ + return; + } + + port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]); + port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]); + /* Leave FD_DELAY_TIMER running. */ + port_clr_tmo(p->fda.fd[FD_QUALIFICATION_TIMER]); + port_clr_tmo(p->fda.fd[FD_MANNO_TIMER]); + port_clr_tmo(p->fda.fd[FD_SYNC_TX_TIMER]); + + /* + * Handle the side effects of the state transition. + */ + switch (p->state) { + case PS_INITIALIZING: + break; + case PS_FAULTY: + case PS_DISABLED: + port_disable(p); + break; + case PS_LISTENING: + port_set_announce_tmo(p); + port_set_delay_tmo(p); + break; + case PS_PRE_MASTER: + port_set_qualification_tmo(p); + break; + case PS_MASTER: + case PS_GRAND_MASTER: + break; + case PS_PASSIVE: + port_set_announce_tmo(p); + break; + case PS_UNCALIBRATED: + case PS_SLAVE: + port_set_announce_tmo(p); + break; + }; +} + +enum fsm_event p2p_event(struct port *p, int fd_index) +{ + int cnt, fd = p->fda.fd[fd_index]; + enum fsm_event event = EV_NONE; + struct ptp_message *msg, *dup; + + switch (fd_index) { + case FD_ANNOUNCE_TIMER: + case FD_SYNC_RX_TIMER: + pr_debug("port %hu: %s timeout", portnum(p), + fd_index == FD_SYNC_RX_TIMER ? "rx sync" : "announce"); + if (p->best) { + fc_clear(p->best); + } + port_set_announce_tmo(p); + return EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES; + + case FD_DELAY_TIMER: + pr_debug("port %hu: delay timeout", portnum(p)); + port_set_delay_tmo(p); + tc_prune(p); + return p2p_delay_request(p) ? EV_FAULT_DETECTED : EV_NONE; + + case FD_QUALIFICATION_TIMER: + pr_debug("port %hu: qualification timeout", portnum(p)); + return EV_QUALIFICATION_TIMEOUT_EXPIRES; + + case FD_MANNO_TIMER: + case FD_SYNC_TX_TIMER: + pr_err("unexpected timer expiration"); + return EV_NONE; + + case FD_RTNL: + pr_debug("port %hu: received link status notification", portnum(p)); + 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)) || + (p->link_status & TS_LABEL_CHANGED)) { + return EV_FAULT_DETECTED; + } else { + return EV_NONE; + } + } + + msg = msg_allocate(); + if (!msg) { + return EV_FAULT_DETECTED; + } + msg->hwts.type = p->timestamping; + + cnt = transport_recv(p->trp, fd, msg); + if (cnt <= 0) { + pr_err("port %hu: recv message failed", portnum(p)); + msg_put(msg); + return EV_FAULT_DETECTED; + } + if (msg_sots_valid(msg)) { + ts_add(&msg->hwts.ts, -p->rx_timestamp_offset); + } + if (msg->header.flagField[0] & UNICAST) { + pl_warning(600, "cannot switch unicast messages!"); + msg_put(msg); + return EV_NONE; + } + + dup = msg_duplicate(msg, cnt); + if (!dup) { + msg_put(msg); + return EV_NONE; + } + if (tc_ignore(p, dup)) { + msg_put(dup); + dup = NULL; + } + + switch (msg_type(msg)) { + case SYNC: + if (tc_fwd_sync(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + if (dup) { + process_sync(p, dup); + } + break; + case DELAY_REQ: + break; + case PDELAY_REQ: + if (dup && process_pdelay_req(p, dup)) { + event = EV_FAULT_DETECTED; + } + break; + case PDELAY_RESP: + if (dup && process_pdelay_resp(p, dup)) { + event = EV_FAULT_DETECTED; + } + break; + case FOLLOW_UP: + if (tc_fwd_folup(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + if (dup) { + process_follow_up(p, dup); + } + break; + case DELAY_RESP: + break; + case PDELAY_RESP_FOLLOW_UP: + if (dup) { + process_pdelay_resp_fup(p, dup); + } + break; + case ANNOUNCE: + if (tc_forward(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + if (dup && process_announce(p, dup)) { + event = EV_STATE_DECISION_EVENT; + } + break; + case SIGNALING: + case MANAGEMENT: + if (tc_forward(p, msg)) { + event = EV_FAULT_DETECTED; + } + break; + } + + msg_put(msg); + if (dup) { + msg_put(dup); + } + return event; +} diff --git a/port.c b/port.c index 55216ae..3b35e6b 100644 --- a/port.c +++ b/port.c @@ -2788,6 +2788,9 @@ struct port *port_open(int phc_index, p->event = bc_event; break; case CLOCK_TYPE_P2P: + p->dispatch = p2p_dispatch; + p->event = p2p_event; + break; case CLOCK_TYPE_E2E: case CLOCK_TYPE_MANAGEMENT: return NULL; @@ -2841,6 +2844,10 @@ struct port *port_open(int phc_index, p->delayMechanism = config_get_int(cfg, p->name, "delay_mechanism"); p->versionNumber = PTP_VERSION; + if (number && type == CLOCK_TYPE_P2P && p->delayMechanism != DM_P2P) { + pr_err("port %d: P2P TC needs P2P ports", number); + goto err_port; + } if (p->hybrid_e2e && p->delayMechanism != DM_E2E) { pr_warning("port %d: hybrid_e2e only works with E2E", number); } diff --git a/port_private.h b/port_private.h index 76dcab2..c07216a 100644 --- a/port_private.h +++ b/port_private.h @@ -135,6 +135,9 @@ struct port { #define portnum(p) (p->portIdentity.portNumber) +void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff); +enum fsm_event p2p_event(struct port *p, int fd_index); + int clear_fault_asap(struct fault_interval *faint); void delay_req_prune(struct port *p); void fc_clear(struct foreign_clock *fc); diff --git a/tc.c b/tc.c index 9b1d219..147bc5f 100644 --- a/tc.c +++ b/tc.c @@ -64,6 +64,10 @@ static int tc_blocked(struct port *q, struct port *p, struct ptp_message *m) if (!q->tc_spanning_tree) { return 0; } + /* Forward frames in the wrong domain unconditionally. */ + if (m->header.domainNumber != clock_domain_number(p->clock)) { + return 0; + } /* Ingress state */ s = port_state(q); switch (s) { -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-05-01 15:08:44
|
Signed-off-by: Richard Cochran <ric...@gm...> --- P2P-TC.cfg | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 P2P-TC.cfg diff --git a/P2P-TC.cfg b/P2P-TC.cfg new file mode 100644 index 0000000..b4fb89b --- /dev/null +++ b/P2P-TC.cfg @@ -0,0 +1,14 @@ +# +# Peer to Peer Transparent Clock example configuration containing +# those attributes which differ from the defaults. See the file, +# default.cfg, for the complete list of available options. +# +[global] +priority1 254 +free_running 1 +freq_est_interval 3 +tc_spanning_tree 1 +summary_interval 1 +clock_type P2P_TC +network_transport L2 +delay_mechanism P2P -- 2.11.0 |
From: Miroslav L. <mli...@re...> - 2018-05-02 07:21:05
|
It looks like I'm missing a bunch of mails from this set. On Tue, May 01, 2018 at 08:08:25AM -0700, Richard Cochran wrote: > --- /dev/null > +++ b/P2P-TC.cfg > @@ -0,0 +1,14 @@ > +# > +# Peer to Peer Transparent Clock example configuration containing > +# those attributes which differ from the defaults. See the file, > +# default.cfg, for the complete list of available options. I like the idea to include only the differences. > +# > +[global] > +priority1 254 > +free_running 1 > +freq_est_interval 3 > +tc_spanning_tree 1 > +summary_interval 1 > +clock_type P2P_TC > +network_transport L2 > +delay_mechanism P2P > -- > 2.11.0 > > > ------------------------------------------------------------------------------ > Check out the vibrant tech community on one of the world's most > engaging tech sites, Slashdot.org! http://sdm.link/slashdot > _______________________________________________ > Linuxptp-devel mailing list > Lin...@li... > https://lists.sourceforge.net/lists/listinfo/linuxptp-devel -- Miroslav Lichvar |
From: Richard C. <ric...@gm...> - 2018-05-01 15:08:44
|
Signed-off-by: Richard Cochran <ric...@gm...> --- E2E-TC.cfg | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 E2E-TC.cfg diff --git a/E2E-TC.cfg b/E2E-TC.cfg new file mode 100644 index 0000000..d5fbeec --- /dev/null +++ b/E2E-TC.cfg @@ -0,0 +1,13 @@ +# +# End to End Transparent Clock example configuration containing +# those attributes which differ from the defaults. See the file, +# default.cfg, for the complete list of available options. +# +[global] +priority1 254 +free_running 1 +freq_est_interval 3 +tc_spanning_tree 1 +summary_interval 1 +clock_type E2E_TC +network_transport L2 -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-05-01 15:17:16
|
According to 1588, PTP message loops are simply someone else's problem with respect to transparent clocks. Since we are running the BMCA for syntonization anyway, we might as well go ahead and implement the spanning tree for PTP messages. Signed-off-by: Richard Cochran <ric...@gm...> --- config.c | 1 + default.cfg | 1 + port.c | 1 + ptp4l.8 | 6 ++++++ 4 files changed, 9 insertions(+) diff --git a/config.c b/config.c index 6d00641..57c239b 100644 --- a/config.c +++ b/config.c @@ -254,6 +254,7 @@ struct config_item config_tab[] = { GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX), GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX), PORT_ITEM_INT("syncReceiptTimeout", 0, 0, UINT8_MAX), + GLOB_ITEM_INT("tc_spanning_tree", 0, 1, 1), GLOB_ITEM_INT("timeSource", INTERNAL_OSCILLATOR, 0x10, 0xfe), GLOB_ITEM_ENU("time_stamping", TS_HARDWARE, timestamping_enu), PORT_ITEM_INT("transportSpecific", 0, 0, 0x0F), diff --git a/default.cfg b/default.cfg index f70f386..86eb15e 100644 --- a/default.cfg +++ b/default.cfg @@ -40,6 +40,7 @@ path_trace_enabled 0 follow_up_info 0 hybrid_e2e 0 net_sync_monitor 0 +tc_spanning_tree 0 tx_timestamp_timeout 1 use_syslog 1 verbose 0 diff --git a/port.c b/port.c index a7d7928..d0e3ca0 100644 --- a/port.c +++ b/port.c @@ -2830,6 +2830,7 @@ struct port *port_open(int phc_index, p->hybrid_e2e = config_get_int(cfg, p->name, "hybrid_e2e"); p->net_sync_monitor = config_get_int(cfg, p->name, "net_sync_monitor"); p->path_trace_enabled = config_get_int(cfg, p->name, "path_trace_enabled"); + p->tc_spanning_tree = config_get_int(cfg, p->name, "tc_spanning_tree"); p->rx_timestamp_offset = config_get_int(cfg, p->name, "ingressLatency"); p->rx_timestamp_offset <<= 16; p->tx_timestamp_offset = config_get_int(cfg, p->name, "egressLatency"); diff --git a/ptp4l.8 b/ptp4l.8 index 003d54e..ae6e491 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -399,6 +399,12 @@ Treat one-step responses as two-step if enabled. It is used to work around buggy 802.1AS switches. The default is 0 (disabled). .TP +.B tc_spanning_tree +When running as a Transparent Clock, increment the "stepsRemoved" +field of Announce messages that pass through the switch. Enabling +this option ensures that PTP message loops never form, provided the +switches all implement this option together with the BMCA. +.TP .B tx_timestamp_timeout The number of milliseconds to poll waiting for the tx time stamp from the kernel when a message has recently been sent. -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-05-01 15:08:39
|
This patch adds code that sends an event messages received on one port out all the other ports and calculates the residence time. The correction, ingress port, and the original message are remembered in a TC transmit descriptor. These descriptors are recycled in a memory pool in a similar way to the message buffers. Signed-off-by: Richard Cochran <ric...@gm...> --- clock.c | 1 + makefile | 4 +- port.c | 3 + port.h | 5 + port_private.h | 10 ++ tc.c | 510 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tc.h | 103 ++++++++++++ 7 files changed, 634 insertions(+), 2 deletions(-) create mode 100644 tc.c create mode 100644 tc.h diff --git a/clock.c b/clock.c index 30348dd..bec23c3 100644 --- a/clock.c +++ b/clock.c @@ -282,6 +282,7 @@ void clock_destroy(struct clock *c) } memset(c, 0, sizeof(*c)); msg_cleanup(); + tc_cleanup(); } static int clock_fault_timeout(struct port *port, int set) diff --git a/makefile b/makefile index d34eb6b..707c5d4 100644 --- a/makefile +++ b/makefile @@ -25,8 +25,8 @@ LDLIBS = -lm -lrt $(EXTRA_LDFLAGS) PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o fault.o \ filter.o fsm.o hash.o linreg.o mave.o mmedian.o msg.o ntpshm.o nullf.o phc.o \ - pi.o port.o print.o ptp4l.o raw.o rtnl.o servo.o sk.o stats.o telecom.o tlv.o \ - transport.o tsproc.o udp.o udp6.o uds.o util.o version.o + pi.o port.o print.o ptp4l.o raw.o rtnl.o servo.o sk.o stats.o tc.o telecom.o \ + tlv.o transport.o tsproc.o udp.o udp6.o uds.o util.o version.o OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \ sysoff.o timemaster.o diff --git a/port.c b/port.c index d4352b0..55216ae 100644 --- a/port.c +++ b/port.c @@ -36,6 +36,7 @@ #include "print.h" #include "rtnl.h" #include "sk.h" +#include "tc.h" #include "tlv.h" #include "tmv.h" #include "tsproc.h" @@ -1534,6 +1535,7 @@ void port_disable(struct port *p) { int i; + tc_flush(p); flush_last_sync(p); flush_delay_req(p); flush_peer_delay(p); @@ -2777,6 +2779,7 @@ struct port *port_open(int phc_index, } memset(p, 0, sizeof(*p)); + TAILQ_INIT(&p->tc_transmitted); switch (type) { case CLOCK_TYPE_ORDINARY: diff --git a/port.h b/port.h index d828875..9639193 100644 --- a/port.h +++ b/port.h @@ -322,4 +322,9 @@ enum fault_type last_fault_type(struct port *port); void fault_interval(struct port *port, enum fault_type ft, struct fault_interval *i); +/** + * Release all of the memory in the TC transmit descriptor cache. + */ +void tc_cleanup(void); + #endif diff --git a/port_private.h b/port_private.h index df6e39e..76dcab2 100644 --- a/port_private.h +++ b/port_private.h @@ -50,6 +50,13 @@ struct nrate_estimator { int ratio_valid; }; +struct tc_txd { + TAILQ_ENTRY(tc_txd) list; + struct ptp_message *msg; + tmv_t residence; + int ingress_port; +}; + struct port { LIST_ENTRY(port) list; char *name; @@ -113,6 +120,7 @@ struct port { int min_neighbor_prop_delay; int net_sync_monitor; int path_trace_enabled; + int tc_spanning_tree; Integer64 rx_timestamp_offset; Integer64 tx_timestamp_offset; enum link_state link_status; @@ -121,6 +129,8 @@ struct port { unsigned int versionNumber; /*UInteger4*/ /* foreignMasterDS */ LIST_HEAD(fm, foreign_clock) foreign_masters; + /* TC book keeping */ + TAILQ_HEAD(tct, tc_txd) tc_transmitted; }; #define portnum(p) (p->portIdentity.portNumber) diff --git a/tc.c b/tc.c new file mode 100644 index 0000000..9b1d219 --- /dev/null +++ b/tc.c @@ -0,0 +1,510 @@ +/** + * @file tc.c + * @note Copyright (C) 2018 Richard Cochran <ric...@gm...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. + */ +#include <stdlib.h> + +#include "port.h" +#include "print.h" +#include "tc.h" +#include "tmv.h" + +enum tc_match { + TC_MISMATCH, + TC_SYNC_FUP, + TC_FUP_SYNC, + TC_DELAY_REQRESP, +}; + +static TAILQ_HEAD(tc_pool, tc_txd) tc_pool = TAILQ_HEAD_INITIALIZER(tc_pool); + +static int tc_match_delay(int ingress_port, struct ptp_message *resp, + struct tc_txd *txd); +static int tc_match_syfup(int ingress_port, struct ptp_message *msg, + struct tc_txd *txd); +static void tc_recycle(struct tc_txd *txd); + +static struct tc_txd *tc_allocate(void) +{ + struct tc_txd *txd = TAILQ_FIRST(&tc_pool); + + if (txd) { + TAILQ_REMOVE(&tc_pool, txd, list); + memset(txd, 0, sizeof(*txd)); + return txd; + } + txd = calloc(1, sizeof(*txd)); + return txd; +} + +static int tc_blocked(struct port *q, struct port *p, struct ptp_message *m) +{ + enum port_state s; + + if (q == p) { + return 1; + } + if (portnum(p) == 0) { + return 1; + } + if (!q->tc_spanning_tree) { + return 0; + } + /* Ingress state */ + s = port_state(q); + switch (s) { + case PS_INITIALIZING: + case PS_FAULTY: + case PS_DISABLED: + case PS_LISTENING: + case PS_PRE_MASTER: + case PS_PASSIVE: + return 1; + case PS_MASTER: + case PS_GRAND_MASTER: + /* Delay_Req swims against the stream. */ + if (msg_type(m) != DELAY_REQ) { + return 1; + } + break; + case PS_UNCALIBRATED: + case PS_SLAVE: + break; + } + /* Egress state */ + s = port_state(p); + switch (s) { + case PS_INITIALIZING: + case PS_FAULTY: + case PS_DISABLED: + case PS_LISTENING: + case PS_PRE_MASTER: + case PS_PASSIVE: + return 1; + case PS_UNCALIBRATED: + case PS_SLAVE: + /* Delay_Req swims against the stream. */ + if (msg_type(m) != DELAY_REQ) { + return 1; + } + break; + case PS_MASTER: + case PS_GRAND_MASTER: + /* No use forwarding Delay_Req out the wrong port. */ + if (msg_type(m) == DELAY_REQ) { + return 1; + } + break; + } + return 0; +} + +static void tc_complete_request(struct port *q, struct port *p, + struct ptp_message *req, tmv_t residence) +{ + struct tc_txd *txd = tc_allocate(); + if (!txd) { + port_dispatch(p, EV_FAULT_DETECTED, 0); + return; + } +#ifdef DEBUG + pr_err("stash delay request from port %hd to %hd seqid %hu residence %lu", + portnum(q), portnum(p), ntohs(req->header.sequenceId), + (unsigned long) tmv_to_nanoseconds(residence)); +#endif + msg_get(req); + txd->msg = req; + txd->residence = residence; + txd->ingress_port = portnum(q); + TAILQ_INSERT_TAIL(&p->tc_transmitted, txd, list); +} + +static void tc_complete_response(struct port *q, struct port *p, + struct ptp_message *resp, tmv_t residence) +{ + enum tc_match type = TC_MISMATCH; + struct tc_txd *txd; + Integer64 c1, c2; + int cnt; + +#ifdef DEBUG + pr_err("complete delay response from port %hd to %hd seqid %hu", + portnum(q), portnum(p), ntohs(resp->header.sequenceId)); +#endif + TAILQ_FOREACH(txd, &q->tc_transmitted, list) { + type = tc_match_delay(portnum(p), resp, txd); + if (type == TC_DELAY_REQRESP) { + residence = txd->residence; + break; + } + } + if (type != TC_DELAY_REQRESP) { + return; + } + c1 = net2host64(resp->header.correction); + c2 = c1 + tmv_to_TimeInterval(residence); + resp->header.correction = host2net64(c2); + cnt = transport_send(p->trp, &p->fda, TRANS_GENERAL, resp); + if (cnt <= 0) { + pr_err("tc failed to forward response on port %d", portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + } + /* Restore original correction value for next egress port. */ + resp->header.correction = host2net64(c1); + TAILQ_REMOVE(&q->tc_transmitted, txd, list); + msg_put(txd->msg); + tc_recycle(txd); +} + +static void tc_complete_syfup(struct port *q, struct port *p, + struct ptp_message *msg, tmv_t residence) +{ + enum tc_match type = TC_MISMATCH; + struct ptp_message *fup; + struct tc_txd *txd; + Integer64 c1, c2; + int cnt; + + TAILQ_FOREACH(txd, &p->tc_transmitted, list) { + type = tc_match_syfup(portnum(q), msg, txd); + switch (type) { + case TC_MISMATCH: + break; + case TC_SYNC_FUP: + fup = msg; + residence = txd->residence; + break; + case TC_FUP_SYNC: + fup = txd->msg; + break; + case TC_DELAY_REQRESP: + pr_err("tc: unexpected match of delay request - sync!"); + return; + } + if (type != TC_MISMATCH) { + break; + } + } + + if (type == TC_MISMATCH) { + txd = tc_allocate(); + if (!txd) { + port_dispatch(p, EV_FAULT_DETECTED, 0); + return; + } + msg_get(msg); + txd->msg = msg; + txd->residence = residence; + txd->ingress_port = portnum(q); + TAILQ_INSERT_TAIL(&p->tc_transmitted, txd, list); + return; + } + + c1 = net2host64(fup->header.correction); + c2 = c1 + tmv_to_TimeInterval(residence); + c2 += tmv_to_TimeInterval(q->peer_delay); + c2 += q->asymmetry; + fup->header.correction = host2net64(c2); + cnt = transport_send(p->trp, &p->fda, TRANS_GENERAL, fup); + if (cnt <= 0) { + pr_err("tc failed to forward follow up on port %d", portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + } + /* Restore original correction value for next egress port. */ + fup->header.correction = host2net64(c1); + TAILQ_REMOVE(&p->tc_transmitted, txd, list); + msg_put(txd->msg); + tc_recycle(txd); +} + +static void tc_complete(struct port *q, struct port *p, + struct ptp_message *msg, tmv_t residence) +{ + switch (msg_type(msg)) { + case SYNC: + case FOLLOW_UP: + tc_complete_syfup(q, p, msg, residence); + break; + case DELAY_REQ: + tc_complete_request(q, p, msg, residence); + break; + case DELAY_RESP: + tc_complete_response(q, p, msg, residence); + break; + } +} + +static int tc_current(struct ptp_message *m, struct timespec now) +{ + int64_t t1, t2, tmo; + + tmo = 1LL * NSEC2SEC; + t1 = m->ts.host.tv_sec * NSEC2SEC + m->ts.host.tv_nsec; + t2 = now.tv_sec * NSEC2SEC + now.tv_nsec; + + return t2 - t1 < tmo; +} + +static int tc_fwd_event(struct port *q, struct ptp_message *msg) +{ + tmv_t egress, ingress = msg->hwts.ts, residence; + struct port *p; + int cnt, err; + double rr; + + clock_gettime(CLOCK_MONOTONIC, &msg->ts.host); + + /* First send the event message out. */ + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (tc_blocked(q, p, msg)) { + continue; + } + cnt = transport_send(p->trp, &p->fda, TRANS_DEFER_EVENT, msg); + if (cnt <= 0) { + pr_err("failed to forward event from port %hd to %hd", + portnum(q), portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + } + } + + /* Go back and gather the transmit time stamps. */ + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (tc_blocked(q, p, msg)) { + continue; + } + err = transport_txts(p->trp, &p->fda, msg); + if (err || !msg_sots_valid(msg)) { + pr_err("failed to fetch txts on port %hd to %hd event", + portnum(q), portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + continue; + } + ts_add(&msg->hwts.ts, p->tx_timestamp_offset); + egress = msg->hwts.ts; + residence = tmv_sub(egress, ingress); + rr = clock_rate_ratio(q->clock); + if (rr != 1.0) { + residence = dbl_tmv(tmv_dbl(residence) * rr); + } + tc_complete(q, p, msg, residence); + } + + return 0; +} + +static int tc_match_delay(int ingress_port, struct ptp_message *resp, + struct tc_txd *txd) +{ + struct ptp_message *req = txd->msg; + + if (ingress_port != txd->ingress_port) { + return TC_MISMATCH; + } + if (req->header.sequenceId != resp->header.sequenceId) { + return TC_MISMATCH; + } + if (!pid_eq(&req->header.sourcePortIdentity, + &resp->delay_resp.requestingPortIdentity)) { + return TC_MISMATCH; + } + if (msg_type(req) == DELAY_REQ && msg_type(resp) == DELAY_RESP) { + return TC_DELAY_REQRESP; + } + return TC_MISMATCH; +} + +static int tc_match_syfup(int ingress_port, struct ptp_message *msg, + struct tc_txd *txd) +{ + if (ingress_port != txd->ingress_port) { + return TC_MISMATCH; + } + if (msg->header.sequenceId != txd->msg->header.sequenceId) { + return TC_MISMATCH; + } + if (!source_pid_eq(msg, txd->msg)) { + return TC_MISMATCH; + } + if (msg_type(txd->msg) == SYNC && msg_type(msg) == FOLLOW_UP) { + return TC_SYNC_FUP; + } + if (msg_type(txd->msg) == FOLLOW_UP && msg_type(msg) == SYNC) { + return TC_FUP_SYNC; + } + return TC_MISMATCH; +} + +static void tc_recycle(struct tc_txd *txd) +{ + TAILQ_INSERT_HEAD(&tc_pool, txd, list); +} + +/* public methods */ + +void tc_cleanup(void) +{ + struct tc_txd *txd; + + while ((txd = TAILQ_FIRST(&tc_pool)) != NULL) { + TAILQ_REMOVE(&tc_pool, txd, list); + free(txd); + } +} + +void tc_flush(struct port *q) +{ + struct tc_txd *txd; + + while ((txd = TAILQ_FIRST(&q->tc_transmitted)) != NULL) { + TAILQ_REMOVE(&q->tc_transmitted, txd, list); + msg_put(txd->msg); + tc_recycle(txd); + } +} + +int tc_forward(struct port *q, struct ptp_message *msg) +{ + uint16_t steps_removed; + struct port *p; + int cnt; + + if (q->tc_spanning_tree && msg_type(msg) == ANNOUNCE) { + steps_removed = ntohs(msg->announce.stepsRemoved); + msg->announce.stepsRemoved = htons(1 + steps_removed); + } + + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (tc_blocked(q, p, msg)) { + continue; + } + cnt = transport_send(p->trp, &p->fda, TRANS_GENERAL, msg); + if (cnt <= 0) { + pr_err("tc failed to forward message on port %d", + portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + } + } + return 0; +} + +int tc_fwd_folup(struct port *q, struct ptp_message *msg) +{ + struct port *p; + + clock_gettime(CLOCK_MONOTONIC, &msg->ts.host); + + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (tc_blocked(q, p, msg)) { + continue; + } + tc_complete(q, p, msg, tmv_zero()); + } + return 0; +} + +int tc_fwd_request(struct port *q, struct ptp_message *msg) +{ + return tc_fwd_event(q, msg); +} + +int tc_fwd_response(struct port *q, struct ptp_message *msg) +{ + struct port *p; + + clock_gettime(CLOCK_MONOTONIC, &msg->ts.host); + + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (tc_blocked(q, p, msg)) { + continue; + } + tc_complete(q, p, msg, tmv_zero()); + } + return 0; +} + +int tc_fwd_sync(struct port *q, struct ptp_message *msg) +{ + struct ptp_message *fup = NULL; + int err; + + if (one_step(msg)) { + fup = msg_allocate(); + if (!fup) { + return -1; + } + fup->header.tsmt = FOLLOW_UP | (msg->header.tsmt & 0xf0); + fup->header.ver = msg->header.ver; + fup->header.messageLength = sizeof(struct follow_up_msg); + fup->header.domainNumber = msg->header.domainNumber; + fup->header.sourcePortIdentity = msg->header.sourcePortIdentity; + fup->header.sequenceId = msg->header.sequenceId; + fup->header.control = CTL_FOLLOW_UP; + fup->header.logMessageInterval = msg->header.logMessageInterval; + fup->follow_up.preciseOriginTimestamp = msg->sync.originTimestamp; + msg->header.flagField[0] |= TWO_STEP; + } + err = tc_fwd_event(q, msg); + if (err) { + return err; + } + if (fup) { + err = tc_fwd_folup(q, fup); + msg_put(fup); + } + return err; +} + +int tc_ignore(struct port *p, struct ptp_message *m) +{ + struct ClockIdentity c1, c2; + + if (p->match_transport_specific && + msg_transport_specific(m) != p->transportSpecific) { + return 1; + } + if (pid_eq(&m->header.sourcePortIdentity, &p->portIdentity)) { + return 1; + } + if (m->header.domainNumber != clock_domain_number(p->clock)) { + return 1; + } + + c1 = clock_identity(p->clock); + c2 = m->header.sourcePortIdentity.clockIdentity; + + if (0 == memcmp(&c1, &c2, sizeof(c1))) { + return 1; + } + return 0; +} + +void tc_prune(struct port *q) +{ + struct timespec now; + struct tc_txd *txd; + + clock_gettime(CLOCK_MONOTONIC, &now); + + while ((txd = TAILQ_FIRST(&q->tc_transmitted)) != NULL) { + if (tc_current(txd->msg, now)) { + break; + } + TAILQ_REMOVE(&q->tc_transmitted, txd, list); + msg_put(txd->msg); + tc_recycle(txd); + } +} diff --git a/tc.h b/tc.h new file mode 100644 index 0000000..ef82351 --- /dev/null +++ b/tc.h @@ -0,0 +1,103 @@ +/** + * @file tc.h + * @brief Provides Transparent Clock logic. + * @note Copyright (C) 2018 Richard Cochran <ric...@gm...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. + */ +#ifndef HAVE_TC_H +#define HAVE_TC_H + +#include "msg.h" +#include "port_private.h" + +/** + * Flushes the list of remembered residence times. + * @param q Port whose list should be flushed + */ +void tc_flush(struct port *q); + +/** + * Forwards a given general message out all other ports. + * @param q The ingress port + * @param msg The message to be sent + * @return Zero on success, non-zero otherwise. + */ +int tc_forward(struct port *q, struct ptp_message *msg); + +/** + * Forwards a given Follow-Up message out all other ports. + * + * This function adds the unique, per egress port residence time into + * the correction field for the transmitted follow up message. + * + * @param q The ingress port + * @param msg The message to be sent + * @return Zero on success, non-zero otherwise. + */ +int tc_fwd_folup(struct port *q, struct ptp_message *msg); + +/** + * Forwards a given delay request message out all other ports. + * + * This function computes the unique residence time for each egress + * port, remembering it in that egress port. + * + * @param q The ingress port + * @param msg The message to be sent + * @return Zero on success, non-zero otherwise. + */ +int tc_fwd_request(struct port *q, struct ptp_message *msg); + +/** + * Forwards a given response message out all other ports. + * + * This function adds the unique, per egress port residence time into + * the correction field for the transmitted delay response message. + * + * @param q The ingress port + * @param msg The message to be sent + * @return Zero on success, non-zero otherwise. + */ +int tc_fwd_response(struct port *q, struct ptp_message *msg); + +/** + * Forwards a given sync message out all other ports. + * + * This function computes the unique residence time for each egress + * port, remembering it in that egress port. + * + * @param q The ingress port + * @param msg The message to be sent + * @return Zero on success, non-zero otherwise. + */ +int tc_fwd_sync(struct port *q, struct ptp_message *msg); + +/** + * Determines whether the local clock should ignore a given message. + * + * @param q The ingress port + * @param msg The message to test + * @return One if the message should be ignored, zero otherwise. + */ +int tc_ignore(struct port *q, struct ptp_message *m); + +/** + * Prunes stale entries from the list of remembered residence times. + * @param q Port whose list should be pruned. + */ +void tc_prune(struct port *q); + +#endif -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-05-01 15:08:41
|
The E2E TC forwards Announce, Delay_Req, Delay_Resp, Management, Signaling, and Sync messages, and drops P2P Delay messages. This implementation tracks the GM using the BMCA in order to syntonize (or possibly even synchronize) with it. Signed-off-by: Richard Cochran <ric...@gm...> --- clock.c | 2 +- e2e_tc.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ makefile | 2 +- port.c | 7 ++ port_private.h | 3 + 5 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 e2e_tc.c diff --git a/clock.c b/clock.c index 7731968..1edbb37 100644 --- a/clock.c +++ b/clock.c @@ -875,9 +875,9 @@ struct clock *clock_create(enum clock_type type, struct config *config, case CLOCK_TYPE_ORDINARY: case CLOCK_TYPE_BOUNDARY: case CLOCK_TYPE_P2P: + case CLOCK_TYPE_E2E: c->type = type; break; - case CLOCK_TYPE_E2E: case CLOCK_TYPE_MANAGEMENT: return NULL; } diff --git a/e2e_tc.c b/e2e_tc.c new file mode 100644 index 0000000..5a5c092 --- /dev/null +++ b/e2e_tc.c @@ -0,0 +1,226 @@ +/** + * @file e2e_tc.c + * @note Copyright (C) 2018 Richard Cochran <ric...@gm...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. + */ +#include <errno.h> + +#include "port.h" +#include "port_private.h" +#include "print.h" +#include "rtnl.h" +#include "tc.h" + +void e2e_dispatch(struct port *p, enum fsm_event event, int mdiff) +{ + if (!port_state_update(p, event, mdiff)) { + return; + } + if (!portnum(p)) { + /* UDS needs no timers. */ + return; + } + + port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]); + port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]); + /* Leave FD_DELAY_TIMER running. */ + port_clr_tmo(p->fda.fd[FD_QUALIFICATION_TIMER]); + port_clr_tmo(p->fda.fd[FD_MANNO_TIMER]); + port_clr_tmo(p->fda.fd[FD_SYNC_TX_TIMER]); + + /* + * Handle the side effects of the state transition. + */ + switch (p->state) { + case PS_INITIALIZING: + break; + case PS_FAULTY: + case PS_DISABLED: + port_disable(p); + break; + case PS_LISTENING: + port_set_announce_tmo(p); + port_set_delay_tmo(p); + break; + case PS_PRE_MASTER: + port_set_qualification_tmo(p); + break; + case PS_MASTER: + case PS_GRAND_MASTER: + break; + case PS_PASSIVE: + port_set_announce_tmo(p); + break; + case PS_UNCALIBRATED: + flush_last_sync(p); + flush_delay_req(p); + /* fall through */ + case PS_SLAVE: + port_set_announce_tmo(p); + break; + }; +} + +enum fsm_event e2e_event(struct port *p, int fd_index) +{ + int cnt, fd = p->fda.fd[fd_index]; + enum fsm_event event = EV_NONE; + struct ptp_message *msg, *dup; + + switch (fd_index) { + case FD_ANNOUNCE_TIMER: + case FD_SYNC_RX_TIMER: + pr_debug("port %hu: %s timeout", portnum(p), + fd_index == FD_SYNC_RX_TIMER ? "rx sync" : "announce"); + if (p->best) { + fc_clear(p->best); + } + port_set_announce_tmo(p); + return EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES; + + case FD_DELAY_TIMER: + pr_debug("port %hu: delay timeout", portnum(p)); + port_set_delay_tmo(p); + delay_req_prune(p); + tc_prune(p); + if (!clock_free_running(p->clock)) { + switch (p->state) { + case PS_UNCALIBRATED: + case PS_SLAVE: + if (port_delay_request(p)) { + event = EV_FAULT_DETECTED; + } + break; + default: + break; + }; + } + return event; + + case FD_QUALIFICATION_TIMER: + pr_debug("port %hu: qualification timeout", portnum(p)); + return EV_QUALIFICATION_TIMEOUT_EXPIRES; + + case FD_MANNO_TIMER: + case FD_SYNC_TX_TIMER: + pr_err("unexpected timer expiration"); + return EV_NONE; + + case FD_RTNL: + pr_debug("port %hu: received link status notification", portnum(p)); + 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)) || + (p->link_status & TS_LABEL_CHANGED)) { + return EV_FAULT_DETECTED; + } else { + return EV_NONE; + } + } + + msg = msg_allocate(); + if (!msg) { + return EV_FAULT_DETECTED; + } + msg->hwts.type = p->timestamping; + + cnt = transport_recv(p->trp, fd, msg); + if (cnt <= 0) { + pr_err("port %hu: recv message failed", portnum(p)); + msg_put(msg); + return EV_FAULT_DETECTED; + } + if (msg_sots_valid(msg)) { + ts_add(&msg->hwts.ts, -p->rx_timestamp_offset); + } + if (msg->header.flagField[0] & UNICAST) { + pl_warning(600, "cannot handle unicast messages!"); + msg_put(msg); + return EV_NONE; + } + + dup = msg_duplicate(msg, cnt); + if (!dup) { + msg_put(msg); + return EV_NONE; + } + if (tc_ignore(p, dup)) { + msg_put(dup); + dup = NULL; + } + + switch (msg_type(msg)) { + case SYNC: + if (tc_fwd_sync(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + if (dup) { + process_sync(p, dup); + } + break; + case DELAY_REQ: + if (tc_fwd_request(p, msg)) { + event = EV_FAULT_DETECTED; + } + break; + case PDELAY_REQ: + break; + case PDELAY_RESP: + break; + case FOLLOW_UP: + if (tc_fwd_folup(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + if (dup) { + process_follow_up(p, dup); + } + break; + case DELAY_RESP: + if (tc_fwd_response(p, msg)) { + event = EV_FAULT_DETECTED; + } + if (dup) { + process_delay_resp(p, dup); + } + break; + case PDELAY_RESP_FOLLOW_UP: + break; + case ANNOUNCE: + if (tc_forward(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + if (dup && process_announce(p, dup)) { + event = EV_STATE_DECISION_EVENT; + } + break; + case SIGNALING: + case MANAGEMENT: + if (tc_forward(p, msg)) { + event = EV_FAULT_DETECTED; + } + break; + } + + msg_put(msg); + if (dup) { + msg_put(dup); + } + return event; +} diff --git a/makefile b/makefile index c5c0cf8..fafacbe 100644 --- a/makefile +++ b/makefile @@ -23,7 +23,7 @@ VER = -DVER=$(version) CFLAGS = -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS) LDLIBS = -lm -lrt $(EXTRA_LDFLAGS) PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster -OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o fault.o \ +OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o e2e_tc.o fault.o \ filter.o fsm.o hash.o linreg.o mave.o mmedian.o msg.o ntpshm.o nullf.o phc.o \ pi.o port.o print.o ptp4l.o p2p_tc.o raw.o rtnl.o servo.o sk.o stats.o tc.o \ telecom.o tlv.o transport.o tsproc.o udp.o udp6.o uds.o util.o version.o diff --git a/port.c b/port.c index 3b35e6b..a7d7928 100644 --- a/port.c +++ b/port.c @@ -2792,6 +2792,9 @@ struct port *port_open(int phc_index, p->event = p2p_event; break; case CLOCK_TYPE_E2E: + p->dispatch = e2e_dispatch; + p->event = e2e_event; + break; case CLOCK_TYPE_MANAGEMENT: return NULL; } @@ -2848,6 +2851,10 @@ struct port *port_open(int phc_index, pr_err("port %d: P2P TC needs P2P ports", number); goto err_port; } + if (number && type == CLOCK_TYPE_E2E && p->delayMechanism != DM_E2E) { + pr_err("port %d: E2E TC needs E2E ports", number); + goto err_port; + } if (p->hybrid_e2e && p->delayMechanism != DM_E2E) { pr_warning("port %d: hybrid_e2e only works with E2E", number); } diff --git a/port_private.h b/port_private.h index c07216a..80b97cd 100644 --- a/port_private.h +++ b/port_private.h @@ -135,6 +135,9 @@ struct port { #define portnum(p) (p->portIdentity.portNumber) +void e2e_dispatch(struct port *p, enum fsm_event event, int mdiff); +enum fsm_event e2e_event(struct port *p, int fd_index); + void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff); enum fsm_event p2p_event(struct port *p, int fd_index); -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-05-01 15:08:42
|
This patch adds a new configuation option that enables transparent clock mode. When TC mode is specified, the configured delay mechanism must also match. The legacy behavior of upgrading a multi-port clock from OC to BC is preserved. Signed-off-by: Richard Cochran <ric...@gm...> --- config.c | 10 ++++++++++ default.cfg | 1 + ptp4l.8 | 15 +++++++++++---- ptp4l.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/config.c b/config.c index acd5f45..6d00641 100644 --- a/config.c +++ b/config.c @@ -24,6 +24,7 @@ #include <string.h> #include "bmc.h" +#include "clock.h" #include "config.h" #include "ether.h" #include "hash.h" @@ -135,6 +136,14 @@ static struct config_enum clock_servo_enu[] = { { NULL, 0 }, }; +static struct config_enum clock_type_enu[] = { + { "OC", CLOCK_TYPE_ORDINARY }, + { "BC", CLOCK_TYPE_BOUNDARY }, + { "P2P_TC", CLOCK_TYPE_P2P }, + { "E2E_TC", CLOCK_TYPE_E2E }, + { NULL, 0 }, +}; + static struct config_enum dataset_comp_enu[] = { { "ieee1588", DS_CMP_IEEE1588 }, { "G.8275.x", DS_CMP_G8275 }, @@ -186,6 +195,7 @@ struct config_item config_tab[] = { GLOB_ITEM_INT("clockAccuracy", 0xfe, 0, UINT8_MAX), GLOB_ITEM_INT("clockClass", 248, 0, UINT8_MAX), GLOB_ITEM_ENU("clock_servo", CLOCK_SERVO_PI, clock_servo_enu), + GLOB_ITEM_ENU("clock_type", CLOCK_TYPE_ORDINARY, clock_type_enu), GLOB_ITEM_ENU("dataset_comparison", DS_CMP_IEEE1588, dataset_comp_enu), PORT_ITEM_INT("delayAsymmetry", 0, INT_MIN, INT_MAX), PORT_ITEM_ENU("delay_filter", FILTER_MOVING_MEDIAN, delay_filter_enu), diff --git a/default.cfg b/default.cfg index 408022d..f70f386 100644 --- a/default.cfg +++ b/default.cfg @@ -75,6 +75,7 @@ uds_address /var/run/ptp4l # # Default interface options # +clock_type OC network_transport UDPv4 delay_mechanism E2E time_stamping hardware diff --git a/ptp4l.8 b/ptp4l.8 index 2aee03e..003d54e 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -1,6 +1,6 @@ -.TH PTP4l 8 "Novemver 2017" "linuxptp" +.TH PTP4l 8 "March 2018" "linuxptp" .SH NAME -ptp4l - PTP Boundary/Ordinary Clock +ptp4l - PTP Boundary/Ordinary/Transparent Clock .SH SYNOPSIS .B ptp4l @@ -23,8 +23,8 @@ ptp4l - PTP Boundary/Ordinary Clock .SH DESCRIPTION .B ptp4l is an implementation of the Precision Time Protocol (PTP) according to IEEE -standard 1588 for Linux. It implements Boundary Clock (BC) and Ordinary Clock -(OC). +standard 1588 for Linux. It implements Boundary Clock (BC), Ordinary Clock +(OC), and Transparent Clock (TC). .SH OPTIONS .TP @@ -425,6 +425,13 @@ number is set to the domain number), and "nullf" for a servo that always dials frequency offset zero (for use in SyncE nodes). The default is "pi." .TP +.B clock_type +Specifies the kind of PTP clock. Valid values are "OC" for ordinary +clock, "BC" for boundary clock, "P2P_TC" for peer to peer transparent +clock, and "E2E_TC" for end to end transparent clock. An multi-port +ordinary clock will automatically be configured as a boundary clock. +The default is "OC". +.TP .B pi_proportional_const The proportional constant of the PI controller. When set to 0.0, the proportional constant will be set by the following formula from the current diff --git a/ptp4l.c b/ptp4l.c index ee31718..9ef8169 100644 --- a/ptp4l.c +++ b/ptp4l.c @@ -1,6 +1,6 @@ /** * @file ptp4l.c - * @brief PTP Boundary Clock main program + * @brief PTP Boundary Clock or Transparent Clock main program * @note Copyright (C) 2011 Richard Cochran <ric...@gm...> * * This program is free software; you can redistribute it and/or modify @@ -59,6 +59,7 @@ static void usage(char *progname) " -p [dev] PTP hardware clock device to use, default auto\n" " (ignored for SOFTWARE/LEGACY HW time stamping)\n" " -s slave only mode (overrides configuration file)\n" + " -t transparent clock\n" " -l [num] set the logging level to 'num'\n" " -m print messages to stdout\n" " -q do not print messages to the syslog\n" @@ -71,6 +72,7 @@ static void usage(char *progname) int main(int argc, char *argv[]) { char *config = NULL, *req_phc = NULL, *progname; + enum clock_type type = CLOCK_TYPE_ORDINARY; int c, err = -1, index, print_level; struct clock *clock = NULL; struct option *opts; @@ -201,8 +203,44 @@ int main(int argc, char *argv[]) goto out; } - clock = clock_create(cfg->n_interfaces > 1 ? CLOCK_TYPE_BOUNDARY : - CLOCK_TYPE_ORDINARY, cfg, req_phc); + type = config_get_int(cfg, NULL, "clock_type"); + switch (type) { + case CLOCK_TYPE_ORDINARY: + if (cfg->n_interfaces > 1) { + type = CLOCK_TYPE_BOUNDARY; + } + break; + case CLOCK_TYPE_BOUNDARY: + if (cfg->n_interfaces < 2) { + fprintf(stderr, "BC needs at least two interfaces\n"); + goto out; + } + break; + case CLOCK_TYPE_P2P: + if (cfg->n_interfaces < 2) { + fprintf(stderr, "TC needs at least two interfaces\n"); + goto out; + } + if (DM_P2P != config_get_int(cfg, NULL, "delay_mechanism")) { + fprintf(stderr, "P2P_TC needs P2P delay mechanism\n"); + goto out; + } + break; + case CLOCK_TYPE_E2E: + if (cfg->n_interfaces < 2) { + fprintf(stderr, "TC needs at least two interfaces\n"); + goto out; + } + if (DM_E2E != config_get_int(cfg, NULL, "delay_mechanism")) { + fprintf(stderr, "E2E_TC needs E2E delay mechanism\n"); + goto out; + } + break; + case CLOCK_TYPE_MANAGEMENT: + goto out; + } + + clock = clock_create(type, cfg, req_phc); if (!clock) { fprintf(stderr, "failed to create a clock\n"); goto out; -- 2.11.0 |
From: Miroslav L. <mli...@re...> - 2018-05-04 14:45:22
|
On Tue, May 01, 2018 at 08:08:17AM -0700, Richard Cochran wrote: > Changed in V3: > ~~~~~~~~~~~~~~ > > - Correct the behavior of the E2E TC with respect to the free_running > option. Now the E2E TC will synchronize when not free running, just > like the P2P TC and the BC. In this case, the TC will generate > delay request messages. > > - Pay attention to the domain field when synchronizing the local clock. I repeated my tests in the HW setup I described before in all four combinations of E2E/P2P with free_running 0/1 and it seems to be working nicely. I'd like to add some tests of the TC to the testsuite, but it looks like that will require handling of the deferred reading of TX timestamps. I'll look into that later. Thanks, -- Miroslav Lichvar |
From: Richard C. <ric...@gm...> - 2018-04-21 05:22:31
|
The transparent clock code will want to set qualification timeouts. This patch exposes the needed method. Signed-off-by: Richard Cochran <ric...@gm...> --- port.c | 2 +- port_private.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/port.c b/port.c index 663f36e..a1241c0 100644 --- a/port.c +++ b/port.c @@ -1048,7 +1048,7 @@ static int port_set_manno_tmo(struct port *p) return set_tmo_log(p->fda.fd[FD_MANNO_TIMER], 1, p->logAnnounceInterval); } -static int port_set_qualification_tmo(struct port *p) +int port_set_qualification_tmo(struct port *p) { return set_tmo_log(p->fda.fd[FD_QUALIFICATION_TIMER], 1+clock_steps_removed(p->clock), p->logAnnounceInterval); diff --git a/port_private.h b/port_private.h index 391db64..616d71f 100644 --- a/port_private.h +++ b/port_private.h @@ -136,6 +136,7 @@ int port_is_enabled(struct port *p); void port_link_status(void *ctx, int index, int linkup); int port_set_announce_tmo(struct port *p); int port_set_delay_tmo(struct port *p); +int port_set_qualification_tmo(struct port *p); void port_show_transition(struct port *p, enum port_state next, enum fsm_event event); int process_announce(struct port *p, struct ptp_message *m); -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-04-21 05:22:33
|
This patch adds code that sends an event messages received on one port out all the other ports and calculates the residence time. The correction, ingress port, and the original message are remembered in a TC transmit descriptor. These descriptors are recycled in a memory pool in a similar way to the message buffers. Signed-off-by: Richard Cochran <ric...@gm...> --- clock.c | 1 + makefile | 4 +- port.c | 3 + port.h | 5 + port_private.h | 10 ++ tc.c | 486 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tc.h | 94 +++++++++++ 7 files changed, 601 insertions(+), 2 deletions(-) create mode 100644 tc.c create mode 100644 tc.h diff --git a/clock.c b/clock.c index 9339b1a..3994f2d 100644 --- a/clock.c +++ b/clock.c @@ -282,6 +282,7 @@ void clock_destroy(struct clock *c) } memset(c, 0, sizeof(*c)); msg_cleanup(); + tc_cleanup(); } static int clock_fault_timeout(struct port *port, int set) diff --git a/makefile b/makefile index d34eb6b..707c5d4 100644 --- a/makefile +++ b/makefile @@ -25,8 +25,8 @@ LDLIBS = -lm -lrt $(EXTRA_LDFLAGS) PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o fault.o \ filter.o fsm.o hash.o linreg.o mave.o mmedian.o msg.o ntpshm.o nullf.o phc.o \ - pi.o port.o print.o ptp4l.o raw.o rtnl.o servo.o sk.o stats.o telecom.o tlv.o \ - transport.o tsproc.o udp.o udp6.o uds.o util.o version.o + pi.o port.o print.o ptp4l.o raw.o rtnl.o servo.o sk.o stats.o tc.o telecom.o \ + tlv.o transport.o tsproc.o udp.o udp6.o uds.o util.o version.o OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \ sysoff.o timemaster.o diff --git a/port.c b/port.c index a1241c0..235076d 100644 --- a/port.c +++ b/port.c @@ -36,6 +36,7 @@ #include "print.h" #include "rtnl.h" #include "sk.h" +#include "tc.h" #include "tlv.h" #include "tmv.h" #include "tsproc.h" @@ -1535,6 +1536,7 @@ void port_disable(struct port *p) { int i; + tc_flush(p); flush_last_sync(p); flush_delay_req(p); flush_peer_delay(p); @@ -2776,6 +2778,7 @@ struct port *port_open(int phc_index, } memset(p, 0, sizeof(*p)); + TAILQ_INIT(&p->tc_transmitted); switch (type) { case CLOCK_TYPE_ORDINARY: diff --git a/port.h b/port.h index d828875..9639193 100644 --- a/port.h +++ b/port.h @@ -322,4 +322,9 @@ enum fault_type last_fault_type(struct port *port); void fault_interval(struct port *port, enum fault_type ft, struct fault_interval *i); +/** + * Release all of the memory in the TC transmit descriptor cache. + */ +void tc_cleanup(void); + #endif diff --git a/port_private.h b/port_private.h index 616d71f..165ca00 100644 --- a/port_private.h +++ b/port_private.h @@ -50,6 +50,13 @@ struct nrate_estimator { int ratio_valid; }; +struct tc_txd { + TAILQ_ENTRY(tc_txd) list; + struct ptp_message *msg; + tmv_t residence; + int ingress_port; +}; + struct port { LIST_ENTRY(port) list; char *name; @@ -114,6 +121,7 @@ struct port { int min_neighbor_prop_delay; int net_sync_monitor; int path_trace_enabled; + int tc_spanning_tree; Integer64 rx_timestamp_offset; Integer64 tx_timestamp_offset; enum link_state link_status; @@ -122,6 +130,8 @@ struct port { unsigned int versionNumber; /*UInteger4*/ /* foreignMasterDS */ LIST_HEAD(fm, foreign_clock) foreign_masters; + /* TC book keeping */ + TAILQ_HEAD(tct, tc_txd) tc_transmitted; }; #define portnum(p) (p->portIdentity.portNumber) diff --git a/tc.c b/tc.c new file mode 100644 index 0000000..496032c --- /dev/null +++ b/tc.c @@ -0,0 +1,486 @@ +/** + * @file tc.c + * @note Copyright (C) 2018 Richard Cochran <ric...@gm...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. + */ +#include <stdlib.h> + +#include "port.h" +#include "print.h" +#include "tc.h" +#include "tmv.h" + +enum tc_match { + TC_MISMATCH, + TC_SYNC_FUP, + TC_FUP_SYNC, + TC_DELAY_REQRESP, +}; + +static TAILQ_HEAD(tc_pool, tc_txd) tc_pool = TAILQ_HEAD_INITIALIZER(tc_pool); + +static int tc_match_delay(int ingress_port, struct ptp_message *resp, + struct tc_txd *txd); +static int tc_match_syfup(int ingress_port, struct ptp_message *msg, + struct tc_txd *txd); +static void tc_recycle(struct tc_txd *txd); + +static struct tc_txd *tc_allocate(void) +{ + struct tc_txd *txd = TAILQ_FIRST(&tc_pool); + + if (txd) { + TAILQ_REMOVE(&tc_pool, txd, list); + memset(txd, 0, sizeof(*txd)); + return txd; + } + txd = calloc(1, sizeof(*txd)); + return txd; +} + +static int tc_blocked(struct port *q, struct port *p, struct ptp_message *m) +{ + enum port_state s; + + if (q == p) { + return 1; + } + if (portnum(p) == 0) { + return 1; + } + if (!q->tc_spanning_tree) { + return 0; + } + /* Ingress state */ + s = port_state(q); + switch (s) { + case PS_INITIALIZING: + case PS_FAULTY: + case PS_DISABLED: + case PS_LISTENING: + case PS_PRE_MASTER: + case PS_PASSIVE: + return 1; + case PS_MASTER: + case PS_GRAND_MASTER: + /* Delay_Req swims against the stream. */ + if (msg_type(m) != DELAY_REQ) { + return 1; + } + break; + case PS_UNCALIBRATED: + case PS_SLAVE: + break; + } + /* Egress state */ + s = port_state(p); + switch (s) { + case PS_INITIALIZING: + case PS_FAULTY: + case PS_DISABLED: + case PS_LISTENING: + case PS_PRE_MASTER: + case PS_PASSIVE: + return 1; + case PS_UNCALIBRATED: + case PS_SLAVE: + /* Delay_Req swims against the stream. */ + if (msg_type(m) != DELAY_REQ) { + return 1; + } + break; + case PS_MASTER: + case PS_GRAND_MASTER: + /* No use forwarding Delay_Req out the wrong port. */ + if (msg_type(m) == DELAY_REQ) { + return 1; + } + break; + } + return 0; +} + +static void tc_complete_request(struct port *q, struct port *p, + struct ptp_message *req, tmv_t residence) +{ + struct tc_txd *txd = tc_allocate(); + if (!txd) { + port_dispatch(p, EV_FAULT_DETECTED, 0); + return; + } +#ifdef DEBUG + pr_err("stash delay request from port %hd to %hd seqid %hu residence %lu", + portnum(q), portnum(p), ntohs(req->header.sequenceId), + (unsigned long) tmv_to_nanoseconds(residence)); +#endif + msg_get(req); + txd->msg = req; + txd->residence = residence; + txd->ingress_port = portnum(q); + TAILQ_INSERT_TAIL(&p->tc_transmitted, txd, list); +} + +static void tc_complete_response(struct port *q, struct port *p, + struct ptp_message *resp, tmv_t residence) +{ + enum tc_match type = TC_MISMATCH; + struct tc_txd *txd; + Integer64 c1, c2; + int cnt; + +#ifdef DEBUG + pr_err("complete delay response from port %hd to %hd seqid %hu", + portnum(q), portnum(p), ntohs(resp->header.sequenceId)); +#endif + TAILQ_FOREACH(txd, &q->tc_transmitted, list) { + type = tc_match_delay(portnum(p), resp, txd); + if (type == TC_DELAY_REQRESP) { + residence = txd->residence; + break; + } + } + if (type != TC_DELAY_REQRESP) { + return; + } + c1 = net2host64(resp->header.correction); + c2 = c1 + tmv_to_TimeInterval(residence); + resp->header.correction = host2net64(c2); + cnt = transport_send(p->trp, &p->fda, TRANS_GENERAL, resp); + if (cnt <= 0) { + pr_err("tc failed to forward response on port %d", portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + } + /* Restore original correction value for next egress port. */ + resp->header.correction = host2net64(c1); + TAILQ_REMOVE(&q->tc_transmitted, txd, list); + msg_put(txd->msg); + tc_recycle(txd); +} + +static void tc_complete_syfup(struct port *q, struct port *p, + struct ptp_message *msg, tmv_t residence) +{ + enum tc_match type = TC_MISMATCH; + struct ptp_message *fup; + struct tc_txd *txd; + Integer64 c1, c2; + int cnt; + + TAILQ_FOREACH(txd, &p->tc_transmitted, list) { + type = tc_match_syfup(portnum(q), msg, txd); + switch (type) { + case TC_MISMATCH: + break; + case TC_SYNC_FUP: + fup = msg; + residence = txd->residence; + break; + case TC_FUP_SYNC: + fup = txd->msg; + break; + case TC_DELAY_REQRESP: + pr_err("tc: unexpected match of delay request - sync!"); + return; + } + if (type != TC_MISMATCH) { + break; + } + } + + if (type == TC_MISMATCH) { + txd = tc_allocate(); + if (!txd) { + port_dispatch(p, EV_FAULT_DETECTED, 0); + return; + } + msg_get(msg); + txd->msg = msg; + txd->residence = residence; + txd->ingress_port = portnum(q); + TAILQ_INSERT_TAIL(&p->tc_transmitted, txd, list); + return; + } + + c1 = net2host64(fup->header.correction); + c2 = c1 + tmv_to_TimeInterval(residence); + c2 += tmv_to_TimeInterval(q->peer_delay); + c2 += q->asymmetry; + fup->header.correction = host2net64(c2); + cnt = transport_send(p->trp, &p->fda, TRANS_GENERAL, fup); + if (cnt <= 0) { + pr_err("tc failed to forward follow up on port %d", portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + } + /* Restore original correction value for next egress port. */ + fup->header.correction = host2net64(c1); + TAILQ_REMOVE(&p->tc_transmitted, txd, list); + msg_put(txd->msg); + tc_recycle(txd); +} + +static void tc_complete(struct port *q, struct port *p, + struct ptp_message *msg, tmv_t residence) +{ + switch (msg_type(msg)) { + case SYNC: + case FOLLOW_UP: + tc_complete_syfup(q, p, msg, residence); + break; + case DELAY_REQ: + tc_complete_request(q, p, msg, residence); + break; + case DELAY_RESP: + tc_complete_response(q, p, msg, residence); + break; + } +} + +static int tc_current(struct ptp_message *m, struct timespec now) +{ + int64_t t1, t2, tmo; + + tmo = 1LL * NSEC2SEC; + t1 = m->ts.host.tv_sec * NSEC2SEC + m->ts.host.tv_nsec; + t2 = now.tv_sec * NSEC2SEC + now.tv_nsec; + + return t2 - t1 < tmo; +} + +static int tc_fwd_event(struct port *q, struct ptp_message *msg) +{ + tmv_t egress, ingress = msg->hwts.ts, residence; + struct port *p; + int cnt, err; + double rr; + + clock_gettime(CLOCK_MONOTONIC, &msg->ts.host); + + /* First send the event message out. */ + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (tc_blocked(q, p, msg)) { + continue; + } + cnt = transport_send(p->trp, &p->fda, TRANS_DEFER_EVENT, msg); + if (cnt <= 0) { + pr_err("failed to forward event from port %hd to %hd", + portnum(q), portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + } + } + + /* Go back and gather the transmit time stamps. */ + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (tc_blocked(q, p, msg)) { + continue; + } + err = transport_txts(p->trp, &p->fda, msg); + if (err || !msg_sots_valid(msg)) { + pr_err("failed to fetch txts on port %hd to %hd event", + portnum(q), portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + continue; + } + ts_add(&msg->hwts.ts, p->tx_timestamp_offset); + egress = msg->hwts.ts; + residence = tmv_sub(egress, ingress); + rr = clock_rate_ratio(q->clock); + if (rr != 1.0) { + residence = dbl_tmv(tmv_dbl(residence) * rr); + } + tc_complete(q, p, msg, residence); + } + + return 0; +} + +static int tc_match_delay(int ingress_port, struct ptp_message *resp, + struct tc_txd *txd) +{ + struct ptp_message *req = txd->msg; + + if (ingress_port != txd->ingress_port) { + return TC_MISMATCH; + } + if (req->header.sequenceId != resp->header.sequenceId) { + return TC_MISMATCH; + } + if (!pid_eq(&req->header.sourcePortIdentity, + &resp->delay_resp.requestingPortIdentity)) { + return TC_MISMATCH; + } + if (msg_type(req) == DELAY_REQ && msg_type(resp) == DELAY_RESP) { + return TC_DELAY_REQRESP; + } + return TC_MISMATCH; +} + +static int tc_match_syfup(int ingress_port, struct ptp_message *msg, + struct tc_txd *txd) +{ + if (ingress_port != txd->ingress_port) { + return TC_MISMATCH; + } + if (msg->header.sequenceId != txd->msg->header.sequenceId) { + return TC_MISMATCH; + } + if (!source_pid_eq(msg, txd->msg)) { + return TC_MISMATCH; + } + if (msg_type(txd->msg) == SYNC && msg_type(msg) == FOLLOW_UP) { + return TC_SYNC_FUP; + } + if (msg_type(txd->msg) == FOLLOW_UP && msg_type(msg) == SYNC) { + return TC_FUP_SYNC; + } + return TC_MISMATCH; +} + +static void tc_recycle(struct tc_txd *txd) +{ + TAILQ_INSERT_HEAD(&tc_pool, txd, list); +} + +/* public methods */ + +void tc_cleanup(void) +{ + struct tc_txd *txd; + + while ((txd = TAILQ_FIRST(&tc_pool)) != NULL) { + TAILQ_REMOVE(&tc_pool, txd, list); + free(txd); + } +} + +void tc_flush(struct port *q) +{ + struct tc_txd *txd; + + while ((txd = TAILQ_FIRST(&q->tc_transmitted)) != NULL) { + TAILQ_REMOVE(&q->tc_transmitted, txd, list); + msg_put(txd->msg); + tc_recycle(txd); + } +} + +int tc_forward(struct port *q, struct ptp_message *msg) +{ + uint16_t steps_removed; + struct port *p; + int cnt; + + if (q->tc_spanning_tree && msg_type(msg) == ANNOUNCE) { + steps_removed = ntohs(msg->announce.stepsRemoved); + msg->announce.stepsRemoved = htons(1 + steps_removed); + } + + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (tc_blocked(q, p, msg)) { + continue; + } + cnt = transport_send(p->trp, &p->fda, TRANS_GENERAL, msg); + if (cnt <= 0) { + pr_err("tc failed to forward message on port %d", + portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + } + } + return 0; +} + +int tc_fwd_folup(struct port *q, struct ptp_message *msg) +{ + struct port *p; + + clock_gettime(CLOCK_MONOTONIC, &msg->ts.host); + + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (tc_blocked(q, p, msg)) { + continue; + } + tc_complete(q, p, msg, tmv_zero()); + } + return 0; +} + +int tc_fwd_request(struct port *q, struct ptp_message *msg) +{ + return tc_fwd_event(q, msg); +} + +int tc_fwd_response(struct port *q, struct ptp_message *msg) +{ + struct port *p; + + clock_gettime(CLOCK_MONOTONIC, &msg->ts.host); + + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (tc_blocked(q, p, msg)) { + continue; + } + tc_complete(q, p, msg, tmv_zero()); + } + return 0; +} + +int tc_fwd_sync(struct port *q, struct ptp_message *msg) +{ + struct ptp_message *fup = NULL; + int err; + + if (one_step(msg)) { + fup = msg_allocate(); + if (!fup) { + return -1; + } + fup->header.tsmt = FOLLOW_UP | (msg->header.tsmt & 0xf0); + fup->header.ver = msg->header.ver; + fup->header.messageLength = sizeof(struct follow_up_msg); + fup->header.domainNumber = msg->header.domainNumber; + fup->header.sourcePortIdentity = msg->header.sourcePortIdentity; + fup->header.sequenceId = msg->header.sequenceId; + fup->header.control = CTL_FOLLOW_UP; + fup->header.logMessageInterval = msg->header.logMessageInterval; + fup->follow_up.preciseOriginTimestamp = msg->sync.originTimestamp; + msg->header.flagField[0] |= TWO_STEP; + } + err = tc_fwd_event(q, msg); + if (err) { + return err; + } + if (fup) { + err = tc_fwd_folup(q, fup); + msg_put(fup); + } + return err; +} + +void tc_prune(struct port *q) +{ + struct timespec now; + struct tc_txd *txd; + + clock_gettime(CLOCK_MONOTONIC, &now); + + while ((txd = TAILQ_FIRST(&q->tc_transmitted)) != NULL) { + if (tc_current(txd->msg, now)) { + break; + } + TAILQ_REMOVE(&q->tc_transmitted, txd, list); + msg_put(txd->msg); + tc_recycle(txd); + } +} diff --git a/tc.h b/tc.h new file mode 100644 index 0000000..b5b0a3f --- /dev/null +++ b/tc.h @@ -0,0 +1,94 @@ +/** + * @file tc.h + * @brief Provides Transparent Clock logic. + * @note Copyright (C) 2018 Richard Cochran <ric...@gm...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. + */ +#ifndef HAVE_TC_H +#define HAVE_TC_H + +#include "msg.h" +#include "port_private.h" + +/** + * Flushes the list of remembered residence times. + * @param q Port whose list should be flushed + */ +void tc_flush(struct port *q); + +/** + * Forwards a given general message out all other ports. + * @param q The ingress port + * @param msg The message to be sent + * @return Zero on success, non-zero otherwise. + */ +int tc_forward(struct port *q, struct ptp_message *msg); + +/** + * Forwards a given Follow-Up message out all other ports. + * + * This function adds the unique, per egress port residence time into + * the correction field for the transmitted follow up message. + * + * @param q The ingress port + * @param msg The message to be sent + * @return Zero on success, non-zero otherwise. + */ +int tc_fwd_folup(struct port *q, struct ptp_message *msg); + +/** + * Forwards a given delay request message out all other ports. + * + * This function computes the unique residence time for each egress + * port, remembering it in that egress port. + * + * @param q The ingress port + * @param msg The message to be sent + * @return Zero on success, non-zero otherwise. + */ +int tc_fwd_request(struct port *q, struct ptp_message *msg); + +/** + * Forwards a given response message out all other ports. + * + * This function adds the unique, per egress port residence time into + * the correction field for the transmitted delay response message. + * + * @param q The ingress port + * @param msg The message to be sent + * @return Zero on success, non-zero otherwise. + */ +int tc_fwd_response(struct port *q, struct ptp_message *msg); + +/** + * Forwards a given sync message out all other ports. + * + * This function computes the unique residence time for each egress + * port, remembering it in that egress port. + * + * @param q The ingress port + * @param msg The message to be sent + * @return Zero on success, non-zero otherwise. + */ +int tc_fwd_sync(struct port *q, struct ptp_message *msg); + +/** + * Prunes stale entries from the list of remembered residence times. + * @param q Port whose list should be pruned. + */ +void tc_prune(struct port *q); + +#endif -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-04-21 05:22:34
|
The P2P TC forwards Announce, Management, Signaling, and Sync messages, consumes P2P Delay messages, and drops E2E Delay messages. This implementation tracks the GM using the BMCA in order to syntonize (or possibly even synchronize) with it. Signed-off-by: Richard Cochran <ric...@gm...> --- clock.c | 2 +- makefile | 4 +- p2p_tc.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ port.c | 7 ++ port_private.h | 3 + 5 files changed, 231 insertions(+), 3 deletions(-) create mode 100644 p2p_tc.c diff --git a/clock.c b/clock.c index 3994f2d..e246acd 100644 --- a/clock.c +++ b/clock.c @@ -869,9 +869,9 @@ struct clock *clock_create(enum clock_type type, struct config *config, switch (type) { case CLOCK_TYPE_ORDINARY: case CLOCK_TYPE_BOUNDARY: + case CLOCK_TYPE_P2P: c->type = type; break; - case CLOCK_TYPE_P2P: case CLOCK_TYPE_E2E: case CLOCK_TYPE_MANAGEMENT: return NULL; diff --git a/makefile b/makefile index 707c5d4..c5c0cf8 100644 --- a/makefile +++ b/makefile @@ -25,8 +25,8 @@ LDLIBS = -lm -lrt $(EXTRA_LDFLAGS) PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o fault.o \ filter.o fsm.o hash.o linreg.o mave.o mmedian.o msg.o ntpshm.o nullf.o phc.o \ - pi.o port.o print.o ptp4l.o raw.o rtnl.o servo.o sk.o stats.o tc.o telecom.o \ - tlv.o transport.o tsproc.o udp.o udp6.o uds.o util.o version.o + pi.o port.o print.o ptp4l.o p2p_tc.o raw.o rtnl.o servo.o sk.o stats.o tc.o \ + telecom.o tlv.o transport.o tsproc.o udp.o udp6.o uds.o util.o version.o OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \ sysoff.o timemaster.o diff --git a/p2p_tc.c b/p2p_tc.c new file mode 100644 index 0000000..e6e044b --- /dev/null +++ b/p2p_tc.c @@ -0,0 +1,218 @@ +/** + * @file p2p_tc.c + * @note Copyright (C) 2018 Richard Cochran <ric...@gm...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. + */ +#include <errno.h> + +#include "port.h" +#include "port_private.h" +#include "print.h" +#include "rtnl.h" +#include "tc.h" + +static int p2p_delay_request(struct port *p) +{ + switch (p->state) { + case PS_INITIALIZING: + case PS_FAULTY: + case PS_DISABLED: + return 0; + case PS_LISTENING: + case PS_PRE_MASTER: + case PS_MASTER: + case PS_PASSIVE: + case PS_UNCALIBRATED: + case PS_SLAVE: + case PS_GRAND_MASTER: + break; + } + return port_delay_request(p); +} + +void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff) +{ + if (!port_state_update(p, event, mdiff)) { + return; + } + if (!portnum(p)) { + /* UDS needs no timers. */ + return; + } + + port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]); + port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]); + /* Leave FD_DELAY_TIMER running. */ + port_clr_tmo(p->fda.fd[FD_QUALIFICATION_TIMER]); + port_clr_tmo(p->fda.fd[FD_MANNO_TIMER]); + port_clr_tmo(p->fda.fd[FD_SYNC_TX_TIMER]); + + /* + * Handle the side effects of the state transition. + */ + switch (p->state) { + case PS_INITIALIZING: + break; + case PS_FAULTY: + case PS_DISABLED: + port_disable(p); + break; + case PS_LISTENING: + port_set_announce_tmo(p); + port_set_delay_tmo(p); + break; + case PS_PRE_MASTER: + port_set_qualification_tmo(p); + break; + case PS_MASTER: + case PS_GRAND_MASTER: + break; + case PS_PASSIVE: + port_set_announce_tmo(p); + break; + case PS_UNCALIBRATED: + case PS_SLAVE: + port_set_announce_tmo(p); + break; + }; +} + +enum fsm_event p2p_event(struct port *p, int fd_index) +{ + int cnt, fd = p->fda.fd[fd_index]; + enum fsm_event event = EV_NONE; + struct ptp_message *msg, *dup; + + switch (fd_index) { + case FD_ANNOUNCE_TIMER: + case FD_SYNC_RX_TIMER: + pr_debug("port %hu: %s timeout", portnum(p), + fd_index == FD_SYNC_RX_TIMER ? "rx sync" : "announce"); + if (p->best) { + fc_clear(p->best); + } + port_set_announce_tmo(p); + return EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES; + + case FD_DELAY_TIMER: + pr_debug("port %hu: delay timeout", portnum(p)); + port_set_delay_tmo(p); + tc_prune(p); + return p2p_delay_request(p) ? EV_FAULT_DETECTED : EV_NONE; + + case FD_QUALIFICATION_TIMER: + pr_debug("port %hu: qualification timeout", portnum(p)); + return EV_QUALIFICATION_TIMEOUT_EXPIRES; + + case FD_MANNO_TIMER: + case FD_SYNC_TX_TIMER: + pr_err("unexpected timer expiration"); + return EV_NONE; + + case FD_RTNL: + pr_debug("port %hu: received link status notification", portnum(p)); + 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)) || + (p->link_status & TS_LABEL_CHANGED)) { + return EV_FAULT_DETECTED; + } else { + return EV_NONE; + } + } + + msg = msg_allocate(); + if (!msg) { + return EV_FAULT_DETECTED; + } + msg->hwts.type = p->timestamping; + + cnt = transport_recv(p->trp, fd, msg); + if (cnt <= 0) { + pr_err("port %hu: recv message failed", portnum(p)); + msg_put(msg); + return EV_FAULT_DETECTED; + } + if (msg_sots_valid(msg)) { + ts_add(&msg->hwts.ts, -p->rx_timestamp_offset); + } + if (msg->header.flagField[0] & UNICAST) { + pl_warning(600, "cannot switch unicast messages!"); + msg_put(msg); + return EV_NONE; + } + + dup = msg_duplicate(msg, cnt); + if (!dup) { + msg_put(msg); + return EV_NONE; + } + + switch (msg_type(msg)) { + case SYNC: + if (tc_fwd_sync(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + process_sync(p, dup); + break; + case DELAY_REQ: + break; + case PDELAY_REQ: + if (process_pdelay_req(p, dup)) { + event = EV_FAULT_DETECTED; + } + break; + case PDELAY_RESP: + if (process_pdelay_resp(p, dup)) { + event = EV_FAULT_DETECTED; + } + break; + case FOLLOW_UP: + if (tc_fwd_folup(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + process_follow_up(p, dup); + break; + case DELAY_RESP: + break; + case PDELAY_RESP_FOLLOW_UP: + process_pdelay_resp_fup(p, dup); + break; + case ANNOUNCE: + if (tc_forward(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + if (process_announce(p, dup)) { + event = EV_STATE_DECISION_EVENT; + } + break; + case SIGNALING: + case MANAGEMENT: + if (tc_forward(p, msg)) { + event = EV_FAULT_DETECTED; + } + break; + } + + msg_put(msg); + msg_put(dup); + + return event; +} diff --git a/port.c b/port.c index 235076d..efb0a09 100644 --- a/port.c +++ b/port.c @@ -2787,6 +2787,9 @@ struct port *port_open(int phc_index, p->event = bc_event; break; case CLOCK_TYPE_P2P: + p->dispatch = p2p_dispatch; + p->event = p2p_event; + break; case CLOCK_TYPE_E2E: case CLOCK_TYPE_MANAGEMENT: return NULL; @@ -2845,6 +2848,10 @@ struct port *port_open(int phc_index, p->delayMechanism = config_get_int(cfg, p->name, "delay_mechanism"); p->versionNumber = PTP_VERSION; + if (number && type == CLOCK_TYPE_P2P && p->delayMechanism != DM_P2P) { + pr_err("port %d: P2P TC needs P2P ports", number); + goto err_port; + } if (p->hybrid_e2e && p->delayMechanism != DM_E2E) { pr_warning("port %d: hybrid_e2e only works with E2E", number); } diff --git a/port_private.h b/port_private.h index 165ca00..c6da299 100644 --- a/port_private.h +++ b/port_private.h @@ -136,6 +136,9 @@ struct port { #define portnum(p) (p->portIdentity.portNumber) +void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff); +enum fsm_event p2p_event(struct port *p, int fd_index); + int clear_fault_asap(struct fault_interval *faint); void fc_clear(struct foreign_clock *fc); int port_clr_tmo(int fd); -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-04-21 05:22:35
|
The E2E TC forwards Announce, Delay_Req, Delay_Resp, Management, Signaling, and Sync messages, and drops P2P Delay messages. This implementation tracks the GM using the BMCA in order to syntonize (or possibly even synchronize) with it. Signed-off-by: Richard Cochran <ric...@gm...> --- clock.c | 2 +- e2e_tc.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ makefile | 2 +- port.c | 7 ++ port_private.h | 3 + 5 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 e2e_tc.c diff --git a/clock.c b/clock.c index e246acd..621cf99 100644 --- a/clock.c +++ b/clock.c @@ -870,9 +870,9 @@ struct clock *clock_create(enum clock_type type, struct config *config, case CLOCK_TYPE_ORDINARY: case CLOCK_TYPE_BOUNDARY: case CLOCK_TYPE_P2P: + case CLOCK_TYPE_E2E: c->type = type; break; - case CLOCK_TYPE_E2E: case CLOCK_TYPE_MANAGEMENT: return NULL; } diff --git a/e2e_tc.c b/e2e_tc.c new file mode 100644 index 0000000..8599d21 --- /dev/null +++ b/e2e_tc.c @@ -0,0 +1,198 @@ +/** + * @file e2e_tc.c + * @note Copyright (C) 2018 Richard Cochran <ric...@gm...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. + */ +#include <errno.h> + +#include "port.h" +#include "port_private.h" +#include "print.h" +#include "rtnl.h" +#include "tc.h" + +void e2e_dispatch(struct port *p, enum fsm_event event, int mdiff) +{ + if (!port_state_update(p, event, mdiff)) { + return; + } + if (!portnum(p)) { + /* UDS needs no timers. */ + return; + } + + port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]); + port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]); + /* Leave FD_DELAY_TIMER running. */ + port_clr_tmo(p->fda.fd[FD_QUALIFICATION_TIMER]); + port_clr_tmo(p->fda.fd[FD_MANNO_TIMER]); + port_clr_tmo(p->fda.fd[FD_SYNC_TX_TIMER]); + + /* + * Handle the side effects of the state transition. + */ + switch (p->state) { + case PS_INITIALIZING: + break; + case PS_FAULTY: + case PS_DISABLED: + port_disable(p); + break; + case PS_LISTENING: + port_set_announce_tmo(p); + port_set_delay_tmo(p); + break; + case PS_PRE_MASTER: + port_set_qualification_tmo(p); + break; + case PS_MASTER: + case PS_GRAND_MASTER: + break; + case PS_PASSIVE: + port_set_announce_tmo(p); + break; + case PS_UNCALIBRATED: + case PS_SLAVE: + port_set_announce_tmo(p); + break; + }; +} + +enum fsm_event e2e_event(struct port *p, int fd_index) +{ + int cnt, fd = p->fda.fd[fd_index]; + enum fsm_event event = EV_NONE; + struct ptp_message *msg, *dup; + + switch (fd_index) { + case FD_ANNOUNCE_TIMER: + case FD_SYNC_RX_TIMER: + pr_debug("port %hu: %s timeout", portnum(p), + fd_index == FD_SYNC_RX_TIMER ? "rx sync" : "announce"); + if (p->best) { + fc_clear(p->best); + } + port_set_announce_tmo(p); + return EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES; + + case FD_DELAY_TIMER: + pr_debug("port %hu: delay timeout", portnum(p)); + port_set_delay_tmo(p); + tc_prune(p); + return EV_NONE; + + case FD_QUALIFICATION_TIMER: + pr_debug("port %hu: qualification timeout", portnum(p)); + return EV_QUALIFICATION_TIMEOUT_EXPIRES; + + case FD_MANNO_TIMER: + case FD_SYNC_TX_TIMER: + pr_err("unexpected timer expiration"); + return EV_NONE; + + case FD_RTNL: + pr_debug("port %hu: received link status notification", portnum(p)); + 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)) || + (p->link_status & TS_LABEL_CHANGED)) { + return EV_FAULT_DETECTED; + } else { + return EV_NONE; + } + } + + msg = msg_allocate(); + if (!msg) { + return EV_FAULT_DETECTED; + } + msg->hwts.type = p->timestamping; + + cnt = transport_recv(p->trp, fd, msg); + if (cnt <= 0) { + pr_err("port %hu: recv message failed", portnum(p)); + msg_put(msg); + return EV_FAULT_DETECTED; + } + if (msg_sots_valid(msg)) { + ts_add(&msg->hwts.ts, -p->rx_timestamp_offset); + } + if (msg->header.flagField[0] & UNICAST) { + pl_warning(600, "cannot handle unicast messages!"); + msg_put(msg); + return EV_NONE; + } + + dup = msg_duplicate(msg, cnt); + if (!dup) { + msg_put(msg); + return EV_NONE; + } + + switch (msg_type(msg)) { + case SYNC: + if (tc_fwd_sync(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + process_sync(p, dup); + break; + case DELAY_REQ: + if (tc_fwd_request(p, msg)) { + event = EV_FAULT_DETECTED; + } + break; + case PDELAY_REQ: + break; + case PDELAY_RESP: + break; + case FOLLOW_UP: + if (tc_fwd_folup(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + process_follow_up(p, dup); + break; + case DELAY_RESP: + if (tc_fwd_response(p, msg)) { + event = EV_FAULT_DETECTED; + } + break; + case PDELAY_RESP_FOLLOW_UP: + break; + case ANNOUNCE: + if (tc_forward(p, msg)) { + event = EV_FAULT_DETECTED; + break; + } + if (process_announce(p, dup)) { + event = EV_STATE_DECISION_EVENT; + } + break; + case SIGNALING: + case MANAGEMENT: + if (tc_forward(p, msg)) { + event = EV_FAULT_DETECTED; + } + break; + } + + msg_put(msg); + msg_put(dup); + + return event; +} diff --git a/makefile b/makefile index c5c0cf8..fafacbe 100644 --- a/makefile +++ b/makefile @@ -23,7 +23,7 @@ VER = -DVER=$(version) CFLAGS = -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS) LDLIBS = -lm -lrt $(EXTRA_LDFLAGS) PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster -OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o fault.o \ +OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o e2e_tc.o fault.o \ filter.o fsm.o hash.o linreg.o mave.o mmedian.o msg.o ntpshm.o nullf.o phc.o \ pi.o port.o print.o ptp4l.o p2p_tc.o raw.o rtnl.o servo.o sk.o stats.o tc.o \ telecom.o tlv.o transport.o tsproc.o udp.o udp6.o uds.o util.o version.o diff --git a/port.c b/port.c index efb0a09..e83f44a 100644 --- a/port.c +++ b/port.c @@ -2791,6 +2791,9 @@ struct port *port_open(int phc_index, p->event = p2p_event; break; case CLOCK_TYPE_E2E: + p->dispatch = e2e_dispatch; + p->event = e2e_event; + break; case CLOCK_TYPE_MANAGEMENT: return NULL; } @@ -2852,6 +2855,10 @@ struct port *port_open(int phc_index, pr_err("port %d: P2P TC needs P2P ports", number); goto err_port; } + if (number && type == CLOCK_TYPE_E2E && p->delayMechanism != DM_E2E) { + pr_err("port %d: E2E TC needs E2E ports", number); + goto err_port; + } if (p->hybrid_e2e && p->delayMechanism != DM_E2E) { pr_warning("port %d: hybrid_e2e only works with E2E", number); } diff --git a/port_private.h b/port_private.h index c6da299..a4fe5bd 100644 --- a/port_private.h +++ b/port_private.h @@ -136,6 +136,9 @@ struct port { #define portnum(p) (p->portIdentity.portNumber) +void e2e_dispatch(struct port *p, enum fsm_event event, int mdiff); +enum fsm_event e2e_event(struct port *p, int fd_index); + void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff); enum fsm_event p2p_event(struct port *p, int fd_index); -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-04-21 05:22:35
|
This patch adds a new configuation option that enables transparent clock mode. When TC mode is specified, the configured delay mechanism must also match. The legacy behavior of upgrading a multi-port clock from OC to BC is preserved. Signed-off-by: Richard Cochran <ric...@gm...> --- config.c | 10 ++++++++++ default.cfg | 1 + gPTP.cfg | 1 + ptp4l.8 | 15 +++++++++++---- ptp4l.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 64 insertions(+), 7 deletions(-) diff --git a/config.c b/config.c index 8011e57..e751d40 100644 --- a/config.c +++ b/config.c @@ -24,6 +24,7 @@ #include <string.h> #include "bmc.h" +#include "clock.h" #include "config.h" #include "ether.h" #include "hash.h" @@ -135,6 +136,14 @@ static struct config_enum clock_servo_enu[] = { { NULL, 0 }, }; +static struct config_enum clock_type_enu[] = { + { "OC", CLOCK_TYPE_ORDINARY }, + { "BC", CLOCK_TYPE_BOUNDARY }, + { "P2P_TC", CLOCK_TYPE_P2P }, + { "E2E_TC", CLOCK_TYPE_E2E }, + { NULL, 0 }, +}; + static struct config_enum dataset_comp_enu[] = { { "ieee1588", DS_CMP_IEEE1588 }, { "G.8275.x", DS_CMP_G8275 }, @@ -186,6 +195,7 @@ struct config_item config_tab[] = { GLOB_ITEM_INT("clockAccuracy", 0xfe, 0, UINT8_MAX), GLOB_ITEM_INT("clockClass", 248, 0, UINT8_MAX), GLOB_ITEM_ENU("clock_servo", CLOCK_SERVO_PI, clock_servo_enu), + GLOB_ITEM_ENU("clock_type", CLOCK_TYPE_ORDINARY, clock_type_enu), GLOB_ITEM_ENU("dataset_comparison", DS_CMP_IEEE1588, dataset_comp_enu), PORT_ITEM_INT("delayAsymmetry", 0, INT_MIN, INT_MAX), PORT_ITEM_ENU("delay_filter", FILTER_MOVING_MEDIAN, delay_filter_enu), diff --git a/default.cfg b/default.cfg index 5773ccb..142675d 100644 --- a/default.cfg +++ b/default.cfg @@ -75,6 +75,7 @@ uds_address /var/run/ptp4l # # Default interface options # +clock_type OC network_transport UDPv4 delay_mechanism E2E time_stamping hardware diff --git a/gPTP.cfg b/gPTP.cfg index 9bbaddd..83db359 100644 --- a/gPTP.cfg +++ b/gPTP.cfg @@ -71,6 +71,7 @@ uds_address /var/run/ptp4l # # Default interface options # +clock_type OC network_transport L2 delay_mechanism P2P time_stamping hardware diff --git a/ptp4l.8 b/ptp4l.8 index 0b17f10..83549a8 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -1,6 +1,6 @@ -.TH PTP4l 8 "Novemver 2017" "linuxptp" +.TH PTP4l 8 "March 2018" "linuxptp" .SH NAME -ptp4l - PTP Boundary/Ordinary Clock +ptp4l - PTP Boundary/Ordinary/Transparent Clock .SH SYNOPSIS .B ptp4l @@ -23,8 +23,8 @@ ptp4l - PTP Boundary/Ordinary Clock .SH DESCRIPTION .B ptp4l is an implementation of the Precision Time Protocol (PTP) according to IEEE -standard 1588 for Linux. It implements Boundary Clock (BC) and Ordinary Clock -(OC). +standard 1588 for Linux. It implements Boundary Clock (BC), Ordinary Clock +(OC), and Transparent Clock (TC). .SH OPTIONS .TP @@ -425,6 +425,13 @@ number is set to the domain number), and "nullf" for a servo that always dials frequency offset zero (for use in SyncE nodes). The default is "pi." .TP +.B clock_type +Specifies the kind of PTP clock. Valid values are "OC" for ordinary +clock, "BC" for boundary clock, "P2P_TC" for peer to peer transparent +clock, and "E2E_TC" for end to end transparent clock. An multi-port +ordinary clock will automatically be configured as a boundary clock. +The default is "OC". +.TP .B pi_proportional_const The proportional constant of the PI controller. When set to 0.0, the proportional constant will be set by the following formula from the current diff --git a/ptp4l.c b/ptp4l.c index ee31718..9ef8169 100644 --- a/ptp4l.c +++ b/ptp4l.c @@ -1,6 +1,6 @@ /** * @file ptp4l.c - * @brief PTP Boundary Clock main program + * @brief PTP Boundary Clock or Transparent Clock main program * @note Copyright (C) 2011 Richard Cochran <ric...@gm...> * * This program is free software; you can redistribute it and/or modify @@ -59,6 +59,7 @@ static void usage(char *progname) " -p [dev] PTP hardware clock device to use, default auto\n" " (ignored for SOFTWARE/LEGACY HW time stamping)\n" " -s slave only mode (overrides configuration file)\n" + " -t transparent clock\n" " -l [num] set the logging level to 'num'\n" " -m print messages to stdout\n" " -q do not print messages to the syslog\n" @@ -71,6 +72,7 @@ static void usage(char *progname) int main(int argc, char *argv[]) { char *config = NULL, *req_phc = NULL, *progname; + enum clock_type type = CLOCK_TYPE_ORDINARY; int c, err = -1, index, print_level; struct clock *clock = NULL; struct option *opts; @@ -201,8 +203,44 @@ int main(int argc, char *argv[]) goto out; } - clock = clock_create(cfg->n_interfaces > 1 ? CLOCK_TYPE_BOUNDARY : - CLOCK_TYPE_ORDINARY, cfg, req_phc); + type = config_get_int(cfg, NULL, "clock_type"); + switch (type) { + case CLOCK_TYPE_ORDINARY: + if (cfg->n_interfaces > 1) { + type = CLOCK_TYPE_BOUNDARY; + } + break; + case CLOCK_TYPE_BOUNDARY: + if (cfg->n_interfaces < 2) { + fprintf(stderr, "BC needs at least two interfaces\n"); + goto out; + } + break; + case CLOCK_TYPE_P2P: + if (cfg->n_interfaces < 2) { + fprintf(stderr, "TC needs at least two interfaces\n"); + goto out; + } + if (DM_P2P != config_get_int(cfg, NULL, "delay_mechanism")) { + fprintf(stderr, "P2P_TC needs P2P delay mechanism\n"); + goto out; + } + break; + case CLOCK_TYPE_E2E: + if (cfg->n_interfaces < 2) { + fprintf(stderr, "TC needs at least two interfaces\n"); + goto out; + } + if (DM_E2E != config_get_int(cfg, NULL, "delay_mechanism")) { + fprintf(stderr, "E2E_TC needs E2E delay mechanism\n"); + goto out; + } + break; + case CLOCK_TYPE_MANAGEMENT: + goto out; + } + + clock = clock_create(type, cfg, req_phc); if (!clock) { fprintf(stderr, "failed to create a clock\n"); goto out; -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-04-21 05:22:37
|
According to 1588, PTP message loops are simply someone else's problem with respect to transparent clocks. Since we are running the BMCA for syntonization anyway, we might as well go ahead and implement the spanning tree for PTP messages. Signed-off-by: Richard Cochran <ric...@gm...> --- config.c | 1 + default.cfg | 1 + port.c | 1 + ptp4l.8 | 6 ++++++ 4 files changed, 9 insertions(+) diff --git a/config.c b/config.c index e751d40..748529d 100644 --- a/config.c +++ b/config.c @@ -254,6 +254,7 @@ struct config_item config_tab[] = { GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX), GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX), PORT_ITEM_INT("syncReceiptTimeout", 0, 0, UINT8_MAX), + GLOB_ITEM_INT("tc_spanning_tree", 0, 1, 1), GLOB_ITEM_INT("timeSource", INTERNAL_OSCILLATOR, 0x10, 0xfe), GLOB_ITEM_ENU("time_stamping", TS_HARDWARE, timestamping_enu), PORT_ITEM_INT("transportSpecific", 0, 0, 0x0F), diff --git a/default.cfg b/default.cfg index 142675d..456a995 100644 --- a/default.cfg +++ b/default.cfg @@ -40,6 +40,7 @@ path_trace_enabled 0 follow_up_info 0 hybrid_e2e 0 net_sync_monitor 0 +tc_spanning_tree 0 tx_timestamp_timeout 1 use_syslog 1 verbose 0 diff --git a/port.c b/port.c index e83f44a..23c1f13 100644 --- a/port.c +++ b/port.c @@ -2834,6 +2834,7 @@ struct port *port_open(int phc_index, p->hybrid_e2e = config_get_int(cfg, p->name, "hybrid_e2e"); p->net_sync_monitor = config_get_int(cfg, p->name, "net_sync_monitor"); p->path_trace_enabled = config_get_int(cfg, p->name, "path_trace_enabled"); + p->tc_spanning_tree = config_get_int(cfg, p->name, "tc_spanning_tree"); p->rx_timestamp_offset = config_get_int(cfg, p->name, "ingressLatency"); p->rx_timestamp_offset <<= 16; p->tx_timestamp_offset = config_get_int(cfg, p->name, "egressLatency"); diff --git a/ptp4l.8 b/ptp4l.8 index 83549a8..0a35db2 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -399,6 +399,12 @@ Treat one-step responses as two-step if enabled. It is used to work around buggy 802.1AS switches. The default is 0 (disabled). .TP +.B tc_spanning_tree +When running as a Transparent Clock, increment the "stepsRemoved" +field of Announce messages that pass through the switch. Enabling +this option ensures that PTP message loops never form, provided the +switches all implement this option together with the BMCA. +.TP .B tx_timestamp_timeout The number of milliseconds to poll waiting for the tx time stamp from the kernel when a message has recently been sent. -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-04-21 05:22:37
|
Signed-off-by: Richard Cochran <ric...@gm...> --- P2P-TC.cfg | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 P2P-TC.cfg diff --git a/P2P-TC.cfg b/P2P-TC.cfg new file mode 100644 index 0000000..94029bb --- /dev/null +++ b/P2P-TC.cfg @@ -0,0 +1,92 @@ +[global] +# +# Default Data Set +# +twoStepFlag 1 +slaveOnly 0 +priority1 254 +priority2 128 +domainNumber 0 +#utc_offset 37 +clockClass 248 +clockAccuracy 0xFE +offsetScaledLogVariance 0xFFFF +free_running 1 +freq_est_interval 3 +dscp_event 0 +dscp_general 0 +# +# Port Data Set +# +logAnnounceInterval 1 +logSyncInterval 0 +logMinDelayReqInterval 0 +logMinPdelayReqInterval 0 +announceReceiptTimeout 3 +syncReceiptTimeout 0 +delayAsymmetry 0 +fault_reset_interval 4 +neighborPropDelayThresh 20000000 +# +# Run time options +# +assume_two_step 0 +logging_level 6 +path_trace_enabled 0 +follow_up_info 0 +hybrid_e2e 0 +net_sync_monitor 0 +tc_spanning_tree 1 +tx_timestamp_timeout 1 +use_syslog 1 +verbose 0 +summary_interval 1 +kernel_leap 1 +check_fup_sync 0 +# +# Servo Options +# +pi_proportional_const 0.0 +pi_integral_const 0.0 +pi_proportional_scale 0.0 +pi_proportional_exponent -0.3 +pi_proportional_norm_max 0.7 +pi_integral_scale 0.0 +pi_integral_exponent 0.4 +pi_integral_norm_max 0.3 +step_threshold 0.0 +first_step_threshold 0.00002 +max_frequency 900000000 +clock_servo pi +sanity_freq_limit 200000000 +ntpshm_segment 0 +# +# Transport options +# +transportSpecific 0x0 +ptp_dst_mac 01:1B:19:00:00:00 +p2p_dst_mac 01:80:C2:00:00:0E +udp_ttl 1 +udp6_scope 0x0E +uds_address /var/run/ptp4l +# +# Default interface options +# +clock_type P2P_TC +network_transport L2 +delay_mechanism P2P +time_stamping hardware +tsproc_mode filter +delay_filter moving_median +delay_filter_length 10 +egressLatency 0 +ingressLatency 0 +boundary_clock_jbod 0 +# +# Clock description +# +productDescription ;; +revisionData ;; +manufacturerIdentity 00:00:00 +userDescription ; +timeSource 0xA0 -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-04-21 05:22:38
|
Signed-off-by: Richard Cochran <ric...@gm...> --- E2E-TC.cfg | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 E2E-TC.cfg diff --git a/E2E-TC.cfg b/E2E-TC.cfg new file mode 100644 index 0000000..b87a07f --- /dev/null +++ b/E2E-TC.cfg @@ -0,0 +1,92 @@ +[global] +# +# Default Data Set +# +twoStepFlag 1 +slaveOnly 0 +priority1 254 +priority2 128 +domainNumber 0 +#utc_offset 37 +clockClass 248 +clockAccuracy 0xFE +offsetScaledLogVariance 0xFFFF +free_running 1 +freq_est_interval 3 +dscp_event 0 +dscp_general 0 +# +# Port Data Set +# +logAnnounceInterval 1 +logSyncInterval 0 +logMinDelayReqInterval 0 +logMinPdelayReqInterval 0 +announceReceiptTimeout 3 +syncReceiptTimeout 0 +delayAsymmetry 0 +fault_reset_interval 4 +neighborPropDelayThresh 20000000 +# +# Run time options +# +assume_two_step 0 +logging_level 6 +path_trace_enabled 0 +follow_up_info 0 +hybrid_e2e 0 +net_sync_monitor 0 +tc_spanning_tree 1 +tx_timestamp_timeout 1 +use_syslog 1 +verbose 0 +summary_interval 1 +kernel_leap 1 +check_fup_sync 0 +# +# Servo Options +# +pi_proportional_const 0.0 +pi_integral_const 0.0 +pi_proportional_scale 0.0 +pi_proportional_exponent -0.3 +pi_proportional_norm_max 0.7 +pi_integral_scale 0.0 +pi_integral_exponent 0.4 +pi_integral_norm_max 0.3 +step_threshold 0.0 +first_step_threshold 0.00002 +max_frequency 900000000 +clock_servo pi +sanity_freq_limit 200000000 +ntpshm_segment 0 +# +# Transport options +# +transportSpecific 0x0 +ptp_dst_mac 01:1B:19:00:00:00 +p2p_dst_mac 01:80:C2:00:00:0E +udp_ttl 1 +udp6_scope 0x0E +uds_address /var/run/ptp4l +# +# Default interface options +# +clock_type E2E_TC +network_transport L2 +delay_mechanism E2E +time_stamping hardware +tsproc_mode filter +delay_filter moving_median +delay_filter_length 10 +egressLatency 0 +ingressLatency 0 +boundary_clock_jbod 0 +# +# Clock description +# +productDescription ;; +revisionData ;; +manufacturerIdentity 00:00:00 +userDescription ; +timeSource 0xA0 -- 2.11.0 |
From: Keller, J. E <jac...@in...> - 2018-04-23 17:06:57
|
> -----Original Message----- > From: Richard Cochran [mailto:ric...@gm...] > Sent: Friday, April 20, 2018 10:22 PM > To: lin...@li... > Subject: [Linuxptp-devel] [PATCH V2 6/8] config: Add a configuration option for > preventing loops in TC mode. > > According to 1588, PTP message loops are simply someone else's problem > with respect to transparent clocks. Since we are running the BMCA for > syntonization anyway, we might as well go ahead and implement the spanning > tree for PTP messages. > Do we already get this implementation for free? It seems weird to see that we can set the config option, but I don't see how we actually implement it in this patch? We set the tc_spanning_tree port variable? Is it actually implemented in the patches to add the two TC implementations? Thanks, Jake > Signed-off-by: Richard Cochran <ric...@gm...> > --- > config.c | 1 + > default.cfg | 1 + > port.c | 1 + > ptp4l.8 | 6 ++++++ > 4 files changed, 9 insertions(+) > > diff --git a/config.c b/config.c > index e751d40..748529d 100644 > --- a/config.c > +++ b/config.c > @@ -254,6 +254,7 @@ struct config_item config_tab[] = { > GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX), > GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX), > PORT_ITEM_INT("syncReceiptTimeout", 0, 0, UINT8_MAX), > + GLOB_ITEM_INT("tc_spanning_tree", 0, 1, 1), > GLOB_ITEM_INT("timeSource", INTERNAL_OSCILLATOR, 0x10, 0xfe), > GLOB_ITEM_ENU("time_stamping", TS_HARDWARE, > timestamping_enu), > PORT_ITEM_INT("transportSpecific", 0, 0, 0x0F), > diff --git a/default.cfg b/default.cfg > index 142675d..456a995 100644 > --- a/default.cfg > +++ b/default.cfg > @@ -40,6 +40,7 @@ path_trace_enabled 0 > follow_up_info 0 > hybrid_e2e 0 > net_sync_monitor 0 > +tc_spanning_tree 0 > tx_timestamp_timeout 1 > use_syslog 1 > verbose 0 > diff --git a/port.c b/port.c > index e83f44a..23c1f13 100644 > --- a/port.c > +++ b/port.c > @@ -2834,6 +2834,7 @@ struct port *port_open(int phc_index, > p->hybrid_e2e = config_get_int(cfg, p->name, "hybrid_e2e"); > p->net_sync_monitor = config_get_int(cfg, p->name, > "net_sync_monitor"); > p->path_trace_enabled = config_get_int(cfg, p->name, > "path_trace_enabled"); > + p->tc_spanning_tree = config_get_int(cfg, p->name, > "tc_spanning_tree"); > p->rx_timestamp_offset = config_get_int(cfg, p->name, > "ingressLatency"); > p->rx_timestamp_offset <<= 16; > p->tx_timestamp_offset = config_get_int(cfg, p->name, > "egressLatency"); > diff --git a/ptp4l.8 b/ptp4l.8 > index 83549a8..0a35db2 100644 > --- a/ptp4l.8 > +++ b/ptp4l.8 > @@ -399,6 +399,12 @@ Treat one-step responses as two-step if enabled. It is > used to work around > buggy 802.1AS switches. > The default is 0 (disabled). > .TP > +.B tc_spanning_tree > +When running as a Transparent Clock, increment the "stepsRemoved" > +field of Announce messages that pass through the switch. Enabling > +this option ensures that PTP message loops never form, provided the > +switches all implement this option together with the BMCA. > +.TP > .B tx_timestamp_timeout > The number of milliseconds to poll waiting for the tx time stamp from the kernel > when a message has recently been sent. > -- > 2.11.0 > > > ------------------------------------------------------------------------------ > Check out the vibrant tech community on one of the world's most > engaging tech sites, Slashdot.org! http://sdm.link/slashdot > _______________________________________________ > Linuxptp-devel mailing list > Lin...@li... > https://lists.sourceforge.net/lists/listinfo/linuxptp-devel |
From: Richard C. <ric...@gm...> - 2018-04-25 05:22:36
|
On Mon, Apr 23, 2018 at 05:06:34PM +0000, Keller, Jacob E wrote: > Do we already get this implementation for free? It seems weird to > see that we can set the config option, but I don't see how we > actually implement it in this patch? We set the tc_spanning_tree > port variable? Is it actually implemented in the patches to add the > two TC implementations? I am not sure what you are asking. The variable was added in patch #2. It is used in the generic TC code to help decide whether to forward a frame or not. If enabled, the states of the ingress and egress ports are considered. The BMCA logic which forms the spanning tree is "for free" in the sense that we can re-use the existing logic. The p2p and e2e TC modules use the duplicated messages to run the BMCA state machine. Thanks, Richard |
From: Keller, J. E <jac...@in...> - 2018-04-25 20:08:08
|
> -----Original Message----- > From: Richard Cochran [mailto:ric...@gm...] > Sent: Tuesday, April 24, 2018 10:22 PM > To: Keller, Jacob E <jac...@in...> > Cc: lin...@li... > Subject: Re: [Linuxptp-devel] [PATCH V2 6/8] config: Add a configuration option > for preventing loops in TC mode. > > On Mon, Apr 23, 2018 at 05:06:34PM +0000, Keller, Jacob E wrote: > > > Do we already get this implementation for free? It seems weird to > > see that we can set the config option, but I don't see how we > > actually implement it in this patch? We set the tc_spanning_tree > > port variable? Is it actually implemented in the patches to add the > > two TC implementations? > > I am not sure what you are asking. > I was expecting there to be some code which uses this variable, but I probably just missed it in the generic TC code. Thanks, Jake |
From: Richard C. <ric...@gm...> - 2018-04-25 05:24:30
|
On Fri, Apr 20, 2018 at 10:22:13PM -0700, Richard Cochran wrote: > Changed in V2: > ~~~~~~~~~~~~~~ > > - Detect and drop frames that arrive with time stamps. s/with/without/ Thanks, Richard |