网络协议栈的缩放(一)

这个文章是我翻译了Documentation/networking/scaling.txt,并加入了自己的理解,可以说是意译。第一次翻译,有些词的翻译把握不准,以及理解的不是那么透彻,应该会有一些问题。先作为v1吧。

 http://blog.chinaunix.net/uid-20695170-id-3036641.html

----------------------------------------翻译开始分割线--------------------------------------------------------

 

网络协议栈的缩放(Scaling in the Linux Networking Stack)

 

简介

============

这个文档介绍了Linux网络协议栈中一系列互补的技术。

这些技术用来增加多处理器系统的并行性和改善性能。

这些技术包括:

RSS: Receive Side Scaling (接收侧的缩放) 
RPS: Receive Packet Steering (接收端包的控制) 
RFS: Receive Flow Steering (接收端流的控制) 
Accelerated Receive Flow Steering (加速的接收端流的控制) 
XPS: Transmit Packet Steering(发送端包的控制)

 

RSS: Receive Side Scaling 
=========================

当代的NICs支持多个接收和传输队列,即多队列。接收的时候,一个网卡

能够发送不同的包到不同的队列,为了在不同的CPU之间分散处理。

NIC针对每一个包,通过一个过滤器来指定这个包属于少数几个流中的一个流。

每个流中的数据包被控制在一个单独的接收队列中,而队列轮回的被CPU进行

处理。这种机制就叫做RSS。RSS的目标和其他控制技术目的都是为了增加性能。

多队列也可以被用于流量优先控制,但那不是这些技术的目的。

 

RSS中的过滤器是一个基于L3和L4层头部的hash函数,

例如,基于IP地址和TCP端口的4元组的hash函数。最常见的RSS硬件实现中,使用了128个间接表,

其中每个表存储一个队列号(注,网卡的队列数比较少,比如igb是8个,bnx2是5个)。

针对某个包而言,使用这个包计算出的hash值(hash是Toeplitz算法)的低7位先确定

间接表,再从间接表中的值访问队列。

一些高级的NICs允许使用可编程的过滤器来控制包属于哪个队列。

例如,绑定TCP端口80的webserver,数据包能被指向他们自己的队列。

“n-tuple”过滤器可以通过ethtool的 --config-ntuple来配置。(注: 2.6.36开始引入!)

 

==== RSS Configuration

多队列网卡的驱动提供了一个内核模块参数,用来指定硬件队列个数。

例如,bnx2x驱动使用的参数是num_queues. 如果设备支持足够多的队列,

一个典型的RSS配置中,最好的是一个CPU一个接收队列。或者至少每个内存域一个接收队列,

一个内存域包含一系列的CPU,并共享一个特殊的内存级别(L1,L2,NUMA节点等等)。

RSS设备的间接表,在驱动初始化的时候被映射。默认的映射是队列均匀的发布在间接表中。

但是,在运行的时候,使用ethtool命令 (--show-rxfh-indir and --set-rxfh-indir),

间接表可以被查看,也可以被修改。修改间接表,可以给不同的队列不同比例的权重。

 

== RSS IRQ Configuration

每个接收队列有一个单独的IRQ,即中断号。NIC通过IRQ来通知CPU什么时候新的数据包到达了指定的队列。

PCIe设备使用MSI-X来路由每个中断到CPU。有效的队列到IRQ的映射是由/proc/interrupts来制定的。

默认,一个中断能被任何一个CPU处理。因为一个重要的包处理部分发生在接收中断处理函数函数中,

在CPU中平分接收中断是有优点的。如果要手动的调节每个中断的亲和性,可以参考Documentation/IRQ-affinity.txt。

一些系统会运行irqbalance服务,这个服务会动态的优化IRQ的亲和性,因此会覆盖任何手动设置。

 

== Suggested Configuration

当关注低延时或者接收中断处理称为瓶颈时,应该启用RSS。分担负载在不同的CPU之间,

