Discussion:
[Linuxptp-devel] 802.1AS Time-Aware Bridge
Erik Hons
2016-09-15 18:51:35 UTC
Permalink
Hey All,

Sorry if this has come up before. I scanned the archive but didn't see anything about this.

Is there a technical or deliberate reason that ptp4l doesn't support 802.1AS time-aware bridge? If not, would the project be open to that being contributed?
Richard Cochran
2016-09-15 20:55:23 UTC
Permalink
Post by Erik Hons
Is there a technical or deliberate reason that ptp4l doesn't support
802.1AS time-aware bridge?
The only reason is that this feature would be a lot of work.
Post by Erik Hons
If not, would the project be open to that being contributed?
(Wow, you have it already? Please post it ;)

I already did a simple proof of concept for a P2P TC, and that is not
too different from a TAB. I know the direction to go, but it will
have to wait until I find some free time...

So I do want to have TC/TAB in linuxptp for version 2.0.

Thanks,
Richard

------------------------------------------------------------------------------
Erik Hons
2016-09-15 22:23:31 UTC
Permalink
I don't have a working version to post I'm afraid. But I'm considering making the effort. Sounds like it'd be well received!

Can you share the TC concept you did? And the direction you need it to go? It wouldn't happen to be this would it: https://github.com/richardcochran/linuxptp-as/

-----Original Message-----
From: Richard Cochran [mailto:***@gmail.com]
Sent: Thursday, September 15, 2016 3:55 PM
To: Erik Hons <***@ni.com>
Cc: linuxptp-***@lists.sourceforge.net
Subject: Re: [Linuxptp-devel] 802.1AS Time-Aware Bridge
Post by Erik Hons
Is there a technical or deliberate reason that ptp4l doesn't support
802.1AS time-aware bridge?
The only reason is that this feature would be a lot of work.
Post by Erik Hons
If not, would the project be open to that being contributed?
(Wow, you have it already? Please post it ;)

I already did a simple proof of concept for a P2P TC, and that is not too different from a TAB. I know the direction to go, but it will have to wait until I find some free time...

So I do want to have TC/TAB in linuxptp for version 2.0.

Thanks,
Richard

------------------------------------------------------------------------------
Richard Cochran
2016-09-16 13:10:30 UTC
Permalink
Post by Erik Hons
Can you share the TC concept you did? And the direction you need it
to go?
Yes, I will. Watch this spot!
Post by Erik Hons
https://github.com/richardcochran/linuxptp-as/
No, that is one of three repos that I keep up to date. The other two
are SF and the one under github/nwtime.

Thanks,
Richard



------------------------------------------------------------------------------
Richard Cochran
2016-10-16 12:02:56 UTC
Permalink
So here is my draft for P2P TC support. I rebased this on top of the
RTNL series posted today. The TC code is getting a bit stale by now,
and so this version is compile tested only!

Just to be clear, this code is NOT going into v1.8! The TC support is
only a proof of concept, and much more work is needed. If and when
the TC code matures, then we will call that release 2.0.

Below are my notes about stuff that needs doing...

Thanks,
Richard


* tc todos
** p2p tc correct path delay
fup.correction += mean_path_delay (of ingress port)
each port has:
- tmv_t p->peer_delay
- TimeInterval p->peerMeanPathDelay
- struct nrate_estimator p->nrate.ratio
only used for avb:
if (p->follow_up_info)
port_nrate_calculate(p, t3c, t4);
** generate fup for one step
** ptp4l man page -t flag
** real time scheduling
** tc.h doxygen
** e2e mode
** syntonization
- recognize foreign master
- adjust clock
- physically, clock_adjfreq
- logically, apply frequency ratio to residence time
** bmc and port blocking
- needed for avb
- need modular approach for different bmc algorithms
- tc/bmc can be a non-standard option for 1588


