批量网络包传输
文/Jonathan Corbet
2014年10月8日
在现在的系统中,要获得高性能的其中一个很重要的途径就是批处理(batching),即在相同的损耗里面,完成更多的任务。举个例子,在一个特定的子系统里面,假如完成一件任务必须获得某一把锁,那么在获得锁的期间,如果能够完成几件任务,结果将会大大降低系统的总负载。在过去的几年里,很多提高系统性能的研究,某种程度上就是尽可能进行批处理。Linux网络子系统的最近一个改动,即表明了批处理确实也能极大地提高性能的。
每次当一个数据包通过网络传输时,就需要进行一系列的步骤。这些步骤依次包括获取数据包所在队列的锁,传输数据包到驱动程序中,把数据包置于硬件设备的发送列表,再通知硬件设备启动发送。这里面的操作有些是跟单个数据包,有些则不是。譬如获得队列的互斥锁时是可以处理数个数据包的,通知硬件设备启动发送也不只是针对单个数据包的。实际上,由于硬件启动发送涉及到硬件操作,甚至在某些系统上,会牵涉到虚拟机的,这些因素使得这个启动过程可能是非常耗时的。
我们经常遇到的是,当某个数据包在发送时,其它人也在等待这个队列;网络流量本质上是具有突发性的,这个特质在某种程度上意味着把启动发送的耗时分摊到尽可能多的数据包是非常意义的。某些技巧,如“数据段发送(segmetation offload)”,就是或多或少利用了批处理(亦即将一大块数据交给网卡,再由网卡把这一大块数据分成小的数据外发送出去)。不过,在内核网络栈,假如有一批的数据包在准备发送,那么,目前的情况的,一次只发送一个数据包,并且,每个数据包的发送都走相同的路径,产生同样的耗时。
上述的这种状况将会在3.18的内核上有所变化,因为相关的一个小改动会合并到这个版本的内核。我们先看下面这个函数,这个函数是内核提供给网络设备驱动使用的API:
netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb, struct net_device *dev);
上述这个函数会把skb指向的数据包通过dev发送出去。该函数的每一次调用都是独立的,则意味着每一次调用都会产生相同的耗时。3.18的内核最初希望能够给驱动提供以下的API:
void (*ndo_xmit_flush)(struct net_device *dev, u16 queue);
假设网卡驱动能够提供这个回调函数,那么就是表明它可以向网络栈提供批发送(batched transmission)。在这种情况下,网线栈会数次调用ndo_start_xmit把多个数据发送给驱动。驱动会接收这些数据包,但是不会立即启动硬件发送。当网线栈处理完数据包,就会调用ndo_start_flush,通知驱动启动硬件发送。
尽管如此,有些人提出了担忧,认为增加这样一个间接发送的函数到发送执行路径中会增加系统的负担。这个担忧也直接导致这个函数在它添加到net-next的代码树的同时,就被剔除了出去。替换它的是sk_buff新增加一个布尔类型xmit_more。假如这个标识位为真,即意味着后面还会有更多的网络数据包,网卡驱动不会启动硬件发送。这个变量标识避免增加一个额外函数的同时,又提供了足够的信息供网卡驱动使用,完成批发送功能。
David Miller添加xmit_more这个机制,让网络数据包的批量发送成为了可能,同时他也修改了数个驱动,使得这些驱动能够支持批量发送。但是Miller并没有去修改网络栈代码来支持批量发送,这部分工作落到了Jesper Dangaard Brouer的头上。Jesper的网络栈改动成功地合并到了3.18的内核,需要指出的是,他的改动只在有限的条件下才会发生作用,即仅适用“排队规则(queueing discipine)”只有一条发送队列的情况。
Jesper所做的改动实际上并不复杂:当网络栈需要发送一个网络数据包的时候,它会在获取队列锁的时间内,尽可能地多发送一系列的数据包。发送数据的上限则由“队列等待发送的总字节数量”限定。当达到队列能够接收数据的上限或者没有更多的数据包时,skb->xmit_more标识就会设为false值,硬件之后就会启动发送。
Eric Dumazet看过Jesper的改动之后,认为还有进一步改善的空间:对数据包有效性的检查可以完全放在队列锁之外来执行,这样更能提高系统的并发性能。根据Eric的测试结果,如果加上他的改动,那么,在没有启动“数据段发送(segmentation offload)”的情况下,可能完全达到40Gb/sec的线速。如此巨大的性能改善让Eric的改动在3.18的合并窗口期内,毫无疑问地合并到net-next的代码树中。也许这仅仅是一个微不足道的改动,但是微不足道的改动也可以收获极大的性能提升,前提是它们能够出现在正确的位置上。上述的这些小改动,将会使得3.18的网线栈是目前最快的网络栈。