Merge the gPTP sync timeout branch.

Signed-off-by: Richard Cochran <richardcochran@gmail.com>
master
Richard Cochran 2013-09-10 16:14:56 +02:00
commit bf659ae09d
15 changed files with 135 additions and 16 deletions

View File

@ -90,11 +90,17 @@ static enum parser_result parse_pod_setting(const char *option,
pod->logMinPdelayReqInterval = val; pod->logMinPdelayReqInterval = val;
} else if (!strcmp(option, "announceReceiptTimeout")) { } else if (!strcmp(option, "announceReceiptTimeout")) {
r = get_ranged_uint(value, &uval, 0, UINT8_MAX); r = get_ranged_uint(value, &uval, 2, UINT8_MAX);
if (r != PARSED_OK) if (r != PARSED_OK)
return r; return r;
pod->announceReceiptTimeout = uval; pod->announceReceiptTimeout = uval;
} else if (!strcmp(option, "syncReceiptTimeout")) {
r = get_ranged_uint(value, &uval, 0, UINT8_MAX);
if (r != PARSED_OK)
return r;
pod->syncReceiptTimeout = uval;
} else if (!strcmp(option, "transportSpecific")) { } else if (!strcmp(option, "transportSpecific")) {
r = get_ranged_uint(value, &uval, 0, 0x0F); r = get_ranged_uint(value, &uval, 0, 0x0F);
if (r != PARSED_OK) if (r != PARSED_OK)
@ -295,6 +301,12 @@ static enum parser_result parse_global_setting(const char *option,
return r; return r;
*cfg->tx_timestamp_timeout = val; *cfg->tx_timestamp_timeout = val;
} else if (!strcmp(option, "check_fup_sync")) {
r = get_ranged_int(value, &val, 0, 1);
if (r != PARSED_OK)
return r;
*cfg->check_fup_sync = val;
} else if (!strcmp(option, "pi_proportional_const")) { } else if (!strcmp(option, "pi_proportional_const")) {
r = get_ranged_double(value, &df, 0.0, DBL_MAX); r = get_ranged_double(value, &df, 0.0, DBL_MAX);
if (r != PARSED_OK) if (r != PARSED_OK)

View File

@ -70,6 +70,7 @@ struct config {
struct port_defaults pod; struct port_defaults pod;
int *assume_two_step; int *assume_two_step;
int *tx_timestamp_timeout; int *tx_timestamp_timeout;
int *check_fup_sync;
enum servo_type clock_servo; enum servo_type clock_servo;

View File

@ -20,6 +20,7 @@ logSyncInterval 0
logMinDelayReqInterval 0 logMinDelayReqInterval 0
logMinPdelayReqInterval 0 logMinPdelayReqInterval 0
announceReceiptTimeout 3 announceReceiptTimeout 3
syncReceiptTimeout 0
delayAsymmetry 0 delayAsymmetry 0
fault_reset_interval 4 fault_reset_interval 4
neighborPropDelayThresh 20000000 neighborPropDelayThresh 20000000
@ -35,6 +36,7 @@ use_syslog 1
verbose 0 verbose 0
summary_interval 0 summary_interval 0
kernel_leap 1 kernel_leap 1
check_fup_sync 0
# #
# Servo Options # Servo Options
# #

1
ds.h
View File

@ -123,6 +123,7 @@ struct port_defaults {
Integer8 logMinDelayReqInterval; Integer8 logMinDelayReqInterval;
Integer8 logMinPdelayReqInterval; Integer8 logMinPdelayReqInterval;
UInteger8 announceReceiptTimeout; UInteger8 announceReceiptTimeout;
UInteger8 syncReceiptTimeout;
UInteger8 transportSpecific; UInteger8 transportSpecific;
int path_trace_enabled; int path_trace_enabled;
int follow_up_info; int follow_up_info;

5
fd.h
View File

@ -20,16 +20,17 @@
#ifndef HAVE_FD_H #ifndef HAVE_FD_H
#define HAVE_FD_H #define HAVE_FD_H
#define N_TIMER_FDS 5 #define N_TIMER_FDS 6
enum { enum {
FD_EVENT, FD_EVENT,
FD_GENERAL, FD_GENERAL,
FD_ANNOUNCE_TIMER, FD_ANNOUNCE_TIMER,
FD_SYNC_RX_TIMER,
FD_DELAY_TIMER, FD_DELAY_TIMER,
FD_QUALIFICATION_TIMER, FD_QUALIFICATION_TIMER,
FD_MANNO_TIMER, FD_MANNO_TIMER,
FD_SYNC_TIMER, FD_SYNC_TX_TIMER,
N_POLLFD, N_POLLFD,
}; };

View File

@ -19,6 +19,7 @@ logAnnounceInterval 1
logSyncInterval -3 logSyncInterval -3
logMinPdelayReqInterval 0 logMinPdelayReqInterval 0
announceReceiptTimeout 3 announceReceiptTimeout 3
syncReceiptTimeout 3
delayAsymmetry 0 delayAsymmetry 0
fault_reset_interval 4 fault_reset_interval 4
neighborPropDelayThresh 800 neighborPropDelayThresh 800
@ -34,6 +35,7 @@ use_syslog 1
verbose 0 verbose 0
summary_interval 0 summary_interval 0
kernel_leap 1 kernel_leap 1
check_fup_sync 0
# #
# Servo options # Servo options
# #

54
port.c
View File

@ -97,6 +97,7 @@ struct port {
TimeInterval peerMeanPathDelay; TimeInterval peerMeanPathDelay;
Integer8 logAnnounceInterval; Integer8 logAnnounceInterval;
UInteger8 announceReceiptTimeout; UInteger8 announceReceiptTimeout;
UInteger8 syncReceiptTimeout;
UInteger8 transportSpecific; UInteger8 transportSpecific;
Integer8 logSyncInterval; Integer8 logSyncInterval;
Enumeration8 delayMechanism; Enumeration8 delayMechanism;
@ -365,6 +366,21 @@ static void free_foreign_masters(struct port *p)
} }
} }
static int fup_sync_ok(struct ptp_message *fup, struct ptp_message *sync)
{
int64_t tfup, tsync;
tfup = tmv_to_nanoseconds(timespec_to_tmv(fup->hwts.sw));
tsync = tmv_to_nanoseconds(timespec_to_tmv(sync->hwts.sw));
/*
* NB - If the sk_check_fupsync option is not enabled, then
* both of these time stamps will be zero.
*/
if (tfup < tsync) {
return 0;
}
return 1;
}
static int incapable_ignore(struct port *p, struct ptp_message *m) static int incapable_ignore(struct port *p, struct ptp_message *m)
{ {
if (port_capable(p)) { if (port_capable(p)) {
@ -800,9 +816,15 @@ static int port_set_qualification_tmo(struct port *p)
1+clock_steps_removed(p->clock), p->logAnnounceInterval); 1+clock_steps_removed(p->clock), p->logAnnounceInterval);
} }
static int port_set_sync_tmo(struct port *p) static int port_set_sync_rx_tmo(struct port *p)
{ {
return set_tmo_log(p->fda.fd[FD_SYNC_TIMER], 1, p->logSyncInterval); return set_tmo_log(p->fda.fd[FD_SYNC_RX_TIMER],
p->syncReceiptTimeout, p->logSyncInterval);
}
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, static void port_show_transition(struct port *p,
@ -832,6 +854,8 @@ static void port_synchronize(struct port *p,
{ {
enum servo_state state; enum servo_state state;
port_set_sync_rx_tmo(p);
state = clock_synchronize(p->clock, ingress_ts, origin_ts, state = clock_synchronize(p->clock, ingress_ts, origin_ts,
correction1, correction2); correction1, correction2);
switch (state) { switch (state) {
@ -1274,6 +1298,7 @@ static int port_initialize(struct port *p)
p->peerMeanPathDelay = 0; p->peerMeanPathDelay = 0;
p->logAnnounceInterval = p->pod.logAnnounceInterval; p->logAnnounceInterval = p->pod.logAnnounceInterval;
p->announceReceiptTimeout = p->pod.announceReceiptTimeout; p->announceReceiptTimeout = p->pod.announceReceiptTimeout;
p->syncReceiptTimeout = p->pod.syncReceiptTimeout;
p->transportSpecific = p->pod.transportSpecific; p->transportSpecific = p->pod.transportSpecific;
p->logSyncInterval = p->pod.logSyncInterval; p->logSyncInterval = p->pod.logSyncInterval;
p->logMinPdelayReqInterval = p->pod.logMinPdelayReqInterval; p->logMinPdelayReqInterval = p->pod.logMinPdelayReqInterval;
@ -1798,6 +1823,7 @@ static void process_sync(struct port *p, struct ptp_message *m)
} }
if (p->syfu == SF_HAVE_FUP && if (p->syfu == SF_HAVE_FUP &&
fup_sync_ok(p->last_syncfup, m) &&
p->last_syncfup->header.sequenceId == m->header.sequenceId) { p->last_syncfup->header.sequenceId == m->header.sequenceId) {
event = SYNC_MATCH; event = SYNC_MATCH;
} else { } else {
@ -1851,10 +1877,11 @@ struct foreign_clock *port_compute_best(struct port *p)
static void port_e2e_transition(struct port *p, enum port_state next) static void port_e2e_transition(struct port *p, enum port_state next)
{ {
port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]); port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]);
port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]);
port_clr_tmo(p->fda.fd[FD_DELAY_TIMER]); port_clr_tmo(p->fda.fd[FD_DELAY_TIMER]);
port_clr_tmo(p->fda.fd[FD_QUALIFICATION_TIMER]); 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_MANNO_TIMER]);
port_clr_tmo(p->fda.fd[FD_SYNC_TIMER]); port_clr_tmo(p->fda.fd[FD_SYNC_TX_TIMER]);
switch (next) { switch (next) {
case PS_INITIALIZING: case PS_INITIALIZING:
@ -1871,8 +1898,8 @@ static void port_e2e_transition(struct port *p, enum port_state next)
break; break;
case PS_MASTER: case PS_MASTER:
case PS_GRAND_MASTER: case PS_GRAND_MASTER:
port_set_manno_tmo(p); set_tmo_log(p->fda.fd[FD_MANNO_TIMER], 1, -10); /*~1ms*/
port_set_sync_tmo(p); port_set_sync_tx_tmo(p);
break; break;
case PS_PASSIVE: case PS_PASSIVE:
port_set_announce_tmo(p); port_set_announce_tmo(p);
@ -1883,6 +1910,7 @@ static void port_e2e_transition(struct port *p, enum port_state next)
/* fall through */ /* fall through */
case PS_SLAVE: case PS_SLAVE:
port_set_announce_tmo(p); port_set_announce_tmo(p);
port_set_sync_rx_tmo(p);
port_set_delay_tmo(p); port_set_delay_tmo(p);
break; break;
}; };
@ -1891,10 +1919,11 @@ static void port_e2e_transition(struct port *p, enum port_state next)
static void port_p2p_transition(struct port *p, enum port_state next) static void port_p2p_transition(struct port *p, enum port_state next)
{ {
port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]); port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]);
port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]);
/* Leave FD_DELAY_TIMER running. */ /* Leave FD_DELAY_TIMER running. */
port_clr_tmo(p->fda.fd[FD_QUALIFICATION_TIMER]); 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_MANNO_TIMER]);
port_clr_tmo(p->fda.fd[FD_SYNC_TIMER]); port_clr_tmo(p->fda.fd[FD_SYNC_TX_TIMER]);
switch (next) { switch (next) {
case PS_INITIALIZING: case PS_INITIALIZING:
@ -1911,8 +1940,8 @@ static void port_p2p_transition(struct port *p, enum port_state next)
break; break;
case PS_MASTER: case PS_MASTER:
case PS_GRAND_MASTER: case PS_GRAND_MASTER:
port_set_manno_tmo(p); set_tmo_log(p->fda.fd[FD_MANNO_TIMER], 1, -10); /*~1ms*/
port_set_sync_tmo(p); port_set_sync_tx_tmo(p);
break; break;
case PS_PASSIVE: case PS_PASSIVE:
port_set_announce_tmo(p); port_set_announce_tmo(p);
@ -1923,6 +1952,7 @@ static void port_p2p_transition(struct port *p, enum port_state next)
/* fall through */ /* fall through */
case PS_SLAVE: case PS_SLAVE:
port_set_announce_tmo(p); port_set_announce_tmo(p);
port_set_sync_rx_tmo(p);
break; break;
}; };
} }
@ -1987,7 +2017,9 @@ enum fsm_event port_event(struct port *p, int fd_index)
switch (fd_index) { switch (fd_index) {
case FD_ANNOUNCE_TIMER: case FD_ANNOUNCE_TIMER:
pr_debug("port %hu: announce timeout", portnum(p)); 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) if (p->best)
fc_clear(p->best); fc_clear(p->best);
port_set_announce_tmo(p); port_set_announce_tmo(p);
@ -2011,9 +2043,9 @@ enum fsm_event port_event(struct port *p, int fd_index)
port_set_manno_tmo(p); port_set_manno_tmo(p);
return port_tx_announce(p) ? EV_FAULT_DETECTED : EV_NONE; return port_tx_announce(p) ? EV_FAULT_DETECTED : EV_NONE;
case FD_SYNC_TIMER: case FD_SYNC_TX_TIMER:
pr_debug("port %hu: master sync timeout", portnum(p)); pr_debug("port %hu: master sync timeout", portnum(p));
port_set_sync_tmo(p); port_set_sync_tx_tmo(p);
return port_tx_sync(p) ? EV_FAULT_DETECTED : EV_NONE; return port_tx_sync(p) ? EV_FAULT_DETECTED : EV_NONE;
} }

