rps和rfs优化脚本
  mtfBvWHnJ7tD 2023年11月02日 60 0
#!/bin/bash
# https://github.com/sklochkov/performance-tuner/blob/master/src/network-queues.sh
# https://github.com/torvalds/linux/blob/master/Documentation/networking/scaling.rst

yum install redhat-lsb -y

if [ -n "`lsb_release -i | grep -i "debian"`" ]
then
    apt-get install -y net-tools bc ethtool
else
    yum install -y net-tools bc ethtool
fi

# cpu核心数
cpus=`grep -c processor /proc/cpuinfo`

str=""
for vth in `ls /sys/devices/virtual/net/`;do
    str="${vth}|${str}"
done
# 虚拟网卡列表,以|分割
vthstr=`echo ${str} | sed 's/^\(.*\)|$/\1/g'`

# 物理网卡列表,以空格分割
interfaces=`sed -n '3,$p' /proc/net/dev | awk -F ":" '{print $1}' | grep -Ev "${vthstr}" | sed 's/ //g'`
# 物理网卡列表,以|分割
#interfaces_shugang=`sed -n '3,$p' /proc/net/dev | awk -F ":" '{print $1}' | grep -Ev "${vthstr}" | sed 's/ //g' |tr "\n" "|"  | sed 's/^\(.*\)|$/\1/g'`

#
filter_interfaces=""
for interface in ${interfaces};do
        ip_down=`ip add|grep -w "${interface}:"| grep -v 'NO-CARRIER'`
        if [ -z "${ip_down}" ];then
             echo "interface: ${interface} is down! remove it"
        else
            echo "filter_interfaces:${filter_interfaces}"
            if [ -z ${filter_interfaces} ];then
                    filter_interfaces="${interface}"
            else
                    filter_interfaces="${filter_interfaces} ${interface}"
            fi
        fi
        echo -e "interfaces:\n${interfaces}"
        echo "filter_interfaces:${filter_interfaces}"
done

echo -e "interfaces:\n${interfaces}"
echo "filter_interfaces:${filter_interfaces}"

# 值覆盖
interfaces=${filter_interfaces}

# eth接口   队列默认是1000
for interface in ${interfaces};do
    /sbin/ethtool -G ${interface} rx 4096
    /sbin/ethtool -G ${interface} tx 4096

    q=(`grep -E "${interface}" /proc/interrupts | awk '{print $1}' | cut -d: -f 1 | grep -e '^[0-9]*$'`)
    echo "${interface} 物理网卡中断号列表:${q[*]}"
    echo "${interface} 中断号数量:${#q[@]}"
    # 跳过cpu0
    k=1 # k伴随中断号递增
    for i in ${q[*]};do # i表示中断号
        echo ${k} > /proc/irq/$i/smp_affinity_list
        echo "irq: ${i} cpu_list: `cat /proc/irq/${i}/smp_affinity_list`"
        k=$[${k}+1]
        if [ ${k} -eq ${cpus} ]
        then
           k=0
        fi
    done

    if_pppoe_running=`ps -ef|grep -i 'pppoe'|grep -v grep |wc -l`

    # 中断数少于核心数
    # https://github.com/torvalds/linux/blob/master/Documentation/networking/scaling.rst#suggested-configuration-3
    rps_sock_flow_entries=32768
    # 跳过cpu0
    if [ ${cpus} -lt 32 ]
    then
        default_affinity="fffffe"
    else
        default_affinity="fffffffe"
    fi  
    if [ ${if_pppoe_running} -gt 0 ];then
        # 有pppoe拨号 rps开 rfs不开
        echo 0 > /proc/sys/net/core/rps_sock_flow_entries
        RX=`ls -ld /sys/class/net/${interface}/queues/rx* 2>/dev/null | wc -l`
        TX=`ls -ld /sys/class/net/${interface}/queues/tx* 2>/dev/null | wc -l`
        if [ $RX -gt 0 ] ; then
            for i in `seq $RX` ; do
                echo ${default_affinity} > /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus
                echo 0 > /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_flow_cnt
                echo "/sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus `cat /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus`"
                echo "/sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_flow_cnt `cat /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_flow_cnt`"
            done
        fi
        if [ $TX -gt 0 ] ; then
            for i in `seq $TX` ; do
                echo "${default_affinity}" > /sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus
                echo "/sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus `cat /sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus`"
            done
        fi
    elif [ ${#q[@]} -lt ${cpus} ];then
            # 没有pppoe拨号 rps rfs都开
            RX=`ls -ld /sys/class/net/${interface}/queues/rx* 2>/dev/null | wc -l`
            TX=`ls -ld /sys/class/net/${interface}/queues/tx* 2>/dev/null | wc -l`
            if [ $RX -gt 0 ] ; then
                for i in `seq $RX` ; do
                    echo ${default_affinity} > /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus
                    # brpc thread-model don't open rfs
                    echo "0" > /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_flow_cnt
                    echo "/sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus `cat /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus`"
                    echo "/sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_flow_cnt `cat /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_flow_cnt`"
                done
            fi
            if [ $TX -gt 0 ] ; then
                for i in `seq $TX` ; do
                    echo "${default_affinity}" > /sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus
                    echo "/sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus `cat /sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus`"
                done
            fi
            # brpc thread-model don't open rfs
            echo "0" > /proc/sys/net/core/rps_sock_flow_entries
    else
        # close rps/rfs
        echo "0" > /proc/sys/net/core/rps_sock_flow_entries
        if [ -d /sys/class/net/${interface}/queues ] ; then
            RX=`ls -ld /sys/class/net/${interface}/queues/rx* 2>/dev/null | wc -l`
            TX=`ls -ld /sys/class/net/${interface}/queues/tx* 2>/dev/null | wc -l`
            if [ $RX -gt 0 ] ; then
                for i in `seq $RX` ; do
                    echo "0" > /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus
                    echo "0" > /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_flow_cnt
                    echo "/sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus `cat /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus`"
                    echo "/sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_flow_cnt `cat /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_flow_cnt`"
                done
            fi
            if [ $TX -gt 0 ] ; then
                for i in `seq $TX` ; do
                    echo "0" > /sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus
                    echo "/sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus `cat /sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus`"
                done
            fi
        fi
    fi
done

nic-rps.sh脚本解释
1. 安装 redhat-lsb 包,用于检测操作系统类型。
2. 检测操作系统类型,如果是 Debian 系统,则使用 apt-get 安装 net-tools、bc 和 ethtool,否则使用 yum 安装这些工具。
3. 获取 CPU 核心数。
4. 获取虚拟网卡列表,存储在变量 vthstr 中。
5. 获取物理网卡列表,存储在变量 interfaces 中,并将虚拟网卡从列表中剔除。
6. 过滤掉网络不可用的物理网卡,并将可用的物理网卡存储在 filter_interfaces 变量中。
7. 将 filter_interfaces 的值覆盖给 interfaces 变量。
8. 对每个物理网卡执行以下操作:
    使用 ethtool 命令设置接收队列(rx)和发送队列(tx)的大小为 4096。
    获取物理网卡的中断号列表,并将其存储在变量 q 中。
    针对每个中断号,设置对应的 CPU 亲和性,使中断在不同的 CPU 核心之间分配。
    如果系统中运行了 pppoe 进程,则将 RPS 和 RFS 相关设置关闭。
    如果物理网卡的中断号数量小于 CPU 核心数,则开启 RPS 和 RFS,并设置相应的 CPU 亲和性。
    如果以上条件均不满足,则关闭 RPS 和 RFS。


使用 ethtool 命令设置接收队列(rx)和发送队列(tx)的大小为 4096。
 RingBuffer   环形缓冲器   默认为512, 最大支持调整到  4096。 不同设备默认值不一样。

ethtool -g eth0
Ring parameters for eth0:
Pre-set maximums:
RX:		4096
RX Mini:	0
RX Jumbo:	0
TX:		4096
Current hardware settings:
RX:		512
RX Mini:	0
RX Jumbo:	0
TX:		512

ethtool -S p7p2 | grep fifo_errors
     rx_fifo_errors: 0
     tx_fifo_errors: 0
 fifo_errors如果不为0,表示有包因为RingBuffer装不下而丢弃了,可以通过调整RingBuffer大小解决。  

获取物理网卡的中断号列表,并将其存储在变量 q 中。针对每个中断号,设置对应的 CPU 亲和性,使中断在不同的 CPU 核心之间分配。
 /proc 实际上是 Linux 的一个虚拟文件系统,用于内核空间与用户空间之间的通信。/proc/interrupts 就是这种通信机制的一部分,提供了一个只读的中断使用情况。  

k=1 表示 跳过cpu0,从cpu1开始。
smp_affinity_list ,用于设置中断处理程序(IRQ handler)的 CPU 亲和性。  
 将变量 ${k} 的值写入 /proc/irq/$i/smp_affinity_list 文件中。其中,${k} 是一个表示 CPU 序号的变量,$i 是表示中断号的变量。  可以将中断处理程序分配到不同的 CPU 上,实现并行处理和负载均衡的效果。
这样做的目的是通过将中断处理程序分布在多个 CPU 上,充分利用系统的多核心资源,提高网络性能和整体系统的吞吐能力。

什么是中断?
由于接收来自外围硬件 (相对于 CPU 和内存) 的异步信号或者来自软件的同步信号,而进行相应的硬件、软件处理;发出这样的信号称为进行中断请求 (interrupt request, IRQ)
硬中断与软中断?
● 硬中断:外围硬件发给 CPU 或者内存的异步信号就称之为硬中断
● 软中断:由软件系统本身发给操作系统内核的中断信号,称之为软中断。通常是由硬中断处理程序或进程调度程序对操作系统内核的中断,也就是我们常说的系统调用 (System Call)

RPS 和 RFS

● RSS(receive side scaling):网卡多队列
● RPS(Receive Packet Steering)是一种软件机制,用于将接收的数据包分发到多个处理器核心,以提高数据包处理的并行性能。
● RFS(Receive Flow Steering)是一种软件机制,用于在多队列设备上智能地选择接收队列,以实现负载平衡和并行处理。
● XPS(transmit packet Steering):应用在发送方向  
 
RPS
在单队列网卡,多核系统上,或者多队列网卡,但是核数比队列多的情况下,可以使用RPS功能,将数据包分发到多个核上,使多核在软中断中处理数据包。这相当于是RSS的软件实现。

 将cpu列表写入队列对应的rps_cpus即可。

 echo ${default_affinity} > /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus
echo "${default_affinity}" > /sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus

 如果系统的 CPU 核心数小于 32,那么 default_affinity 被设置为 "fffffe",意味着中断处理程序可以在除了 CPU0 外的所有核心上运行。如果系统的 CPU 核心数大于或等于 32,default_affinity 被设置为 "fffffffe",表示中断处理程序可以在所有 CPU 核心上运行(包括 CPU0)。  

 RFS(Receive flow steering)和 RPS 配合使用。
RPS 试图在 CPU 之间平衡收包,但是没考虑数据的本地性问题,如何最大化 CPU 缓存的命中率。RFS 将属于相同 flow 的包送到相同的 CPU 进行处理,可以提高缓存命中率。  

sysctl -w net.core.rps_sock_flow_entries=32768
 echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
## 2048  是     32768/队列数量
echo 2048 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt

关闭rps 和 rfs
echo "0" > /proc/sys/net/core/rps_sock_flow_entries
echo "0" > /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_cpus
echo "0" > /sys/class/net/${interface}/queues/rx-$[$i - 1]/rps_flow_cnt
echo "0" > /sys/class/net/${interface}/queues/tx-$[$i - 1]/xps_cpus
说明
1. 高网络负载环境:当系统面临高网络负载时,同时开启RPS和RFS可以提高数据包处理的并行性能。RPS将接收的数据包分发到多个处理器核心,而RFS将流量分发到多个接收队列,以实现负载平衡。
建议配置:将RPS和RFS都启用,并根据系统配置和硬件支持,配置合适的队列和CPU映射关系。

2. 多队列网络接口:对于支持多队列的网络接口,可以利用RPS和RFS来提高并行处理能力。通过将接收队列映射到传输队列,可以实现更好的负载平衡和数据包处理效率。
建议配置:启用RPS和RFS,并根据系统配置和网络接口特性,配置适当的接收队列和传输队列映射关系。

3.不适合开启 RFS 的情况:
1. 低网络负载:如果你的系统面临较低的网络负载,启用 RFS 可能没有明显的性能提升效果。RFS 主要在高负载情况下发挥作用,通过负载平衡将流量分发到多个接收队列,以实现并行处理。在低负载情况下,这种额外的负载平衡机制可能不会带来显著的性能改进。
2. 单队列网络接口:如果你的网络接口只支持单个接收队列,那么启用 RFS 的效果将受限。RFS 需要多个接收队列来实现流量分发和负载平衡,如果只有一个队列可用,RFS 将无法发挥作用
3. PPPoE拨号本身通常是单队列的,但是在拨号连接建立后,操作系统和网络驱动程序可能会使用多队列来处理从PPP接口接收和发送的数据。这样可以利用多个CPU核心或线程来处理网络流量,提高性能和吞吐量。具体多队列的配置和实现取决于操作系统、网络驱动程序以及网络接口的硬件能力。  


优化建议
1. 查看网卡支持多队列的情况,ethtool -l eth0  
2.  mpstat -P ALL 2  
● irq 为硬中断
● soft 为软中断
3. 开启 irqbalance 服务,让系统自动调整网络中断在多个 CPU 核上的分配
yum -y install glib2-devel
yum -y install irqbalance 
systemctl start irqbalance

4. 开启 RPS/RFS 特性,软件方式实现 CPU 均衡,接收包中断的优化


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  jnZtF7Co41Wg   2023年12月06日   25   0   0 sedlinux数据
  uvM09mQNI0hF   2023年11月19日   27   0   0 sedshell字符串
  4Txe79BjyroE   2023年11月19日   19   0   0 中断处理
  eHipUjOuzYYH   2023年12月06日   32   0   0 sedbootstrapIPV6
  xIUntf9oR6GI   2023年11月28日   31   0   0 sedvim基础命令
  oIa1edJoFmXP   2023年11月24日   30   0   0 AppsedVue