翻译自国外技术博客:
鉴于正在进行的提高 TCP Tx 零拷贝效率的工作 [1],人们开始想知道在 Rx 端可以做些什么。Tx 零拷贝通常更容易实现,因为它不需要额外的硬件支持。它主要是一个软件练习,用于保留对用户数据的引用,而不是拥有它的副本。
[1] https://lore.kernel.org/all/cover.1653992701.git.asml.silence@gmail.com/
通过在硬件中执行标头数据拆分并将标头(供内核使用)发送到与数据不同的缓冲区,已经努力支持直接 Rx 到用户空间缓冲区。有两种已知的实现方式依赖于标头数据拆分 (HDS)。
第一个是今天的上游[2],它依赖于以页面大小的块接收数据有效负载并将数据映射(mmap'ing?)到进程的虚拟地址空间。尽管修改虚拟地址映射并不便宜,但这种方案适用于大传输。重要的是,使用零拷贝的应用程序通常比 CPU 受限更多 DRAM 带宽。这意味着即使对于较小的传输,他们也可能更愿意花费额外的周期来修改页表,而不是复制数据并用完宝贵的 DRAM 传输。
[2] https://lwn.net/Articles/752188/
第二种方法是预先注册用户内存并让 NIC 直接将数据 DMA 到那里 [3]。在这种情况下,内存可以是主 DRAM 或加速器内存,这对我们来说并不重要。该模型类似于 AF_XDP UMEM 的模型。它仍然取决于标头数据拆分,因为我们不希望应用程序能够修改标头,因为它们流经网络堆栈并导致内核崩溃。此外,设备必须提供足够的流量控制,以便能够将应用程序的流量引导到其缓冲区/队列——我们不希望数据最终进入错误应用程序的内存中。一旦 TCP 堆栈处理完标头,它就会简单地告诉应用程序数据在哪里。
[3] https://lore.kernel.org/netdev/20200727224444.2987641-1-jonathan.lemon@gmail.com/
除了能够将数据定向到 DRAM(计算加速器、磁盘)以外的内存之外,第二种方法的优点是不需要更改页表和整齐的页面大小的有效负载。在 SW 中更难实现,因为堆栈不一定可以访问有效负载内存。尽管 netgpu/zctap 补丁已经停滞不前,但这个想法可能有足够的价值最终实现。
上述两种方法都处理数据包大小的数据块。诚然,一些硬件支持数据合并 (GRO-HW/LRO),但它在大小、最大努力、延迟诱导方面受到限制,并且通常未经大规模验证。
鉴于我们已经修改了硬件以支持零拷贝 Rx(前一种情况为 HDS,后者为 HDS+转向),哪些修改将帮助我们更上一层楼?数据合并当然可以改进。
LRO 依赖于 NIC 中的智能和维护有关连接的状态,这总是带来扩展挑战。同时,现代应用程序通常预先知道传输的参数,因此它们可以将处理提示放入数据包中。
接收方需要知道 (1) 将数据放入哪个内存池/区域,以及 (2) 位于哪个偏移量。我们可以假设发送者通过 RPC 层获取这些参数。发送方可以将放置信息插入它发送的数据包中。包裹在 TCP 标头前面的 UDP 标头中,或作为 TCP 选项。
一种有用的修改可能是用 TCP 序列号表示偏移量。代替提供数据包数据必须到达的绝对偏移量(即“该数据包的数据必须在偏移量 Y 处到达内存 X”),而是提供基本 TCP 序列号和偏移量。然后目标地址将被计算为
address = mem_pool[packet.dma.mem_id].base +
packet.dma.offset +
packet.tcp.seq_no - packet.dma.base_tcp_seq
这简化了发送方的工作,因为它不再需要更改偏移量,因为它将 TSO 超帧分解为 MTU 大小的段。
最后一件事——我们必须保护应用程序免受恶意写入。这可以通过将流到内存池对的允许列表编程到 NIC 中来完成。不幸的是,完整列表与流的数量呈线性关系。更好的方法是依赖于像 Google 的 PSP [4] 这样的安全协议。PSP 将关联 ID 分发给发送者,并且仅在身份验证之后。如果我们必须保留完整列表,则 PSP ID (4B) 比 IPv6 流密钥 (36B) 小得多。我们可以尝试巧妙地分配它们——例如,从高数字分配“直接写入”ID,从低数字分配不太受信任的连接。我们还可以修改 PSP 以维护每个内存池而不是每个设备的密钥,或者将内存区域 ID 合并到密钥派生算法中。我'
[4] https://raw.githubusercontent.com/google/psp/main/doc/PSP_Arch_Spec.pdf
总结一下我梦寐以求的方案,我们在 PSP 标头后添加以下字段:
由于我们有 16 个额外的位来四舍五入到完整的 128b 标头,我们可以考虑为内存区域添加一个代号。如果内存区域配置/页表更新是异步的,这可能很有用,这样我们就可以在 NIC 确认配置完成之前发布读取请求。在大多数情况下,配置应该在数据到达时完成,如果不是,我们可以回退到非零拷贝 TCP。
我不再为 Netronome/Corigine 工作,但这肯定是他们的硬件可以通过固件更新轻松支持的东西。很像 PSP 本身。这是多么了不起的硬件啊……
留言与评论(共有 0 条评论) “” |