19
ptp4l.8
View File

@ -148,6 +148,13 @@ The number of missed Announce messages before the last Announce messages
expires. expires.
The default is 3. The default is 3.
.TP .TP
.B syncReceiptTimeout
The number of sync/follow up messages that may go missing before
triggering a Best Master Clock election. This option is used for
running in gPTP mode according to the 802.1AS-2011 standard. Setting
this option to zero will disable the sync message timeout.
The default is 0 or disabled.
.TP
.B transportSpecific .B transportSpecific
The transport specific field. Must be in the range 0 to 255. The transport specific field. Must be in the range 0 to 255.
The default is 0. The default is 0.
@ -248,6 +255,18 @@ The number of milliseconds to poll waiting for the tx time stamp from the kernel
when a message has recently been sent. when a message has recently been sent.
The default is 1. The default is 1.
.TP .TP
.B check_fup_sync
Because of packet reordering that can occur in the network, in the
hardware, or in the networking stack, a follow up message can appear
to arrive in the application before the matching sync message. As this
is a normal occurrence, and the sequenceID message field ensures
proper matching, the ptp4l program accepts out of order packets. This
option adds an additional check using the software time stamps from
the networking stack to verify that the sync message did arrive
first. This option is only useful if you do not trust the sequence IDs
generated by the master.
The default is 0 (disabled).
.TP
.B clock_servo .B clock_servo
The servo which is used to synchronize the local clock. Currently only one The servo which is used to synchronize the local clock. Currently only one
servo is implemented, a PI controller. servo is implemented, a PI controller.