Richard Cochran (6):
ptp4l: add a command line switch for TC mode.
port: make the dispatch and event methods variable based on clock
type.
port: export a private interface.
port: share init code, peer delay code, and helpers
tc: add the transparent clock implementation.
p2p_tc: implement a peer to peer transparent clock.

clock.c | 3 +-
makefile | 2 +-
p2p_tc.c | 155 +++++++++++++++++++++++++++++++++
port.c | 137 ++++++++++-------------------
port.h | 5 ++
port_private.h | 140 ++++++++++++++++++++++++++++++
ptp4l.c | 39 ++++++++-
tc.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tc.h | 36 ++++++++
9 files changed, 682 insertions(+), 100 deletions(-)
create mode 100644 p2p_tc.c
create mode 100644 port_private.h
create mode 100644 tc.c
create mode 100644 tc.h
--
2.1.4
Richard Cochran
2016-10-16 12:02:58 UTC
Permalink
Signed-off-by: Richard Cochran <***@gmail.com>
---
port.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/port.c b/port.c
index a1ad6f6..4f91dfd 100644
--- a/port.c
+++ b/port.c
@@ -72,6 +72,10 @@ struct port {
struct fdarray fda;
int fault_fd;
int phc_index;
+
+ int (*dispatch)(struct port *p, enum fsm_event event, int mdiff);
+ enum fsm_event (*event)(struct port *p, int fd_index);
+
int jbod;
struct foreign_clock *best;
enum syfu_state syfu;
@@ -2134,6 +2138,11 @@ static void port_p2p_transition(struct port *p, enum port_state next)

int port_dispatch(struct port *p, enum fsm_event event, int mdiff)
{
+ return p->dispatch(p, event, mdiff);
+}
+
+static int bc_dispatch(struct port *p, enum fsm_event event, int mdiff)
+{
enum port_state next;
struct fault_interval i;
int fri_asap = 0;
@@ -2197,6 +2206,11 @@ int port_dispatch(struct port *p, enum fsm_event event, int mdiff)

enum fsm_event port_event(struct port *p, int fd_index)
{
+ return p->event(p, fd_index);
+}
+
+static enum fsm_event bc_event(struct port *p, int fd_index)
+{
enum fsm_event event = EV_NONE;
struct ptp_message *msg;
int cnt, fd = p->fda.fd[fd_index], err;
@@ -2545,6 +2559,7 @@ struct port *port_open(int phc_index,
struct interface *interface,
struct clock *clock)
{
+ enum clock_type type = clock_type(clock);
struct config *cfg = clock_config(clock);
struct port *p = malloc(sizeof(*p));
enum transport_type transport;
@@ -2555,6 +2570,18 @@ struct port *port_open(int phc_index,

memset(p, 0, sizeof(*p));

+ switch (type) {
+ case CLOCK_TYPE_ORDINARY:
+ case CLOCK_TYPE_BOUNDARY:
+ p->dispatch = bc_dispatch;
+ p->event = bc_event;
+ break;
+ case CLOCK_TYPE_P2P:
+ case CLOCK_TYPE_E2E:
+ case CLOCK_TYPE_MANAGEMENT:
+ return NULL;
+ }
+
p->phc_index = phc_index;
p->jbod = config_get_int(cfg, interface->name, "boundary_clock_jbod");
transport = config_get_int(cfg, interface->name, "network_transport");
--
2.1.4
Richard Cochran
2016-10-16 12:02:57 UTC
Permalink
This patch adds a new flag '-t' that enables transparent clock mode. When
active, the clock will be created as an E2E or P2P TC, depending on the
configured delay mechanism.

Signed-off-by: Richard Cochran <***@gmail.com>
---
ptp4l.c | 39 +++++++++++++++++++++++++++++++++++----
1 file changed, 35 insertions(+), 4 deletions(-)

diff --git a/ptp4l.c b/ptp4l.c
index a87e7e6..f700499 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 <***@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -61,6 +61,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"
@@ -73,6 +74,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, print_level;
struct clock *clock = NULL;
struct config *cfg;
@@ -88,7 +90,7 @@ int main(int argc, char *argv[])
/* Process the command line arguments. */
progname = strrchr(argv[0], '/');
progname = progname ? 1+progname : argv[0];
- while (EOF != (c = getopt(argc, argv, "AEP246HSLf:i:p:sl:mqvh"))) {
+ while (EOF != (c = getopt(argc, argv, "AEP246HSLf:i:p:stl:mqvh"))) {
switch (c) {
case 'A':
if (config_set_int(cfg, "delay_mechanism", DM_AUTO))
@@ -144,6 +146,9 @@ int main(int argc, char *argv[])
goto out;
}
break;
+ case 't':
+ type = CLOCK_TYPE_E2E;
+ break;
case 'l':
if (get_arg_val_i(c, optarg, &print_level,
PRINT_LEVEL_MIN, PRINT_LEVEL_MAX))
@@ -195,8 +200,34 @@ int main(int argc, char *argv[])
goto out;
}

- clock = clock_create(cfg->n_interfaces > 1 ? CLOCK_TYPE_BOUNDARY :
- CLOCK_TYPE_ORDINARY, cfg, req_phc);
+ switch (type) {
+ case CLOCK_TYPE_ORDINARY:
+ case CLOCK_TYPE_BOUNDARY:
+ if (cfg->n_interfaces > 1) {
+ type = CLOCK_TYPE_BOUNDARY;
+ }
+ break;
+ case CLOCK_TYPE_P2P:
+ case CLOCK_TYPE_E2E:
+ if (cfg->n_interfaces < 2) {
+ fprintf(stderr, "TC needs at least two interfaces\n");
+ goto out;
+ }
+ switch (config_get_int(cfg, NULL, "delay_mechanism")) {
+ case DM_AUTO:
+ case DM_E2E:
+ type = CLOCK_TYPE_E2E;
+ break;
+ case DM_P2P:
+ type = CLOCK_TYPE_P2P;
+ break;
+ }
+ 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.1.4
Richard Cochran
2016-10-16 12:03:00 UTC
Permalink
This patch makes a number of subroutines into global functions in order
to share code with the TC implementations to come.

Signed-off-by: Richard Cochran <***@gmail.com>
---
port.c | 28 +++++++++++++---------------
port_private.h | 17 +++++++++++++++++
2 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/port.c b/port.c
index fc02183..a7dbff7 100644
--- a/port.c
+++ b/port.c
@@ -49,8 +49,6 @@ enum syfu_event {
FUP_MATCH,
};

-#define portnum(p) (p->portIdentity.portNumber)
-
static int port_capable(struct port *p);
static int port_is_ieee8021as(struct port *p);
static void port_nrate_initialize(struct port *p);
@@ -114,7 +112,7 @@ static int pid_eq(struct PortIdentity *a, struct PortIdentity *b)
return 0 == memcmp(a, b, sizeof(*a));
}

-static int source_pid_eq(struct ptp_message *m1, struct ptp_message *m2)
+int source_pid_eq(struct ptp_message *m1, struct ptp_message *m2)
{
return pid_eq(&m1->header.sourcePortIdentity,
&m2->header.sourcePortIdentity);
@@ -254,7 +252,7 @@ static void fc_prune(struct foreign_clock *fc)
}
}

-static void ts_add(struct timespec *ts, int ns)
+void ts_add(struct timespec *ts, int ns)
{
if (!ns) {
return;
@@ -525,7 +523,7 @@ not_capable:
return 0;
}

-static int port_clr_tmo(int fd)
+int port_clr_tmo(int fd)
{
struct itimerspec tmo = {
{0, 0}, {0, 0}
@@ -885,7 +883,7 @@ static int port_set_announce_tmo(struct port *p)
p->announce_span, p->logAnnounceInterval);
}

-static int port_set_delay_tmo(struct port *p)
+int port_set_delay_tmo(struct port *p)
{
if (p->delayMechanism == DM_P2P) {
return set_tmo_log(p->fda.fd[FD_DELAY_TIMER], 1,
@@ -918,8 +916,8 @@ static int port_set_sync_tx_tmo(struct port *p)
return set_tmo_log(p->fda.fd[FD_SYNC_TX_TIMER], 1, p->logSyncInterval);
}

-static void port_show_transition(struct port *p,
- enum port_state next, enum fsm_event event)
+void port_show_transition(struct port *p, enum port_state next,
+ enum fsm_event event)
{
if (event == EV_FAULT_DETECTED) {
pr_notice("port %hu: %s to %s on %s (%s)", portnum(p),
@@ -1111,7 +1109,7 @@ out:
return -1;
}

-static int port_delay_request(struct port *p)
+int port_delay_request(struct port *p)
{
struct ptp_message *msg;

@@ -1296,7 +1294,7 @@ out:
/*
* port initialize and disable
*/
-static int port_is_enabled(struct port *p)
+int port_is_enabled(struct port *p)
{
switch (p->state) {
case PS_INITIALIZING:
@@ -1355,7 +1353,7 @@ static void port_clear_fda(struct port *p, int count)
p->fda.fd[i] = -1;
}

-static void port_disable(struct port *p)
+void port_disable(struct port *p)
{
int i;

@@ -1374,7 +1372,7 @@ static void port_disable(struct port *p)
clock_fda_changed(p->clock);
}

-static int port_initialize(struct port *p)
+int port_initialize(struct port *p)
{
struct config *cfg = clock_config(p->clock);
int fd[N_TIMER_FDS], i;
@@ -1655,7 +1653,7 @@ static void process_follow_up(struct port *p, struct ptp_message *m)
port_syfufsm(p, event, m);
}

-static int process_pdelay_req(struct port *p, struct ptp_message *m)
+int process_pdelay_req(struct port *p, struct ptp_message *m)
{
struct ptp_message *rsp, *fup;
int err;
@@ -1825,7 +1823,7 @@ calc:
p->peer_delay_req = NULL;
}

-static int process_pdelay_resp(struct port *p, struct ptp_message *m)
+int process_pdelay_resp(struct port *p, struct ptp_message *m)
{
if (p->peer_delay_resp) {
if (!source_pid_eq(p->peer_delay_resp, m)) {
@@ -1869,7 +1867,7 @@ static int process_pdelay_resp(struct port *p, struct ptp_message *m)
return 0;
}

-static void process_pdelay_resp_fup(struct port *p, struct ptp_message *m)
+void process_pdelay_resp_fup(struct port *p, struct ptp_message *m)
{
if (!p->peer_delay_req)
return;
diff --git a/port_private.h b/port_private.h
index e87d26b..6d10fb0 100644
--- a/port_private.h
+++ b/port_private.h
@@ -22,6 +22,7 @@
#include <sys/queue.h>

#include "clock.h"
+#include "fsm.h"
#include "msg.h"
#include "tmv.h"

@@ -108,4 +109,20 @@ struct port {
LIST_HEAD(fm, foreign_clock) foreign_masters;
};

+#define portnum(p) (p->portIdentity.portNumber)
+
+int port_clr_tmo(int fd);
+int port_delay_request(struct port *p);
+void port_disable(struct port *p);
+int port_initialize(struct port *p);
+int port_is_enabled(struct port *p);
+int port_set_delay_tmo(struct port *p);
+void port_show_transition(struct port *p, enum port_state next,
+ enum fsm_event event);
+int process_pdelay_req(struct port *p, struct ptp_message *m);
+int process_pdelay_resp(struct port *p, struct ptp_message *m);
+void process_pdelay_resp_fup(struct port *p, struct ptp_message *m);
+int source_pid_eq(struct ptp_message *m1, struct ptp_message *m2);
+void ts_add(struct timespec *ts, int ns);
+
#endif
--
2.1.4
Richard Cochran
2016-10-16 12:02:59 UTC
Permalink
This patch places the internal port data structure into a common header
for use by the original BC and the new TC code.

Signed-off-by: Richard Cochran <***@gmail.com>
---
port.c | 84 +------------------------------------------
port_private.h | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 112 insertions(+), 83 deletions(-)
create mode 100644 port_private.h

diff --git a/port.c b/port.c
index 4f91dfd..fc02183 100644
--- a/port.c
+++ b/port.c
@@ -31,6 +31,7 @@
#include "msg.h"
#include "phc.h"
#include "port.h"
+#include "port_private.h"
#include "print.h"
#include "sk.h"
#include "tlv.h"
@@ -41,12 +42,6 @@
#define ALLOWED_LOST_RESPONSES 3
#define ANNOUNCE_SPAN 1

-enum syfu_state {
- SF_EMPTY,
- SF_HAVE_SYNC,
- SF_HAVE_FUP,
-};
-
enum syfu_event {
SYNC_MISMATCH,
SYNC_MATCH,
@@ -54,85 +49,8 @@ enum syfu_event {
FUP_MATCH,
};

-struct nrate_estimator {
- double ratio;
- tmv_t origin1;
- tmv_t ingress1;
- unsigned int max_count;
- unsigned int count;
- int ratio_valid;
-};
-
-struct port {
- LIST_ENTRY(port) list;
- char *name;
- struct clock *clock;
- struct transport *trp;
- enum timestamp_type timestamping;
- struct fdarray fda;
- int fault_fd;
- int phc_index;
-
- int (*dispatch)(struct port *p, enum fsm_event event, int mdiff);
- enum fsm_event (*event)(struct port *p, int fd_index);
-
- int jbod;
- struct foreign_clock *best;
- enum syfu_state syfu;
- struct ptp_message *last_syncfup;
- struct ptp_message *delay_req;
- struct ptp_message *peer_delay_req;
- struct ptp_message *peer_delay_resp;
- struct ptp_message *peer_delay_fup;
- int peer_portid_valid;
- struct PortIdentity peer_portid;
- struct {
- UInteger16 announce;
- UInteger16 delayreq;
- UInteger16 sync;
- } seqnum;
- tmv_t peer_delay;
- struct tsproc *tsproc;
- int log_sync_interval;
- struct nrate_estimator nrate;
- unsigned int pdr_missing;
- unsigned int multiple_seq_pdr_count;
- unsigned int multiple_pdr_detected;
- /* portDS */
- struct PortIdentity portIdentity;
- enum port_state state; /*portState*/
- Integer64 asymmetry;
- int asCapable;
- Integer8 logMinDelayReqInterval;
- TimeInterval peerMeanPathDelay;
- Integer8 logAnnounceInterval;
- UInteger8 announceReceiptTimeout;
- int announce_span;
- UInteger8 syncReceiptTimeout;
- UInteger8 transportSpecific;
- Integer8 logSyncInterval;
- Enumeration8 delayMechanism;
- Integer8 logMinPdelayReqInterval;
- UInteger32 neighborPropDelayThresh;
- int follow_up_info;
- int freq_est_interval;
- int hybrid_e2e;
- int min_neighbor_prop_delay;
- int path_trace_enabled;
- int rx_timestamp_offset;
- int tx_timestamp_offset;
- int link_status;
- struct fault_interval flt_interval_pertype[FT_CNT];
- enum fault_type last_fault_type;
- unsigned int versionNumber; /*UInteger4*/
- /* foreignMasterDS */
- LIST_HEAD(fm, foreign_clock) foreign_masters;
-};
-
#define portnum(p) (p->portIdentity.portNumber)

-#define NSEC2SEC 1000000000LL
-
static int port_capable(struct port *p);
static int port_is_ieee8021as(struct port *p);
static void port_nrate_initialize(struct port *p);
diff --git a/port_private.h b/port_private.h
new file mode 100644
index 0000000..e87d26b
--- /dev/null
+++ b/port_private.h
@@ -0,0 +1,111 @@
+/**
+ * @file port_private.h
+ * @note Copyright (C) 2015 Richard Cochran <***@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_PORT_PRIVATE_H
+#define HAVE_PORT_PRIVATE_H
+
+#include <sys/queue.h>
+
+#include "clock.h"
+#include "msg.h"
+#include "tmv.h"
+
+#define NSEC2SEC 1000000000LL
+
+enum syfu_state {
+ SF_EMPTY,
+ SF_HAVE_SYNC,
+ SF_HAVE_FUP,
+};
+
+struct nrate_estimator {
+ double ratio;
+ tmv_t origin1;
+ tmv_t ingress1;
+ unsigned int max_count;
+ unsigned int count;
+ int ratio_valid;
+};
+
+struct port {
+ LIST_ENTRY(port) list;
+ char *name;
+ struct clock *clock;
+ struct transport *trp;
+ enum timestamp_type timestamping;
+ struct fdarray fda;
+ int fault_fd;
+ int phc_index;
+
+ int (*dispatch)(struct port *p, enum fsm_event event, int mdiff);
+ enum fsm_event (*event)(struct port *p, int fd_index);
+
+ int jbod;
+ struct foreign_clock *best;
+ enum syfu_state syfu;
+ struct ptp_message *last_syncfup;
+ struct ptp_message *delay_req;
+ struct ptp_message *peer_delay_req;
+ struct ptp_message *peer_delay_resp;
+ struct ptp_message *peer_delay_fup;
+ int peer_portid_valid;
+ struct PortIdentity peer_portid;
+ struct {
+ UInteger16 announce;
+ UInteger16 delayreq;
+ UInteger16 sync;
+ } seqnum;
+ tmv_t peer_delay;
+ struct tsproc *tsproc;
+ int log_sync_interval;
+ struct nrate_estimator nrate;
+ unsigned int pdr_missing;
+ unsigned int multiple_seq_pdr_count;
+ unsigned int multiple_pdr_detected;
+ /* portDS */
+ struct PortIdentity portIdentity;
+ enum port_state state; /*portState*/
+ Integer64 asymmetry;
+ int asCapable;
+ Integer8 logMinDelayReqInterval;
+ TimeInterval peerMeanPathDelay;
+ Integer8 logAnnounceInterval;
+ UInteger8 announceReceiptTimeout;
+ int announce_span;
+ UInteger8 syncReceiptTimeout;
+ UInteger8 transportSpecific;
+ Integer8 logSyncInterval;
+ Enumeration8 delayMechanism;
+ Integer8 logMinPdelayReqInterval;
+ UInteger32 neighborPropDelayThresh;
+ int follow_up_info;
+ int freq_est_interval;
+ int hybrid_e2e;
+ int min_neighbor_prop_delay;
+ int path_trace_enabled;
+ int rx_timestamp_offset;
+ int tx_timestamp_offset;
+ int link_status;
+ struct fault_interval flt_interval_pertype[FT_CNT];
+ enum fault_type last_fault_type;
+ unsigned int versionNumber; /*UInteger4*/
+ /* foreignMasterDS */
+ LIST_HEAD(fm, foreign_clock) foreign_masters;
+};
+
+#endif
--
2.1.4
Richard Cochran
2016-10-16 12:03:01 UTC
Permalink
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 <***@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 4adb492..26e69a6 100644
--- a/clock.c
+++ b/clock.c
@@ -288,6 +288,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 44be067..8488679 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 a7dbff7..1b2f4e5 100644
--- a/port.c
+++ b/port.c
@@ -34,6 +34,7 @@
#include "port_private.h"
#include "print.h"
#include "sk.h"
+#include "tc.h"
#include "tlv.h"
#include "tmv.h"
#include "tsproc.h"
@@ -1357,6 +1358,7 @@ void port_disable(struct port *p)
{
int i;

+ tc_flush(p);
flush_last_sync(p);
flush_delay_req(p);
flush_peer_delay(p);
@@ -2485,6 +2487,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 19dec4a..edcfd39 100644
--- a/port.h
+++ b/port.h
@@ -323,4 +323,9 @@ enum fault_type last_fault_type(struct port *port);
int 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 6d10fb0..2fc927a 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;
@@ -107,6 +114,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..9669042
--- /dev/null
+++ b/tc.c
@@ -0,0 +1,265 @@
+/**
+ * @file tc.c
+ * @note Copyright (C) 2015 Richard Cochran <***@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 <***@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.1.4
Richard Cochran
2016-10-16 12:03:02 UTC
Permalink
Signed-off-by: Richard Cochran <***@gmail.com>
---
clock.c | 2 +-
makefile | 2 +-
p2p_tc.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
port.c | 3 ++
port_private.h | 3 ++
5 files changed, 163 insertions(+), 2 deletions(-)
create mode 100644 p2p_tc.c

diff --git a/clock.c b/clock.c
index 26e69a6..e5554bc 100644
--- a/clock.c
+++ b/clock.c
@@ -872,9 +872,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 8488679..ac9de87 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 tc.o tlv.o \
+ pi.o port.o print.o ptp4l.o p2p_tc.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/p2p_tc.c b/p2p_tc.c
new file mode 100644
index 0000000..eea09e2
--- /dev/null
+++ b/p2p_tc.c
@@ -0,0 +1,155 @@
+/**
+ * @file p2p_tc.c
+ * @note Copyright (C) 2015 Richard Cochran <***@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 <errno.h>
+
+#include "port.h"
+#include "port_private.h"
+#include "print.h"
+#include "tc.h"
+
+int p2p_dispatch(struct port *p, enum fsm_event event, int mdiff)
+{
+ enum port_state next;
+ struct fault_interval fti;
+ int clear_asap = 0;
+
+ next = ptp_fsm(p->state, event, mdiff);
+
+ if (!fault_interval(p, last_fault_type(p), &fti)) {
+ if (fti.val == FRI_ASAP && fti.type == FTMO_LOG2_SECONDS) {
+ clear_asap = 1;
+ }
+ if (fti.val == 0 && fti.type == FTMO_LINEAR_SECONDS) {
+ clear_asap = 1;
+ }
+ }
+ if (PS_INITIALIZING == next || (PS_FAULTY == next && clear_asap)) {
+ /* Special case, skip to listening. */
+ if (port_is_enabled(p)) {
+ port_disable(p);
+ }
+ next = port_initialize(p) ? PS_FAULTY : PS_LISTENING;
+ port_show_transition(p, next, event);
+ p->state = next;
+ if (next == PS_LISTENING && portnum(p)) {
+ port_set_delay_tmo(p);
+ }
+ port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]);
+ port_notify_event(p, NOTIFY_PORT_STATE);
+ return 1;
+ }
+
+ return 0;
+}
+
+enum fsm_event p2p_event(struct port *p, int fd_index)
+{
+ enum fsm_event event = EV_NONE;
+ struct ptp_message *msg;
+ int cnt, fd = p->fda.fd[fd_index], err;
+
+ switch (fd_index) {
+ case FD_ANNOUNCE_TIMER:
+ case FD_SYNC_RX_TIMER:
+ pr_err("unexpected timer expiration");
+ return EV_NONE;
+ case FD_DELAY_TIMER:
+ pr_debug("port %hu: delay timeout", portnum(p));
+ port_set_delay_tmo(p);
+ tc_prune(p);
+ return port_delay_request(p) ? EV_FAULT_DETECTED : EV_NONE;
+ case FD_QUALIFICATION_TIMER:
+ case FD_MANNO_TIMER:
+ case FD_SYNC_TX_TIMER:
+ pr_err("unexpected timer expiration");
+ 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;
+ }
+ err = msg_post_recv(msg, cnt);
+ if (err) {
+ switch (err) {
+ case -EBADMSG:
+ pr_err("port %hu: bad message", portnum(p));
+ break;
+ case -ETIME:
+ pr_err("port %hu: received %s without timestamp",
+ portnum(p), msg_type_string(msg_type(msg)));
+ break;
+ case -EPROTO:
+ pr_debug("port %hu: ignoring message", portnum(p));
+ break;
+ }
+ msg_put(msg);
+ return EV_NONE;
+ }
+ 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!");
+ goto out;
+ }
+
+ switch (msg_type(msg)) {
+ case SYNC:
+ if (tc_fwd_event(p, msg))
+ event = EV_FAULT_DETECTED;
+ break;
+ case DELAY_REQ:
+ break;
+ case PDELAY_REQ:
+ if (process_pdelay_req(p, msg))
+ event = EV_FAULT_DETECTED;
+ break;
+ case PDELAY_RESP:
+ if (process_pdelay_resp(p, msg))
+ event = EV_FAULT_DETECTED;
+ break;
+ case FOLLOW_UP:
+ if (tc_fwd_folup(p, msg))
+ event = EV_FAULT_DETECTED;
+ break;
+ case DELAY_RESP:
+ break;
+ case PDELAY_RESP_FOLLOW_UP:
+ process_pdelay_resp_fup(p, msg);
+ break;
+ case ANNOUNCE:
+ case SIGNALING:
+ case MANAGEMENT:
+ if (tc_forward(p, msg))
+ event = EV_FAULT_DETECTED;
+ break;
+ }
+out:
+ msg_put(msg);
+ return event;
+}
diff --git a/port.c b/port.c
index 1b2f4e5..8d99675 100644
--- a/port.c
+++ b/port.c
@@ -2496,6 +2496,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;
diff --git a/port_private.h b/port_private.h
index 2fc927a..083cf7b 100644
--- a/port_private.h
+++ b/port_private.h
@@ -120,6 +120,9 @@ struct port {

#define portnum(p) (p->portIdentity.portNumber)

+int p2p_dispatch(struct port *p, enum fsm_event event, int mdiff);
+enum fsm_event p2p_event(struct port *p, int fd_index);
+
int port_clr_tmo(int fd);
int port_delay_request(struct port *p);
void port_disable(struct port *p);
--
2.1.4
Erik Hons
2016-09-15 22:23:25 UTC
Permalink
I’d like to use the JBOD mode with intel i210s.

It seems like the “just use standard Linux interfaces” approach means it will work with just about any configuration!
From: Chris Healy [mailto:***@gmail.com]
Sent: Thursday, September 15, 2016 2:21 PM
To: Erik Hons <***@ni.com>
Subject: Re: [Linuxptp-devel] 802.1AS Time-Aware Bridge

Hi Erik,
I was interested in getting this working in the past but have not had a chance to dedicate the energy.
What type of HW configuration are you looking at making it work with?
Regards,
Chris

On Thu, Sep 15, 2016 at 11:51 AM, Erik Hons <***@ni.com<mailto:***@ni.com>> wrote:

Hey All,

Sorry if this has come up before. I scanned the archive but didn’t see anything about this.

Is there a technical or deliberate reason that ptp4l doesn’t support 802.1AS time-aware bridge? If not, would the project be open to that being contributed?





------------------------------------------------------------------------------

_______________________________________________
Linuxptp-devel mailing list
Linuxptp-***@lists.sourceforge.net<mailto:Linuxptp-***@lists.sourceforge.net>
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel
Loading...