在probe一些函数的时候,会有额外的开销,systemtap在检测到overhead过大时,会终止脚本运行,下面我们利用一个脚本来验证:
Copy #system-overhead.stp
1 global veth_xmit_cnt
2
3 probe begin
4 {
5 veth_xmit_cnt=0;
6 }
7
8 probe module("veth").function("veth_xmit")
9 {
10 veth_xmit_cnt++;
11 }
12
13 probe timer.s(1)
14 {
15 printf("veth xmit :%d pps\n",veth_xmit_cnt);
16 veth_xmit_cnt=0;
17 }
这个脚本能够probe veth模块中veth_xmit()函数被调用的次数,通过pktgen对veth建立的两个接口发包测试,开启probe时候,通过stap统计得到veth上传输的速率:
Copy [root@100-18-16-172 stap]# stap stap-overhead.stp
veth xmit :1634325 pps
veth xmit :1685109 pps
veth xmit :1650389 pps
veth xmit :1595656 pps
veth xmit :1645790 pps
veth xmit :1667281 pps
veth xmit :1670139 pps
veth xmit :1645435 pps
veth xmit :1634741 pps
veth xmit :1695657 pps
另一方面,通过sar命令统计接口上的传输速率,得到一致的结果:
Copy [root@100-18-16-172 stap]# sar -n DEV 1 |grep vtap1
12:06:00 PM vtap1 1605978.00 0.00 2327413.43 0.00 0.00 0.00 0.00
12:06:01 PM vtap1 1652471.00 0.00 2394791.96 0.00 0.00 0.00 0.00
12:06:02 PM vtap1 1626362.00 0.00 2356954.30 0.00 0.00 0.00 0.00
12:06:03 PM vtap1 1635025.00 0.00 2369508.89 0.00 0.00 0.00 0.00
12:06:04 PM vtap1 1631569.00 0.00 2364500.39 0.00 0.00 0.00 0.00
接下来,我们停止stap脚本,可以看出veth传输具有明显的提升:
Copy [root@100-18-16-172 stap]# sar -n DEV 1 |grep vtap1
12:10:15 PM vtap1 2104668.00 0.00 3050124.33 0.00 0.00 0.00 0.00
12:10:16 PM vtap1 2143741.00 0.00 3106749.65 0.00 0.00 0.00 0.00
12:10:17 PM vtap1 2106860.00 0.00 3053301.02 0.00 0.00 0.00 0.00
12:10:18 PM vtap1 2092763.00 0.00 3032871.38 0.00 0.00 0.00 0.00
12:10:19 PM vtap1 2090121.00 0.00 3029042.54 0.00 0.00 0.00 0.00
甚至在解析数据结构的时候会出现overhead过重而终止脚本,我们可以使用-DSTP_NO_OVERLOAD来使得脚本即使在负重下也能运行。因此接下来我们使用stap脚本在性能探测的时候probe点每次只放置在一个地方。probe结果应该偏功能。
接下来探测linux设备驱动将数据包扔到linux网络协议栈后,来自一个以太网接口的数据包的在SMP多对成处理器上的分散情形。
首先探测来自网卡驱动的手包情况,通常网卡驱动的硬件中断会触发所在队列的NAPI,从而进行收发包并通过netif_receive_skb()或者netif_rx()函数递交给协议栈,单最终会经过enqueue_to_backlog()函数在硬件中断NAPI底半部分发给其他cpu,且在这个过程中完成RPS分布。
探测RPS功能的脚本如下所示:
Copy 1 global rx_array;
2 global rx_byte_array;
3 global dist_cpus;
4 probe kernel.function("enqueue_to_backlog")
5 {
6 target_cpu=$cpu;
7 cpu=cpu();
8 len=$skb->len;
9 rx_array[cpu]++;
10 rx_byte_array[cpu]+=len;
11 dist_cpus[target_cpu]++;
12 }
13
14 probe timer.s(1)
15 {
16 printf("\n");
17 foreach(cpu in rx_array-){
18 printf("cpu %d: %dpps %dbps\n",
19 cpu,
20 rx_array[cpu],
21 rx_byte_array[cpu]*8);
22 }
23 foreach (cpu in dist_cpus-){
24 printf("rps cpu %d %dpps\n",cpu,dist_cpus[cpu]);
25 }
26 delete dist_cpus;
27 delete rx_array;
28 delete rx_byte_array;
29 }
主要记录enqueue_to_backlog()由谁调用,以及入队列的时候将数据包交给谁。
接下来我们利用pktgen发包测试,pktgen构造数据包的时候源地址随机保障hash值变化。并且初始状态下网卡的队列掩码为0.
Copy #echo 0 > /sys/class/net/vtap1/queues/rx-0/rps_cpus
开始发包后,可以看到脚本输出:
Copy cpu 0: 1368514pps 16093543648bps
cpu 11: 53pps 55528bps
cpu 1: 27pps 13024bps
cpu 5: 21pps 21568bps
cpu 9: 18pps 24384bps
cpu 6: 10pps 8672bps
cpu 15: 9pps 26000bps
cpu 23: 9pps 4232bps
cpu 13: 7pps 34896bps
cpu 3: 4pps 12048bps
cpu 12: 4pps 2016bps
cpu 4: 4pps 2640bps
cpu 25: 2pps 944bps
cpu 2: 1pps 416bps
rps cpu 0 1368514pps
rps cpu 11 53pps
rps cpu 1 27pps
rps cpu 5 21pps
rps cpu 9 18pps
rps cpu 6 10pps
rps cpu 15 9pps
rps cpu 23 9pps
rps cpu 13 7pps
rps cpu 3 4pps
rps cpu 12 4pps
rps cpu 4 4pps
rps cpu 25 2pps
rps cpu 2 1pps
通过观察发现,我们可以知道:
由驱动接收数据包都在一个cpu上完成,这个cpu是cpu0,原因是veth虚拟网卡只有一个队列。
虚拟网卡队列0默认没有开启RPS,所以在网上入队列的时候会与处理函数这个cpu所在的队列softnet上。