View File

@ -77,6 +77,7 @@ static struct config cfg_settings = {
.logMinDelayReqInterval = 0, .logMinDelayReqInterval = 0,
.logMinPdelayReqInterval = 0, .logMinPdelayReqInterval = 0,
.announceReceiptTimeout = 3, .announceReceiptTimeout = 3,
.syncReceiptTimeout = 0,
.transportSpecific = 0, .transportSpecific = 0,
.path_trace_enabled = 0, .path_trace_enabled = 0,
.follow_up_info = 0, .follow_up_info = 0,
@ -91,6 +92,7 @@ static struct config cfg_settings = {
.assume_two_step = &assume_two_step, .assume_two_step = &assume_two_step,
.tx_timestamp_timeout = &sk_tx_timeout, .tx_timestamp_timeout = &sk_tx_timeout,
.check_fup_sync = &sk_check_fupsync,
.clock_servo = CLOCK_SERVO_PI, .clock_servo = CLOCK_SERVO_PI,

3
raw.c
View File

@ -209,6 +209,9 @@ static int raw_open(struct transport *t, char *name,
if (sk_timestamping_init(efd, name, ts_type, TRANS_IEEE_802_3)) if (sk_timestamping_init(efd, name, ts_type, TRANS_IEEE_802_3))
goto no_timestamping; goto no_timestamping;
if (sk_general_init(gfd))
goto no_timestamping;
fda->fd[FD_EVENT] = efd; fda->fd[FD_EVENT] = efd;
fda->fd[FD_GENERAL] = gfd; fda->fd[FD_GENERAL] = gfd;
return 0; return 0;

27
sk.c
View File

@ -36,6 +36,7 @@
/* globals */ /* globals */
int sk_tx_timeout = 1; int sk_tx_timeout = 1;
int sk_check_fupsync;
/* private methods */ /* private methods */
@ -91,6 +92,16 @@ int sk_interface_index(int fd, char *name)
return ifreq.ifr_ifindex; return ifreq.ifr_ifindex;
} }
int sk_general_init(int fd)
{
int on = sk_check_fupsync ? 1 : 0;
if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS, &on, sizeof(on)) < 0) {
pr_err("ioctl SO_TIMESTAMPNS failed: %m");
return -1;
}
return 0;
}
int sk_get_ts_info(char *name, struct sk_ts_info *sk_info) int sk_get_ts_info(char *name, struct sk_ts_info *sk_info)
{ {
#ifdef ETHTOOL_GET_TS_INFO #ifdef ETHTOOL_GET_TS_INFO
@ -205,7 +216,7 @@ int sk_receive(int fd, void *buf, int buflen,
struct cmsghdr *cm; struct cmsghdr *cm;
struct iovec iov = { buf, buflen }; struct iovec iov = { buf, buflen };
struct msghdr msg; struct msghdr msg;
struct timespec *ts = NULL; struct timespec *sw, *ts = NULL;
memset(control, 0, sizeof(control)); memset(control, 0, sizeof(control));
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
@ -241,7 +252,14 @@ int sk_receive(int fd, void *buf, int buflen,
return -1; return -1;
} }
ts = (struct timespec *) CMSG_DATA(cm); ts = (struct timespec *) CMSG_DATA(cm);
break; }
if (SOL_SOCKET == level && SO_TIMESTAMPNS == type) {
if (cm->cmsg_len < sizeof(*sw)) {
pr_warning("short SO_TIMESTAMPNS message");
return -1;
}
sw = (struct timespec *) CMSG_DATA(cm);
hwts->sw = *sw;
} }
} }
@ -325,5 +343,10 @@ int sk_timestamping_init(int fd, char *device, enum timestamp_type type,
return -1; return -1;
} }
/* Enable the sk_check_fupsync option, perhaps. */
if (sk_general_init(fd)) {
return -1;
}
return 0; return 0;
} }

