Signed-off-by: Richard Cochran <richardcoch...@gmail.com> --- clock.c | 1 + makefile | 2 +- port.c | 3 + port.h | 5 ++ port_private.h | 9 ++ tc.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tc.h | 36 ++++++++ 7 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 tc.c create mode 100644 tc.h
diff --git a/clock.c b/clock.c index da15882..0ec437f 100644 --- a/clock.c +++ b/clock.c @@ -284,6 +284,7 @@ void clock_destroy(struct clock *c) clockcheck_destroy(c->sanity_check); 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 f898336..83e4787 100644 --- a/makefile +++ b/makefile @@ -25,7 +25,7 @@ LDLIBS = -lm -lrt $(EXTRA_LDFLAGS) PRG = ptp4l pmc phc2sys hwstamp_ctl phc_ctl 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 tlv.o \ + pi.o port.o print.o ptp4l.o raw.o rtnl.o servo.o sk.o stats.o tc.o tlv.o \ transport.o tsproc.o udp.o udp6.o uds.o util.o version.o OBJECTS = $(OBJ) hwstamp_ctl.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \ diff --git a/port.c b/port.c index eba46a0..0465d95 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" @@ -1367,6 +1368,7 @@ void port_disable(struct port *p) { int i; + tc_flush(p); flush_last_sync(p); flush_delay_req(p); flush_peer_delay(p); @@ -2525,6 +2527,7 @@ struct port *port_open(int phc_index, return NULL; 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 60fd0a4..5bf3f6a 100644 --- a/port.h +++ b/port.h @@ -313,4 +313,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 dc69df0..bcf6f02 100644 --- a/port_private.h +++ b/port_private.h @@ -43,6 +43,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; @@ -109,6 +116,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..5939fb8 --- /dev/null +++ b/tc.c @@ -0,0 +1,265 @@ +/** + * @file tc.c + * @note Copyright (C) 2015 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-1301 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, +}; + +static TAILQ_HEAD(tc_pool, tc_txd) tc_pool = TAILQ_HEAD_INITIALIZER(tc_pool); + +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 *p) +{ + if (portnum(p) == 0) { + return 1; + } + enum port_state s = port_state(p); + switch (s) { + case PS_INITIALIZING: + case PS_FAULTY: + case PS_DISABLED: + case PS_PASSIVE: + break; + case PS_LISTENING: + case PS_PRE_MASTER: + case PS_MASTER: + case PS_UNCALIBRATED: + case PS_SLAVE: + case PS_GRAND_MASTER: + return 0; + } + return 1; +} + +static int tc_current(struct ptp_message *m, struct timespec now) +{ + int64_t t1, t2, 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 void tc_free(struct tc_txd *txd) +{ + TAILQ_INSERT_HEAD(&tc_pool, txd, list); +} + +static int tc_match(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_complete(struct port *q, struct port *p, struct ptp_message *msg, + tmv_t residence) +{ + struct ptp_message *fup; + struct tc_txd *txd; + enum tc_match type = TC_MISMATCH; + Integer64 c1, c2; + int cnt; + + TAILQ_FOREACH(txd, &p->tc_transmitted, list) { + type = tc_match(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; + } + if (type != TC_MISMATCH) { + break; + } + } + + if (type == TC_MISMATCH) { + txd = tc_allocate(); + if (!txd) { + pr_err("low memory, TC failed to forward event"); + port_dispatch(p, EV_FAULT_DETECTED, 0); + return; + } + msg_get(msg); + txd->msg = msg; + txd->residence = residence; + txd->ingress_port = port_number(q); + TAILQ_INSERT_TAIL(&p->tc_transmitted, txd, list); + return; + } + + c1 = net2host64(fup->header.correction); + c2 = c1 + tmv_to_TimeInterval(residence); + fup->header.correction = host2net64(c2); + cnt = transport_send(p->trp, &p->fda, 0, fup); + if (cnt <= 0) { + port_dispatch(p, EV_FAULT_DETECTED, 0); + } + TAILQ_REMOVE(&p->tc_transmitted, txd, list); + msg_put(txd->msg); + tc_free(txd); +} + +/* 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_free(txd); + } +} + +int tc_forward(struct port *q, struct ptp_message *msg) +{ + struct port *p; + int cnt; + + if (msg_pre_send(msg)) { + return -1; + } + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (p == q || tc_blocked(p)) { + continue; + } + cnt = transport_send(p->trp, &p->fda, 0, msg); + if (cnt <= 0) { + /* egress port is faulty. */ + port_dispatch(p, EV_FAULT_DETECTED, 0); + } + } + return 0; +} + +int tc_fwd_event(struct port *q, struct ptp_message *msg) +{ + tmv_t egress, ingress = timespec_to_tmv(msg->hwts.ts), residence; + struct port *p; + int cnt; + + clock_gettime(CLOCK_MONOTONIC, &msg->ts.host); + if (msg_pre_send(msg)) { + return -1; + } + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (p == q || tc_blocked(p)) { + continue; + } + cnt = transport_send(p->trp, &p->fda, 1, msg); + if (cnt <= 0 || !msg_sots_valid(msg)) { + port_dispatch(p, EV_FAULT_DETECTED, 0); + continue; + } + ts_add(&msg->hwts.ts, p->tx_timestamp_offset); + egress = timespec_to_tmv(msg->hwts.ts); + residence = tmv_sub(egress, ingress); + tc_complete(q, p, msg, residence); + } + return 0; +} + +int tc_fwd_folup(struct port *q, struct ptp_message *msg) +{ + struct Timestamp *ts = &msg->follow_up.preciseOriginTimestamp; + struct port *p; + + clock_gettime(CLOCK_MONOTONIC, &msg->ts.host); + + ts->seconds_lsb = msg->ts.pdu.sec & 0xFFFFFFFF; + ts->seconds_msb = msg->ts.pdu.sec >> 32; + ts->nanoseconds = msg->ts.pdu.nsec; + + if (msg_pre_send(msg)) { + return -1; + } + for (p = clock_first_port(q->clock); p; p = LIST_NEXT(p, list)) { + if (p == q || tc_blocked(p)) { + continue; + } + tc_complete(q, p, msg, tmv_zero()); + } + return 0; +} + +void tc_prune(struct port *q) +{ + struct tc_txd *txd; + struct timespec now; + + 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_free(txd); + } +} diff --git a/tc.h b/tc.h new file mode 100644 index 0000000..2463d87 --- /dev/null +++ b/tc.h @@ -0,0 +1,36 @@ +/** + * @file tc.h + * @brief Implements a Transparent Clock. + * @note Copyright (C) 2015 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-1301 USA. + */ +#ifndef HAVE_TC_H +#define HAVE_TC_H + +#include "msg.h" +#include "port_private.h" + +void tc_flush(struct port *q); + +int tc_forward(struct port *q, struct ptp_message *msg); + +int tc_fwd_event(struct port *q, struct ptp_message *msg); + +int tc_fwd_folup(struct port *q, struct ptp_message *msg); + +void tc_prune(struct port *q); + +#endif -- 2.13.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 Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel