在许多商业应用场景下,使用linux来搭建路由器是一种可选的方案。在这篇博文中,我们将探讨在对称多处理器(SMP)架构下,对多队列网卡进行调优,以使其获得最佳的包转发性能。在这个过程中,我们将利用linux内核的一些特性,比如Receive Packet Steering (RPS)、Transmit Packet Steering (XPS)、IRQ affinity(均中断)。
队列、中断和cpu
这篇文章中讨论的内容适用于多种服务器网卡,对于我们自己的路由平台,我们选择了一连串的intel i350系列网卡。在linux下,这些网卡可以使用igb驱动程序,这个驱动程序由Intel自己编写,并且由于其的优秀性能,获得了很好的业界赞誉。更加重要的是,每个i350网卡为系统提供了8条发送队列和8条接受队列,这会帮助我们将网络流量分载到多个cpu上。
i350网卡实现了自己的hash逻辑,以至于(在接受端)同一个流的数据包将会有同一个队列持续分发。在发送端,则是由网络协议栈来完成这个工作。通过建立这个流/队列的亲和性,把队列绑定到不同的cpu上,从而实现流/cpu的亲和性。这种方法能够换来性能上的提高,是因为,它在根本上导致了网络流与cpu缓存的亲和性,减轻了每个cpu cache的压力。因为每个队列(或者是在这某些情况下出现的队列对)与一个硬中断相关联,处理这些中断响应会同样的分布在不同的cpu上。
igb驱动明智地将每个网口-方向的(rx或tx)队列数据减小到逻辑cpu的数量。减少的原因是每个队列都会导致cpu的性能瓶颈,如果队列的数据比cpu的数量大,将不会对性能提升带来好处。在我们的测试系统中,我们使用一个支持超线程的6核志强cpu,默认情况下就会呈现出来的12个逻辑cpu。因为实际上,在cpu芯片上只有6个物理的L1缓存,并且我们的目的是创建网络流与cpu缓存的亲和性,于是我们禁用了超线程。这样就给我们剩下了6个逻辑cpu,每个网卡上则设置了与之相对应的各个方向(发送和接收)上的6条队列。
如何控制亲和性
linux提供了Receive Packet Steering (RPS)、Transmit Packet Steering (XPS)、IRQ affinity, 允许我们控制cpu、网卡队列、中断之间的亲和性其中每一项多可以通过linux的/proc目录下的伪文件系统来配置,可以通过读、写位图文件(以十六位数形式),它用来表示cpu的分配。让我们先开始配置网卡RPS,首先以eth1的队列0为例,与RPS功能相关的配置项在/sys/class/net/eth1/queues/rx-0/rps_cpus。在这个路径中,eth1是网卡名,rx-0为接收队列0,rps自然是代表RPS。对于XPS来说,对于相应的发送队列,rx-0变为tx-0,rps则变为xps。为了显示eth1下的可用队列,可用简单的列出目录/sys/class/net/eth1/queues下的文件。
继续讨论中断的亲和性,相关的配置文件在/proc/irq/*/smp_affinity,其中*代表(硬)中断号。你可以通过阅读/proc/inerrupts中的内容找到相应的硬件中断号。在一些情况下,发送和接收队列搭配起来平分中断作业。无论如何,我们关心的是控制网络队列的cpu亲和力,而不是主网络设备。在/proc/interrupts的输出中,可以很直观的通过队列名来定位中断号。对于未成对搭配的队列,它们会简单的命名为eth1-rx-0和eth1-tx-0,搭配成对的队列则会显示为eth1-TxRx-0.
RPS、XPS和IRQ亲和性的配置很简单,直接向相关的配置文件中写入一个16进制数字,代表cpu分配网络队列中断响应的位图。0是默认值,其含义根据不同的内核版本有所差异(之后将讨论)。在我们的测试路由器上,拥有6个cpu核,可以向配置文件中写入的范围是二进制的000000到二进制的111111。最右侧(最小值)位代表第0号cpu。例如,我们想分配第5号cpu,则需要向相关的配置文件中以16进制的形式20,写入二进制值100000。
实验过程
我们的实验装置由3个网卡组成,其中2个网卡(eth1、eth2)连接到一台发包设备,这台设备生成的IP包,由随机的源地址和目的地址组成(randomized source addresses spanning a /8 (2^24 addresses) and destinations spanning a /24 (2^8 addresses))剩余的一个网卡(eth3)用来连接另外的网卡,提供链路层的连接,数据包在这三个网卡之间进行转发,并且数据包有eth1/eth2注入,通过一个路由表,由eth3发送出去。所以的输出包的目标是一个不存在的网关(通过本地ARP表分配的一个静态MAC地址)以便排除由于真实的MAC层连接引入的不当干扰。我们开发了一个工具,叫做ifstat2,可以用来观察每秒的网卡实时转发性能,包括每秒转发的包数和字节数。我们使用内核版本是3.2.39-2的Debian发行版,默认情况下,这个内核将所有的网络负载交给第一个cpu处理,导致了低下的包转发性能,当网络流量达到558k包/秒的时候,就会开始发生丢包现象。到了1.6M包/秒的输入,丢包率高达20%。之后,我们通过修改CPU、中断、队列相关的亲和性进行对比测试。将网卡的负载分配到6个不同的cpu,获得了最好的包转发性能,并且消除了丢包的现象:
然后,这样的配置导致了用户态性能开销的大幅度增大,包括ssh终端会话的操作时延变长,其实这时网络并没有出现拥塞。将网络负载分发到6个cpu中的5个上(留下一个cpu用来运行其他程序),减轻了用户态的性能问题,但是,将问题留给了如何去处理剩下的第6号队列。
Intel的官方igb驱动支持减少队列数,通过加装驱动的时候设置RSS参数。但是,在当前的官方linux内核中的igb驱动,并不支持这个调优参数。所以,我们自己在驱动程序中实现了一个等价的参数,以便允许减少队列的数据到5,实现如下的分发方式:
通过释放第一个cpu,戏剧性的提高了用户态的性能。然而,另外一个问题,我们认识到Linux路由cache(即目的[dst]cache,或者路由信息库[rib])的性能.我们认识到,当大量的数据包携带端点地址通过这个系统,这个cache很容易被堵塞,产生了目的cache溢出的内核信息警告。这个cache看起来不能提供持续性能的性能。与普通的路由表查找相比较。
我们得知,在linux内核版本3.8,将会弃用并删除掉路由cache,所以我们在3.8版本的内核上继续我们的实验。在vanilla linux 3.8.5内核版本上,默认情况下,cpu、中断已经被优化了。我们注意到/proc/interrupts中,出现了一些奇怪的配置,说明已经被优化了:
在fedora 18发行版,使用了3.8.5-201.fc18内核,我们发现了更优化的一个默认配置,每个网卡都相同:
这些表格显示配置cpu/中断亲和性,而cpu/队列的亲和性不是必需的。因为cpu/队列亲和性(RPS和XPS)的sysfs文件显示它们的默认值为0,并且,通过观察网络负载下的cpu工作情况,可以发现已经处于平衡状态,这说明在默认的cpu/队列亲和性与上述的cpu/中断亲和性相似或者相同。
https://greenhost.nl/2013/04/10/multi-queue-network-interfaces-with-smp-on-linux/