14
sk.h
View File

@ -46,6 +46,13 @@ struct sk_ts_info {
*/ */
int sk_interface_index(int fd, char *device); int sk_interface_index(int fd, char *device);
/**
* Prepare a given socket for PTP "general" messages.
* @param fd An open socket.
* @return Zero on success, non-zero otherwise.
*/
int sk_general_init(int fd);
/** /**
* Obtain supported timestamping information * Obtain supported timestamping information
* @param name The name of the interface * @param name The name of the interface
@ -102,4 +109,11 @@ int sk_timestamping_init(int fd, char *device, enum timestamp_type type,
*/ */
extern int sk_tx_timeout; extern int sk_tx_timeout;
/**
* Enables the SO_TIMESTAMPNS socket option on the both the event and
* general sockets in order to test the order of paired sync and
* follow up messages using their network stack receipt time stamps.
*/
extern int sk_check_fupsync;
#endif #endif

View File

@ -57,6 +57,7 @@ enum timestamp_type {
struct hw_timestamp { struct hw_timestamp {
enum timestamp_type type; enum timestamp_type type;
struct timespec ts; struct timespec ts;
struct timespec sw;
}; };
struct transport; struct transport;

3
udp.c
View File

@ -179,6 +179,9 @@ static int udp_open(struct transport *t, char *name, struct fdarray *fda,
if (sk_timestamping_init(efd, name, ts_type, TRANS_UDP_IPV4)) if (sk_timestamping_init(efd, name, ts_type, TRANS_UDP_IPV4))
goto no_timestamping; goto no_timestamping;
if (sk_general_init(gfd))
goto no_timestamping;
fda->fd[FD_EVENT] = efd; fda->fd[FD_EVENT] = efd;
fda->fd[FD_GENERAL] = gfd; fda->fd[FD_GENERAL] = gfd;
return 0; return 0;

3
udp6.c
View File

@ -191,6 +191,9 @@ static int udp6_open(struct transport *t, char *name, struct fdarray *fda,
if (sk_timestamping_init(efd, name, ts_type, TRANS_UDP_IPV6)) if (sk_timestamping_init(efd, name, ts_type, TRANS_UDP_IPV6))
goto no_timestamping; goto no_timestamping;
if (sk_general_init(gfd))
goto no_timestamping;
fda->fd[FD_EVENT] = efd; fda->fd[FD_EVENT] = efd;
fda->fd[FD_GENERAL] = gfd; fda->fd[FD_GENERAL] = gfd;
return 0; return 0;