Linux 内核网络协议栈的性能瓶颈导致在处理大量数据包时效果不是很好,考虑使用 netmap 结合网卡来收包。
一、使用方法
# cd LINUX
# ./configure --drivers=ixgbe --kernel-sources=/tmp/linux-2.6.32-431.el6
我的操作系统对应的内核源码在 /tmp 下,将 linux-2.6.32-431.el6 换成你系统内核的源码位置。
# make
这里 netmap 会对你的 ixgbe 打 patch,ixgbe 可以用源码中的,也可以去 intel 的网站上单独下载其他版本的 ixgbe。
如果打 patch 时冲突了,需要手动解决冲突。
编译成功后会在 LINUX 目录下生成 netmap.ko 模块,以及在 ixgbe 下生成 ixgbe.ko 模块。
二、加载模块及运行程序
# rmmod ixgbe
# insmod ./netmap.ko
# insmod ./ixgbe/ixgbe.ko
注意事项:
1、使用 netmap 最好是 root 权限,如果不是 root,运行时会报错 Open /dev/netmap failed: Permission denied
切换成 root 用户,或者更改 /dev/netmap 的属主,使你的用户可以操作它。
2、设置网卡为混合模式
# ifconfig ethX promisc
3、最好设置网卡中断的亲和性
接下来运行你的程序,或者 netmap 自带的测试程序看看效果吧。
三、实验环境
操作系统:RHEL 6.5 x86_64
CPU信息:Intel(R) Xeon(R) CPU E3-1230 v3 @ 3.30GHz 8核
内核版本:Linux 2.6.32-431
网卡信息:Intel 82599ES 10-Gigabit
驱动版本:ixgbe - 3.14.5
我用写的程序测试收包,范围在 8W ~ 10W pps,这也太低了吧,公司的网卡驱动收包是在 80W pps 左右。当我在领导面前演示网络驱动性能调优效果的时候,知道有多打脸么。。。
好吧,那问题究竟出在哪里,是不是我的用法有问题?
四、问题排查
对比了 netmap 中的一些 demo 程序,确认我的用法没有问题。这什么情况?
先 gdb 跟一下,看看各参数传递是否正确。
gdb 跟踪果然发现一些端倪,netmap 的实际 TX 是 8,而 RX 是 1。不应该啊,赶快在系统上验证下
# cat /proc/interrupts | grep eth1
43: 30273311 139523574 259845624 92355654 43090828 78958804 89480226 49414129 IR-PCI-MSI-edge eth1-TxRx-0
44: 158832 328092 153524 254977 528675 3390308 1294062 305923 IR-PCI-MSI-edge eth1-TxRx-1
45: 1169409 1825198 206061 2535093 650780 144343 684348 191703 IR-PCI-MSI-edge eth1-TxRx-2
46: 114886 110540 3464628 360298 1709290 2050637 412207 47765 IR-PCI-MSI-edge eth1-TxRx-3
47: 87148 883696 913525 2525735 636851 19266 479904 281840 IR-PCI-MSI-edge eth1-TxRx-4
48: 167973 338479 886292 1108967 2178833 3781318 872638 122529 IR-PCI-MSI-edge eth1-TxRx-5
49: 197241 2167612 656826 89847 1687339 221567 942726 75923 IR-PCI-MSI-edge eth1-TxRx-6
50: 301446 176112 3465756 768635 344587 952908 1438564 107423 IR-PCI-MSI-edge eth1-TxRx-7
我在 ixgbe 驱动代码中也打印日志,的确是 8 个 TX 和 RX,那么问题肯定是 netmap 将 8 个 RX 改成 1 个了。它为什么要这么做?
查看 netmap 源码,发现在 netmap_ioctl 中注册网卡成 netmap 模式的时候,有一个 netmap_update_config 函数。
netmap_ioctl()
|-- netmap_do_regif()
|-- netmap_update_config()
|-- na->nm_config() // 函数指针,指向 netmap_linux_config 函数
netmap_linux_config()
|-- nm_os_generic_find_num_desc() // 设置槽个数
|-- nm_os_generic_find_num_queues() // 设置队列个数
槽个数是没有问题的,我们来看看设置队列的这个函数
void nm_os_generic_find_num_queues(struct ifnet *ifp, u_int *txq, u_int *rxq)
{
#ifdef NETMAP_LINUX_HAVE_SET_CHANNELS
struct ethtool_channels ch;
memset(&ch, 0, sizeof(ch));
if (ifp->ethtool_ops && ifp->ethtool_ops->get_channels) {
ifp->ethtool_ops->get_channels(ifp, &ch);
*txq = ch.tx_count ? ch.tx_count : ch.combined_count;
*rxq = ch.rx_count ? ch.rx_count : ch.combined_count;
} else
#endif /* HAVE_SET_CHANNELS */
{
*txq = ifp->real_num_tx_queues;
#if defined(NETMAP_LINUX_HAVE_REAL_NUM_RX_QUEUES)
*rxq = ifp->real_num_rx_queues;
#else
*rxq = 1;
#endif /* HAVE_REAL_NUM_RX_QUEUES */
}
}
咦,是不是不满足 NETMAP_LINUX_HAVE_SET_CHANNELS 和 NETMAP_LINUX_HAVE_REAL_NUM_RX_QUEUES 这两个宏啊,所以使用了默认的 1。
查看源码,发现这个驱动的版本还真不支持这个宏,试了几个更高的 ixgbe 版本,里面有宏定义,然而编译的时候,有几个符号未定义,一看要 RHEL 6.6 以上才行。
那好吧,我能不能强制的将 RX 队列设置为 8 个呢,好,说做就做,开开心心编译完,插入模块,运行程序,然后系统 hang 住了。。。。简单粗暴看来是不行了,乖乖地改回来吧。
好吧,我们整理下,现在网卡实际有 8 TX 和 RX,而 netmap 实际情况是使用了 1 RX,相当于 只收取了 1/8 的数据,自然数据量很小了。
目前有两个解决方案。
方案一:能否将流量指定到单一接收队列上,这样即使有一个 RX,我也可以接收了
方案二:让 netmap 可以使用 8 RX 收包。
针对方案一,使用 ethtool 指定固定队列收包
# ethtool -K eth1 ntuple on
# ethtool -U eth1 flow-type ip4 action 0
这里 ethtool 不支持设置为 ether,最后测试的实际收包效果是 60W ~ 70W pps,还是没有公司的高。
五、TODO
针对方案二,更换高版本 ixgbe 驱动,使用 net_device->ethtool_ops->get_channels 获取队列后赋值。