10G(82599EB) 网卡测试优化(other)

接下来的都相对比较杂。

txqueuelen 表示的是 device driver 里面 tx queue 的包的数量,跟 netdev_max_backlog 相反。主流的默认是 1000,不过即使是 1G 的网络,1000 也算是比较小的了,对于 10G  网络,完全可以调大:
# ifconfig eth2 txqueuelen 10000

DDIO(DCA)
之前的叫 DCA,对于 E5 的来说叫 DDIO(1, 2),就是让 NIC 不需要经过内存,而是跟处理器的 cache 直接通信。E5 的处理器默认都开启了 DDIO,并且 DDIO 没有任何的硬件依赖,对 app 来说也是透明的,不需要安装什么 driver,总之就是不需要人工的干预。但是,要真正的发挥他的作用,还是需要做 cpu bind 的。

NUMA 的问题
这个问题还比较常见,尤其在 db 上,不管是 MySQL 还是 MongoDB 都会遇到此类问题。最主要的就是理解下面这个:
# numactl –cpubind X –membind X my_app my_app_arg0 my_app_arg1 …

10G(82599EB) 网卡测试优化(中断处理)

这里主要涉及的是 core 的绑定问题,有时候不是简单的算几个 bitmask,写几个数字到 smp_affinity/smf_affinity_list 里面就完事了。

在做 affinity 的时候,有下面几点需要注意:
1. 千万千万在不要在做 affinity 的时候开启 irqbalance,否则你刚把 irq 给人肉指定好,他就自动给你給恢复成他那套了
2. 使用 iperf 或者其他的工具做 benchmark 时,需要开启多线程(-P),以满足多个 stream 的要求,否则不管你怎么分布,都只能压在某一个 core 上,造成 affinity 无效的幻觉
3. 能不用 HT 尽量别用,尤其对于 affinity 来说,效果并不明显,比较好的做法是以物理 core 的数量为参照,比如单路 6-core(开启 HT 则 12 core),设置 queue 的数量为 6 而非 12 比较好,具体的还是需要测试

目前,要对 NIC 做 affinity,最好的方式还是 NIC 支持 RSS(Receive Side Scaling) 也就是 multiqueue RX,这样可以很方便的将 irq(RSS) 映射到 core 上。基本上主流的千兆 NIC(bnx2, igb) 都支持,10G 的更是默认配置了,这个上篇已经说过。 各个 bitmask 的问题,不在这里说了。

在 interrupts 文件里面,能看到不少的诸如 IO-APIC-level, IO-APIC-edge, PCI-MSI-X 等术语,这些其实是跟各个设备支持的特性相关的,要看他们之间的演变过程,可以看这里

关于 IO-APIC-level 跟 PCI-MSI-X  的区别,是两个完全不同的产生中断的方式,前者(1, 2)  是使用硬件实现,比如 8259A,后者则是往某个特定的 address 里面进行写操作实现中断。
因此,要判断一个设备使用使用了 MSI/MSI-X 很简单,只要查看 interrputs 文件就可以了,IO-APIC-level 非 MSI,PCI-MSI 表示试用了 MSI,这个不仅仅限于 NIC。

而 IO-APIC-fasteoi 跟 IO-APIC-edge 的区别,很好解释。interrupts 触发的方式不同而已,一个是边缘(edge)触发,一个是条件(level)触发。

理解了上面了,可以发现,可能的中断方式基本是下面两种:
1. 将某个 irq 绑定到所有的 core 上
2. 将某个 irq 绑定到某几个 core 上,第一个只是第二个的一个特例
3. 将某个 irq 绑定到指定的某个 core 上

