ingress timestamp
下面是收到message时如何生成ingress timestamp的相关内容。
以下内容出自sk_receive函数,ts和sw分别表示以硬件时间戳和软件时间戳生成的ingress timestamp。
struct cmsghdr *cm;
struct msghdr msg;
recvmsg(fd, &msg, flags);
for (cm = CMSG_FIRSTHDR(&msg); cm != NULL; cm = CMSG_NXTHDR(&msg, cm)) {
level = cm->cmsg_level;
type = cm->cmsg_type;
if (SOL_SOCKET == level && SO_TIMESTAMPING == type) {
ts = (struct timespec *) CMSG_DATA(cm);
}
if (SOL_SOCKET == level && SO_TIMESTAMPNS == type) {
sw = (struct timespec *) CMSG_DATA(cm);
hwts->sw = timespec_to_tmv(*sw);
}
}
上面的代码中涉及到几个socket中的概念。
根据链接中的描述,上面用到的 CMSG_xxx宏用于读取control message(或叫辅助数据)。
它不属于socket payload,通常用于携带一些额外的说明信息,如packet被接收的interface,一些额外的header field如IP options等。而在这里,明显用于承载时间戳。
它用sendmsg发送,通过recvmsg被接收。
其结构如下
struct cmsghdr {
size_t cmsg_len; /* Data byte count, including header
(type is socklen_t in POSIX) */
int cmsg_level; /* Originating protocol */
int cmsg_type; /* Protocol-specific type */
/* followed by unsigned char cmsg_data[]; */
};
SO_TIMESTAMP
Generates a timestamp for each incoming packet in (not necessarily monotonic) system time. Reports the timestamp via recvmsg() in a control message in usec resolution.
SO_TIMESTAMPNS
Same timestamping mechanism as SO_TIMESTAMP, but reports the timestamp as struct timespec in nsec resolution.
SO_TIMESTAMPING
Generates timestamps on reception, transmission or both. Supports multiple timestamp sources, including hardware. Supports generating timestamps for stream sockets.
从这里可以看出,SO_TIMESTAMPING支持硬件时间源,而其他的type则使用system time。所以在上面的代码中,type=SO_TIMESTAMPING表示收到的是硬件时间戳,否则为软件时间戳。
egress timestamp
主要看的是port_tx_sync函数,即sync message的发送流程。
说实话,在Linuxptp的代码中,从sync msg的allocate到使用send函数将其发出,我都没有看到有对msg的hwts进行任何操作。
在最后准备发出的raw_send函数内,&msg->hwts作为参数被传入,应该是要为其赋值。
cnt = send(fd, ptr, len, 0);
if (cnt < 1) {
return -errno;
}
/*
* Get the time stamp right away.
*/
return event == TRANS_EVENT ? sk_receive(fd, pkt, len, NULL, hwts, MSG_ERRQUEUE) : cnt;
但是在源码中,hwts却在msg被send后才首次被使用。
代码中的注释说明sk_receive函数是要用于get timestamp,但是在msg被send后再get timestamp有意义吗?
在链接中有这样一段话:
2.1.1 Transmit timestamps with MSG_ERRQUEUE
--For transmit timestamps the outgoing packet is looped back to the
socket's error queue with the send timestamp(s) attached. A process
receives the timestamps by calling recvmsg() with flag MSG_ERRQUEUE
set and with a msg_control buffer sufficiently large to receive the
relevant metadata structures. The recvmsg call returns the original
outgoing data packet with two ancillary messages attached.
--A message of cm_level SOL_IP(V6) and cm_type IP(V6)_RECVERR
embeds a struct sock_extended_err. This defines the error type. For
timestamps, the ee_errno field is ENOMSG. The other ancillary message
will have cm_level SOL_SOCKET and cm_type SCM_TIMESTAMPING. This
embeds the struct scm_timestamping.
根据这段话的内容,我猜测timestamp应该是放在了control message内。
在port_tx_sync函数中,调用port_prepare_and_send发送msg后,还有以下代码。
port_prepare_and_send(p, msg, event);
if (p->timestamping == TS_ONESTEP || p->timestamping == TS_P2P1STEP) {
goto out;
} else if (msg_sots_missing(msg)) {//检查msg->hwts.ts是否为0
pr_err("missing timestamp on transmitted sync");
err = -1;
goto out;
}
...//省略部分代码
fup->follow_up.preciseOriginTimestamp = tmv_to_Timestamp(msg->hwts.ts);
可知程序发出sync message后,通过recvmsg函数get egress timestamp,用于之后follow_up message的preciseOriginTimestamp。
那么在two-step机制下,生成时间戳的流程是比较合理的。
至于one-step clock下,sync message的originTimestamp field到底是在哪填的,我还没有头绪。