AVnu specifies a measurement method to remotely measure the clock quality. Basically, this method sends Sync and/or Follow-up messages on a slave towards the master, which can capture them and measure the quality.
The AVnu specification [1] introduces three parameters: - reverseSyncEnabled - reverseSyncDomain - reverseSyncRate The reverse sync messages can be sent in another domain to ensure they don't conflict with the real sync messages. The rate is specified as a fraction of the rate of the incoming sync messages. By default this is 95% of the incoming rate to make sure the sample interval is distributed between two synchronization points. Additionally, the timer is free running and not locked to the incoming sync interval. The three paramenters are implemented as a per port property. [1] https://avnu.org/wp-content/uploads/2014/05/Avnu-Testability-802.1AS-Recovered-Clock-Quality-Measurement-1.0_Approved-for-Public-Release.pdf Signed-off-by: Michael Walle <mich...@walle.cc> --- config.c | 3 +++ fd.h | 3 ++- p2p_tc.c | 4 ++++ port.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++----------- port.h | 17 ++++++++++++++ port_private.h | 5 +++++ ptp4l.8 | 15 +++++++++++++ 7 files changed, 104 insertions(+), 14 deletions(-) diff --git a/config.c b/config.c index 7914ba4..19ac8da 100644 --- a/config.c +++ b/config.c @@ -250,6 +250,9 @@ struct config_item config_tab[] = { GLOB_ITEM_STR("productDescription", ";;"), PORT_ITEM_STR("ptp_dst_mac", "01:1B:19:00:00:00"), PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"), + PORT_ITEM_INT("reverseSyncDomain", 0, 0, 127), + PORT_ITEM_INT("reverseSyncEnabled", 0, 0, 1), + PORT_ITEM_DBL("reverseSyncRate", 0.95, 0.0, 1.0), GLOB_ITEM_STR("revisionData", ";;"), GLOB_ITEM_INT("sanity_freq_limit", 200000000, 0, INT_MAX), GLOB_ITEM_INT("slaveOnly", 0, 0, 1), diff --git a/fd.h b/fd.h index 16420d7..d7fcd8a 100644 --- a/fd.h +++ b/fd.h @@ -20,7 +20,7 @@ #ifndef HAVE_FD_H #define HAVE_FD_H -#define N_TIMER_FDS 8 +#define N_TIMER_FDS 9 /* * The order matters here. The DELAY timer must appear before the @@ -39,6 +39,7 @@ enum { FD_SYNC_TX_TIMER, FD_UNICAST_REQ_TIMER, FD_UNICAST_SRV_TIMER, + FD_REV_SYNC_TX_TIMER, FD_RTNL, N_POLLFD, }; diff --git a/p2p_tc.c b/p2p_tc.c index f798af3..cb0e2ef 100644 --- a/p2p_tc.c +++ b/p2p_tc.c @@ -59,6 +59,7 @@ void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff) 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]); + port_clr_tmo(p->fda.fd[FD_REV_SYNC_TX_TIMER]); /* * Handle the side effects of the state transition. @@ -86,6 +87,9 @@ void p2p_dispatch(struct port *p, enum fsm_event event, int mdiff) case PS_UNCALIBRATED: case PS_SLAVE: port_set_announce_tmo(p); + if (p->reverseSyncEnabled) { + port_set_reverse_sync_tx_tmo(p); + } break; }; } diff --git a/port.c b/port.c index 5e0aed7..6428ce4 100644 --- a/port.c +++ b/port.c @@ -177,7 +177,8 @@ struct fdarray *port_fda(struct port *port) return &port->fda; } -int set_tmo_log(int fd, unsigned int scale, int log_seconds) +int set_tmo_log_proportional(int fd, unsigned int scale, int log_seconds, + double proportion) { struct itimerspec tmo = { {0, 0}, {0, 0} @@ -201,9 +202,22 @@ int set_tmo_log(int fd, unsigned int scale, int log_seconds) } else tmo.it_value.tv_sec = scale * (1 << log_seconds); + /* take the slow path only for the unlikely case */ + if (proportion != 1.0) { + ns = tmo.it_value.tv_sec * NS_PER_SEC + tmo.it_value.tv_nsec; + ns = ns * proportion; + tmo.it_value.tv_sec = ns / NS_PER_SEC; + tmo.it_value.tv_nsec = ns % NS_PER_SEC; + } + return timerfd_settime(fd, 0, &tmo, NULL); } +int set_tmo_log(int fd, unsigned int scale, int log_seconds) +{ + return set_tmo_log_proportional(fd, scale, log_seconds, 1.0); +} + int set_tmo_lin(int fd, int seconds) { struct itimerspec tmo = { @@ -1046,6 +1060,13 @@ int port_set_delay_tmo(struct port *p) } } +int port_set_reverse_sync_tx_tmo(struct port *p) +{ + return set_tmo_log_proportional(p->fda.fd[FD_REV_SYNC_TX_TIMER], 1, + p->log_sync_interval, + p->reverseSyncRate); +} + static int port_set_manno_tmo(struct port *p) { return set_tmo_log(p->fda.fd[FD_MANNO_TIMER], 1, p->logAnnounceInterval); @@ -1378,7 +1399,8 @@ int port_tx_announce(struct port *p, struct address *dst) return err; } -int port_tx_sync(struct port *p, struct address *dst) +static int port_tx_sync_domain(struct port *p, struct address *dst, + UInteger8 domain_number) { struct ptp_message *msg, *fup; int err, event; @@ -1399,15 +1421,6 @@ int port_tx_sync(struct port *p, struct address *dst) return -1; } - if (p->inhibit_multicast_service && !dst) { - return 0; - } - if (!port_capable(p)) { - return 0; - } - if (port_sync_incapable(p)) { - return 0; - } msg = msg_allocate(); if (!msg) { return -1; @@ -1423,7 +1436,7 @@ int port_tx_sync(struct port *p, struct address *dst) msg->header.tsmt = SYNC | p->transportSpecific; msg->header.ver = PTP_VERSION; msg->header.messageLength = sizeof(struct sync_msg); - msg->header.domainNumber = clock_domain_number(p->clock); + msg->header.domainNumber = domain_number; msg->header.sourcePortIdentity = p->portIdentity; msg->header.sequenceId = p->seqnum.sync++; msg->header.control = CTL_SYNC; @@ -1459,7 +1472,7 @@ int port_tx_sync(struct port *p, struct address *dst) fup->header.tsmt = FOLLOW_UP | p->transportSpecific; fup->header.ver = PTP_VERSION; fup->header.messageLength = sizeof(struct follow_up_msg); - fup->header.domainNumber = clock_domain_number(p->clock); + fup->header.domainNumber = domain_number; fup->header.sourcePortIdentity = p->portIdentity; fup->header.sequenceId = p->seqnum.sync - 1; fup->header.control = CTL_FOLLOW_UP; @@ -1487,6 +1500,26 @@ out: return err; } +int port_tx_sync(struct port *p, struct address *dst) +{ + if (p->inhibit_multicast_service && !dst) { + return 0; + } + if (!port_capable(p)) { + return 0; + } + if (port_sync_incapable(p)) { + return 0; + } + + return port_tx_sync_domain(p, dst, clock_domain_number(p->clock)); +} + +int port_tx_reverse_sync(struct port *p, struct address *dst) +{ + return port_tx_sync_domain(p, dst, p->reverseSyncDomain); +} + /* * port initialize and disable */ @@ -2317,6 +2350,7 @@ static void port_p2p_transition(struct port *p, enum port_state next) port_clr_tmo(p->fda.fd[FD_MANNO_TIMER]); port_clr_tmo(p->fda.fd[FD_SYNC_TX_TIMER]); /* Leave FD_UNICAST_REQ_TIMER running. */ + port_clr_tmo(p->fda.fd[FD_REV_SYNC_TX_TIMER]); switch (next) { case PS_INITIALIZING: @@ -2346,6 +2380,9 @@ static void port_p2p_transition(struct port *p, enum port_state next) /* fall through */ case PS_SLAVE: port_set_announce_tmo(p); + if (p->reverseSyncEnabled) { + port_set_reverse_sync_tx_tmo(p); + } break; }; } @@ -2485,6 +2522,11 @@ static enum fsm_event bc_event(struct port *p, int fd_index) port_set_sync_tx_tmo(p); return port_tx_sync(p, NULL) ? EV_FAULT_DETECTED : EV_NONE; + case FD_REV_SYNC_TX_TIMER: + pr_debug("port %hu: reverse sync timeout", portnum(p)); + port_set_reverse_sync_tx_tmo(p); + return port_tx_reverse_sync(p, NULL) ? EV_FAULT_DETECTED : EV_NONE; + case FD_UNICAST_SRV_TIMER: pr_debug("port %hu: unicast service timeout", portnum(p)); return unicast_service_timer(p) ? EV_FAULT_DETECTED : EV_NONE; @@ -2885,6 +2927,9 @@ struct port *port_open(int phc_index, p->tx_timestamp_offset <<= 16; p->link_status = LINK_UP; p->clock = clock; + p->reverseSyncEnabled = config_get_int(cfg, p->name, "reverseSyncEnabled"); + p->reverseSyncRate = config_get_double(cfg, p->name, "reverseSyncRate"); + p->reverseSyncDomain = config_get_int(cfg, p->name, "reverseSyncDomain"); p->trp = transport_create(cfg, transport); if (!p->trp) { goto err_port; diff --git a/port.h b/port.h index 9639193..c5dbce3 100644 --- a/port.h +++ b/port.h @@ -254,6 +254,23 @@ int port_fault_fd(struct port *port); int set_tmo_log(int fd, unsigned int scale, int log_seconds); /** + * Utility function for setting or resetting a file descriptor timer. + * + * Same as 'set_tmo_log()' but with an additional parameter 'proportion'. + * Eg. the timer 'fd' is set to the value M(2^N) * proportion. + * + * Passing both 'scale' and 'log_seconds' as zero disables the timer. + * + * @param fd A file descriptor previously opened with timerfd_create(2). + * @param scale The multiplicative factor for the timer. + * @param log_seconds The exponential factor for the timer. + * @param proportion The proportion which the timer value is multiplied with. + * @return Zero on success, non-zero otherwise. + */ +int set_tmo_log_proportional(int fd, unsigned int scale, int log_seconds, + double proportion); + +/** * Utility function for setting a file descriptor timer. * * This function sets the timer 'fd' to a random value between M * 2^N and diff --git a/port_private.h b/port_private.h index 19d1d7b..f676890 100644 --- a/port_private.h +++ b/port_private.h @@ -138,6 +138,10 @@ struct port { /* unicast service mode */ struct unicast_service *unicast_service; int inhibit_multicast_service; + /* reverse sync */ + int reverseSyncEnabled; + double reverseSyncRate; + int reverseSyncDomain; }; #define portnum(p) (p->portIdentity.portNumber) @@ -162,6 +166,7 @@ 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); +int port_set_reverse_sync_tx_tmo(struct port *p); void port_show_transition(struct port *p, enum port_state next, enum fsm_event event); struct ptp_message *port_signaling_construct(struct port *p, diff --git a/ptp4l.8 b/ptp4l.8 index 10c5c2f..457e97d 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -353,6 +353,21 @@ limit for IPv6 multicast messages. This option is only relevant with the IPv4 and IPv6 UDP transports. The default is 1 to restrict the messages sent by .B ptp4l to the same subnet. +.TP +.B reverseSyncEnabled +Enable the reverse sync measurement method. If enabled, a port in +SLAVE state will send (reverse) sync and/or follow up messages. This can +then be used by a tester to measure the clock quality. +.TP +.B reverseSyncDomain +Specifies the domain number of the reverse sync and follow up messages. +.TP +.B reverseSyncRate +Specifies the rate of the reverse Sync messages in percent as a fraction of +the incoming rate. For example, if the incoming interval is 125ms and +reverseSyncRate is 0.9, the interval of the reverse Sync messages is +112.5ms. +The default is 0.95 (95%). .SH PROGRAM AND CLOCK OPTIONS -- 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 Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel