当前位置: 首页 > 工具软件 > rss-status > 使用案例 >

网卡调优RSS、RPS、RFS和XPS

罗烨霖
2023-12-01

1.RSS(Receive Side Scaling)

​ RSS(Receive Side Scaling)也叫多队列接收,可以硬件级别实现使用多核处理接收网络数据。可用于缓解因单个CPU过载,导致的接收中断处理瓶颈,并减少网络延迟。

​ 可以通过/proc/interrupts确定网络接口卡是否支持RSS。也可以在加载网络驱动程序后,通过“ls -1 /sys/devices///device_pci_address/msi_irq” 的输出来查看。(需要网卡硬件及驱动同时支持)

​ 默认情况下,RSS是启用的。RSS的队列数量通常在/sys/class/net/device/queues/rx queue/中配置(device是网络设备的名称(例如eth1),rx queue是相应接收队列的名称(如rx-0))。

​ 在配置RSS时,RedHat建议将队列数量限制为每个物理CPU核心一个队列。启用RSS后,会根据每个CPU使用情况来分配数据的处理。也可以使用ethtool --show-rxfh-indir和–set-rxfh-indir参数来修改使用的权重。

​ irqbalance守护进程可以与RSS一起使用,它将减少跨节点内存传输和缓存线跳转的可能性。如果开启了irqbalance和RSS,通过irqbalance将与网络设备相关的中断定向到适当的RSS队列,可以实现最低的延迟。

2.RPS( Receive Packet Steering)

​ RPS与RSS类似,用于将数据包定向到特定的CPU进行处理。RPS是在软件级别实现的,有助于避免单队列网卡成为网络流量的瓶颈。与RSS相比,RPS有几个优势:

  • RPS由于是基于软件的,因此可以支持任意型号的网卡;

  • 在处理新协议时,更容易增加软件过滤器;

  • RPS不会增加网络设备的硬件中断率。注意:RPS会引入了处理器间的中断(软中断);

​ RPS是在/sys/class/net/device/queues/rx-queue/rps_cpus文件中配置接收队列(device是网络设备的名称(如eth0),rx-queue是相应接收队列的名称(如rx-0))。rps_cpus文件默认值为0,也就是禁用RPS,CPU既会处理数据中断,也会处理数据包。要让指定的CPU处理接收队列的中断数据,需要将对应CPU索引位设置为1。如,要使用CPU 0、1、2和3处理中断,只需要将rps_cpus文件值设置为00001111或 f(十六进制值=1+2+4+8)。

​ 对于单队列的网络设备,可以通过将RPS配置为在同一内存域中使用CPU,来实现最佳性能。在非NUMA系统上,这意味着可以使用所有可用的CPU。如果网络中断率极高时,非处理网络中断的CPU,也可能会提高性能。

​ 对于多队列的网络设备,同时开启RPS和RSS通常是没有用处的。因为默认情况下,RSS会将CPU映射到每个接收队列。注意:如果硬件队列少于CPU,并且RPS被配置为在同一内存域中使用CPU,则RPS仍然是有用的。

​ 开启RPS后softirq消耗仍不均衡:发现开启rps后,softirq不均衡的情况有缓解,但仍有较大差异。用perf分析高消耗cpu的ksoftirqd,发现有很大一部分cpu消耗在网卡驱动的清理流程中。rps只能减轻cpu处理网络协议的开销,对于网卡驱动上的开销无优化效果,对于这种情况可以设置rps_cup时,排除本身硬中断处理的cpu,来进一步减轻其开销。

3.RFS(Receive Flow Steering)

​ RFS是RPS的功能扩展,用来提高CPU缓存命中率,减少网络延迟。RPS仅根据队列长度转发数据包,而RFS使用RPS后端计算最合适的CPU,然后根据数据包的应用程序的位置来转发数据包。(同1个sockect的包,命中为同1个CPU进行处理)

​ 默认情况下禁用RFS。要启用RFS,必须编辑两个文件:

  • /proc/sys/net/core/rps_sock_flow_entries:指定并发活动连接的最大预期数量。对于中等服务器负载,建议使用32768。在实践中,所有输入的值都被四舍五入到最接近的2次方。

  • /sys/class/net/device/queues/rx-queue/rps_flow_cnt:它的值为rps_sock_flow_entries的值除以接收队列的数量。例如,如果rps_flow_条目设置为32768,并且有16个配置的接收队列,则rps_flow_cnt应设置为2048。对于单队列设备,rps_flow_cnt的值与rps_sock_flow_条目的值相同。

    ​ 同1个sockect的包,命中为同1个CPU进行处理。如果单个应用数据量大于单个CPU可以处理的数据量,可以通过配置更大的帧大小,来减少中断次数,从而减少CPU的处理工作量。或者,考虑 NIC offload选项或更快的CPU。

4.RFS加速

​ 增加硬件辅助来提高RFS的速度,从而实现PFS的加速。

​ 只有满足以下条件,加速RFS才可用:网卡支持加速RFS;启用ntuple筛选;

​ 一旦满足这些条件,就会根据传统的RFS配置自动推导CPU到队列的映射。也就是说,CPU到队列的映射是基于驱动程序为每个接收队列配置的IRQ亲和力推导出来的。

5.NIC OFFLOADS

​ 默认的以太网最大传输单元(MTU)是1500字节,这是通常可以传输的最大帧大小。这可能会导致系统资源未充分利用,例如,如果有3200字节的数据用于传输,这将意味着生成三个较小的数据包。有几个称为offloads的选项,允许相关协议栈传输比正常MTU大的数据包。可以创建最大允许64KiB的数据包,并提供发送(Tx)和接收(Rx)选项。当发送或接收大量数据时,这意味着每发送或接收64KiB数据,就要处理一个大数据包,而不是多个较小的数据包。这意味着生成的中断请求更少,用于拆分或组合流量的处理开销更少,传输机会更多,从而导致吞吐量的总体增加。

5.1.Offload类型

TCP Segmentation Offload (TSO)

​ 使用TCP协议发送大数据包。使用NIC处理段,然后将TCP、IP和数据链路层协议头添加到每个数据段。

UDP Fragmentation Offload (UFO)

​ 使用UDP协议发送大数据包。使用NIC将IP碎片处理为MTU大小的数据包,用于大型UDP数据报文。

Generic Segmentation Offload (GSO)

​ 使用TCP或UDP协议发送大数据包。如果NIC无法处理数据段/报文,GSO将绕过NIC硬件执行相同的操作。它通过尽可能晚地延迟分段来实现的。

Large Receive Offload (LRO)

​ 使用TCP协议。所有传入数据包在接收时,都会重新分段,从而减少系统必须处理的段数。它们可以在驱动程序中合并,也可以使用NIC合并。

​ LRO的一个问题是:它倾向于对所有传入的数据包进行重新排序,通常会忽略标头和其他可能导致错误的信息的差异。当启用IP转发时,通常不可能使用LRO。LRO与IP转发相结合可能会导致校验和错误。注意:如果/proc/sys/net/ipv4/ip_forward设置为1,则启用转发。

Generic Receive Offload (GRO)

​ 使用TCP或UDP协议。在对数据包重新排序时,GRO比LRO更严格。例如,它检查每个数据包的MAC报头(必须匹配),只有有限数量的TCP或IP报头可以不同,并且TCP时间戳必须匹配。重新排序可以由NIC或GSO代码处理。

5.2.使用NIC Offloads

​ Offload应该用于传输或接收大量数据的高速系统,并且有利于吞吐量而不是延迟。使用Offload大大增加了驱动程序队列的容量,所以延迟可能会成为一个问题。

​ 这方面的一个例子,一个系统使用大数据包大小传输大量数据,但也运行许多交互式应用程序。由于交互式应用程序以一定的时间间隔发送小数据包,因此在处理前面较大的数据包时,这些数据包可能会“被困”在缓冲区中,从而造成不可接受的延迟,这是一个非常现实的风险。

​ 可以使用ethtool命令检查Offload设置,某些设备可能被列为固定设置,这意味着它们无法更改。

6.XPS(Transmit Packet Steering)

​ XPS通过创建CPU到网卡发送队列的对应关系,来保证处理发送软中断请求的CPU和向外发送数据包的CPU是同一个CPU,用来保证发送数据包时候的局部性。

​ 对于发送队列到CPU的映射有两种选择:

  • 使用CPU映射:通过指定发送队列在某几个CPU上处理,通过减小分发的CPU范围来减少锁开销以及cache miss。最常见的就是1对1,和上面说到的接收软中断绑核类似,通过 /sys/class/net//queues/tx-/xps_cpus文件设置,同样是bitmaps方式。
  • 接收队列映射方式:基于接收队列的映射来选择CPU,也就是说让接收队列和发送队列在同一个CPU,或指定范围的几个CPU来处理。这种方式对于多线程一直收发包的系统效果比较明显,收发包队列处理在同一个CPU,不仅减少了对其他CPU的打断,同时提高应用处理效率,收完包后直接在同个CPU继续发包,从而减小CPU消耗,同时减小包的时延。可通过/sys/class/net//queues/tx-/xps_rxqs文件设置(不是所有网卡都支持)。

​ XPS对于单发送队列网卡没有效果