先来看前两种情况。其实就是 RR(round-robin) 的方式,或许能解决一些 bottleneck,但是更严重的问题来了,尤其对于网卡这种中断很频繁的设备来说,会造成 cpu cache missed,造成影响是直接访问内存比访问 cache 里面的内容慢 30x;并且对于流媒体来说,还可能造成 congestion collapse。另外,由于 APIC 的问题,可能无法实现上面的要求,比如,我想将 irq 60 绑定到 core 0 以及  core 1 上,或者想把 irq 67 分布到所有的 core 中,将 3 写入到 60 的 smp_affinity 不就可以了吗?实事是做不到的,并且上面已经说明了这样做没有意义的原因。将 CONFIG_HITPLUG_CPU 由原先的 yes 设置成 no 可能会解决问题,不过我并没有试过,尤其明白了上面将 irq 分布到多个 core 的不合理之处之后更没有必要这样做了。

明白了上面了问题,第三点说的将 irq 绑定到一个特定的 core 上的意义就很明显了。

10G(82599EB) 网卡测试优化(kernel)

kernel 方面的优化,这里面大部分都会涉及到 tcp stack。
首先需要了解一个概念,BDP(bandwidth delay product),即两端的带宽跟 RTT 的乘积,比如,我们的 10G 网络,正常情况在 9.4G 到 9.9G 浮动,按照最差的 9.4 来算,RTT 正常的在 0.2ms 左右,他们的 BDP=9.4×10^6kbit/sx0.2×10^-3s=1.88×10^3kbit=235kByte
下面两张图截取自《TCP Implementation in Linux: A Brief Tutorial》,大致的描绘了包从进到出涉及到的几个层面(Device driver, kernel space, User space) 

对于一个包来说,从 IP 层到 TCP 层,经过状态机,最终进入到 TCP recv buffer 通过 read() 转入到 User space(app)。如果这个 buffer(net.ipv4.tcp_rmem) 比 BDP 小的话,其 throughput 会降低。对应的,对于发包来说,从 user space 的 app 通过 write() 调用到 kernel space 进入 TCP send buffer(net.ipv4.tcp_wmem, per socket)。

net.core.netdev_max_backlog 这个决定了一个设备里面等待 cpu 处理(rx)的的最大 queue 数量,如果新接受到的包超过该值,则会被 discarded,这个发生在 ip layer 到 tcp layer 过程中。

注意: core.rmem_max 会覆盖 ipv4.tcp_rmem,core.wmem_max 会覆盖 ipv4.tcp_wmem,上面的几个的单位都是 bytes。ipv4.tcp_mem 是以 pages 为单位而非 bytes。net.core.rmem_default,rmem_default 不应该大于 rmem_max。

上面的四个都是发生在 socket 层面,只对 end-to-end 有效,如果是 bridge/router 则无效。而 netdev_max_backlog 会同时影响端到端以及路由交换的机器。

对于整个网络而言,在接受方向有两个主要的 queue: NIC 的 rx buffer 以及 socket queue。前者在《10G(82599EB) 测试优化(ethtool)》提到过,后者则是上面提到的。两者都需要经过实践,才能得到一个比较满意的效果。

还有个就是 tcp_congestion_control/tcp_available_congestion_control。目前主流的 OS 会提供 cubic reno 这两个,一致偏向使用 CUBIC,2.6.19 之后使用该算法,包括目前的 3.0.0 等,也就是目前主流的稳定的内核(2.6.18, 2.6.32)使用的都是该算法。这个没得说,目前综合性能最好的。当然,如果你是想访问国外的,比如科学上网之类的, 当然使用 hybla,加载,开启:
# modprobe tcp_hybla
# echo hybla > /proc/sys/net/ipv4/tcp_congestion_control

除了上面几个重要的之外,还有几个需要注意的。

1. tcp_timstamps 会在 TCP header 额外的增加 12bytes,10G 网络可以关闭。
2. 对于 tcp_sack 来说,对于 WAN 的效果可能会比较好,但是会增加 CPU util,开不开对 LAN 影响不是很大,都 10G 的 LAN 了,可以肯定的说不会出现丢包这种网络很糟糕的情况
3. tcp_low_latency higher throughput > low latency,这个目前主流的系统默认都是 0。
4. tcp_mtu_probing 对于开启了 jumbo frames 的机器来说,需要开启,默认是 0。这个主要是为了弥补使用 PMTUD 调整 MTU 出现的一些问题。如果置为 2,则会牵扯到 tcp_base_mss,默认为 512,即使用 tcp_base_mss 作为起始的 MSS 值。与之相关的还有 ip_no_pmtu_disc 这个参数,默认为 0,即依然会优先使用传统的 PMTUD,只有当遇到 blackhole 的时候才会使用 PLPMTUD(Packetization Layer Path MTU Discovery),也就是上面提到的技术。 另外,像 fs.file-max, net.ipv4.ip_local_port_range 这类通用性的就不在这里说了,记得修改。

下面是一个范例:
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 600000
net.core.wmem_default = 600000
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_mem = 10000000 10000000 10000000
net.core.netdev_max_backlog = 600000
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_sack = 0
net.ipv4.tcp_low_latency = 0
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_base_mss = 512
net.ipv4.ip_no_pmtu_disc = 0
net.ipv4.tcp_congestion_control = cubic

10G(82599EB) 网卡测试优化(ethtool)

 

ethtool,主要涉及 NIC 的本身比如 ring buffer,内容还是蛮多,不少跟硬件的特性相关,分几个大方向说。

speed,duplex 这类的是非常基本的设置,不在这里说了。需要注意的是,能 auto 的尽量 auto,因此,正常情况下,Auto-negotiation 应该是 on 状态。这里说明了原因。
上面这些就是不加任何的参数获取到的基本的设置。


1. receive(RX) ring buffer size:
# ethtool -g eth2
这个 gist 列举了几个主流设备的 rx 值,包括 BCM5720, I350 在内,当然还有 82599EB。

buffer 越小 latency 也越低,与之带来的是 throughput 的下降,也就是说,buffer 小带来的是丢包而大了带来的则是 latency。丢包的问题,一个是在 nic 层面丢了,也就是 ifconfig 看到的,一个则是在 tcp 协议栈看到的丢包。前者产生丢包很有可能就是 ring buffer 设置的太小的缘故。但是,并不是越大越好,尤其对于实时的流媒体流量来说。

可以适当的增加 ring buffer:
# ethtool -G eth0 rx 1020

一般来说,在 latency 本来就比较小的情况下,适当的增大 buffer 并不会带来明显的影响,除非是一些对 latency 极度敏感的交易场所、流媒体等。因此,这又是一个 throughput 跟 latency 的 tradeoff。 对于 rx, tx 的设置,并没有一个通用的公式,还是需要在实际环境中测试。
注意: -g 看到的只是 buffers 的数量,而非 size。


2. coalescing
interrupt coalescing 主要是用来做 latency 跟 cpu overhead 之间的 trade-off。当 adapter 收到一个 frame 之后,不会立即的对系统产生中断,而是等一段时间,收集到更多的包之后再一次性的处理,这会降低 cpu 的负载,但是会产生 latency。要查看当前的 coalescing,可以:
# ethtool  -c eth2 | grep "rx-usecs:"
rx-usecs: 1

interrupt coalescence 也叫 interrupt Moderation, Interrupt Blanking, or Interrupt Throttling。这个在 ixgbe 的参数列表里面也可以看到:
parm:           InterruptThrottleRate:Maximum interrupts per second, per vector, (0,1,956-488281), default 1 (array of int)


关于 rx-frames[-irq] rx-usecs[-irq] tx-frames[-irq] tx-usecs[-irq] 的解释,可以看这里

可以使用下面这个命令来修改当前的值:
# ethtool -C eth2 rx-usecs VALUE
rx-usecs 会同时控制 tx 以及 rx 方向的 coalescing。值越低的,interrupt coalescence 越不容易发生,latency 越小,相应的,cpu 负载也会越大。如果增加了 rx-usecs,对于 bulk traffic 会比较好,但是会对 tcp 的性能造成大的损失,可以通过调大 TSO window divisor(默认为 3) 来弥补:
net.ipv4.tcp_tso_win_divisor = 30

前提需要开启 tso:
# ethtool -K eth2 tso on

Continue reading

10G(82599EB) 网卡测试优化(jumbo frame, tcp win scaling)

1. 开启 jumbo frame
开启之前:
# netperf  -H 192.168.10.2  -t TCP_STREAM -c -C -l 10
MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.10.2 () port 0 AF_INET
Recv   Send    Send                          Utilization       Service Demand
Socket Socket  Message  Elapsed              Send     Recv     Send    Recv
Size   Size    Size     Time     Throughput  local    remote   local   remote
bytes  bytes   bytes    secs.    10^6bits/s  % S      % S      us/KB   us/KB

 87380  16384  16384    10.00      9394.05   2.68     2.46     0.561   0.516  

对于 2900, 4000/4500, 6000/6500 系列的,可以看这里。我们这里使用的是 N5K:
sw 上:
N5K(config)#policy-map type network-qos jumbo
N5K(config-pmap-nq)#class type network-qos class-default
N5K(config-pmap-c-nq)#mtu 900
N5K(config-pmap-c-nq)#exit
N5K(config-pmap-nq)#exit
N5K(config)#system qos
N5K(config-sys-qos)#service-policy type network-qos jumbo

server:
# ifconfig eth2 mtu 9000
永久写入:
# vi /etc/sysconfig/network-script/ifcfg-eth2
MTU="9000"

测试:
# ip route get 192.168.10.2

开启之后,效果十分的明显,达到了 9.9Gbps:
# netperf  -H 192.168.10.2  -l 15 -c -C -t TCP_SENDFILE -f /boot/initramfs-2.6.32-220.el6.x86_64.img  -f g
TCP SENDFILE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.10.2 () port 0 AF_INET
Recv   Send    Send                          Utilization       Service Demand
Socket Socket  Message  Elapsed              Send     Recv     Send    Recv
Size   Size    Size     Time     Throughput  local    remote   local   remote
bytes  bytes   bytes    secs.    10^9bits/s  % S      % S      us/KB   us/KB
 87380  16384  16384    15.00         9.90   2.15     2.91     0.427   0.578  

注意,jambo frame 是需要端到端的开启的,即 server-sw-server,只要有一方没有开启都无法实现。

2. tcp_window_scaling 对网络的影响
这个参数主要是在 tcp win size > 2^16(64Kb) 时发挥作用,默认 tcp_window_scaling 是开启的:
# cat /proc/sys/net/ipv4/tcp_window_scaling
1
# netperf  -H 192.168.10.2  -P1 -l 30
MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.10.2 () port 0 AF_INET
Recv   Send    Send                          
Socket Socket  Message  Elapsed              
Size   Size    Size     Time     Throughput  
bytes  bytes   bytes    secs.    10^6bits/sec  
 87380  16384  16384    30.00    9411.50   


将 server 端的关闭:
# echo 0 > !$
echo 0 > /proc/sys/net/ipv4/tcp_window_scaling
# netperf  -H 192.168.10.2  -P1 -l 30
MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.10.2 () port 0 AF_INET
Recv   Send    Send                          
Socket Socket  Message  Elapsed              
Size   Size    Size     Time     Throughput  
bytes  bytes   bytes    secs.    10^6bits/sec  
 87380  16384  16384    30.00    2323.36   

只要有一端不开启,吞吐只能维持在 2Gbps 左右。当然,可以通过 interrupt coalescing 来部分的提高吞吐。

10G(82599EB) 网卡测试优化(总)

在正式测试优化之前,需要熟悉下,一个包从进入 NIC 到 userspace 的处理过程(1, 2, 3)。 
服务器硬件的基本(cs 有微小差异,可忽略不计)配置为 E5-2630×2, 8Gx8, 6x600G 10krpm, raid 10。系统为 RedHat 6.2 2.6.32-279.el6.x86_64。交换机为 Nexus5548, 5.1(3)N2(1)。关于设备之间的连接,正常多膜(SFP-10G-SR(=))的足够了,但是要注意区分下不同波长(850nm/1310nm)的模块。
服务器的网卡为 82599EB 芯片,x520-2,PCIe x8, 5GT/s:
# lspci -vvv -s | grep net -A 20 -B 20
81:00.0 Ethernet controller: Intel Corporation 82599EB 10-Gigabit SFI/SFP+ Network Connection (rev 01)
    Subsystem: Intel Corporation Ethernet Server Adapter X520-2
        …
        Capabilities: [40] Power Management version 3
        Flags: PMEClk- DSI+ D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold-)
        Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=1 PME-
    Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
        Address: 0000000000000000  Data: 0000
        Masking: 00000000  Pending: 00000000
    Capabilities: [70] MSI-X: Enable+ Count=64 Masked-
        Vector table: BAR=4 offset=00000000
        PBA: BAR=4 offset=00002000
    Capabilities: [a0] Express (v2) Endpoint, MSI 00
        DevCap:    MaxPayload 512 bytes, PhantFunc 0, Latency L0s <512ns, L1 <64us
            ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset+
        DevCtl:    Report errors: Correctable+ Non-Fatal+ Fatal+ Unsupported+
            RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop+ FLReset-
            MaxPayload 256 bytes, MaxReadReq 512 bytes
        DevSta:    CorrErr+ UncorrErr- FatalErr- UnsuppReq+ AuxPwr- TransPend-
        LnkCap:    Port #2, Speed 5GT/s, Width x8, ASPM L0s, Latency L0 <1us, L1 <8us
                        ClockPM- Surprise- LLActRep- BwNot-
        LnkCtl:    ASPM Disabled; RCB 64 bytes Disabled- Retrain- CommClk-
            ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
        LnkSta:    Speed 5GT/s, Width x8, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
        
ixgbe 支持的众多参数,比 broadcom57xx, intel i350 档次明显高很多:
# modinfo ixgbe
parm:           InterruptType:Change Interrupt Mode (0=Legacy, 1=MSI, 2=MSI-X), default IntMode (deprecated) (array of int)
parm:           IntMode:Change Interrupt Mode (0=Legacy, 1=MSI, 2=MSI-X), default 2 (array of int)
parm:           MQ:Disable or enable Multiple Queues, default 1 (array of int)
parm:           DCA:Disable or enable Direct Cache Access, 0=disabled, 1=descriptor only, 2=descriptor and data (array of int)
parm:           RSS:Number of Receive-Side Scaling Descriptor Queues, default 0=number of cpus (array of int)
parm:           VMDQ:Number of Virtual Machine Device Queues: 0/1 = disable, 2-16 enable (default=8) (array of int)
parm:           max_vfs:Number of Virtual Functions: 0 = disable (default), 1-63 = enable this many VFs (array of int)
parm:           L2LBen:L2 Loopback Enable: 0 = disable, 1 = enable (default) (array of int)
parm:           InterruptThrottleRate:Maximum interrupts per second, per vector, (0,1,956-488281), default 1 (array of int)
parm:           LLIPort:Low Latency Interrupt TCP Port (0-65535) (array of int)
parm:           LLIPush:Low Latency Interrupt on TCP Push flag (0,1) (array of int)
parm:           LLISize:Low Latency Interrupt on Packet Size (0-1500) (array of int)
parm:           LLIEType:Low Latency Interrupt Ethernet Protocol Type (array of int)
parm:           LLIVLANP:Low Latency Interrupt on VLAN priority threshold (array of int)
parm:           FdirPballoc:Flow Director packet buffer allocation level:
            1 = 8k hash filters or 2k perfect filters
            2 = 16k hash filters or 4k perfect filters
            3 = 32k hash filters or 8k perfect filters (array of int)
parm:           AtrSampleRate:Software ATR Tx packet sample rate (array of int)
parm:           FCoE:Disable or enable FCoE Offload, default 1 (array of int)
parm:           LRO:Large Receive Offload (0,1), default 1 = on (array of int)
parm:           allow_unsupported_sfp:Allow unsupported and untested SFP+ modules on 82599 based adapters, default 0 = Disable (array of int)

各个 parm 的含义可以看这里

测试监控的工具,这个很早之前都有提到。包括如下的一些:
* iftop
* jnettop
* iptraf
* nethogs
* vnstat
* ibmonitor
* iperf
* netserver
* ntop
* cacti(需要安装 Realtime plugins)
* mpstat
* vmstat
* netstat
* lspci
* dropwatch
* ifconfig
* ip

关于 netstat,使用不同的参数,读取的源文件不大一样,比如,读取 /proc/net/tcp 在连接数比较大的时候就很慢,而诸如 /proc/net/dev, /prco/net/unix 则相对会快的多。

而对于做 tweak 来说,主要集中在 ethtool, sysctl, ifconfig, setpci 这几个工具之间。

ping 延时跟千兆比基本不变,维持在 0.2ms 左右。在进行压测期间,会增加到 2ms 左右。以下的都是在未对服务器交换机做任何优化之前的测量结果,即所有的都是默认设置。 整个过程主要涉及 iperf, netserver 两个工具。

关于 iperf 的使用,需要注意几点:
# iperf -c 192.168.10.2 -t 20 –format KBytes -d/-r -x CMS -w 400
如果想让 c 和 s 同时往对端发送流量,可以在 c 端使用 -d 参数;如果想依次进行,可以使用 -r 参数。
双方同时发 9.35 左右,单方向发 9.41 左右。

还有一点需要注意,有没有 -P 参数,即是否使用多线程,对测试的影响还是很大的,尤其涉及到中断时。

netperf 相比更为专业,使用也比较简单,最常见的如下:
# netperf  -H 192.168.10.2  -l 15 -c -C -f g — -m 8192 -r 1024,1024

可以先对 localhost 发起一个请求确认网卡有无异常,正常情况下应该是打满了:
# netperf  -f g
MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to localhost () port 0 AF_INET
Recv   Send    Send                          
Socket Socket  Message  Elapsed              
Size   Size    Size     Time     Throughput  
bytes  bytes   bytes    secs.    10^9bits/sec  

 87380  16384  16384    10.00      10.75   

默认会使用 TCP_STREAM 也就是 send() 来发包,也可以通过 TCP_SENDFILE 调用 sendfile() 来测试,不过在测试达到 9.4G 之后,使用 sendfile 提升效果微乎其微。

接下来几篇博客会从不同的方面总结一些通用的操作优化,每一个测试都是相互独立的。最终的目的就是充分使用带宽,打满 10G,系统的负载尽可能的低,latency 尽可能的低。并且,更多的是集中在优化而非测试上,因此并没有正式的测试报告,更没有漂亮的图表。在测试过程中,可以通过下面的命令观察基本的系统负载状况:
# sar -n DEV -u 10 -P ALL
# mpstat -P ALL 5
当然,最基本的 top 也能看出不少的数据。

对于 BIOS 方面的问题,基本是在 "profile" 上的选择上,细化就是 C1E, Turbo boost, HT 等问题,不再多说。有点需要注意的是,关于 CPU freq 的选择,需要着重注意下。

以下的几篇关于 10G 网卡测试优化的博客参考了不少下面的两个经典的文档,在此表示感谢。时间都是 09 年左右的,但是绝大部分的实践都是适用的。
一个是 IBM 写的 《Tuning 10Gb network cards on Linux》,另外一个是 redhat 在 08 年的峰会上分享的名为 《10 Gb Ethernet》,他的加强改良版出现在 12 年《achieving top network performance这是他的视频。