f-stack 官网http://www.f-stack.org/
f-stack是一个移植了freebsd 协议栈和支持dpdk的用户空间协议栈,源码里包含了对nginx的支持。
网卡SRIOVhttp://dpdk.org/doc/guides/nics/intel_vf.html
1、准备工作
使用intel 82599网卡的SRIOV功能开启VF,创建虚拟机直接使用网卡的VF
使用内核网卡驱动时,打开网卡(pci号0000\:04\:00.0)VF,支持32个虚拟网卡
echo 32 > /sys/bus/pci/devices/0000\:04\:00.0/sriov_numvfs
网卡虚拟功能VF vlan设置,需要设置vlan才使用
ip link set p5p1 vf 0 vlan 1000
查看打开情况
ip link
如下面输出就代表vf打开成功
在使用网卡的SRIOV功能时,可以直接在物理机上使用内核驱动,也可以用dpdk的驱动打开,dpdk驱动详细参考开始提供的链接。本文章主要是使用内核驱动的基础上进行试验的。
2、测试
web服务器:192.168.1.3
nginx server:192.168.1.2
客户机:192.168.1.41
ip在同一网段就不要纠结了,测试而已,我们希望的是客户机访问192.168.1.2时,其实真正访问的是192.168.1.3,nginx 代理配置网上一堆,自己搜吧
网卡的VF功能最多好像只能支持4个队列(接收或发送)。默认是打开的2个队列。
支持2个队列时,当打开nginx多进程时,基本上是0号接收队列和0号发送队列分配给一个进程,1号接收和1号发送队列分配给另一个进程,只能一一对应,不然运行会报错的。
2.1、重点分析
192.168.1.41新建tcp会话到192.168.1.2。192.168.1.2会和192.168.1.3新建tcp会话,关键点就在192.168.1.2到192.168.1.3这里。
当运行nginx单进程时,不会有错误,因为多个接收和发送队列都在一个进程中。
但运行nginx多进程时,就有问题了,因为每个nginx进程都有自己的tcp/ip协议栈,当192.168.1.2里的0号nginx进程(拥有0号接收和发送队列)发起对192.168.1.3的会话时,会选取一个临时端口建立到192.168.1.3的连接,当192.168.1.3返回tcp数据包时,0号进程不一定会收到,可能会是1号nginx进程(拥有1号接收和发送队列)收到,又因为nginx进程拥有自己的协议栈,1号进程没有这个tcp连接会话信息,所以连接失败,0号进程有这个tcp会话信息但没收到回话,连接也断开了,最后整个web会话就失败了。
为什么0号进程发出的数据包,返回数据可能会被1号进程收到呢?因为网卡会通过数据包信息和自己的分配方法分配数据到指定接收队列里,当打开RSS功能时,会通过数据包的5元组(tcp包),通过hash算法决定数据包发送到网卡哪个接收队列,网卡的VF功能也类似网卡PF功能,当没打开RSS功能,鬼知道网卡怎么分配数据包到哪个接收队列。
192.168.1.2发起连接时,源ip是192.168.1.2,目的ip是192.168.1.3,源端口不确定,目的端口是80(这个是代理设置的),协议是确定的。这里只有源端口可以由进程自己决定了,为了确定返回数据包能回到本进程包含的接收队列,首先得打开网卡的RSS,其次是在发起连接选取源端口的时候,就要选择一个在5元组hash后会分配到本进程接收队列的端口号,选取端口号会模拟网卡通过5元组计算hash key。
2.2、f-stack现状
在f-stack源码里,in_pcbconnect_setup函数就是选取端口的,里面的ff_rss_check函数就是保证返回数据包能回到本进程接收队列。网卡hash有一个重要东西是hash rss key,f-stack源码里init_port_start函数会设置网卡的hash rss key,只有保证网卡和选取端口时的hash rss key一致时,整个流程才能正常进行。f-stack 里的nginx在物理机运行多进程时,是不会有问题的,但在虚拟机运行时就有问题了。
问题出在源码在设置网卡hash rss key时,如果网卡是虚拟网卡(SRIOV VF),hash rss key是不会设置成功的。因为intel 82599网卡VF功能不支持设置hash rss key,只能是物理网卡时才能设置成功。具体原因参考http://dpdk.org/doc/guides/nics/intel_vf.html,f-stack源码里ixgbe_dev_rss_hash_update函数会判断网卡类型。
2.3 解决方案
修改ixgbe驱动程序
修改点1:
ixgbe网卡驱动默认是不会打开VF的RSS功能,可以通过修改源码打开
位置:ixgbe_sriov.c里的__ixgbe_enable_sriov函数
#ifdef HAVE_NDO_SET_VF_RSS_QUERY_EN
/* We support VF RSS querying only for 82599 and x540
* devices at the moment. These devices share RSS
* indirection table and RSS hash key with PF therefore
* we want to disable the querying by default.
*/
// adapter->vfinfo[i].rss_query_enabled = 0;
adapter->vfinfo[i].rss_query_enabled = 1;
#endif
修改点2:
ixgbe网卡驱动在ixgbe_main.c的ixgbe_setup_mrqc函数里会通过netdev_rss_key_fill函数随机产生rss key,我们需要把这个key设置成上面ff_rss_check函数里的rss key一致。
static uint8_t default_rsskey_40bytes[40] = {
0xd1, 0x81, 0xc6, 0x2c, 0xf7, 0xf4, 0xdb, 0x5b,
0x19, 0x83, 0xa2, 0xfc, 0x94, 0x3e, 0x1a, 0xdb,
0xd9, 0x38, 0x9e, 0x6b, 0xd1, 0x03, 0x9c, 0x2c,
0xa7, 0x44, 0x99, 0xad, 0x59, 0x3d, 0x56, 0xd9,
0xf3, 0x25, 0x3c, 0x06, 0x2a, 0xdc, 0x1f, 0xfc
};
--netdev_rss_key_fill(adapter->rss_key, sizeof(adapter->rss_key));
++memcpy(adapter->rss_key, default_rsskey_40bytes, 40);