7.单网卡调优:开启RPS和RFS(默认关闭)

  • 开启CPU性能模式:cpupower frequency-set -g performance

  • 开启RPS和RFS;

  • ethtool -G p1p1 [rx|tx] 4096,检查设置结果ethtool -g p1p1

  • sysctl -w net.core.netdev_budget=600

  • 查看网卡相应收发包情况;

开启RPS和RFS的脚本示例:

#!/bin/bash
# cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# cpupower frequency-set -g performance
# activate rps/rfs by script: https://gist.github.com/wsgzao/18828f69147635f3e38a14690a633daf
# double ring buffer size: ethtool -G p1p1 [rx|tx] 4096, ethtool -g p1p1
# double NAPI poll budget: sysctl -w net.core.netdev_budget=600

rps_start() 
{
	net_interface=`ip link show | grep "state UP" | awk '{print $2}' | egrep -v '^docker|^veth' | tr ":\n" " "`
	for em in ${net_interface[@]}
	do
		rq_count=`ls /sys/class/net/$em/queues/rx-* -d | wc -l`
		rps_flow_cnt_value=`expr 32768 / $rq_count`

		for ((i=0; i< $rq_count; i++))
		do
			echo $rps_flow_cnt_value > /sys/class/net/$em/queues/rx-$i/rps_flow_cnt
		done
	
		flag=0
		while [ -f /sys/class/net/$em/queues/rx-$flag/rps_cpus ]
		do
			echo `cat  /sys/class/net/$em/queues/rx-$flag/rps_cpus | sed 's/0/f/g' ` >  /sys/class/net/$em/queues/rx-$flag/rps_cpus
			flag=$(($flag+1))
		done
	done
	echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
	sysctl -p
}

rps_stop() {
	net_interface=`ip link show | grep "state UP" | awk '{print $2}' | egrep -v '^docker|^veth' | tr ":\n" " "`
	for em in ${net_interface[@]}
	do
		rq_count=`ls /sys/class/net/$em/queues/rx-* -d | wc -l`
		rps_flow_cnt_value=`expr 32768 / $rq_count`
	
		for ((i=0; i< $rq_count; i++))
	  do
	      echo 0 > /sys/class/net/$em/queues/rx-$i/rps_flow_cnt
	  done
	
	  flag=0
	  while [ -f /sys/class/net/$em/queues/rx-$flag/rps_cpus ]
	  do
	      echo 0 >  /sys/class/net/$em/queues/rx-$flag/rps_cpus
	      flag=$(($flag+1))
	  done
  done
  echo 0 > /proc/sys/net/core/rps_sock_flow_entries
  sysctl -p
}

rps_status() {
  ni_list=`ip link show | grep "state UP" | awk '{print $2}' | egrep -v "^docker|^veth" | tr ":\n" " "`
  for n in $ni_list
  do
      rx_queues=`ls /sys/class/net/$n/queues/ | grep "rx-[0-9]"`
      for q in $rx_queues
      do
          rps_cpus=`cat /sys/class/net/$n/queues/$q/rps_cpus`
          rps_flow_cnt=`cat /sys/class/net/$n/queues/$q/rps_flow_cnt`
          echo "[$n]" $q "--> rps_cpus =" $rps_cpus ", rps_flow_cnt =" $rps_flow_cnt
      done

  done
  rps_sock_flow_entries=`cat /proc/sys/net/core/rps_sock_flow_entries`
  echo "rps_sock_flow_entries =" $rps_sock_flow_entries
}

case "$1" in
  start)
        echo -n "Starting $DESC: "
        rps_start
        rps_status
        ;;
  stop)
        rps_stop
        rps_status
        ;;
  restart|reload|force-reload)
        rps_stop
		 rps_start
        ;;
  status)
        rps_status
        ;;
  *)
        echo "Usage: $0 [start|stop|status]"
        ;;
esac
exit 0

**实际测试:**开启RPS后,可以一定程度上解决网卡数据集中在一个CPU核处理的问题,但CPU消耗会明显增加;

8.多队列网卡

8.1.查看主网卡支持多队列的情况

​ 其中Pre-set maximums中的combined字段大于1时,表示网卡支持多队列。而Current harware settings中的conbined则表示当前设置的网卡队列数,如果队列数大于1,就是已经开启了网卡多队列。

[root@localhost ~]# ethtool -l eth0
    Channel parameters for eth0:
		Pre-set maximums:
		RX: 0
		TX: 0
		Other: 0
		Combined: 64
    Current hardware settings:
		RX: 0
		TX: 0
		Other: 0
		Combined: 32

8.2.设置网卡多队列

ethtool -L eth1 combined 16

​ 注意:设置的队列数的前提是网卡首先要支持多队列,且不能超过网卡支持的最大的队列数。当网卡驱动比较老旧的时候,也有可能会设置失败,建议将网卡驱动先升级至最新版本。

Redhat的RSS参考文档

 类似资料: