名词解释
出现RFS/RPS的原因主要是由于网卡不支持多队列,过多的网卡收包和发包中断集中在一个CPU核心,通过软件实现将网卡的中断分散到各个CPU上,模拟网卡多队列。
在单队列网卡的情况下,RPS/RFS相当于在系统层用软件模拟了多队列的情况,以便达到CPU的均衡。
如网卡支持多队列则可使用SMP irq affinity直接绑定硬中断。
- RPS(Receive Packet Steering,接收数据包引导)
- RFS(Receive flow steering,接收流引导)
- RSS(receive side scaling,接收方缩放)
RPS
RPS(Receive Packet Steering)主要是把软中断的负载均衡到各个cpu,简单来说,是网卡驱动对每个流生成一个hash标识,这个HASH值得计算可以通过四元组来计算(SIP,SPORT,DIP,DPORT),然后由中断处理的地方根据这个hash标识分配到相应的CPU上去,这样就可以比较充分的发挥多核的能力了。
通俗点来说就是在软件层面模拟实现硬件的多队列网卡功能,如果网卡本身支持多队列功能的话RPS就不会有任何的作用。
该功能主要针对单队列网卡多CPU环境,如网卡支持多队列则可使用SMP irq affinity直接绑定硬中断。
RPS配置方法
配置示例如下所示
# eth1
for i in {0..23}; do echo "ffffff" > /sys/class/net/eth1/queues/rx-${i}/rps_cpus; done
for i in {0..23}; do echo "4096" > /sys/class/net/eth1/queues/rx-${i}/rps_flow_cnt; done
# eth2
for i in {0..23}; do echo "ffffff" > /sys/class/net/eth2/queues/rx-${i}/rps_cpus; done
for i in {0..23}; do echo "4096" > /sys/class/net/eth2/queues/rx-${i}/rps_flow_cnt; done
- 这里的
ffffff
是CPU的核心掩码,24核心掩码对应2机制为1111 1111 1111 1111 1111 1111
,每个数字对应1个cpu核心,数字0表示不开启rps,数字1表示开启rps,转换为16进制为ffffff
- rps_flow_cnt就是RFS,表示当前网络设备rps队列的流表数,需要设置为2的整数次幂,建议设置为4096。数值越大,同时所能处理的rps流越多。
RFS
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/ethxx/queues/rx-${i}/rps_flow_cnt:它的值为rps_sock_flow_entries的值除以接收队列的数量。例如,如果rps_flow_条目设置为32768,并且有16个配置的接收队列,则rps_flow_cnt应设置为2048。对于单队列设备,rps_flow_cnt的值与rps_sock_flow_条目的值相同。
同一个sockect的包,命中为同一个CPU进行处理。如果单个应用数据量大于单个CPU可以处理的数据量,可以通过配置更大的帧大小,来减少中断次数,从而减少CPU的处理工作量。或者,考虑 NIC offload选项或更快的CPU。
附录
自动设置rps脚本
#!/bin/bash
cpu_count=`cat /proc/cpuinfo | grep -c processor`
binary=$(printf "%${cpu_count}s" | tr " " "1")
hex=$(printf "%x\n" "$((2#$binary))")
for j in `ip -br link show | grep -E '^[eth|ens]' | awk '{print $1}'`;do
for ((i = 0; i < cpu_count; i++));do
echo ${hex} > /sys/class/net/${j}/queues/rx-${i}/rps_cpus
echo "4096" > /sys/class/net/${j}/queues/rx-${i}/rps_flow_cnt
# cat /sys/class/net/${j}/queues/rx-${i}/rps_cpus
# cat /sys/class/net/${j}/queues/rx-${i}/rps_flow_cnt
done
done
grep 'net.core.rps_sock_flow_entries' /etc/sysctl.conf > /dev/null
if [ $? == 1 ]; then
echo "# set rps" >> /etc/sysctl.conf
echo net.core.rps_sock_flow_entries=$(($cpu_count * 4096)) >> /etc/sysctl.conf
sysctl -w net.core.rps_sock_flow_entries=$(($cpu_count * 4096))
fi
回退
#!/bin/bash
cpu_count=`cat /proc/cpuinfo | grep -c processor`
binary=$(printf "%${cpu_count}s" | tr " " "1")
hex=$(printf "%x\n" "$((2#$binary))")
for j in `ip -br link show | grep -E '^[eth|ens]' | awk '{print $1}'`;do
for ((i = 0; i < cpu_count; i++));do
echo "0" > /sys/class/net/${j}/queues/rx-${i}/rps_cpus
echo "0" > /sys/class/net/${j}/queues/rx-${i}/rps_flow_cnt
# cat /sys/class/net/${j}/queues/rx-${i}/rps_cpus
# cat /sys/class/net/${j}/queues/rx-${i}/rps_flow_cnt
done
done