Add an optional extra check on sync and follow up message ordering.

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 patch adds an additional check using the software time stamps
from the networking stack to verify that the sync message did arrive
first. This check is only useful if the sequence IDs generated by
the master might possibly be incorrect.

Signed-off-by: Richard Cochran <richardcochran@gmail.com>
master
Richard Cochran 2013-08-29 10:54:55 +02:00
parent f299b6fb07
commit 7b7e046e91
7 changed files with 65 additions and 2 deletions

16
port.c
View File

@ -366,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)) {
@ -1808,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 {

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;