sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/networking/timestampingmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/zh_TW/networking/timestampingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/it_IT/networking/timestampingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/ja_JP/networking/timestampingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/ko_KR/networking/timestampingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/sp_SP/networking/timestampingmodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhcomment)}(h SPDX-License-Identifier: GPL-2.0h]h SPDX-License-Identifier: GPL-2.0}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhhX/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/networking/timestamping.rsthKubhnote)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]h paragraph)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hh5Documentation/translations/zh_CN/disclaimer-zh_CN.rsthKhhubah}(h]h ]h"]h$]h&]uh1hhhhhhhhNubh field_list)}(hhh](hfield)}(hhh](h field_name)}(hOriginalh]hOriginal}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhKubh field_body)}(h*Documentation/networking/timestamping.rst h]h)}(h)Documentation/networking/timestamping.rsth]h)Documentation/networking/timestamping.rst}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h翻译h]h翻译}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj hhhKubh)}(h-王亚鑫 Wang Yaxin h]h)}(h,王亚鑫 Wang Yaxin h](h王亚鑫 Wang Yaxin <}(hj hhhNhNubh reference)}(hwang.yaxin@zte.com.cnh]hwang.yaxin@zte.com.cn}(hj*hhhNhNubah}(h]h ]h"]h$]h&]refurimailto:wang.yaxin@zte.com.cnuh1j(hj ubh>}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK hjubah}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhsection)}(hhh](htitle)}(h 时间戳h]h 时间戳}(hj]hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hjXhhhhhK ubjW)}(hhh](j\)}(h1. 控制接口h]h1. 控制接口}(hjnhhhNhNubah}(h]h ]h"]h$]h&]uh1j[hjkhhhhhKubh)}(h0接收网络数据包时间戳的接口包括:h]h0接收网络数据包时间戳的接口包括:}(hj|hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjkhhubhdefinition_list)}(hhh](hdefinition_list_item)}(hXSO_TIMESTAMP 为每个传入数据包生成(不一定是单调的)系统时间时间戳。通过 recvmsg() 在控制消息中以微秒分辨率报告时间戳。 SO_TIMESTAMP 根据架构类型和 libc 的 lib 中的 time_t 表示方式定义为 SO_TIMESTAMP_NEW 或 SO_TIMESTAMP_OLD。 SO_TIMESTAMP_OLD 和 SO_TIMESTAMP_NEW 的控制消息格式分别为 struct __kernel_old_timeval 和 struct __kernel_sock_timeval。 h](hterm)}(h SO_TIMESTAMPh]h SO_TIMESTAMP}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubh definition)}(hhh]h)}(hX为每个传入数据包生成(不一定是单调的)系统时间时间戳。通过 recvmsg() 在控制消息中以微秒分辨率报告时间戳。 SO_TIMESTAMP 根据架构类型和 libc 的 lib 中的 time_t 表示方式定义为 SO_TIMESTAMP_NEW 或 SO_TIMESTAMP_OLD。 SO_TIMESTAMP_OLD 和 SO_TIMESTAMP_NEW 的控制消息格式分别为 struct __kernel_old_timeval 和 struct __kernel_sock_timeval。h]hX为每个传入数据包生成(不一定是单调的)系统时间时间戳。通过 recvmsg() 在控制消息中以微秒分辨率报告时间戳。 SO_TIMESTAMP 根据架构类型和 libc 的 lib 中的 time_t 表示方式定义为 SO_TIMESTAMP_NEW 或 SO_TIMESTAMP_OLD。 SO_TIMESTAMP_OLD 和 SO_TIMESTAMP_NEW 的控制消息格式分别为 struct __kernel_old_timeval 和 struct __kernel_sock_timeval。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hXsSO_TIMESTAMPNS 与 SO_TIMESTAMP 相同的时间戳机制,但以 struct timespec 格式报告时间戳, 纳秒分辨率。 SO_TIMESTAMPNS 根据架构类型和 libc 的 time_t 表示方式定义为 SO_TIMESTAMPNS_NEW 或 SO_TIMESTAMPNS_OLD。 控制消息格式对于 SO_TIMESTAMPNS_OLD 为 struct timespec, 对于 SO_TIMESTAMPNS_NEW 为 struct __kernel_timespec。 h](j)}(hSO_TIMESTAMPNSh]hSO_TIMESTAMPNS}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhK#hjubj)}(hhh]h)}(hXc与 SO_TIMESTAMP 相同的时间戳机制,但以 struct timespec 格式报告时间戳, 纳秒分辨率。 SO_TIMESTAMPNS 根据架构类型和 libc 的 time_t 表示方式定义为 SO_TIMESTAMPNS_NEW 或 SO_TIMESTAMPNS_OLD。 控制消息格式对于 SO_TIMESTAMPNS_OLD 为 struct timespec, 对于 SO_TIMESTAMPNS_NEW 为 struct __kernel_timespec。h]hXc与 SO_TIMESTAMP 相同的时间戳机制,但以 struct timespec 格式报告时间戳, 纳秒分辨率。 SO_TIMESTAMPNS 根据架构类型和 libc 的 time_t 表示方式定义为 SO_TIMESTAMPNS_NEW 或 SO_TIMESTAMPNS_OLD。 控制消息格式对于 SO_TIMESTAMPNS_OLD 为 struct timespec, 对于 SO_TIMESTAMPNS_NEW 为 struct __kernel_timespec。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhK#hjhhubj)}(hIP_MULTICAST_LOOP + SO_TIMESTAMP[NS] 仅用于多播:通过读取回环数据包接收时间戳,获得近似的传输时间戳。 h](j)}(h$IP_MULTICAST_LOOP + SO_TIMESTAMP[NS]h]h$IP_MULTICAST_LOOP + SO_TIMESTAMP[NS]}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhK&hjubj)}(hhh]h)}(h`仅用于多播:通过读取回环数据包接收时间戳,获得近似的传输时间戳。h]h`仅用于多播:通过读取回环数据包接收时间戳,获得近似的传输时间戳。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK&hjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhK&hjhhubj)}(hSO_TIMESTAMPING 在接收、传输或两者时生成时间戳。支持多个时间戳源,包括硬件。 支持为流套接字生成时间戳。 h](j)}(hSO_TIMESTAMPINGh]hSO_TIMESTAMPING}(hj&hhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhK+hj"ubj)}(hhh]h)}(h在接收、传输或两者时生成时间戳。支持多个时间戳源,包括硬件。 支持为流套接字生成时间戳。h]h在接收、传输或两者时生成时间戳。支持多个时间戳源,包括硬件。 支持为流套接字生成时间戳。}(hj7hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK)hj4ubah}(h]h ]h"]h$]h&]uh1jhj"ubeh}(h]h ]h"]h$]h&]uh1jhhhK+hjhhubeh}(h]h ]h"]h$]h&]uh1jhjkhhhhhNubjW)}(hhh](j\)}(hE1.1 SO_TIMESTAMP(也包括 SO_TIMESTAMP_OLD 和 SO_TIMESTAMP_NEW)h]hE1.1 SO_TIMESTAMP(也包括 SO_TIMESTAMP_OLD 和 SO_TIMESTAMP_NEW)}(hjZhhhNhNubah}(h]h ]h"]h$]h&]uh1j[hjWhhhhhK.ubh)}(h此套接字选项在接收路径上启用数据报的时间戳。由于目标套接字(如果有) 在网络栈早期未知,因此必须为所有数据包启用此功能。所有早期接收的时间 戳选项也是如此。h]h此套接字选项在接收路径上启用数据报的时间戳。由于目标套接字(如果有) 在网络栈早期未知,因此必须为所有数据包启用此功能。所有早期接收的时间 戳选项也是如此。}(hjhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK0hjWhhubh)}(h6有关接口详细信息,请参阅 `man 7 socket`。h](h%有关接口详细信息,请参阅 }(hjvhhhNhNubhtitle_reference)}(h`man 7 socket`h]h man 7 socket}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjvubh。}(hjvhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK4hjWhhubh)}(hc始终使用 SO_TIMESTAMP_NEW 时间戳以获得 struct __kernel_sock_timeval 格式的时间戳。h]hc始终使用 SO_TIMESTAMP_NEW 时间戳以获得 struct __kernel_sock_timeval 格式的时间戳。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK6hjWhhubh)}(h`如果时间在 2038 年后,SO_TIMESTAMP_OLD 在 32 位机器上将返回错误的时间戳。h]h`如果时间在 2038 年后,SO_TIMESTAMP_OLD 在 32 位机器上将返回错误的时间戳。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK9hjWhhubeh}(h].so-timestamp-so-timestamp-old-so-timestamp-newah ]h"]E1.1 so_timestamp(也包括 so_timestamp_old 和 so_timestamp_new)ah$]h&]uh1jVhjkhhhhhK.ubjW)}(hhh](j\)}(hK1.2 SO_TIMESTAMPNS(也包括 SO_TIMESTAMPNS_OLD 和 SO_TIMESTAMPNS_NEW)h]hK1.2 SO_TIMESTAMPNS(也包括 SO_TIMESTAMPNS_OLD 和 SO_TIMESTAMPNS_NEW)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j[hjhhhhhKhjhhubh)}(h_始终使用 SO_TIMESTAMPNS_NEW 时间戳获得 struct __kernel_timespec 格式 的时间戳。h]h_始终使用 SO_TIMESTAMPNS_NEW 时间戳获得 struct __kernel_timespec 格式 的时间戳。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKAhjhhubh)}(hb如果时间在 2038 年后,SO_TIMESTAMPNS_OLD 在 32 位机器上将返回错误的时间戳。h]hb如果时间在 2038 年后,SO_TIMESTAMPNS_OLD 在 32 位机器上将返回错误的时间戳。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKDhjhhubeh}(h]4so-timestampns-so-timestampns-old-so-timestampns-newah ]h"]K1.2 so_timestampns(也包括 so_timestampns_old 和 so_timestampns_new)ah$]h&]uh1jVhjkhhhhhKcmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_TS_OPT_ID; cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); *((__u32 *) CMSG_DATA(cmsg)) = opt_id; err = sendmsg(fd, msg, 0); h](j)}(hSOF_TIMESTAMPING_OPT_ID:h]hSOF_TIMESTAMPING_OPT_ID:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh](h)}(hX每个数据包生成一个唯一标识符。一个进程可以同时存在多个未完成的时间戳请求。 数据包在传输路径中可能会发生重排序(例如在数据包调度器中)。在这种情况下, 时间戳会以与原始send()调用不同的顺序排队到错误队列中。如此一来,仅根据 时间戳顺序或 payload(有效载荷)检查,并不总能将时间戳与原始send()调用 唯一匹配。h]hX每个数据包生成一个唯一标识符。一个进程可以同时存在多个未完成的时间戳请求。 数据包在传输路径中可能会发生重排序(例如在数据包调度器中)。在这种情况下, 时间戳会以与原始send()调用不同的顺序排队到错误队列中。如此一来,仅根据 时间戳顺序或 payload(有效载荷)检查,并不总能将时间戳与原始send()调用 唯一匹配。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubh)}(hXy此选项在 send() 时将每个数据包与唯一标识符关联,并与时间戳一起返回。 标识符源自每个套接字的 u32 计数器(会回绕)。对于数据报套接字,计数器 随每个发送的数据包递增。对于流套接字,它随每个字节递增。对于流套接字, 还要设置 SOF_TIMESTAMPING_OPT_ID_TCP,请参阅下面的部分。h]hXy此选项在 send() 时将每个数据包与唯一标识符关联,并与时间戳一起返回。 标识符源自每个套接字的 u32 计数器(会回绕)。对于数据报套接字,计数器 随每个发送的数据包递增。对于流套接字,它随每个字节递增。对于流套接字, 还要设置 SOF_TIMESTAMPING_OPT_ID_TCP,请参阅下面的部分。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubh)}(h计数器从零开始。在首次启用套接字选项时初始化。在禁用后再重新启用选项时 重置。重置计数器不会更改系统中现有数据包的标识符。h]h计数器从零开始。在首次启用套接字选项时初始化。在禁用后再重新启用选项时 重置。重置计数器不会更改系统中现有数据包的标识符。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubh)}(h此选项仅针对传输时间戳实现。在这种情况下,时间戳总是与sock_extended_err 结构体一起回环。该选项会修改ee_data字段,以传递一个在该套接字所有同时 存在的未完成时间戳请求中唯一的 ID。h]h此选项仅针对传输时间戳实现。在这种情况下,时间戳总是与sock_extended_err 结构体一起回环。该选项会修改ee_data字段,以传递一个在该套接字所有同时 存在的未完成时间戳请求中唯一的 ID。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubh)}(h进程可以通过控制消息SCM_TS_OPT_ID(TCP 套接字不支持)传递特定 ID, 从而选择性地覆盖默认生成的 ID,示例如下::h]h进程可以通过控制消息SCM_TS_OPT_ID(TCP 套接字不支持)传递特定 ID, 从而选择性地覆盖默认生成的 ID,示例如下:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubj)}(hXstruct msghdr *msg; ... cmsg = CMSG_FIRSTHDR(msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_TS_OPT_ID; cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); *((__u32 *) CMSG_DATA(cmsg)) = opt_id; err = sendmsg(fd, msg, 0);h]hXstruct msghdr *msg; ... cmsg = CMSG_FIRSTHDR(msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_TS_OPT_ID; cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); *((__u32 *) CMSG_DATA(cmsg)) = opt_id; err = sendmsg(fd, msg, 0);}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjubeh}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hXSOF_TIMESTAMPING_OPT_ID_TCP: 与 SOF_TIMESTAMPING_OPT_ID 一起传递给新的 TCP 时间戳应用程序。 SOF_TIMESTAMPING_OPT_ID 定义了流套接字计数器的增量,但其起始点 并不完全显而易见。此选项修复了这一点。 对于流套接字,如果设置了 SOF_TIMESTAMPING_OPT_ID,则此选项应始终 设置。在数据报套接字上,选项没有效果。 一个合理的期望是系统调用后计数器重置为零,因此后续写入 N 字节将生成 计数器为 N-1 的时间戳。SOF_TIMESTAMPING_OPT_ID_TCP 在所有条件下 都实现了此行为。 SOF_TIMESTAMPING_OPT_ID 不带修饰符时通常报告相同,特别是在套接字选项 在无数据传输时设置时。如果正在传输数据,它可能与输出队列的长度(SIOCOUTQ) 偏差。 差异是由于基于 snd_una 与 write_seq 的。snd_una 是 peer 确认的 stream 的偏移量。这取决于外部因素,例如网络 RTT。write_seq 是进程写入的最后一个 字节。此偏移量不受外部输入影响。 差异细微,在套接字选项初始化时配置时不易察觉,但 SOF_TIMESTAMPING_OPT_ID_TCP 行为在任何时候都更稳健。 h](j)}(hSOF_TIMESTAMPING_OPT_ID_TCP:h]hSOF_TIMESTAMPING_OPT_ID_TCP:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh](h)}(h与 SOF_TIMESTAMPING_OPT_ID 一起传递给新的 TCP 时间戳应用程序。 SOF_TIMESTAMPING_OPT_ID 定义了流套接字计数器的增量,但其起始点 并不完全显而易见。此选项修复了这一点。h]h与 SOF_TIMESTAMPING_OPT_ID 一起传递给新的 TCP 时间戳应用程序。 SOF_TIMESTAMPING_OPT_ID 定义了流套接字计数器的增量,但其起始点 并不完全显而易见。此选项修复了这一点。}(hj,hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj)ubh)}(h对于流套接字,如果设置了 SOF_TIMESTAMPING_OPT_ID,则此选项应始终 设置。在数据报套接字上,选项没有效果。h]h对于流套接字,如果设置了 SOF_TIMESTAMPING_OPT_ID,则此选项应始终 设置。在数据报套接字上,选项没有效果。}(hj:hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj)ubh)}(h一个合理的期望是系统调用后计数器重置为零,因此后续写入 N 字节将生成 计数器为 N-1 的时间戳。SOF_TIMESTAMPING_OPT_ID_TCP 在所有条件下 都实现了此行为。h]h一个合理的期望是系统调用后计数器重置为零,因此后续写入 N 字节将生成 计数器为 N-1 的时间戳。SOF_TIMESTAMPING_OPT_ID_TCP 在所有条件下 都实现了此行为。}(hjHhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj)ubh)}(hSOF_TIMESTAMPING_OPT_ID 不带修饰符时通常报告相同,特别是在套接字选项 在无数据传输时设置时。如果正在传输数据,它可能与输出队列的长度(SIOCOUTQ) 偏差。h]hSOF_TIMESTAMPING_OPT_ID 不带修饰符时通常报告相同,特别是在套接字选项 在无数据传输时设置时。如果正在传输数据,它可能与输出队列的长度(SIOCOUTQ) 偏差。}(hjVhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj)ubh)}(h差异是由于基于 snd_una 与 write_seq 的。snd_una 是 peer 确认的 stream 的偏移量。这取决于外部因素,例如网络 RTT。write_seq 是进程写入的最后一个 字节。此偏移量不受外部输入影响。h]h差异是由于基于 snd_una 与 write_seq 的。snd_una 是 peer 确认的 stream 的偏移量。这取决于外部因素,例如网络 RTT。write_seq 是进程写入的最后一个 字节。此偏移量不受外部输入影响。}(hjdhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj)ubh)}(h差异细微,在套接字选项初始化时配置时不易察觉,但 SOF_TIMESTAMPING_OPT_ID_TCP 行为在任何时候都更稳健。h]h差异细微,在套接字选项初始化时配置时不易察觉,但 SOF_TIMESTAMPING_OPT_ID_TCP 行为在任何时候都更稳健。}(hjrhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj)ubeh}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubj)}(hXSOF_TIMESTAMPING_OPT_CMSG: 支持所有时间戳数据包的 recv() cmsg。控制消息已无条件地在所有接收时间戳数据包 和 IPv6 数据包上支持,以及在发送时间戳数据包的 IPv4 数据包上支持。此选项扩展 了它们以在发送时间戳数据包的 IPv4 数据包上支持。一个用例是启用 socket 选项 IP_PKTINFO 以关联数据包与其出口设备,通过启用 socket 选项 IP_PKTINFO 同时。 h](j)}(hSOF_TIMESTAMPING_OPT_CMSG:h]hSOF_TIMESTAMPING_OPT_CMSG:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]h)}(hX支持所有时间戳数据包的 recv() cmsg。控制消息已无条件地在所有接收时间戳数据包 和 IPv6 数据包上支持,以及在发送时间戳数据包的 IPv4 数据包上支持。此选项扩展 了它们以在发送时间戳数据包的 IPv4 数据包上支持。一个用例是启用 socket 选项 IP_PKTINFO 以关联数据包与其出口设备,通过启用 socket 选项 IP_PKTINFO 同时。h]hX支持所有时间戳数据包的 recv() cmsg。控制消息已无条件地在所有接收时间戳数据包 和 IPv6 数据包上支持,以及在发送时间戳数据包的 IPv4 数据包上支持。此选项扩展 了它们以在发送时间戳数据包的 IPv4 数据包上支持。一个用例是启用 socket 选项 IP_PKTINFO 以关联数据包与其出口设备,通过启用 socket 选项 IP_PKTINFO 同时。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubj)}(hX\SOF_TIMESTAMPING_OPT_TSONLY: 仅适用于传输时间戳。使内核返回一个 cmsg 与一个空数据包一起,而不是与原 始数据包一起。这减少了套接字接收预算(SO_RCVBUF)中收取的内存量,并即使 在 sysctl net.core.tstamp_allow_data 为 0 时也提供时间戳。此选项禁用 SOF_TIMESTAMPING_OPT_CMSG。 h](j)}(hSOF_TIMESTAMPING_OPT_TSONLY:h]hSOF_TIMESTAMPING_OPT_TSONLY:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]h)}(hX>仅适用于传输时间戳。使内核返回一个 cmsg 与一个空数据包一起,而不是与原 始数据包一起。这减少了套接字接收预算(SO_RCVBUF)中收取的内存量,并即使 在 sysctl net.core.tstamp_allow_data 为 0 时也提供时间戳。此选项禁用 SOF_TIMESTAMPING_OPT_CMSG。h]hX>仅适用于传输时间戳。使内核返回一个 cmsg 与一个空数据包一起,而不是与原 始数据包一起。这减少了套接字接收预算(SO_RCVBUF)中收取的内存量,并即使 在 sysctl net.core.tstamp_allow_data 为 0 时也提供时间戳。此选项禁用 SOF_TIMESTAMPING_OPT_CMSG。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubj)}(hXSOF_TIMESTAMPING_OPT_STATS: 与传输时间戳一起获取的选项性统计信息。它必须与 SOF_TIMESTAMPING_OPT_TSONLY 一起使用。当传输时间戳可用时,统计信息可在类型为 SCM_TIMESTAMPING_OPT_STATS 的单独控制消息中获取,作为 TLV(struct nlattr)类型的列表。这些统计信息允许应 用程序将各种传输层统计信息与传输时间戳关联,例如某个数据块被 peer 的接收窗口限 制了多长时间。 h](j)}(hSOF_TIMESTAMPING_OPT_STATS:h]hSOF_TIMESTAMPING_OPT_STATS:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]h)}(hX与传输时间戳一起获取的选项性统计信息。它必须与 SOF_TIMESTAMPING_OPT_TSONLY 一起使用。当传输时间戳可用时,统计信息可在类型为 SCM_TIMESTAMPING_OPT_STATS 的单独控制消息中获取,作为 TLV(struct nlattr)类型的列表。这些统计信息允许应 用程序将各种传输层统计信息与传输时间戳关联,例如某个数据块被 peer 的接收窗口限 制了多长时间。h]hX与传输时间戳一起获取的选项性统计信息。它必须与 SOF_TIMESTAMPING_OPT_TSONLY 一起使用。当传输时间戳可用时,统计信息可在类型为 SCM_TIMESTAMPING_OPT_STATS 的单独控制消息中获取,作为 TLV(struct nlattr)类型的列表。这些统计信息允许应 用程序将各种传输层统计信息与传输时间戳关联,例如某个数据块被 peer 的接收窗口限 制了多长时间。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubj)}(hXSOF_TIMESTAMPING_OPT_PKTINFO: 启用 SCM_TIMESTAMPING_PKTINFO 控制消息以接收带有硬件时间戳的数据包。 消息包含 struct scm_ts_pktinfo,它提供接收数据包的实际接口索引和层 2 长度。 只有在 CONFIG_NET_RX_BUSY_POLL 启用且驱动程序使用 NAPI 时,才会返回非零的 有效接口索引。该结构还包含另外两个字段,但它们是保留字段且未定义。 h](j)}(hSOF_TIMESTAMPING_OPT_PKTINFO:h]hSOF_TIMESTAMPING_OPT_PKTINFO:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]h)}(hX启用 SCM_TIMESTAMPING_PKTINFO 控制消息以接收带有硬件时间戳的数据包。 消息包含 struct scm_ts_pktinfo,它提供接收数据包的实际接口索引和层 2 长度。 只有在 CONFIG_NET_RX_BUSY_POLL 启用且驱动程序使用 NAPI 时,才会返回非零的 有效接口索引。该结构还包含另外两个字段,但它们是保留字段且未定义。h]hX启用 SCM_TIMESTAMPING_PKTINFO 控制消息以接收带有硬件时间戳的数据包。 消息包含 struct scm_ts_pktinfo,它提供接收数据包的实际接口索引和层 2 长度。 只有在 CONFIG_NET_RX_BUSY_POLL 启用且驱动程序使用 NAPI 时,才会返回非零的 有效接口索引。该结构还包含另外两个字段,但它们是保留字段且未定义。}(hj.hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj+ubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubj)}(hX4SOF_TIMESTAMPING_OPT_TX_SWHW: 请求在 SOF_TIMESTAMPING_TX_HARDWARE 和 SOF_TIMESTAMPING_TX_SOFTWARE 同时启用时,为传出数据包生成硬件和软件时间戳。如果同时生成两个时间戳,两个单 独的消息将回环到套接字的错误队列,每个消息仅包含一个时间戳。 h](j)}(hSOF_TIMESTAMPING_OPT_TX_SWHW:h]hSOF_TIMESTAMPING_OPT_TX_SWHW:}(hjLhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjHubj)}(hhh]h)}(hX请求在 SOF_TIMESTAMPING_TX_HARDWARE 和 SOF_TIMESTAMPING_TX_SOFTWARE 同时启用时,为传出数据包生成硬件和软件时间戳。如果同时生成两个时间戳,两个单 独的消息将回环到套接字的错误队列,每个消息仅包含一个时间戳。h]hX请求在 SOF_TIMESTAMPING_TX_HARDWARE 和 SOF_TIMESTAMPING_TX_SOFTWARE 同时启用时,为传出数据包生成硬件和软件时间戳。如果同时生成两个时间戳,两个单 独的消息将回环到套接字的错误队列,每个消息仅包含一个时间戳。}(hj]hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjZubah}(h]h ]h"]h$]h&]uh1jhjHubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubj)}(hXSOF_TIMESTAMPING_OPT_RX_FILTER: 过滤掉虚假接收时间戳:仅当匹配的时间戳生成标志已启用时才报告接收时间戳。 接收时间戳在入口路径中生成较早,在数据包的目的套接字确定之前。如果任何套接 字启用接收时间戳,所有套接字的数据包将接收时间戳数据包。包括那些请求时间戳 报告与 SOF_TIMESTAMPING_SOFTWARE 和/或 SOF_TIMESTAMPING_RAW_HARDWARE, 但未请求接收时间戳生成。这可能发生在仅请求发送时间戳时。 接收虚假时间戳通常是无害的。进程可以忽略意外的非零值。但它使行为在其他套接 字上微妙地依赖。此标志隔离套接字以获得更确定的行为。 h](j)}(hSOF_TIMESTAMPING_OPT_RX_FILTER:h]hSOF_TIMESTAMPING_OPT_RX_FILTER:}(hj{hhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjwubj)}(hhh](h)}(hl过滤掉虚假接收时间戳:仅当匹配的时间戳生成标志已启用时才报告接收时间戳。h]hl过滤掉虚假接收时间戳:仅当匹配的时间戳生成标志已启用时才报告接收时间戳。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubh)}(hX接收时间戳在入口路径中生成较早,在数据包的目的套接字确定之前。如果任何套接 字启用接收时间戳,所有套接字的数据包将接收时间戳数据包。包括那些请求时间戳 报告与 SOF_TIMESTAMPING_SOFTWARE 和/或 SOF_TIMESTAMPING_RAW_HARDWARE, 但未请求接收时间戳生成。这可能发生在仅请求发送时间戳时。h]hX接收时间戳在入口路径中生成较早,在数据包的目的套接字确定之前。如果任何套接 字启用接收时间戳,所有套接字的数据包将接收时间戳数据包。包括那些请求时间戳 报告与 SOF_TIMESTAMPING_SOFTWARE 和/或 SOF_TIMESTAMPING_RAW_HARDWARE, 但未请求接收时间戳生成。这可能发生在仅请求发送时间戳时。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubh)}(h接收虚假时间戳通常是无害的。进程可以忽略意外的非零值。但它使行为在其他套接 字上微妙地依赖。此标志隔离套接字以获得更确定的行为。h]h接收虚假时间戳通常是无害的。进程可以忽略意外的非零值。但它使行为在其他套接 字上微妙地依赖。此标志隔离套接字以获得更确定的行为。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubeh}(h]h ]h"]h$]h&]uh1jhjwubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubeh}(h]h ]h"]h$]h&]uh1jhjhhhhhNubh)}(h新应用程序鼓励传递 SOF_TIMESTAMPING_OPT_ID 以区分时间戳并传递 SOF_TIMESTAMPING_OPT_TSONLY 以操作,而不管 sysctl net.core.tstamp_allow_data 的设置。h]h新应用程序鼓励传递 SOF_TIMESTAMPING_OPT_ID 以区分时间戳并传递 SOF_TIMESTAMPING_OPT_TSONLY 以操作,而不管 sysctl net.core.tstamp_allow_data 的设置。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX 例外情况是当进程需要额外的 cmsg 数据时,例如 SOL_IP/IP_PKTINFO 以检测出 口网络接口。然后传递选项 SOF_TIMESTAMPING_OPT_CMSG。此选项依赖于访问原 始数据包的内容,因此不能与 SOF_TIMESTAMPING_OPT_TSONLY 组合。h]hX 例外情况是当进程需要额外的 cmsg 数据时,例如 SOL_IP/IP_PKTINFO 以检测出 口网络接口。然后传递选项 SOF_TIMESTAMPING_OPT_CMSG。此选项依赖于访问原 始数据包的内容,因此不能与 SOF_TIMESTAMPING_OPT_TSONLY 组合。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id5ah ]h"]1.3.3 时间戳选项ah$]h&]uh1jVhjhhhhhKubjW)}(hhh](j\)}(h(1.3.4. 通过控制消息启用时间戳h]h(1.3.4. 通过控制消息启用时间戳}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j[hjhhhhhMubh)}(h除了套接字选项外,时间戳生成还可以通过 cmsg 按写入请求,仅适用于 SOF_TIMESTAMPING_TX_*(见第 1.3.1 节)。使用此功能,应用程序可以无需启用和 禁用时间戳即可采样每个 sendmsg() 的时间戳::h]h除了套接字选项外,时间戳生成还可以通过 cmsg 按写入请求,仅适用于 SOF_TIMESTAMPING_TX_*(见第 1.3.1 节)。使用此功能,应用程序可以无需启用和 禁用时间戳即可采样每个 sendmsg() 的时间戳:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubj)}(hXstruct msghdr *msg; ... cmsg = CMSG_FIRSTHDR(msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SO_TIMESTAMPING; cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); *((__u32 *) CMSG_DATA(cmsg)) = SOF_TIMESTAMPING_TX_SCHED | SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_ACK; err = sendmsg(fd, msg, 0);h]hXstruct msghdr *msg; ... cmsg = CMSG_FIRSTHDR(msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SO_TIMESTAMPING; cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); *((__u32 *) CMSG_DATA(cmsg)) = SOF_TIMESTAMPING_TX_SCHED | SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_ACK; err = sendmsg(fd, msg, 0);}hj sbah}(h]h ]h"]h$]h&]hhuh1jhhhM hjhhubh)}(hv通过 cmsg 设置的 SOF_TIMESTAMPING_TX_* 标志将覆盖通过 setsockopt 设置的 SOF_TIMESTAMPING_TX_* 标志。h]hv通过 cmsg 设置的 SOF_TIMESTAMPING_TX_* 标志将覆盖通过 setsockopt 设置的 SOF_TIMESTAMPING_TX_* 标志。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h\此外,应用程序仍然需要通过 setsockopt 启用时间戳报告以接收时间戳::h]h[此外,应用程序仍然需要通过 setsockopt 启用时间戳报告以接收时间戳:}(hj'hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubj)}(h__u32 val = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_OPT_ID /* 或任何其他标志 */; err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));h]h__u32 val = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_OPT_ID /* 或任何其他标志 */; err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));}hj5sbah}(h]h ]h"]h$]h&]hhuh1jhhhMhjhhubeh}(h]id6ah ]h"](1.3.4. 通过控制消息启用时间戳ah$]h&]uh1jVhjhhhhhMubeh}(h]7so-timestamping-so-timestamping-old-so-timestamping-newah ]h"]N1.3 so_timestamping(也包括 so_timestamping_old 和 so_timestamping_new)ah$]h&]uh1jVhjkhhhhhKGubjW)}(hhh](j\)}(h1.4 字节流时间戳h]h1.4 字节流时间戳}(hjVhhhNhNubah}(h]h ]h"]h$]h&]uh1j[hjShhhhhM"ubh)}(hX#SO_TIMESTAMPING 接口支持字节流的时间戳。每个请求解释为请求当整个缓冲区内容 通过时间戳点时。也就是说,对于流选项 SOF_TIMESTAMPING_TX_SOFTWARE 将记录 当所有字节都到达设备驱动程序时,无论数据被转换成多少个数据包。h]hX#SO_TIMESTAMPING 接口支持字节流的时间戳。每个请求解释为请求当整个缓冲区内容 通过时间戳点时。也就是说,对于流选项 SOF_TIMESTAMPING_TX_SOFTWARE 将记录 当所有字节都到达设备驱动程序时,无论数据被转换成多少个数据包。}(hjdhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM$hjShhubh)}(hXE一般来说,字节流没有自然分隔符,因此将时间戳与数据相关联是非平凡的。字节范围 可能跨段,任何段可能合并(可能合并先前分段缓冲区关联的独立 send() 调用)。段 可以重新排序,同一字节范围可以在多个段中并存,对于实现重传的协议。h]hXE一般来说,字节流没有自然分隔符,因此将时间戳与数据相关联是非平凡的。字节范围 可能跨段,任何段可能合并(可能合并先前分段缓冲区关联的独立 send() 调用)。段 可以重新排序,同一字节范围可以在多个段中并存,对于实现重传的协议。}(hjrhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM(hjShhubh)}(h所有时间戳必须实现相同的语义,否则它们是不可比较的。以不同于简单情况(缓冲区 到 skb 的 1:1 映射)的方式处理“罕见”角落情况是不够的,因为性能调试通常需要 关注这些异常。h]h所有时间戳必须实现相同的语义,否则它们是不可比较的。以不同于简单情况(缓冲区 到 skb 的 1:1 映射)的方式处理“罕见”角落情况是不够的,因为性能调试通常需要 关注这些异常。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM,hjShhubh)}(hX在实践中,时间戳可以与字节流段一致地关联,如果时间戳语义和测量时序的选择正确。 此挑战与决定 IP 分片策略没有不同。在那里,定义是仅对第一个分片进行时间戳。对 于字节流,我们选择仅在所有字节通过某个点时生成时间戳。SOF_TIMESTAMPING_TX_ACK 定义的实现和推理是容易的。一个需要考虑 SACK 的实现会更复杂,因为可能存在传输 空洞和乱序到达。h]hX在实践中,时间戳可以与字节流段一致地关联,如果时间戳语义和测量时序的选择正确。 此挑战与决定 IP 分片策略没有不同。在那里,定义是仅对第一个分片进行时间戳。对 于字节流,我们选择仅在所有字节通过某个点时生成时间戳。SOF_TIMESTAMPING_TX_ACK 定义的实现和推理是容易的。一个需要考虑 SACK 的实现会更复杂,因为可能存在传输 空洞和乱序到达。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM0hjShhubh)}(hX在主机上,TCP 也可以通过 Nagle、cork、autocork、分段和 GSO 打破简单的 1:1 缓冲区到 skbuff 映射。实现确保在所有情况下都正确,通过跟踪每个 send() 传递 给send() 的最后一个字节,即使它在 skbuff 扩展或合并操作后不再是最后一个字 节。它存储相关的序列号在 skb_shinfo(skb)->tskey。因为一个 skbuff 只有一 个这样的字段,所以只能生成一个时间戳。h]hX在主机上,TCP 也可以通过 Nagle、cork、autocork、分段和 GSO 打破简单的 1:1 缓冲区到 skbuff 映射。实现确保在所有情况下都正确,通过跟踪每个 send() 传递 给send() 的最后一个字节,即使它在 skbuff 扩展或合并操作后不再是最后一个字 节。它存储相关的序列号在 skb_shinfo(skb)->tskey。因为一个 skbuff 只有一 个这样的字段,所以只能生成一个时间戳。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM6hjShhubh)}(hXH在罕见情况下,如果两个请求折叠到同一个 skb,则时间戳请求可能会被错过。进程可 以通过始终在请求之间刷新 TCP 栈来检测此情况,例如启用 TCP_NODELAY 和禁用 TCP_CORK和 autocork。在 linux-4.7 之后,更好的预防合并方法是使用 MSG_EOR 标志在sendmsg()时。h]hXH在罕见情况下,如果两个请求折叠到同一个 skb,则时间戳请求可能会被错过。进程可 以通过始终在请求之间刷新 TCP 栈来检测此情况,例如启用 TCP_NODELAY 和禁用 TCP_CORK和 autocork。在 linux-4.7 之后,更好的预防合并方法是使用 MSG_EOR 标志在sendmsg()时。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM<hjShhubh)}(hX@这些预防措施确保时间戳仅在所有字节通过时间戳点时生成,假设网络栈本身不会重新 排序段。栈确实试图避免重新排序。唯一的例外是管理员控制:可以构造一个数据包调 度器配置,将来自同一流的不同段延迟不同。这种设置通常不常见。h]hX@这些预防措施确保时间戳仅在所有字节通过时间戳点时生成,假设网络栈本身不会重新 排序段。栈确实试图避免重新排序。唯一的例外是管理员控制:可以构造一个数据包调 度器配置,将来自同一流的不同段延迟不同。这种设置通常不常见。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMAhjShhubeh}(h]id7ah ]h"]1.4 字节流时间戳ah$]h&]uh1jVhjkhhhhhM"ubeh}(h]id2ah ]h"]1. 控制接口ah$]h&]uh1jVhjXhhhhhKubjW)}(hhh](j\)}(h2 数据接口h]h2 数据接口}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j[hjhhhhhMGubh)}(h时间戳通过 recvmsg() 的辅助数据功能读取。请参阅 `man 3 cmsg` 了解此接口的 详细信息。套接字手册页面 (`man 7 socket`) 描述了如何检索SO_TIMESTAMP 和 SO_TIMESTAMPNS 生成的数据包时间戳。h](hB时间戳通过 recvmsg() 的辅助数据功能读取。请参阅 }(hjhhhNhNubj)}(h `man 3 cmsg`h]h man 3 cmsg}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh: 了解此接口的 详细信息。套接字手册页面 (}(hjhhhNhNubj)}(h`man 7 socket`h]h man 7 socket}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubhU) 描述了如何检索SO_TIMESTAMP 和 SO_TIMESTAMPNS 生成的数据包时间戳。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMIhjhhubjW)}(hhh](j\)}(h2.1 SCM_TIMESTAMPING 记录h]h2.1 SCM_TIMESTAMPING 记录}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj hhhhhMOubh)}(hR这些时间戳在 cmsg_level SOL_SOCKET、cmsg_type SCM_TIMESTAMPING 和类型为h]hR这些时间戳在 cmsg_level SOL_SOCKET、cmsg_type SCM_TIMESTAMPING 和类型为}(hj* hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMQhj hhubh)}(h对于 SO_TIMESTAMPING_OLD::h]h对于 SO_TIMESTAMPING_OLD:}(hj8 hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMShj hhubj)}(h;struct scm_timestamping { struct timespec ts[3]; };h]h;struct scm_timestamping { struct timespec ts[3]; };}hjF sbah}(h]h ]h"]h$]h&]hhuh1jhhhMUhj hhubh)}(h对于 SO_TIMESTAMPING_NEW::h]h对于 SO_TIMESTAMPING_NEW:}(hjT hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMYhj hhubj)}(hCstruct scm_timestamping64 { struct __kernel_timespec ts[3];h]hCstruct scm_timestamping64 { struct __kernel_timespec ts[3];}hjb sbah}(h]h ]h"]h$]h&]hhuh1jhhhM[hj hhubh)}(hi始终使用 SO_TIMESTAMPING_NEW 时间戳以始终获得 struct scm_timestamping64 格式的时间戳。h]hi始终使用 SO_TIMESTAMPING_NEW 时间戳以始终获得 struct scm_timestamping64 格式的时间戳。}(hjp hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM^hj hhubh)}(hNSO_TIMESTAMPING_OLD 在 32 位机器上 2038 年后返回错误的时间戳。h]hNSO_TIMESTAMPING_OLD 在 32 位机器上 2038 年后返回错误的时间戳。}(hj~ hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMahj hhubh)}(h该结构可以返回最多三个时间戳。这是一个遗留功能。任何时候至少有一个字 段不为零。大多数时间戳都通过 ts[0] 传递。硬件时间戳通过 ts[2] 传递。h]h该结构可以返回最多三个时间戳。这是一个遗留功能。任何时候至少有一个字 段不为零。大多数时间戳都通过 ts[0] 传递。硬件时间戳通过 ts[2] 传递。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMchj hhubh)}(hX:ts[1] 以前用于存储硬件时间戳转换为系统时间。相反,将硬件时钟设备直接 暴露为HW PTP时钟源,以允许用户空间进行时间转换,并可选地与用户空间 PTP 堆栈(如linuxptp)同步系统时间。对于 PTP 时钟 API,请参阅 Documentation/driver-api/ptp.rst。h]hX:ts[1] 以前用于存储硬件时间戳转换为系统时间。相反,将硬件时钟设备直接 暴露为HW PTP时钟源,以允许用户空间进行时间转换,并可选地与用户空间 PTP 堆栈(如linuxptp)同步系统时间。对于 PTP 时钟 API,请参阅 Documentation/driver-api/ptp.rst。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMfhj hhubh)}(hX注意,如果同时启用了 SO_TIMESTAMP 或 SO_TIMESTAMPNS 与 SO_TIMESTAMPING 使用 SOF_TIMESTAMPING_SOFTWARE,在 recvmsg() 调用时会生成一个虚假的软件时间戳,并传递给 ts[0] 当真实软件时间戳缺 失时。这也发生在硬件传输时间戳上。h]hX注意,如果同时启用了 SO_TIMESTAMP 或 SO_TIMESTAMPNS 与 SO_TIMESTAMPING 使用 SOF_TIMESTAMPING_SOFTWARE,在 recvmsg() 调用时会生成一个虚假的软件时间戳,并传递给 ts[0] 当真实软件时间戳缺 失时。这也发生在硬件传输时间戳上。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMkhj hhubjW)}(hhh](j\)}(h%2.1.1 传输时间戳与 MSG_ERRQUEUEh]h%2.1.1 传输时间戳与 MSG_ERRQUEUE}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj hhhhhMqubh)}(hXZ对于传输时间戳,传出数据包回环到套接字的错误队列,并附加发送时间戳(s)。 进程通过调用带有 MSG_ERRQUEUE 标志的 recvmsg() 接收时间戳,并传递 一个足够大的 msg_control缓冲区以接收相关的元数据结构。recvmsg 调用 返回原始传出数据包,并附加两个辅助消息。h]hXZ对于传输时间戳,传出数据包回环到套接字的错误队列,并附加发送时间戳(s)。 进程通过调用带有 MSG_ERRQUEUE 标志的 recvmsg() 接收时间戳,并传递 一个足够大的 msg_control缓冲区以接收相关的元数据结构。recvmsg 调用 返回原始传出数据包,并附加两个辅助消息。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMshj hhubh)}(hX一个 cm_level SOL_IP(V6) 和 cm_type IP(V6)_RECVERR 嵌入一个 struct sock_extended_err这定义了错误类型。对于时间戳,ee_errno 字段是 ENOMSG。另一个辅助消息将具有 cm_level SOL_SOCKET 和 cm_type SCM_TIMESTAMPING。这嵌入了 struct scm_timestamping。h]hX一个 cm_level SOL_IP(V6) 和 cm_type IP(V6)_RECVERR 嵌入一个 struct sock_extended_err这定义了错误类型。对于时间戳,ee_errno 字段是 ENOMSG。另一个辅助消息将具有 cm_level SOL_SOCKET 和 cm_type SCM_TIMESTAMPING。这嵌入了 struct scm_timestamping。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMxhj hhubjW)}(hhh](j\)}(h2.1.1.2 时间戳类型h]h2.1.1.2 时间戳类型}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj hhhhhMubh)}(h三个 struct timespec 的语义由 struct sock_extended_err 中的 ee_info 字段定义。它包含一个类型 SCM_TSTAMP_* 来定义实际传递给 scm_timestamping 的时间戳。h]h三个 struct timespec 的语义由 struct sock_extended_err 中的 ee_info 字段定义。它包含一个类型 SCM_TSTAMP_* 来定义实际传递给 scm_timestamping 的时间戳。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(hX\SCM_TSTAMP_* 类型与之前讨论的 SOF_TIMESTAMPING_* 控制字段完全 匹配,只有一个例外对于遗留原因,SCM_TSTAMP_SND 等于零,可以设置为 SOF_TIMESTAMPING_TX_HARDWARE 和 SOF_TIMESTAMPING_TX_SOFTWARE。 它是第一个,如果 ts[2] 不为零,否则是第二个,在这种情况下,时间戳存 储在ts[0] 中。h]hX\SCM_TSTAMP_* 类型与之前讨论的 SOF_TIMESTAMPING_* 控制字段完全 匹配,只有一个例外对于遗留原因,SCM_TSTAMP_SND 等于零,可以设置为 SOF_TIMESTAMPING_TX_HARDWARE 和 SOF_TIMESTAMPING_TX_SOFTWARE。 它是第一个,如果 ts[2] 不为零,否则是第二个,在这种情况下,时间戳存 储在ts[0] 中。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubeh}(h]id9ah ]h"]2.1.1.2 时间戳类型ah$]h&]uh1jVhj hhhhhMubjW)}(hhh](j\)}(h2.1.1.3 分片h]h2.1.1.3 分片}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj hhhhhMubh)}(h传出数据报分片很少见,但可能发生,例如通过显式禁用 PMTU 发现。如果 传出数据包被分片,则仅对第一个分片进行时间戳,并返回给发送套接字。h]h传出数据报分片很少见,但可能发生,例如通过显式禁用 PMTU 发现。如果 传出数据包被分片,则仅对第一个分片进行时间戳,并返回给发送套接字。}(hj) hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubeh}(h]id10ah ]h"]2.1.1.3 分片ah$]h&]uh1jVhj hhhhhMubjW)}(hhh](j\)}(h2.1.1.4 数据包负载h]h2.1.1.4 数据包负载}(hjB hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj? hhhhhMubh)}(hX调用应用程序通常不关心接收它传递给堆栈的整个数据包负载:套接字错误队 列机制仅是一种将时间戳附加到其上的方法。在这种情况下,应用程序可以选 择读取较小的数据报,甚至长度为 0。负载相应地被截断。直到进程调用 recvmsg() 到错误队列,然而,整个数据包仍在队列中,占用 SO_RCVBUF 预算。h]hX调用应用程序通常不关心接收它传递给堆栈的整个数据包负载:套接字错误队 列机制仅是一种将时间戳附加到其上的方法。在这种情况下,应用程序可以选 择读取较小的数据报,甚至长度为 0。负载相应地被截断。直到进程调用 recvmsg() 到错误队列,然而,整个数据包仍在队列中,占用 SO_RCVBUF 预算。}(hjP hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj? hhubeh}(h]id11ah ]h"]2.1.1.4 数据包负载ah$]h&]uh1jVhj hhhhhMubjW)}(hhh](j\)}(h2.1.1.5 阻塞读取h]h2.1.1.5 阻塞读取}(hji hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hjf hhhhhMubh)}(hX*从错误队列读取始终是非阻塞操作。要阻塞等待时间戳,请使用 poll 或 select。poll() 将在 pollfd.revents 中返回 POLLERR,如果错误队列 中有数据。没有必要在 pollfd.events中传递此标志。此标志在请求时被忽 略。另请参阅 `man 2 poll`。h](hX从错误队列读取始终是非阻塞操作。要阻塞等待时间戳,请使用 poll 或 select。poll() 将在 pollfd.revents 中返回 POLLERR,如果错误队列 中有数据。没有必要在 pollfd.events中传递此标志。此标志在请求时被忽 略。另请参阅 }(hjw hhhNhNubj)}(h `man 2 poll`h]h man 2 poll}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjw ubh。}(hjw hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjf hhubeh}(h]id12ah ]h"]2.1.1.5 阻塞读取ah$]h&]uh1jVhj hhhhhMubeh}(h] msg-errqueueah ]h"]%2.1.1 传输时间戳与 msg_errqueueah$]h&]uh1jVhj hhhhhMqubjW)}(hhh](j\)}(h2.1.2 接收时间戳h]h2.1.2 接收时间戳}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj hhhhhMubh)}(hX在接收时,没有理由从套接字错误队列读取。SCM_TIMESTAMPING 辅助数据与 数据包数据一起通过正常 recvmsg() 发送。由于这不是套接字错误,它不伴 随消息 SOL_IP(V6)/IP(V6)_RECVERROR。在这种情况下,struct scm_timestamping 中的三个字段含义隐式定义。ts[0] 在设置时包含软件 时间戳,ts[1] 再次被弃用,ts[2] 在设置时包含硬件时间戳。h]hX在接收时,没有理由从套接字错误队列读取。SCM_TIMESTAMPING 辅助数据与 数据包数据一起通过正常 recvmsg() 发送。由于这不是套接字错误,它不伴 随消息 SOL_IP(V6)/IP(V6)_RECVERROR。在这种情况下,struct scm_timestamping 中的三个字段含义隐式定义。ts[0] 在设置时包含软件 时间戳,ts[1] 再次被弃用,ts[2] 在设置时包含硬件时间戳。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubeh}(h]id13ah ]h"]2.1.2 接收时间戳ah$]h&]uh1jVhj hhhhhMubeh}(h]scm-timestampingah ]h"]2.1 scm_timestamping 记录ah$]h&]uh1jVhjhhhhhMOubeh}(h]id8ah ]h"]2 数据接口ah$]h&]uh1jVhjXhhhhhMGubjW)}(hhh](j\)}(h73. 硬件时间戳配置:ETHTOOL_MSG_TSCONFIG_SET/GETh]h73. 硬件时间戳配置:ETHTOOL_MSG_TSCONFIG_SET/GET}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj hhhhhMubh)}(h硬件时间戳也必须为每个设备驱动程序初始化,该驱动程序预期执行硬件时间戳。 参数在 include/uapi/linux/net_tstamp.h 中定义为::h]h硬件时间戳也必须为每个设备驱动程序初始化,该驱动程序预期执行硬件时间戳。 参数在 include/uapi/linux/net_tstamp.h 中定义为:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubj)}(hstruct hwtstamp_config { int flags; /* 目前没有定义的标志,必须为零 */ int tx_type; /* HWTSTAMP_TX_* */ int rx_filter; /* HWTSTAMP_FILTER_* */ };h]hstruct hwtstamp_config { int flags; /* 目前没有定义的标志,必须为零 */ int tx_type; /* HWTSTAMP_TX_* */ int rx_filter; /* HWTSTAMP_FILTER_* */ };}hj sbah}(h]h ]h"]h$]h&]hhuh1jhhhMhj hhubh)}(hX期望的行为通过 tsconfig netlink 套接字 ``ETHTOOL_MSG_TSCONFIG_SET`` 传递到内核,并通过 ``ETHTOOL_A_TSCONFIG_TX_TYPES``、 ``ETHTOOL_A_TSCONFIG_RX_FILTERS`` 和 ``ETHTOOL_A_TSCONFIG_HWTSTAMP_FLAGS`` netlink 属性设置 struct hwtstamp_config 相应地。h](h1期望的行为通过 tsconfig netlink 套接字 }(hj hhhNhNubhliteral)}(h``ETHTOOL_MSG_TSCONFIG_SET``h]hETHTOOL_MSG_TSCONFIG_SET}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j hj ubh 传递到内核,并通过 }(hj hhhNhNubj )}(h``ETHTOOL_A_TSCONFIG_TX_TYPES``h]hETHTOOL_A_TSCONFIG_TX_TYPES}(hj' hhhNhNubah}(h]h ]h"]h$]h&]uh1j hj ubh、 }(hj hhhNhNubj )}(h!``ETHTOOL_A_TSCONFIG_RX_FILTERS``h]hETHTOOL_A_TSCONFIG_RX_FILTERS}(hj9 hhhNhNubah}(h]h ]h"]h$]h&]uh1j hj ubh 和 }(hj hhhNhNubj )}(h%``ETHTOOL_A_TSCONFIG_HWTSTAMP_FLAGS``h]h!ETHTOOL_A_TSCONFIG_HWTSTAMP_FLAGS}(hjK hhhNhNubah}(h]h ]h"]h$]h&]uh1j hj ubh9 netlink 属性设置 struct hwtstamp_config 相应地。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(h``ETHTOOL_A_TSCONFIG_HWTSTAMP_PROVIDER`` netlink 嵌套属性用于选择 硬件时间戳的来源。它由设备源的索引和时间戳类型限定符组成。h](j )}(h(``ETHTOOL_A_TSCONFIG_HWTSTAMP_PROVIDER``h]h$ETHTOOL_A_TSCONFIG_HWTSTAMP_PROVIDER}(hjg hhhNhNubah}(h]h ]h"]h$]h&]uh1j hjc ubhy netlink 嵌套属性用于选择 硬件时间戳的来源。它由设备源的索引和时间戳类型限定符组成。}(hjc hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(hXN驱动程序可以自由使用比请求更宽松的配置。预期驱动程序应仅实现可以直接支持的 最通用模式。例如,如果硬件可以支持 HWTSTAMP_FILTER_PTP_V2_EVENT,则它 通常应始终升级HWTSTAMP_FILTER_PTP_V2_L2_SYNC,依此类推,因为 HWTSTAMP_FILTER_PTP_V2_EVENT 更通用(更实用)。h]hXN驱动程序可以自由使用比请求更宽松的配置。预期驱动程序应仅实现可以直接支持的 最通用模式。例如,如果硬件可以支持 HWTSTAMP_FILTER_PTP_V2_EVENT,则它 通常应始终升级HWTSTAMP_FILTER_PTP_V2_L2_SYNC,依此类推,因为 HWTSTAMP_FILTER_PTP_V2_EVENT 更通用(更实用)。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(hX支持硬件时间戳的驱动程序应更新 struct,并可能返回更宽松的实际配置。如果 请求的数据包无法进行时间戳,则不应更改任何内容,并返回 ERANGE(与 EINVAL 相反,这表明 SIOCSHWTSTAMP 根本不支持)。h]hX支持硬件时间戳的驱动程序应更新 struct,并可能返回更宽松的实际配置。如果 请求的数据包无法进行时间戳,则不应更改任何内容,并返回 ERANGE(与 EINVAL 相反,这表明 SIOCSHWTSTAMP 根本不支持)。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(h只有具有管理权限的进程才能更改配置。用户空间负责确保多个进程不会相互干扰, 并确保设置被重置。h]h只有具有管理权限的进程才能更改配置。用户空间负责确保多个进程不会相互干扰, 并确保设置被重置。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(ho任何进程都可以通过请求 tsconfig netlink 套接字 ``ETHTOOL_MSG_TSCONFIG_GET`` 读取实际配置。h](h=任何进程都可以通过请求 tsconfig netlink 套接字 }(hj hhhNhNubj )}(h``ETHTOOL_MSG_TSCONFIG_GET``h]hETHTOOL_MSG_TSCONFIG_GET}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j hj ubh 读取实际配置。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(hX遗留配置是使用 ioctl(SIOCSHWTSTAMP) 与指向 struct ifreq 的指针,其 ifr_data指向 struct hwtstamp_config。tx_type 和 rx_filter 是驱动 程序期望执行的提示。如果请求的细粒度过滤对传入数据包不支持,驱动程序可能 会对请求的数据包进行时间戳。ioctl(SIOCGHWTSTAMP) 以与 ioctl(SIOCSHWTSTAMP) 相同的方式使用。然而,并非所有驱动程序都实现了这一点。h]hX遗留配置是使用 ioctl(SIOCSHWTSTAMP) 与指向 struct ifreq 的指针,其 ifr_data指向 struct hwtstamp_config。tx_type 和 rx_filter 是驱动 程序期望执行的提示。如果请求的细粒度过滤对传入数据包不支持,驱动程序可能 会对请求的数据包进行时间戳。ioctl(SIOCGHWTSTAMP) 以与 ioctl(SIOCSHWTSTAMP) 相同的方式使用。然而,并非所有驱动程序都实现了这一点。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubj)}(hX/* 可能的 hwtstamp_config->tx_type 值 */ enum { /* * 不会需要硬件时间戳的传出数据包; * 如果数据包到达并请求它,则不会进行硬件时间戳 */ HWTSTAMP_TX_OFF, /* * 启用传出数据包的硬件时间戳; * 数据包的发送者决定哪些数据包需要时间戳, * 在发送数据包之前设置 SOF_TIMESTAMPING_TX_SOFTWARE */ HWTSTAMP_TX_ON, }; /* 可能的 hwtstamp_config->rx_filter 值 */ enum { /* 时间戳不传入任何数据包 */ HWTSTAMP_FILTER_NONE, /* 时间戳任何传入数据包 */ HWTSTAMP_FILTER_ALL, /* 返回值:时间戳所有请求的数据包加上一些其他数据包 */ HWTSTAMP_FILTER_SOME, /* PTP v1,UDP,任何事件数据包 */ HWTSTAMP_FILTER_PTP_V1_L4_EVENT, /* 有关完整值列表,请检查 * 文件 include/uapi/linux/net_tstamp.h */ };h]hX/* 可能的 hwtstamp_config->tx_type 值 */ enum { /* * 不会需要硬件时间戳的传出数据包; * 如果数据包到达并请求它,则不会进行硬件时间戳 */ HWTSTAMP_TX_OFF, /* * 启用传出数据包的硬件时间戳; * 数据包的发送者决定哪些数据包需要时间戳, * 在发送数据包之前设置 SOF_TIMESTAMPING_TX_SOFTWARE */ HWTSTAMP_TX_ON, }; /* 可能的 hwtstamp_config->rx_filter 值 */ enum { /* 时间戳不传入任何数据包 */ HWTSTAMP_FILTER_NONE, /* 时间戳任何传入数据包 */ HWTSTAMP_FILTER_ALL, /* 返回值:时间戳所有请求的数据包加上一些其他数据包 */ HWTSTAMP_FILTER_SOME, /* PTP v1,UDP,任何事件数据包 */ HWTSTAMP_FILTER_PTP_V1_L4_EVENT, /* 有关完整值列表,请检查 * 文件 include/uapi/linux/net_tstamp.h */ };}hj sbah}(h]h ]h"]h$]h&]hhuh1jhhhMhj hhubjW)}(hhh](j\)}(h.3.1 硬件时间戳实现:设备驱动程序h]h.3.1 硬件时间戳实现:设备驱动程序}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj hhhhhMubh)}(h支持硬件时间戳的驱动程序必须支持 ndo_hwtstamp_set NDO 或遗留 SIOCSHWTSTAMP ioctl 并更新提供的 struct hwtstamp_config 与实际值,如 SIOCSHWTSTAMP 部分 所述。它还应支持 ndo_hwtstamp_get 或遗留 SIOCGHWTSTAMP。h]h支持硬件时间戳的驱动程序必须支持 ndo_hwtstamp_set NDO 或遗留 SIOCSHWTSTAMP ioctl 并更新提供的 struct hwtstamp_config 与实际值,如 SIOCSHWTSTAMP 部分 所述。它还应支持 ndo_hwtstamp_get 或遗留 SIOCGHWTSTAMP。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(h接收数据包的时间戳必须存储在 skb 中。要获取 skb 的共享时间戳结构,请调用 skb_hwtstamps()。然后设置结构中的时间戳::h]h接收数据包的时间戳必须存储在 skb 中。要获取 skb 的共享时间戳结构,请调用 skb_hwtstamps()。然后设置结构中的时间戳:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubj)}(hstruct skb_shared_hwtstamps { /* 硬件时间戳转换为自任意时间点的持续时间 * 自定义点 */ ktime_t hwtstamp; };h]hstruct skb_shared_hwtstamps { /* 硬件时间戳转换为自任意时间点的持续时间 * 自定义点 */ ktime_t hwtstamp; };}hj sbah}(h]h ]h"]h$]h&]hhuh1jhhhMhj hhubh)}(h6传出数据包的时间戳应按如下方式生成:h]h6传出数据包的时间戳应按如下方式生成:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh bullet_list)}(hhh](h list_item)}(h在 hard_start_xmit() 中,检查 (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) 是否不为零。如果是,则驱动程序期望执行硬件时间戳。h]h)}(h在 hard_start_xmit() 中,检查 (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) 是否不为零。如果是,则驱动程序期望执行硬件时间戳。h]h在 hard_start_xmit() 中,检查 (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) 是否不为零。如果是,则驱动程序期望执行硬件时间戳。}(hj9 hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj5 ubah}(h]h ]h"]h$]h&]uh1j3 hj0 hhhhhNubj4 )}(hX如果此 skb 和请求都可能,则声明驱动程序正在执行时间戳,通过设置 skb_shinfo(skb)->tx_flags 中的标志SKBTX_IN_PROGRESS,例如:: skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 您可能希望保留与 skb 关联的指针,而不是释放 skb。不支持硬件时间戳的驱 动程序不会这样做。驱动程序绝不能触及 sk_buff::tstamp!它用于存储网络 子系统生成的软件时间戳。h](h)}(h如果此 skb 和请求都可能,则声明驱动程序正在执行时间戳,通过设置 skb_shinfo(skb)->tx_flags 中的标志SKBTX_IN_PROGRESS,例如::h]h如果此 skb 和请求都可能,则声明驱动程序正在执行时间戳,通过设置 skb_shinfo(skb)->tx_flags 中的标志SKBTX_IN_PROGRESS,例如:}(hjQ hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjM ubj)}(h/skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;h]h/skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;}hj_ sbah}(h]h ]h"]h$]h&]hhuh1jhhhMhjM ubh)}(h您可能希望保留与 skb 关联的指针,而不是释放 skb。不支持硬件时间戳的驱 动程序不会这样做。驱动程序绝不能触及 sk_buff::tstamp!它用于存储网络 子系统生成的软件时间戳。h]h您可能希望保留与 skb 关联的指针,而不是释放 skb。不支持硬件时间戳的驱 动程序不会这样做。驱动程序绝不能触及 sk_buff::tstamp!它用于存储网络 子系统生成的软件时间戳。}(hjm hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjM ubeh}(h]h ]h"]h$]h&]uh1j3 hj0 hhhhhNubj4 )}(h驱动程序应在尽可能接近将 sk_buff 传递给硬件时调用 skb_tx_timestamp()。 skb_tx_timestamp()提供软件时间戳(如果请求),并且硬件时间戳不可用 (SKBTX_IN_PROGRESS 未设置)。h]h)}(h驱动程序应在尽可能接近将 sk_buff 传递给硬件时调用 skb_tx_timestamp()。 skb_tx_timestamp()提供软件时间戳(如果请求),并且硬件时间戳不可用 (SKBTX_IN_PROGRESS 未设置)。h]h驱动程序应在尽可能接近将 sk_buff 传递给硬件时调用 skb_tx_timestamp()。 skb_tx_timestamp()提供软件时间戳(如果请求),并且硬件时间戳不可用 (SKBTX_IN_PROGRESS 未设置)。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubah}(h]h ]h"]h$]h&]uh1j3 hj0 hhhhhNubj4 )}(hX一旦驱动程序发送数据包并/或获取硬件时间戳,它就会通过 skb_tstamp_tx() 传递时间戳,原始 skb,原始硬件时间戳。skb_tstamp_tx() 克隆原始 skb 并 添加时间戳,因此原始 skb 现在必须释放。如果获取硬件时间戳失败,则驱动程序 不应回退到软件时间戳。理由是,这会在处理管道中的稍后时间发生,而不是其他软 件时间戳,因此可能导致时间戳之间的差异。 h]h)}(hX一旦驱动程序发送数据包并/或获取硬件时间戳,它就会通过 skb_tstamp_tx() 传递时间戳,原始 skb,原始硬件时间戳。skb_tstamp_tx() 克隆原始 skb 并 添加时间戳,因此原始 skb 现在必须释放。如果获取硬件时间戳失败,则驱动程序 不应回退到软件时间戳。理由是,这会在处理管道中的稍后时间发生,而不是其他软 件时间戳,因此可能导致时间戳之间的差异。h]hX一旦驱动程序发送数据包并/或获取硬件时间戳,它就会通过 skb_tstamp_tx() 传递时间戳,原始 skb,原始硬件时间戳。skb_tstamp_tx() 克隆原始 skb 并 添加时间戳,因此原始 skb 现在必须释放。如果获取硬件时间戳失败,则驱动程序 不应回退到软件时间戳。理由是,这会在处理管道中的稍后时间发生,而不是其他软 件时间戳,因此可能导致时间戳之间的差异。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubah}(h]h ]h"]h$]h&]uh1j3 hj0 hhhhhNubeh}(h]h ]h"]h$]h&]bullet-uh1j. hhhMhj hhubeh}(h]id14ah ]h"].3.1 硬件时间戳实现:设备驱动程序ah$]h&]uh1jVhj hhhhhMubjW)}(hhh](j\)}(h*3.2 堆叠 PTP 硬件时钟的特殊考虑h]h*3.2 堆叠 PTP 硬件时钟的特殊考虑}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj hhhhhM$ubh)}(hX;在数据包的路径中可能存在多个 PHC(PTP 硬件时钟)。内核没有明确的机制允许用 户选择用于时间戳以太网帧的 PHC。相反,假设最外层的 PHC 始终是最优的,并且 内核驱动程序协作以实现这一目标。目前有 3 种堆叠 PHC 的情况,如下所示:h]hX;在数据包的路径中可能存在多个 PHC(PTP 硬件时钟)。内核没有明确的机制允许用 户选择用于时间戳以太网帧的 PHC。相反,假设最外层的 PHC 始终是最优的,并且 内核驱动程序协作以实现这一目标。目前有 3 种堆叠 PHC 的情况,如下所示:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM&hj hhubjW)}(hhh](j\)}(h-3.2.1 DSA(分布式交换架构)交换机h]h-3.2.1 DSA(分布式交换架构)交换机}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj hhhhhM+ubh)}(hXk这些是具有一个端口连接到(完全不知情的)主机以太网接口的以太网交换机,并且 执行端口多路复用或可选转发加速功能。每个 DSA 交换机端口在用户看来都是独立的 (虚拟)网络接口,其网络 I/O 在底层通过主机接口(在 TX 上重定向到主机端口, 在 RX 上拦截帧)执行。h]hXk这些是具有一个端口连接到(完全不知情的)主机以太网接口的以太网交换机,并且 执行端口多路复用或可选转发加速功能。每个 DSA 交换机端口在用户看来都是独立的 (虚拟)网络接口,其网络 I/O 在底层通过主机接口(在 TX 上重定向到主机端口, 在 RX 上拦截帧)执行。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM-hj hhubh)}(hXk当 DSA 交换机连接到主机端口时,PTP 同步必须受到限制,因为交换机的可变排队 延迟引入了主机端口与其 PTP 伙伴之间的路径延迟抖动。因此,一些 DSA 交换机 包含自己的时间戳时钟,并具有在自身 MAC上执行网络时间戳的能力,因此路径延迟 仅测量线缆和 PHY 传播延迟。支持 Linux 的 DSA 交换机暴露了与任何其他网络 接口相同的 ABI(除了 DSA 接口在网络 I/O 方面实际上是虚拟的,它们确实有自 己的PHC)。典型地,但不是强制性地,所有DSA 交换机接口共享相同的 PHC。h]hXk当 DSA 交换机连接到主机端口时,PTP 同步必须受到限制,因为交换机的可变排队 延迟引入了主机端口与其 PTP 伙伴之间的路径延迟抖动。因此,一些 DSA 交换机 包含自己的时间戳时钟,并具有在自身 MAC上执行网络时间戳的能力,因此路径延迟 仅测量线缆和 PHY 传播延迟。支持 Linux 的 DSA 交换机暴露了与任何其他网络 接口相同的 ABI(除了 DSA 接口在网络 I/O 方面实际上是虚拟的,它们确实有自 己的PHC)。典型地,但不是强制性地,所有DSA 交换机接口共享相同的 PHC。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM2hj hhubh)}(hX通过设计,DSA 交换机对连接到其主机端口的 PTP 时间戳不需要任何特殊的驱动程 序处理。然而,当主机端口也支持 PTP 时间戳时,DSA 将负责拦截 ``.ndo_eth_ioctl`` 调用,并阻止尝试在主机端口上启用硬件时间戳。这是因为 SO_TIMESTAMPING API 不允许为同一数据包传递多个硬件时间戳,因此除了 DSA 交换机端口之外的任何人都不应阻止这样做。h](h通过设计,DSA 交换机对连接到其主机端口的 PTP 时间戳不需要任何特殊的驱动程 序处理。然而,当主机端口也支持 PTP 时间戳时,DSA 将负责拦截 }(hj hhhNhNubj )}(h``.ndo_eth_ioctl``h]h.ndo_eth_ioctl}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j hj ubh 调用,并阻止尝试在主机端口上启用硬件时间戳。这是因为 SO_TIMESTAMPING API 不允许为同一数据包传递多个硬件时间戳,因此除了 DSA 交换机端口之外的任何人都不应阻止这样做。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM9hj hhubh)}(hE在通用层,DSA 提供了以下基础设施用于 PTP 时间戳:h]hE在通用层,DSA 提供了以下基础设施用于 PTP 时间戳:}(hj- hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM?hj hhubj/ )}(hhh](j4 )}(hXN``.port_txtstamp()``:在用户空间从用户空间请求带有硬件 TX 时间戳请求 的数据包之前调用的钩子。这是必需的,因为硬件时间戳在实际 MAC 传输后才可 用,因此驱动程序必须准备将时间戳与原始数据包相关联,以便它可以重新入队数 据包到套接字的错误队列。为了保存可能在时间戳可用时需要的数据包,驱动程序 可以调用 ``skb_clone_sk``,在 skb->cb 中保存克隆指针,并入队一个 tx skb 队列。通常,交换机会有一个PTP TX 时间戳寄存器(或有时是一个 FIFO), 其中时间戳可用。在 FIFO 的情况下,硬件可能会存储PTP 序列 ID/消息类型/ 域号和实际时间戳的键值对。为了在等待时间戳的数据包队列和实际时间戳之间正 确关联,驱动程序可以使用 BPF 分类器(``ptp_classify_raw``) 来识别 PTP 传输类型,并使用 ``ptp_parse_header`` 解释 PTP 头字段。可能存在一个 IRQ, 当此时间戳可用时触发,或者驱动程序可能需要轮询,在调用 ``dev_queue_xmit()`` 到主机接口之后。单步 TX 时间戳不需要数据包克隆,因为 PTP 协议不需要后续消 息(因为TX 时间戳已嵌入到数据包中),因此用户空间不期望数据包带有 TX 时间戳 被重新入队到其套接字的错误队列。 h]h)}(hXM``.port_txtstamp()``:在用户空间从用户空间请求带有硬件 TX 时间戳请求 的数据包之前调用的钩子。这是必需的,因为硬件时间戳在实际 MAC 传输后才可 用,因此驱动程序必须准备将时间戳与原始数据包相关联,以便它可以重新入队数 据包到套接字的错误队列。为了保存可能在时间戳可用时需要的数据包,驱动程序 可以调用 ``skb_clone_sk``,在 skb->cb 中保存克隆指针,并入队一个 tx skb 队列。通常,交换机会有一个PTP TX 时间戳寄存器(或有时是一个 FIFO), 其中时间戳可用。在 FIFO 的情况下,硬件可能会存储PTP 序列 ID/消息类型/ 域号和实际时间戳的键值对。为了在等待时间戳的数据包队列和实际时间戳之间正 确关联,驱动程序可以使用 BPF 分类器(``ptp_classify_raw``) 来识别 PTP 传输类型,并使用 ``ptp_parse_header`` 解释 PTP 头字段。可能存在一个 IRQ, 当此时间戳可用时触发,或者驱动程序可能需要轮询,在调用 ``dev_queue_xmit()`` 到主机接口之后。单步 TX 时间戳不需要数据包克隆,因为 PTP 协议不需要后续消 息(因为TX 时间戳已嵌入到数据包中),因此用户空间不期望数据包带有 TX 时间戳 被重新入队到其套接字的错误队列。h](j )}(h``.port_txtstamp()``h]h.port_txtstamp()}(hjF hhhNhNubah}(h]h ]h"]h$]h&]uh1j hjB ubhX:在用户空间从用户空间请求带有硬件 TX 时间戳请求 的数据包之前调用的钩子。这是必需的,因为硬件时间戳在实际 MAC 传输后才可 用,因此驱动程序必须准备将时间戳与原始数据包相关联,以便它可以重新入队数 据包到套接字的错误队列。为了保存可能在时间戳可用时需要的数据包,驱动程序 可以调用 }(hjB hhhNhNubj )}(h``skb_clone_sk``h]h skb_clone_sk}(hjX hhhNhNubah}(h]h ]h"]h$]h&]uh1j hjB ubhX,在 skb->cb 中保存克隆指针,并入队一个 tx skb 队列。通常,交换机会有一个PTP TX 时间戳寄存器(或有时是一个 FIFO), 其中时间戳可用。在 FIFO 的情况下,硬件可能会存储PTP 序列 ID/消息类型/ 域号和实际时间戳的键值对。为了在等待时间戳的数据包队列和实际时间戳之间正 确关联,驱动程序可以使用 BPF 分类器(}(hjB hhhNhNubj )}(h``ptp_classify_raw``h]hptp_classify_raw}(hjj hhhNhNubah}(h]h ]h"]h$]h&]uh1j hjB ubh)) 来识别 PTP 传输类型,并使用 }(hjB hhhNhNubj )}(h``ptp_parse_header``h]hptp_parse_header}(hj| hhhNhNubah}(h]h ]h"]h$]h&]uh1j hjB ubh 解释 PTP 头字段。可能存在一个 IRQ, 当此时间戳可用时触发,或者驱动程序可能需要轮询,在调用 }(hjB hhhNhNubj )}(h``dev_queue_xmit()``h]hdev_queue_xmit()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j hjB ubhX  到主机接口之后。单步 TX 时间戳不需要数据包克隆,因为 PTP 协议不需要后续消 息(因为TX 时间戳已嵌入到数据包中),因此用户空间不期望数据包带有 TX 时间戳 被重新入队到其套接字的错误队列。}(hjB hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMAhj> ubah}(h]h ]h"]h$]h&]uh1j3 hj; hhhhhNubj4 )}(hX``.port_rxtstamp()``:在 RX 上,DSA 运行 BPF 分类器以识别 PTP 事件消息 (任何其他数据包,包括 PTP 通用消息,不进行时间戳)。驱动程序提供原始(也是唯一) 时间戳数据包,以便它可以标记它,如果它是立即可用的,或者延迟。在接收时,时间 戳可能要么在频带内(通过DSA 头中的元数据,或以其他方式附加到数据包),要么在频 带外(通过另一个 RX 时间戳FIFO)。在 RX 上延迟通常是必要的,当检索时间戳需要 可睡眠上下文时。在这种情况下,DSA驱动程序有责任调用 ``netif_rx()`` 在新鲜时 间戳的 skb 上。 h]h)}(hX``.port_rxtstamp()``:在 RX 上,DSA 运行 BPF 分类器以识别 PTP 事件消息 (任何其他数据包,包括 PTP 通用消息,不进行时间戳)。驱动程序提供原始(也是唯一) 时间戳数据包,以便它可以标记它,如果它是立即可用的,或者延迟。在接收时,时间 戳可能要么在频带内(通过DSA 头中的元数据,或以其他方式附加到数据包),要么在频 带外(通过另一个 RX 时间戳FIFO)。在 RX 上延迟通常是必要的,当检索时间戳需要 可睡眠上下文时。在这种情况下,DSA驱动程序有责任调用 ``netif_rx()`` 在新鲜时 间戳的 skb 上。h](j )}(h``.port_rxtstamp()``h]h.port_rxtstamp()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j hj ubhX[:在 RX 上,DSA 运行 BPF 分类器以识别 PTP 事件消息 (任何其他数据包,包括 PTP 通用消息,不进行时间戳)。驱动程序提供原始(也是唯一) 时间戳数据包,以便它可以标记它,如果它是立即可用的,或者延迟。在接收时,时间 戳可能要么在频带内(通过DSA 头中的元数据,或以其他方式附加到数据包),要么在频 带外(通过另一个 RX 时间戳FIFO)。在 RX 上延迟通常是必要的,当检索时间戳需要 可睡眠上下文时。在这种情况下,DSA驱动程序有责任调用 }(hj hhhNhNubj )}(h``netif_rx()``h]h netif_rx()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j hj ubh" 在新鲜时 间戳的 skb 上。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMPhj ubah}(h]h ]h"]h$]h&]uh1j3 hj; hhhhhNubeh}(h]h ]h"]h$]h&]j j uh1j. hhhMAhj hhubeh}(h]dsaah ]h"]-3.2.1 dsa(分布式交换架构)交换机ah$]h&]uh1jVhj hhhhhM+ubjW)}(hhh](j\)}(h3.2.2 以太网 PHYsh]h3.2.2 以太网 PHYs}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j[hj hhhhhMYubh)}(hX5这些是通常在网络栈中履行第 1 层角色的设备,因此它们在 DSA 交换机中没有网络接 口的表示。然而,PHY可能能够检测和时间戳 PTP 数据包,出于性能原因:在尽可能接 近导线的地方获取的时间戳具有更稳定的同步性和更精确的精度。h]hX5这些是通常在网络栈中履行第 1 层角色的设备,因此它们在 DSA 交换机中没有网络接 口的表示。然而,PHY可能能够检测和时间戳 PTP 数据包,出于性能原因:在尽可能接 近导线的地方获取的时间戳具有更稳定的同步性和更精确的精度。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM[hj hhubh)}(h支持 PTP 时间戳的 PHY 驱动程序必须创建 ``struct mii_timestamper`` 并添加 指向它的指针在 ``phydev->mii_ts`` 中。 ``phydev->mii_ts`` 的存在将由网络 堆栈检查。h](h5支持 PTP 时间戳的 PHY 驱动程序必须创建 }(hjhhhNhNubj )}(h``struct mii_timestamper``h]hstruct mii_timestamper}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh! 并添加 指向它的指针在 }(hjhhhNhNubj )}(h``phydev->mii_ts``h]hphydev->mii_ts}(hj+hhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh 中。 }(hjhhhNhNubj )}(h``phydev->mii_ts``h]hphydev->mii_ts}(hj=hhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh& 的存在将由网络 堆栈检查。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM_hj hhubh)}(hX由于 PHY 没有网络接口表示,PHY 的时间戳和 ethtool ioctl 操作需要通过其各自 的 MAC驱动程序进行中介。因此,与 DSA 交换机不同,需要对每个单独的 MAC 驱动 程序进行 PHY时间戳支持的修改。这包括:h]hX由于 PHY 没有网络接口表示,PHY 的时间戳和 ethtool ioctl 操作需要通过其各自 的 MAC驱动程序进行中介。因此,与 DSA 交换机不同,需要对每个单独的 MAC 驱动 程序进行 PHY时间戳支持的修改。这包括:}(hjUhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMchj hhubj/ )}(hhh](j4 )}(h在 ``.ndo_eth_ioctl`` 中检查,是否 ``phy_has_hwtstamp(netdev->phydev)`` 为真或假。如果是,则 MAC 驱动程序不应处理此请求,而应将其传递给 PHY 使用 ``phy_mii_ioctl()``。 h]h)}(h在 ``.ndo_eth_ioctl`` 中检查,是否 ``phy_has_hwtstamp(netdev->phydev)`` 为真或假。如果是,则 MAC 驱动程序不应处理此请求,而应将其传递给 PHY 使用 ``phy_mii_ioctl()``。h](h在 }(hjjhhhNhNubj )}(h``.ndo_eth_ioctl``h]h.ndo_eth_ioctl}(hjrhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjjubh 中检查,是否 }(hjjhhhNhNubj )}(h$``phy_has_hwtstamp(netdev->phydev)``h]h phy_has_hwtstamp(netdev->phydev)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjjubhi 为真或假。如果是,则 MAC 驱动程序不应处理此请求,而应将其传递给 PHY 使用 }(hjjhhhNhNubj )}(h``phy_mii_ioctl()``h]hphy_mii_ioctl()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjjubh。}(hjjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMghjfubah}(h]h ]h"]h$]h&]uh1j3 hjchhhhhNubj4 )}(hX/在 RX 上,特殊干预可能或可能不需要,具体取决于将 skb 传递到网络堆栈的函数。 在 plain ``netif_rx()`` 和类似情况下,MAC 驱动程序必须检查是否 ``skb_defer_rx_timestamp(skb)`` 是必要的,如果是,则不调用 ``netif_rx()``。 如果 ``CONFIG_NETWORK_PHY_TIMESTAMPING`` 启用,并且 ``skb->dev->phydev->mii_ts`` 存在,它的 ``.rxtstamp()`` 钩子现在将被调 用,以使用与 DSA 类似的逻辑确定 RX 时间戳延迟是否必要。同样像 DSA,它成为 PHY 驱动程序的责任,在时间戳可用时发送数据包到堆栈。 对于其他 skb 接收函数,例如 ``napi_gro_receive`` 和 ``netif_receive_skb``, 堆栈会自动检查是否 ``skb_defer_rx_timestamp()`` 是必要的,因此此检查不 需要在驱动程序内部。 h](h)}(hXZ在 RX 上,特殊干预可能或可能不需要,具体取决于将 skb 传递到网络堆栈的函数。 在 plain ``netif_rx()`` 和类似情况下,MAC 驱动程序必须检查是否 ``skb_defer_rx_timestamp(skb)`` 是必要的,如果是,则不调用 ``netif_rx()``。 如果 ``CONFIG_NETWORK_PHY_TIMESTAMPING`` 启用,并且 ``skb->dev->phydev->mii_ts`` 存在,它的 ``.rxtstamp()`` 钩子现在将被调 用,以使用与 DSA 类似的逻辑确定 RX 时间戳延迟是否必要。同样像 DSA,它成为 PHY 驱动程序的责任,在时间戳可用时发送数据包到堆栈。h](hw在 RX 上,特殊干预可能或可能不需要,具体取决于将 skb 传递到网络堆栈的函数。 在 plain }(hjhhhNhNubj )}(h``netif_rx()``h]h netif_rx()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh9 和类似情况下,MAC 驱动程序必须检查是否 }(hjhhhNhNubj )}(h``skb_defer_rx_timestamp(skb)``h]hskb_defer_rx_timestamp(skb)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh) 是必要的,如果是,则不调用 }(hjhhhNhNubj )}(h``netif_rx()``h]h netif_rx()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh 。 如果 }(hjhhhNhNubj )}(h#``CONFIG_NETWORK_PHY_TIMESTAMPING``h]hCONFIG_NETWORK_PHY_TIMESTAMPING}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh 启用,并且 }(hjhhhNhNubj )}(h``skb->dev->phydev->mii_ts``h]hskb->dev->phydev->mii_ts}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh 存在,它的 }(hjhhhNhNubj )}(h``.rxtstamp()``h]h .rxtstamp()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh 钩子现在将被调 用,以使用与 DSA 类似的逻辑确定 RX 时间戳延迟是否必要。同样像 DSA,它成为 PHY 驱动程序的责任,在时间戳可用时发送数据包到堆栈。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMkhjubh)}(h对于其他 skb 接收函数,例如 ``napi_gro_receive`` 和 ``netif_receive_skb``, 堆栈会自动检查是否 ``skb_defer_rx_timestamp()`` 是必要的,因此此检查不 需要在驱动程序内部。h](h'对于其他 skb 接收函数,例如 }(hj2hhhNhNubj )}(h``napi_gro_receive``h]hnapi_gro_receive}(hj:hhhNhNubah}(h]h ]h"]h$]h&]uh1j hj2ubh 和 }(hj2hhhNhNubj )}(h``netif_receive_skb``h]hnetif_receive_skb}(hjLhhhNhNubah}(h]h ]h"]h$]h&]uh1j hj2ubh , 堆栈会自动检查是否 }(hj2hhhNhNubj )}(h``skb_defer_rx_timestamp()``h]hskb_defer_rx_timestamp()}(hj^hhhNhNubah}(h]h ]h"]h$]h&]uh1j hj2ubhA 是必要的,因此此检查不 需要在驱动程序内部。}(hj2hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMshjubeh}(h]h ]h"]h$]h&]uh1j3 hjchhhhhNubj4 )}(hX在 TX 上,同样,特殊干预可能或可能不需要。调用 ``mii_ts->txtstamp()``钩 子的函数名为``skb_clone_tx_timestamp()``。此函数可以直接调用(在这种情 况下,确实需要显式 MAC 驱动程序支持),但函数也 piggybacks 从 ``skb_tx_timestamp()`` 调用,许多 MAC 驱动程序已经为软件时间戳目的执行。 因此,如果 MAC 支持软件时间戳,则它不需要在此阶段执行任何其他操作。 h]h)}(hX在 TX 上,同样,特殊干预可能或可能不需要。调用 ``mii_ts->txtstamp()``钩 子的函数名为``skb_clone_tx_timestamp()``。此函数可以直接调用(在这种情 况下,确实需要显式 MAC 驱动程序支持),但函数也 piggybacks 从 ``skb_tx_timestamp()`` 调用,许多 MAC 驱动程序已经为软件时间戳目的执行。 因此,如果 MAC 支持软件时间戳,则它不需要在此阶段执行任何其他操作。h](hD在 TX 上,同样,特殊干预可能或可能不需要。调用 }(hjhhhNhNubj )}(hH``mii_ts->txtstamp()``钩 子的函数名为``skb_clone_tx_timestamp()``h]hDmii_ts->txtstamp()``钩 子的函数名为``skb_clone_tx_timestamp()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh。此函数可以直接调用(在这种情 况下,确实需要显式 MAC 驱动程序支持),但函数也 piggybacks 从 }(hjhhhNhNubj )}(h``skb_tx_timestamp()``h]hskb_tx_timestamp()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh 调用,许多 MAC 驱动程序已经为软件时间戳目的执行。 因此,如果 MAC 支持软件时间戳,则它不需要在此阶段执行任何其他操作。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMwhj|ubah}(h]h ]h"]h$]h&]uh1j3 hjchhhhhNubeh}(h]h ]h"]h$]h&]j j uh1j. hhhMghj hhubeh}(h]physah ]h"]3.2.2 以太网 physah$]h&]uh1jVhj hhhhhMYubjW)}(hhh](j\)}(h3.2.3 MII 总线嗅探设备h]h3.2.3 MII 总线嗅探设备}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j[hjhhhhhM~ubh)}(hX这些执行与时间戳以太网 PHY 相同的角色,除了它们是离散设备,因此可以与任何 PHY 组合,即使它不支持时间戳。在 Linux 中,它们是可发现的,可以通过 Device Tree 附加到 ``struct phy_device``,对于其余部分,它们使用与那些相同的 mii_ts 基 础设施。请参阅 Documentation/devicetree/bindings/ptp/timestamper.txt 了 解更多详细信息。h](h这些执行与时间戳以太网 PHY 相同的角色,除了它们是离散设备,因此可以与任何 PHY 组合,即使它不支持时间戳。在 Linux 中,它们是可发现的,可以通过 Device Tree 附加到 }(hjhhhNhNubj )}(h``struct phy_device``h]hstruct phy_device}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh,对于其余部分,它们使用与那些相同的 mii_ts 基 础设施。请参阅 Documentation/devicetree/bindings/ptp/timestamper.txt 了 解更多详细信息。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]miiah ]h"]3.2.3 mii 总线嗅探设备ah$]h&]uh1jVhj hhhhhM~ubjW)}(hhh](j\)}(h+3.2.4 MAC 驱动程序的其他注意事项h]h+3.2.4 MAC 驱动程序的其他注意事项}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j[hjhhhhhMubh)}(h堆叠 PHC 可能会暴露 MAC 驱动程序的错误,这些错误在未堆叠 PHC 时无法触发。一个 例子涉及此行代码,已经在前面的部分中介绍过::h]h堆叠 PHC 可能会暴露 MAC 驱动程序的错误,这些错误在未堆叠 PHC 时无法触发。一个 例子涉及此行代码,已经在前面的部分中介绍过:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubj)}(h/skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;h]h/skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhMhjhhubh)}(hX任何 TX 时间戳逻辑,无论是普通的 MAC 驱动程序、DSA 交换机驱动程序、PHY 驱动程 序还是 MII 总线嗅探设备驱动程序,都应该设置此标志。但一个未意识到 PHC 堆叠的 MAC 驱动程序可能会被其他不是它自己的实体设置此标志,并传递一个重复的时间戳。例 如,典型的 TX 时间戳逻辑可能是将传输部分分为 2 个部分:h]hX任何 TX 时间戳逻辑,无论是普通的 MAC 驱动程序、DSA 交换机驱动程序、PHY 驱动程 序还是 MII 总线嗅探设备驱动程序,都应该设置此标志。但一个未意识到 PHC 堆叠的 MAC 驱动程序可能会被其他不是它自己的实体设置此标志,并传递一个重复的时间戳。例 如,典型的 TX 时间戳逻辑可能是将传输部分分为 2 个部分:}(hj,hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubhenumerated_list)}(hhh](j4 )}(hX"TX":检查是否通过 ``.ndo_eth_ioctl``("``priv->hwtstamp_tx_enabled == true``")和当前 skb 是否需要 TX 时间戳("``skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP``")。如果为真,则设置 "``skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS``" 标志。注意:如上所述,在堆叠 PHC 系统中,此条件 不应触发,因为此 MAC 肯定不是最外层的 PHC。但这是典型的错误所在。传输继续 使用此数据包。 h]h)}(hX"TX":检查是否通过 ``.ndo_eth_ioctl``("``priv->hwtstamp_tx_enabled == true``")和当前 skb 是否需要 TX 时间戳("``skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP``")。如果为真,则设置 "``skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS``" 标志。注意:如上所述,在堆叠 PHC 系统中,此条件 不应触发,因为此 MAC 肯定不是最外层的 PHC。但这是典型的错误所在。传输继续 使用此数据包。h](h“TX”:检查是否通过 }(hjChhhNhNubj )}(h;``.ndo_eth_ioctl``("``priv->hwtstamp_tx_enabled == true``h]h7.ndo_eth_ioctl``("``priv->hwtstamp_tx_enabled == true}(hjKhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjCubh3”)和当前 skb 是否需要 TX 时间戳(”}(hjChhhNhNubj )}(h/``skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP``h]h+skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP}(hj]hhhNhNubah}(h]h ]h"]h$]h&]uh1j hjCubh%”)。如果为真,则设置 “}(hjChhhNhNubj )}(h2``skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS``h]h.skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS}(hjohhhNhNubah}(h]h ]h"]h$]h&]uh1j hjCubh” 标志。注意:如上所述,在堆叠 PHC 系统中,此条件 不应触发,因为此 MAC 肯定不是最外层的 PHC。但这是典型的错误所在。传输继续 使用此数据包。}(hjChhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj?ubah}(h]h ]h"]h$]h&]uh1j3 hj<hhhhhNubj4 )}(hXu"TX 确认":传输完成。驱动程序检查是否需要收集任何 TX 时间戳。这里通常是典 型的错误所在:驱动程序采取捷径,只检查 "``skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS``" 是否设置。在堆叠 PHC 系统中,这是错误的,因为此 MAC 驱动程序不是唯一在 TX 数据路径中启用 SKBTX_IN_PROGRESS 的实体。 h]h)}(hXt"TX 确认":传输完成。驱动程序检查是否需要收集任何 TX 时间戳。这里通常是典 型的错误所在:驱动程序采取捷径,只检查 "``skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS``" 是否设置。在堆叠 PHC 系统中,这是错误的,因为此 MAC 驱动程序不是唯一在 TX 数据路径中启用 SKBTX_IN_PROGRESS 的实体。h](h“TX 确认”:传输完成。驱动程序检查是否需要收集任何 TX 时间戳。这里通常是典 型的错误所在:驱动程序采取捷径,只检查 “}(hjhhhNhNubj )}(h1``skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS``h]h-skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh” 是否设置。在堆叠 PHC 系统中,这是错误的,因为此 MAC 驱动程序不是唯一在 TX 数据路径中启用 SKBTX_IN_PROGRESS 的实体。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j3 hj<hhhhhNubeh}(h]h ]h"]h$]h&]enumtypearabicprefixhsuffix.uh1j:hjhhhhhMubh)}(hXb此问题的正确解决方案是 MAC 驱动程序在其 "TX 确认" 部分中有一个复合检查,不仅 针对 "``skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS``",还针对 "``priv->hwtstamp_tx_enabled == true``"。因为系统确保 PTP 时间戳仅对最 外层 PHC 启用,此增强检查将避免向用户空间传递重复的 TX 时间戳。h](h{此问题的正确解决方案是 MAC 驱动程序在其 “TX 确认” 部分中有一个复合检查,不仅 针对 “}(hjhhhNhNubj )}(h1``skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS``h]h-skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh”,还针对 “}(hjhhhNhNubj )}(h%``priv->hwtstamp_tx_enabled == true``h]h!priv->hwtstamp_tx_enabled == true}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j hjubh”。因为系统确保 PTP 时间戳仅对最 外层 PHC 启用,此增强检查将避免向用户空间传递重复的 TX 时间戳。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]macah ]h"]+3.2.4 mac 驱动程序的其他注意事项ah$]h&]uh1jVhj hhhhhMubeh}(h]ptpah ]h"]*3.2 堆叠 ptp 硬件时钟的特殊考虑ah$]h&]uh1jVhj hhhhhM$ubeh}(h]ethtool-msg-tsconfig-set-getah ]h"]73. 硬件时间戳配置:ethtool_msg_tsconfig_set/getah$]h&]uh1jVhjXhhhhhMubeh}(h]id1ah ]h"] 时间戳ah$]h&]uh1jVhhhhhhhK ubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(j[N generatorN datestampN source_linkN source_urlN toc_backlinksentryfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerj7error_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourceh _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}refids}nameids}(jjjjjjjjjPjMjjj}jzjjjHjEjjj j j j j j j j j< j9 jc j` j j j j j jj j jjj j jjjjjju nametypes}(jjjjjPjj}jjHjj j j j j< jc j j j j jj jjjuh}(jjXjjkjjWjjjMjjjJjzjjjjEjjjSj jj j j j j j j9 j j` j? j jf j j jj j j jj j j jj jjjju footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}jEKsRparse_messages]transform_messages] transformerN include_log]