This is an immature implementation of non-GM 802.1AS-2011 time-aware bridge which is based on P2P TC. - Add BRIDGE clock type. - Create bridge.c which is a copy of p2p_tc.c. - Implement tc_fwd_folup_tlv() for bridge instead of tc_fwd_folup() used for p2p_tc. - Create time-aware-bridge.cfg.
Signed-off-by: Yangbo Lu <yangbo...@nxp.com> --- bridge.c | 231 ++++++++++++++++++++++++++++++++++++++++++ clock.c | 1 + clock.h | 1 + config.c | 1 + configs/time-aware-bridge.cfg | 38 +++++++ makefile | 2 +- msg.c | 7 +- port.c | 6 +- port.h | 1 + port_private.h | 3 + tc.c | 108 ++++++++++++++++++++ tc.h | 2 + 12 files changed, 398 insertions(+), 3 deletions(-) create mode 100644 bridge.c create mode 100644 configs/time-aware-bridge.cfg diff --git a/bridge.c b/bridge.c new file mode 100644 index 0000000..89be2b9 --- /dev/null +++ b/bridge.c @@ -0,0 +1,231 @@ +/** + * @file bridge.c + * @note Copyright (C) 2018 Richard Cochran <richardcoch...@gmail.com> + * + * 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 bridge_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 bridge_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 bridge_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 bridge_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: + case FD_UNICAST_REQ_TIMER: + case FD_UNICAST_SRV_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_unicast(msg)) { + 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_tlv(p, msg, cnt)) { + 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/clock.c b/clock.c index 146576a..4e6fc79 100644 --- a/clock.c +++ b/clock.c @@ -878,6 +878,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, case CLOCK_TYPE_BOUNDARY: case CLOCK_TYPE_P2P: case CLOCK_TYPE_E2E: + case CLOCK_TYPE_BRIDGE: c->type = type; break; case CLOCK_TYPE_MANAGEMENT: diff --git a/clock.h b/clock.h index 9d3133a..646aed7 100644 --- a/clock.h +++ b/clock.h @@ -40,6 +40,7 @@ enum clock_type { CLOCK_TYPE_P2P = 0x2000, CLOCK_TYPE_E2E = 0x1000, CLOCK_TYPE_MANAGEMENT = 0x0800, + CLOCK_TYPE_BRIDGE = 0x0400, }; /** diff --git a/config.c b/config.c index 12eb1f9..c335b02 100644 --- a/config.c +++ b/config.c @@ -143,6 +143,7 @@ static struct config_enum clock_type_enu[] = { { "BC", CLOCK_TYPE_BOUNDARY }, { "P2P_TC", CLOCK_TYPE_P2P }, { "E2E_TC", CLOCK_TYPE_E2E }, + { "BRIDGE", CLOCK_TYPE_BRIDGE }, { NULL, 0 }, }; diff --git a/configs/time-aware-bridge.cfg b/configs/time-aware-bridge.cfg new file mode 100644 index 0000000..2517c6d --- /dev/null +++ b/configs/time-aware-bridge.cfg @@ -0,0 +1,38 @@ +# +# IEEE 802.1AS-2011 time-aware bridge example configuration containing +# those attributes which differ from the defaults. See the file, +# default.cfg, for the complete list of available options. +# +[global] +# +# Default Data Set +# +priority1 255 +priority2 248 +free_running 0 +# +# Port Data Set +# +neighborPropDelayThresh 800 +min_neighbor_prop_delay -20000000 +syncReceiptTimeout 3 +logSyncInterval -3 +# +# Run time options +# +assume_two_step 1 +follow_up_info 1 +summary_interval -3 +path_trace_enabled 1 +tc_spanning_tree 1 +# +# Transport options +# +transportSpecific 0x1 +ptp_dst_mac 01:80:C2:00:00:0E +# +# Default interface options +# +clock_type BRIDGE +network_transport L2 +delay_mechanism P2P diff --git a/makefile b/makefile index 3397d3e..9bbac7a 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 designated_fsm.o \ +OBJ = bmc.o bridge.o clock.o clockadj.o clockcheck.o config.o designated_fsm.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 port_signaling.o pqueue.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 \ diff --git a/msg.c b/msg.c index d1619d4..ecabc28 100644 --- a/msg.c +++ b/msg.c @@ -240,9 +240,14 @@ static void timestamp_post_recv(struct ptp_message *m, struct Timestamp *ts) { uint32_t lsb = ntohl(ts->seconds_lsb); uint16_t msb = ntohs(ts->seconds_msb); + uint32_t nsec = ntohl(ts->nanoseconds); m->ts.pdu.sec = ((uint64_t)lsb) | (((uint64_t)msb) << 32); - m->ts.pdu.nsec = ntohl(ts->nanoseconds); + m->ts.pdu.nsec = nsec; + + ts->seconds_lsb = lsb; + ts->seconds_msb = msb; + ts->nanoseconds = nsec; } static void timestamp_pre_send(struct Timestamp *ts) diff --git a/port.c b/port.c index 07ad3f0..413611d 100644 --- a/port.c +++ b/port.c @@ -474,7 +474,7 @@ static int net_sync_resp_append(struct port *p, struct ptp_message *m) return 0; } -static struct follow_up_info_tlv *follow_up_info_extract(struct ptp_message *m) +struct follow_up_info_tlv *follow_up_info_extract(struct ptp_message *m) { struct follow_up_info_tlv *f; struct tlv_extra *extra; @@ -2969,6 +2969,10 @@ struct port *port_open(int phc_index, p->dispatch = e2e_dispatch; p->event = e2e_event; break; + case CLOCK_TYPE_BRIDGE: + p->dispatch = bridge_dispatch; + p->event = bridge_event; + break; case CLOCK_TYPE_MANAGEMENT: goto err_port; } diff --git a/port.h b/port.h index aa3b1ec..61ace36 100644 --- a/port.h +++ b/port.h @@ -335,4 +335,5 @@ enum bmca_select port_bmca(struct port *p); */ void tc_cleanup(void); +struct follow_up_info_tlv *follow_up_info_extract(struct ptp_message *m); #endif diff --git a/port_private.h b/port_private.h index 5789fbb..a39bfdb 100644 --- a/port_private.h +++ b/port_private.h @@ -159,6 +159,9 @@ 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); +void bridge_dispatch(struct port *p, enum fsm_event event, int mdiff); +enum fsm_event bridge_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 fb46603..e1e8dd5 100644 --- a/tc.c +++ b/tc.c @@ -23,6 +23,8 @@ #include "tc.h" #include "tmv.h" +#define POW2_41 ((double)(1ULL << 41)) + enum tc_match { TC_MISMATCH, TC_SYNC_FUP, @@ -235,6 +237,89 @@ static void tc_complete_syfup(struct port *q, struct port *p, tc_recycle(txd); } +static void tc_send_folup_tlv(struct port *q, struct port *p, + struct ptp_message *fup_dup, + tmv_t residence) +{ + struct follow_up_info_tlv *fui; + Integer64 c1, c2; + double gm_rr, nrr; + int count; + + fui = follow_up_info_extract(fup_dup); + if (!fui) { + pr_err("tc failed to extract folup_tlv!"); + return; + } + + nrr = q->nrate.ratio; + gm_rr = 1.0 + (fui->cumulativeScaledRateOffset + 0.0) / POW2_41; + + /* Convert residence/peer_delay to grand master time */ + c1 = fup_dup->header.correction; + c2 = c1 + tmv_to_TimeInterval(residence) * gm_rr * nrr; + c2 += tmv_to_TimeInterval(q->peer_delay) * gm_rr; + c2 += q->asymmetry; + fup_dup->header.correction = c2; + + /* Accumulate neighbor rate ratio */ + fui->cumulativeScaledRateOffset = (gm_rr * nrr - 1.0) * POW2_41; + + if (msg_pre_send(fup_dup)) { + pr_err("tc failed to prepare folup_tlv on port %d", portnum(p)); + } + + count = transport_send(p->trp, &p->fda, TRANS_GENERAL, fup_dup); + if (count <= 0) { + pr_err("tc failed to forward follow up on port %d", portnum(p)); + port_dispatch(p, EV_FAULT_DETECTED, 0); + } +} + +static void tc_complete_folup_tlv(struct port *q, struct port *p, + struct ptp_message *msg, + struct ptp_message *dup) +{ + enum tc_match type = TC_MISMATCH; + struct tc_txd *txd; + + 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: + break; + default: + pr_err("tc: unexpected match in tc_complete_folup_tlv!"); + 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 = tmv_zero(); + txd->ingress_port = portnum(q); + TAILQ_INSERT_TAIL(&p->tc_transmitted, txd, list); + return; + } + + tc_send_folup_tlv(q, p, dup, txd->residence); + + 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) { @@ -420,6 +505,29 @@ int tc_fwd_folup(struct port *q, struct ptp_message *msg) return 0; } +int tc_fwd_folup_tlv(struct port *q, struct ptp_message *msg, int cnt) +{ + struct port *p; + + clock_gettime(CLOCK_MONOTONIC, &msg->ts.host); + + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + struct ptp_message *dup; + + if (tc_blocked(q, p, msg)) { + continue; + } + + dup = msg_duplicate(msg, cnt); + if (!dup) + return EV_NONE; + + tc_complete_folup_tlv(q, p, msg, dup); + msg_put(dup); + } + return 0; +} + int tc_fwd_request(struct port *q, struct ptp_message *msg) { return tc_fwd_event(q, msg); diff --git a/tc.h b/tc.h index ef82351..83dcf70 100644 --- a/tc.h +++ b/tc.h @@ -49,6 +49,8 @@ int tc_forward(struct port *q, struct ptp_message *msg); */ int tc_fwd_folup(struct port *q, struct ptp_message *msg); +int tc_fwd_folup_tlv(struct port *q, struct ptp_message *msg, int cnt); + /** * Forwards a given delay request message out all other ports. * -- 2.7.4 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel