IO_uring 不断成熟,同时成为过去几年 Linux 内核中最伟大的创新之一。在 Linux 6.15 中,IO_uring引入了网络零拷贝接收支持,这让事情变得更加有趣。在最近的演示中,有了这个新代码,一个 200G 链路可能会从单个 CPU 内核上饱和。
Meta 的 Jens Axboe 在本周的拉取请求中出色地描述了令人兴奋的 IO_uring 网络零副本接收支持:
“此拉取请求增加了对 io_uring 零拷贝接收的支持,从而能够快速批量接收数据直接进入应用程序内存,而无需将数据从内核内存中复制出来。虽然此版本仅支持主机内存,因为这是初始目标,但也计划了其他内存类型,特别是 GPU 内存。
这项工作依赖于一些网络组件,这些组件在网络端排队,但现在已进入您的树中。
这是 Pavel Begunkov 和 David Wei 的作品。来自 v14 帖子:“
我们配置了一个页面池,驱动程序使用它来填充 hw rx 队列,以分发用户页面而不是内核页面。因此,任何最终命中此硬件 rx 队列的数据都将被 DMA 直接发送到用户空间内存中,而无需通过内核内存反弹。从套接字中“读取”数据变成了 _notification_ 机制,其中内核告诉用户空间数据在哪里。总体方法类似于 devmem TCP 提案。
这依赖于硬件标头/数据拆分、流转向和 RSS 来确保数据包标头保留在内核内存中,并且只有所需的流才会到达配置为零复制的硬件 rx 队列。配置此修补程序集的范围不在此范围内。
我们与 devmem TCP 共享 netdev 核心基础设施。主要区别在于 io_uring 用于 uAPI,并且所有对象的生命周期都绑定到 io_uring 实例。使用新的 io_uring 请求类型“读取”数据。完成后,数据将通过新的共享重新填充队列返回。零副本页池直接从此重新填充队列中填充 hw rx 队列。当然,这些数据缓冲区的生命周期由 io_uring 而不是网络堆栈管理,具有不同的引用计数规则。
此补丁集是添加基本零拷贝支持的第一步。我们将通过新功能迭代扩展此功能,例如动态分配的零复制区域、THP 支持、dmabuf 支持、改进的复制回退、一般优化等。
在本地设置中,我能够使用单个 CPU 内核使 200G 链路饱和,在本月早些时候的 netdev conf 0x19上,Jamal 报告了使用单个内核(无 HT,包括 soft-irq)的 188Gbit 带宽。可以肯定地说,效率是存在的,因为需要更大的链接来找到每个内核的限制,而且它比现有的 devmem 解决方案更高效、更快速。
拉取请求和相关更改已合并到 Linux 6.15 中!
还有另一个合并的拉取请求用于引入 IO_uring epoll 收割:
“这增加了对通过 io_uring 读取 epoll 事件的支持。虽然这似乎有悖常理(和/或富有成效),但这里的原因是相当多的现有 epoll 事件循环可以很容易地部分转换为基于完成模型的模型,但仍然停留在一种(或少数)仍然基于就绪状态的事件类型上。对于这种情况,他们需要将 io_uring fd 添加到 epoll 上下文中,并继续依赖 epoll_wait(2) 来等待事件。这错过了 io_uring 可以做的更精细的等待,以减少上下文切换并可靠地等待一个批处理中的多个事件。
通过添加对通过 io_uring 获取 epoll 事件的支持,整个基于遗留就绪状态的事件类型仍然可以通过 epoll 获取,整个循环中的等待由 io_uring 驱动。
加上第三个拉取请求中的其他一些IO_uring更改,其中包含矢量固定/注册缓冲区和其他小改进。
转自 IO_uring Network Zero-Copy Receive Lands In Linux 6.15 – Phoronix