Linux网卡丢包分类整理(2)——驱动篇
- 网卡驱动丢包
- 驱动溢出丢包
- 频繁网卡IRQ导致丢包
- 单核负载高导致丢包
网卡驱动丢包
查看:ifconfig eth1/eth0 等接口
- RX errors:
表示总的收包的错误数量,还包括too-long-frames错误,Ring Buffer 溢出错误,crc 校验错误,帧同步错误,fifo overruns 以及 missed pkg 等等。 - RX dropped:
表示数据包已经进入了 Ring Buffer,但是由于内存不够等系统原因,导致在拷贝到内存的过程中被丢弃。
从2.6.37内核开始,对RX dropped统计的内容做除了调整,目前出现一下几种情况会被统计到RX dropped当中:
Softnet backlog full (分配内存失败 在/proc/net/softnet_stat有统计)
Bad/Unintended VLAN tags
Unknown/Unregistered protocols
IPv6 frames
- 其中backlog部分会在下面详细讨论。
- RX overruns:
overruns 意味着数据包没到 ring buffer 就被网卡物理层给丢弃了。
当驱动处理速度跟不上网卡收包速度时,驱动来不及分配缓冲区,NIC 接收到的数据包无法及时写到 skb,就会产生堆积。当 NIC 内部缓冲区写满后,就会丢弃部分数据,引起丢包。
这部分丢包为 rx_fifo_errors,在 /proc/net/dev 中体现为 fifo 字段增长,在 ifconfig 中体现为 overruns 指标增长。
CPU 无法及时的处理中断是造成 ring ruffer 满的原因之一,例如中断分配的不均匀。或者 ring buffer 太小导致的。 - RX frame: 表示 misaligned 的 frames。
- 对于 TX 的来说,出现上述 counter 增大的原因主要包括 aborted transmission, errors due to carrirer, fifo error, heartbeat erros 以及 windown error,而 collisions 则表示由于 CSMA/CD 造成的传输中断。
驱动溢出丢包
netdev_max_backlog是内核从NIC收到包后,交由协议栈(如IP、TCP)处理之前的缓冲队列。每个CPU核都有一个backlog队列,与Ring Buffer同理,当接收包的速率大于内核协议栈处理的速率时,CPU的backlog队列不断增长,当达到设定的netdev_max_backlog值时,数据包将被丢弃。
查看:
通过查看/proc/net/softnet_stat可以确定是否发生了netdev backlog队列溢出:
其中:每一行代表每个CPU核的状态统计,从CPU0依次往下;每一列代表一个CPU核的各项统计:第一列代表中断处理程序收到的包总数;第二列即代表由于netdev_max_backlog队列溢出而被丢弃的包总数。从上面的输出可以看出,这台服务器统计中,并没有因为netdev_max_backlog导致的丢包。
netdev_max_backlog(接收) 和 txqueuelen(发送) 相对应。
解决方案:
netdev_max_backlog的默认值是1000,在高速链路上,可能会出现上述第二统计不为0的情况,可以通过修改内核参数
net.core.netdev_max_backlog来解决:
sysctl -w net.core.netdev_max_backlog=2000
频繁网卡IRQ导致丢包
驱动在进入到NAPI时会禁用当前网卡的 IRQ,从而能在 poll 完所有数据之前不会再有新的硬中断。
接下来介绍一下 NAPI 是怎么做到 IRQ 合并的。它主要是让 NIC 的 driver 能注册一个 poll 函数,之后 NAPI 的 subsystem 能通过 poll 函数去从 Ring Buffer 中批量拉取收到的数据。主要事件及其顺序如下:
- 网卡驱动初始化时向 Kernel 注册 poll 函数,用于后续从 Ring Buffer 拉取收到的数据
- 驱动注册开启 NAPI,这个机制默认是关闭的,只有支持 NAPI 的 driver 才会去开启
- 收到数据后网卡通过 DMA 将数据存到内存
- 网卡触发一个 IRQ,并触发 CPU 开始执行驱动注册的 Interrupt Handler
- 驱动的 Interrupt Handler 通过 napi_schedule 函数触发 softirq (NET_RX_SOFTIRQ) 来唤醒 NAPI subsystem,NET_RX_SOFTIRQ 的 handler 是 net_rx_action 会在另一个线程中被执行,在其中会调用驱动注册的 poll 函数获取收到的 Packet
- 驱动会禁用当前网卡的 IRQ,从而能在 poll 完所有数据之前不会再有新的 IRQ
- 当所有事情做完之后,NAPI subsystem 会被禁用,并且会重新启用网卡的 IRQ
- 回到第三步
驱动在poll函数中循环读取数据包,通过 buget 控制while循环的次数,从而在 Packet 特别多的时候不要让 CPU 在这里无穷循环下去,要让别的事情也能够被执行。budget 会影响到 CPU 执行 poll 的时间。
budget 越大当数据包特别多的时候可以提高 CPU 利用率并减少数据包的延迟。但是 CPU 时间都花在这里会影响别的任务的执行。
budget太小会导致频繁退出 NAPI subsyste,并且频繁触发网卡 IRQ。
查看:
通过查看/proc/net/softnet_stat可以确定是否存在网卡 IRQ频繁导致的丢包:
第三列一直在增加的话需要,表示 soft IRQ 获取的 CPU 时间太短,来不及处理足够多的网络包,那么需要增大 netdev_budget 值。
解决方案:
budget 默认 300,可以调整
sysctl -w net.core.netdev_budget=600
单核负载高导致丢包
单核CPU软中断占有高, 导致应用没有机会收发或者收包比较慢,即使调整netdev_max_backlog队列大小仍然会一段时间后丢包,处理速度跟不上网卡接收的速度;
查看:
mpstat -P ALL 1
单核软中断占有100%,导致应用没有机会收发或者收包比较慢而丢包;
解决方案:
1. 调整网卡RSS队列配置:
查看:ethtool -x ethx;
调整:ethtool -X ethx xxxx;
2. 看一下网卡中断配置是否均衡 cat /proc/interrupts
调整:
- irqbalance 调整;
service irqbalance start
service irqbalance status
service irqbalance stop
- 中断绑CPU核 echo mask > /proc/irq/xxx/smp_affinity
echo 6 > /proc/irq/41/smp_affinity
其中6 表示的是 CPU2 和 CPU1,0 号 CPU 的掩码是 0x1 (0001),1 号 CPU 掩码是 0x2 (0010),2 号 CPU 掩码是 0x4 (0100),3 号 CPU 掩码是 0x8 (1000) 依此类推。
另外需要注意的是设置 smp_affinity 的话不能开启 irqbalance 或者需要为 irqbalance 设置 –banirq 列表,将设置了 smp_affinity 的 IRQ 排除。不然 irqbalance 机制运作时会忽略你设置的 IRQ affinity 配置。
3. 根据CPU和网卡队列个数调整网卡多队列和RPS配置
- CPU大于网卡队列个数:
查看网卡队列 ethtool -x ethx;
协议栈开启RPS并设置RPS;
echo $mask(CPU配置)> /sys/class/net/$eth/queues/rx-$i/rps_cpus
echo 4096(网卡buff)> /sys/class/net/$eth/queues/rx-$i/rps_flow_cnt
- CPU小于网卡队列个数,绑中断就可以,可以试着关闭RPS看一下效果:
echo 0 > /sys/class/net/<dev>/queues/rx-<n>/rps_cpus
4. numa CPU调整,对齐网卡位置,可以提高内核处理速度,从而给更多CPU给应用收包,减缓丢包概率;
查看网卡numa位置:
ethtool -i eth1|grep bus-info
lspci -s bus-info -vv|grep node
上面中断和RPS设置里面mask需要重新按numa CPU分配重新设置;
5. 可以试着开启中断聚合(看网卡是否支持)
查看 :
ethtool -c ens5f0
调整:
ethtool -C ethx adaptive-rx on