减少了队列长度。对于低延时的网络,最佳的设置是创建和CPU个数一样多的队列。

最高效的配置是拥有最少的队列,并且没有队列溢出。这是因为,默认下

中断聚合启用的情况下,中断的总数目户随着每个增加的队列而增加。

每个cpu的负载可以使用mpstat工具来观测到。但是,注意,启用超线程的处理器,

每一个超线程代笔了单独一个cpu。对于中断处理,在最初的测试中显示超线程

并没有产生优势。所以,根据CPU的核个数,而不是逻辑cpu个数,来限制队列数目。

 

RPS: Receive Packet Steering 
============================

RPS,逻辑上是一种以软件的方式来实现RSS。在数据路径上,稍后被调用。

介于RSS选择了队列和CPU(这个cpu会处理硬中断),RPS选择CPU来执行硬件中断处理之后的协议处理。

通过把数据包放在目标CPU的backlog队列,并唤醒CPU来处理。

RPS相比RSS有几个好处:

1) RPS能够被任何NIC使用。

2) 软件过滤器能够轻易的被添加,用来hash新的协议。

3) 它不会增加硬件设备的中断。尽管,引入了IPIs(inter-processor interrupts)。

 

当一个设备使用 netif_rx() 函数和netif_receive_skb()函数,(从网卡驱动)向网络协议栈传递数据包时,

RPS在底半环境(通过软中断来实现的,在硬中断处理函数之后。)中被调用。

这2个函数调用get_rps_cpu() 函数,来选择应该执行包的队列。

 

决定目标CPU的第一步是基于包的地址和端口(有的协议是2元组,有的协议是4元组)

来计算hash值。这个值与这个包的流保持一致。这个hash值要么是由硬件来提供的,

要么是由协议栈来计算的。厉害的硬件能够在包的接收描述符中传递hash值,这个值

与RSS计算的值是相等的。这个hash值保存在skb->rx_hash中,并且这个值可以作为流的hash值

可以被使用在栈的其他任何地方。

 

每一个接收硬件队列有一个相关的CPU列表,RPS可以将包放到这个队列中进行处理。

对于每一个接收到的包,指向这个列表的索引是通过流hash值对列表大小取模来计算的。

被指向的CPU是处理 数据包的目标CPU,并且这个包被加到CPU的backlog队列的尾部。

最底半处理的最后,IPI被发送到这个包所插到的那个CPU。IPI唤醒远程CPU来处理backlog队列,

之后队列中数据包被发送到网络协议栈进行处理。

 

==== RPS Configuration

RPS要求内核编译了CONFIG_RPS选项(SMP上默认是打开的)。尽管编译到内核,直到

被配置了才能启用。对于某个接收队列,RPS可以转发流量到哪个CPU,是由

/sys/class/net/<dev>/queues/rx-<n>/rps_cpus来控制的。这个文件实现了

CPU的位图。默认,当值是0,RPS是无效的,数据包是由中断的CPU来处理的。

Documentation/IRQ-affinity.txt 解释了CPU是怎么由位图来设置的。

 

== Suggested Configuration

对于单个队列的设备,一个典型的RPS配置是设置rps_cpus指向与中断CPU属于相同内存域的

CPU列表。如果NUMA位置不是一个问题,也可以设置所有的CPUs。如果高中断率,

从cpu位图中排除高中断率的CPU是明智的,因为那个CPU已经执行了太多的工作。

对于一个多队列的系统,如果RSS已经配置了,导致一个硬件接收队列已经映射到每一个CPU。

那么RPS就是多余的和不必要的。如果只有很少的硬件中断队列(比CPU个数少),每个队列

的rps_cpus 指向的CPU列表与这个队列的中断CPU共享相同的内存域,那RPS将会是有效的。

 

(未完,待续!)

你可能感兴趣的:(网络协议栈的缩放(一))