如何用pktgen测试网卡性能?

在优化网络性能时, 不可避免要对网络的带宽进行测试. 通常大家可能都会使用iperf来执行网络链路的吞吐量测试, 但iperf只能测试TCP/IP协议层的速度, 这个带宽数据跟TCP/UDP协议的参数配置以及应用层缓冲区的大小都有关系. 有时, 我们希望直接测试网卡本身的实际吞吐量, 看看网卡实际的发包能力. Linux内核提供了pktgen工具用以产生数据包, 向网卡注入TCP/UDP数据. 这里, 我们就来看下具体如何通过pktgen来测试网卡性能.

模块配置/加载

pktgen在内核中是以模块的形式编译的, 使用之前确保内核配置了CONFIG_NET_PKTGEN; 如果CONFIG_NET_PKTGEN配置为m, 则使用时需要modprobe pktgen先加载模块. 模块加载完成后, 我们可以在/proc/net/pktgen下面看到有如下几个目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

~# ls -al /proc/net/pktgen/
total 0
dr-xr-xr-x 2 root root 0 Mar 22 10:57 .
dr-xr-xr-x 6 root root 0 Mar 22 10:57 ..
-rw------- 1 root root 0 Mar 22 10:57 kpktgend_0
-rw------- 1 root root 0 Mar 22 10:57 kpktgend_1
-rw------- 1 root root 0 Mar 22 10:57 kpktgend_2
-rw------- 1 root root 0 Mar 22 10:57 kpktgend_3
-rw------- 1 root root 0 Mar 22 10:57 kpktgend_4
-rw------- 1 root root 0 Mar 22 10:57 kpktgend_5
-rw------- 1 root root 0 Mar 22 10:57 kpktgend_6
-rw------- 1 root root 0 Mar 22 10:57 kpktgend_7
-rw------- 1 root root 0 Mar 22 10:57 pgctrl

其中kpktgend_*是内核数据包生产线程, 每个CPU都会启动一个相应的线程, 如果要添加某个测试网卡, 可以通过向该文件节点写入add_device ethX; pgctrl用于控制网卡性能测试状态需要在对应的线程执行网卡的吞吐量测试, 有start/stop/reset三种状态.

更多pktgen的信息可以参考内核文档pktgen.txt以及源代码pktgen.c

脚本测试

刚开始碰到pktgen测试网卡, 会被一堆参数搞得头晕, 好在内核的代码示例已经有了一些基本的脚本, 我们只需要根据自己的需要稍加改造就可以用起来了, 具体可以参考内核源码目录samples/pktgen. 下面贴了一段用于配置/启动测试的脚本, 完整的代码示例可以参考pktgen_test.sh.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

pg_ctrl "reset"
# Config Start Here -------------------------------------
for (( thread = 0; thread < $THREADS; ++thread )); do
PGDEV=${DEV}@$thread
echo "Removing all devices"
pg_thread $thread "rem_device_all"
echo "Adding eth0"
pg_thread $thread "add_device $PGDEV"

# Notice config queue to map to cpu (mirrors smp_processor_id())
# It is beneficial to map IRQ /proc/irq/*/smp_affinity 1:1 to CPU number
pg_set $PGDEV "flag QUEUE_MAP_CPU"

# device config
CLONE_SKB="clone_skb 000000"
#PGDEV=/proc/net/pktgen/eth0
echo "Configuring $PGDEV"
pg_set $PGDEV "count 10000000"
pg_set $PGDEV "clone_skb 000000"
pg_set $PGDEV "pkt_size $PKT_SIZE"
pg_set $PGDEV "delay 0"
pg_set $PGDEV "dst $DEST_IP"
pg_set $PGDEV "dst_mac $DEST_MAC"
done

# Time to run
echo "Running... ctrl^C to stop"
trap pg_stop SIGINT

pg_ctrl "start"
echo "Done"

# Print results
for ((thread = 0; thread < $THREADS; thread++)); do
dev=${DEV}@${thread}
echo "Device: $dev"
cat /proc/net/pktgen/$dev | grep -A2 "Result:"
done

这个脚本总体说来主要分为三个步骤:

  • pg_ctrl "reset": 清除上一次测试状态
  • pg_thread $thread "add_device $PGDEV": 配置测试的网卡
  • pg_ctrl "start": 开启测试, 按ctrl+C后, 进程退出, 内核结束发包

比如, 执行./pktgen_test.sh -i enp0s31f6 -d 10.193.200.1 -m 31:b6:89:55:b3:8c -t 2(使用两个内核线程发包), 测试结束后, 会打印出测试结果(实际查看/proc/net/pktgen/enp0s31f6*的结果会输出更多信息, 脚本里对输出做了过滤), 包括测试的时间/是否出现错误以及吞吐量:

1
2
3
4
5
6
7
8

Device: enp0s31f6@0
Result: OK: 1662287(c1662114+d172) usec, 89218 (1024byte,0frags)
53671pps 439Mb/sec (439672832bps) errors: 0
Device: enp0s31f6@1
Result: OK: 1606214(c1605962+d252) usec, 108442 (1024byte,0frags)
67514pps 553Mb/sec (553074688bps) errors: 0

从上面可以看到, 网卡的PPS(Packet Per Second)大约在121185(1.2Mpps), 带宽在992Mb/s, 这个速度已经比较接近于千兆网卡的理论吞吐量1.5Mpps(1000bps/((64 + 20)*8))了(这里64B为以太网数据帧的最小尺寸, 46(pad) + 14(dmac,smac,type) + 4(CRC), 20B 为以太网帧前导和帧间距的大小).

参考资料