CXD Linux Engineer

Linux协议栈--RPS机制

2017-10-21

Receive packet steering

现代网络设备的发包速度非常快,以至于电脑主机的速度已经很难跟上网卡了。近几年CPU的频率已经停止增长了,但是CPU的核心数却在增长。这意味着想要Linux网络协议栈能够更好的适应硬件,系统必须能够充分利用多核的性能。Tom Herbert的receive packet steering - RPS补丁正是用于解决这个问题的。

从操作系统的角度来看在发送数据包的过程中跨CPU传送数据相对比较简单,因为在生成数据的过程中会自然的跨越整个系统,因此网络协议栈不需要为此担心,尤其是协议栈现在已经可以支持多发送队列了。 但是对于接收数据包来说多CPU分发就比较困难了,因为接收的数据包都来源于同一个地方。有些网卡可以协助分发数据包,因为他们有多个接收队列和多个中断线(RSS)。但是对于那些只配备了一个发送队列的网卡来说,驱动程序只能在一个单一的中断线上处理所有接收到的数据包。想要并行化处理这种类型的数据流主机操作系统就必须采取一些措施了。

Tom的RPS补丁包在数据包接收路径上的netif_rx()netif_receive_skb()函数中安装钩子函数。然后以数据包中的协议数据(IP地址和端口号)计算一个哈希值,更根据这个哈希值来选择一个CPU,然后将数据包放到目标CPU的队列中。默认情况下网络数据可以均衡的分配到系统中所有的CPU上去处理,但是如果需要你也可以通过超级管理员账户来配置哪些CPU可以用来处理数据。

代码非常简单但是可以有效的使整个系统的负载均衡到各个CPU上,这个哈希值的计算非常重要,它确保了同一条流中的数据被分配到同一个CPU上去处理,增加了cache的局部性从而提高了性能。RPS的另一个优点是它不需要更改驱动,所以他可以非常容易部署。

但是还是有一个地方可以利用驱动程序来提供帮助的,哈希值的计算需要访问数据包的包头,这种访问将会导致一个或多个缓存未命中的情况,而且要访问的数据是通过网卡将数据放在那里的,所以不能在任何CPU的缓存中。当数据包被传递到正在工作的CPU上时缓存未命中的情况很可能会再次出现。不必要的缓存未命中是高速网络处理的祸根,已经做了很多工作来避免缓存未命中的情况,但是为了调度每个数据包而增加了缓存未命中的情况是得不偿失的。

事实上很多网卡可以自己为每一个接收到的数据包计算一个哈希值。这种计算是免费的,并且可以避免在数据包调度CPU上计算该值(并且避免了缓存失效的开销)。为了利用网卡的这个功能, RPS补丁包在sk_buff结构体中增加了一个rxhash参数。驱动程序可以从硬件中得到这个哈希值并保存到rxhash参数中;协议栈就可以不必计算哈希值了。这样就可以使数据包调度CPU完全不用访问数据包中的数据,从而加快了处理速度。

RPS究竟可以提高多少性能?这个补丁包提供了一组使用netperf工具测试的数据。在一个8核CPU和tg3-based网卡的服务器上数据包的处理速度从90,000个数据包每秒增加到285,000;在一个e1000-based网卡其他配置都一样的系统上处理速度从90,000增加到了292,000。在nForce和bnx2芯片组的16核CPU服务器上的测试结果一样。这表明RPS成功的在多核系统上提高了网络处理速度。

顺便说一下这个补丁来自于Google,Google拥有非常丰富的网络处理经验。很显然它已经在google的生产服务器上运行了很长时间。google公司希望RPS补丁能够尽早的合并到Linux内核的主线代码中,因为他希望为主线内核提供尽可能多的贡献,这是似乎是一个很好的开始。

参考

本文翻译自Receive packet steering


Comments

Content