在 SMP 系统上使用多队列 NIC 进行多线程处理

sve*_*enx 8 linux scalability load-balancing threads linux-kernel

数据包如何从网络接口队列调度到 CPU,然后再转发到线程进行处理?在涉及如何跨队列散列数据包、硬件中断与软中断、CPU/内存/应用程序/线程局部性以及多线程与多进程守护进程时,需要考虑什么,以避免尽可能多的数据包重新调度/复制?

我有一个多线程网络守护进程(比如,Unbound 解析器)在 Debian amd64 和 Linux 2.6.32(是的,旧的)上运行 16 个本机线程,因此应用程序负载分布在 16 个 CPU 上。网卡是 bnx2 (BCM5709S),支持 8 个 MSI-X rx/tx 队列。每个队列的 IRQ 通过静态映射 /proc/irq/n/smp_affinity 中的中断亲和性分配给单独的 CPU(irqbalance 从来没有做得很好),队列散列类型(RSS 类型)是默认的(IP src+dst , TCP 运动+dport),使用默认的散列键。

所有这些都有助于分散负载,但并不均匀:通常有一个应用程序线程完成其他线程两倍的工作(=每秒请求数),一个 CPU(可能是处理该特定线程的一个)软中断率是其他线程的两倍CPU。

CPU 启用了超线程,但我还没有做任何事情来在“真实”内核之间分散负载(我真的应该这样做)。

Linux 附带了一个相当全面的网络扩展文档,但我遗漏了一些空白:

该文档是关于 RSS 配置的:

如果设备支持足够的队列,典型的 RSS 配置将是每个 CPU 有一个接收队列,否则每个内存域至少有一个接收队列,其中内存域是一组共享特定内存级别(L1、L2 、NUMA 节点等)。

问:如何确定服务器的 CPU/缓存/内存域配置?

有关接收流控制 (RFS) 的信息似乎回答了我关于将数据包发送到正确的 CPU/线程的一些问题:

RFS 的目标是通过将数据包的内核处理引导到正在运行使用数据包的应用程序线程的 CPU 来提高数据缓存命中率。

Q:在DNS解析的情况下,通常有一个查询包和一个应答包。使用多线程守护进程,是否只有一个线程运行 bind()+recvfrom(),因此在将工作调度到其他线程之前,无论如何必须处理所有新传入的数据包?这个特殊的用例会从分叉操作(每个 CPU 一个进程)中受益吗?

问:那么接收流控制通常最适合多线程 TCP 守护进程吗?

Q:如何确定是多线程还是多进程操作?显然有共享内存和数据结构、资源争用等,但我正在考虑数据包流和应用程序侦听器。

问:如果没有接收流控制,或者使用简单的 UDP 服务,数据包是否会到达“错误”的 CPU,因此会以某种方式重新调度到“正确”的 CPU?这会触发 NET_RX 软中断吗?

Q:网卡队列和CPU之间是否有NET_RX软中断?CPU 和监听线程/进程之间是否也有一个?如果接收线程将数据包调度到工作线程,是否还有另一个可能?

太糟糕了,Ben Hutchings 的netconf 2011 演讲没有视频或其他详细信息,他在那里涵盖了大部分内容。幻灯片有些简短。

我将尝试升级到具有可用性能版本的更新内核,然后检查 CPU 的运行情况,也许会发现与其他 CPU 相比,负载较高的 CPU 的运行情况。

注意:我不是想在这里解决一个特定的问题,而是想了解这些东西在 Linux 内核中是如何工作的。我也知道中断合并的各种选项。