天天看点

使用 netmap 提升 ixgbe 性能

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 获取队列后赋值。