组播RPS的一些优化方案

关于组播RPS的一些优化方案整理

一. 内核实现:函数 get_rps_cpu

  1. 计算skb->hash
  2. flow-table: 主要针对TCP,TCP连接建立时,会计算sk-skhash加入global_flow_table记录sock对应的CPU
    (1). 先查global_flow_table,如果不存在则直接进行RPS,按照hash%cpu-count进行分发
    (2). rx_queue->flow_table中,如果和sock对应的CPU不一致或者当前记录CPU无效,则更新为sock-CPU,并更新flow表
  3. 直接在map表中根据hash查找对应cpu, 约等于hash % cpu-count

二. OVS实现参考rx-steering&pmd-rxq-assign

每个转发核自行调度存在很多局限,还是需要综合各流的繁忙程度和CPU亲和性进行分配:
调度时机: 最高CPU > 70% 并且 最高CPU-最小CPU > 55%
调度策略: 根据流繁忙指标进行排序,然后每个转发核绑定一条最高的流,剩余的流找最空闲的CPU分配
转发原则: 转发核按照转发表转发(lcore), 转发表由调度者修改(dataplane)

缺陷:
与OVS还是有差异的:
OVS分配的对象是队列,我们要做的分配的对象是流,粒度更细
OVS统计指标是队列收包花费的时间,我们统计的是包数量

关于流指标:

  1. tsc:DP是批量处理,且每轮处理完可能会有缓存,这就导致很难准确统计到流具体用的tsc时间
  2. pps/bps: 收包个数和收包字节数,这个比较简单,但是综合评判关系未知
  3. 调度到其他CPU,看其涨了多少 – 这个过于复杂了,先不考虑了

TODO:

  1. 广播包优先级更高,可以乘1个系数
  2. 繁忙指标需要根据收包数和收包字节数综合来算,当前根据收包数来衡量
  3. 度量指标需要计算最近一段时间内的,而不是总的
  4. 统计周期目前至少1秒,需要提高精度需要缩小周期
  5. 进入调度的限制看下能否再优化
  6. 少于转发核流数看下是否需要处理
  7. 调度过程中,又有突发流量过来

三. flow director: 直接将对应flow→queueid规则下发到网卡,在网卡层面将数据流定向到对应队列来做负载均衡

(1) 需要网卡支持,各网卡支持程序可能也不尽相同
(2) 不支持通用模式
(3) 需要我们自行管理flow,管理起来目前还有些复杂,网卡重启即失效,需要显示删除重建
(4) dp使用主从模型,硬件相关操作交给主进程,需要处理IPC通信

  1. 软件RPS调度效果: 2核偏载 负载后性能可提升50%以上
  2. 硬件flow director: VVX710(i40e)网卡flow规则下发成功不生效 MT27710(mlx4)网卡可以正常生效

linux测试flow director

  1. iperf打流,对面eth5网卡收包

    1
    $ ./iperf -c 192.168.123.30 -B 192.168.123.31 -b 1G -t 10000 -u -i 3
  2. 收包网卡设置fdir规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    $ ethtool -n eth5
    4 RX rings available
    Total 0 rules

    $ ethtool --config-ntuple eth5 flow-type udp4 src-ip 192.168.123.31 dst-ip 192.168.123.30 src-port 5001 dst-port 5001 action 0
    Added rule with ID 7679

    $ ethtool -n eth5
    4 RX rings available
    Total 1 rules

    Filter: 7679
    Rule Type: UDP over IPv4
    Src IP addr: 192.168.123.31 mask: 0.0.0.0
    Dest IP addr: 192.168.123.30 mask: 0.0.0.0
    TOS: 0x0 mask: 0x0
    Src port: 5001 mask: 0x0
    Dest port: 5001 mask: 0x0
    Action: Direct to queue 0

    $ ethtool -N eth5 delete 7679

  3. 修改队列号,观察对应队列中断情况

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $ cat /proc/interrupts | grep eth5 | awk '{
    for (i=1; i<=NF; i++) {
    if ($i != 0) printf "%s ", $i
    }
    print "" # 换行
    }'

    Sun Apr 27 20:38:17 GMT-8 2025
    679: 470 276 467151 IR-PCI-MSI 30935041-edge i40e-eth5-TxRx-0
    680: 3 241 282192 IR-PCI-MSI 30935042-edge i40e-eth5-TxRx-1
    681: 2 2732853 33 IR-PCI-MSI 30935043-edge i40e-eth5-TxRx-2
    682: 24275771 IR-PCI-MSI 30935044-edge i40e-eth5-TxRx-3

RPS方案概述:
对于泛洪流量:即组播和广播流量,当前虚拟交换机处理为每个出口复制一份数据包进行转发,导致数据包拷贝CPU消耗较大,影响DP转发性能。
验证过的优化方案: 对于泛洪流量,每次拷贝只拷贝2/3/4层包头,剩余数据部分只增加引用,不做真实拷贝,在组播包长较大时减少拷贝的数量比较可观,
理论上可提升组播转发性能,但实际测试优化效果仅能提升10%左右,由于关联影响太大,此方案性价比不高,且客户侧测试效果不明显,已废弃。

针对当前的流量特征(流数少,但流量大,3-4条流,最高达20wpps), 存在流量严重偏载的问题,单核打满,其他核较为空闲,做了负载均衡特性解决这个问题。
负载均衡优化在转发核偏载场景效果很好,不仅仅局限于当前客户组播场景,对于其他偏载场景均有效果,但同时也存在一些限制,具体工作原理见下文模块方案概述。

适用条件及限制:
1.多转发核,众所周知,单转发核不存在负载这一说法。
2.CPU偏载,最高占用CPU比最低占用CPU高出一定范围,如50%。CPU已经均衡了,再作均衡没有意义。
3.流数不多,和CPU转发核数接近,一般这种不均衡的都是流数不多的情况,因为流数较多时偏载概率极小。
4.均衡后流量不超过单核限制,如本来流量已经超过设备性能极限,再怎么负载都会丢包。
多线程方案对偏载问题也有优化效果,选用负载均衡而不是多线程的方案,主要考虑不影响当前DP转发模型,每个转发核还是保持逻辑一致,不会扩散维护成本。

具体操作:当前方案依赖于数据流调度,数据流基于数据包rss识别,具体过程如下
1.物理口收包时,提前计算好数据包rss
2.转发线程每包做流统计,基于数据包rss记录下包个数,大小,网口和队列,收包CPU
3.主线程定期调度(默认100ms),根据当前CPU及统计的流量情况进行调度处理,并更新转发表
4.转发线程根据转发表转发,如果调度后需其他CPU核处理则将收到的数据包送往对应转发线程处理。
5.调度过程中会尽量保证数据流的CPU亲和性。

RPS整体方案:调度与转发分离,通过转发表关联。
RPS具体调度流程:优先保证流的CPU亲和性

细节说明:
第一轮:本次调度的核心流量,如果在上次调度过程中也是核心流量,则继续沿用上次调度的CPU,避免流量频繁切换。
第二轮:在第一轮之后,从大往下查找,如果该流对应的收包CPU没有绑定流,则绑定该CPU,保障CPU绑定最大的新流。
第三轮:剩余流量按照最低流量调度,即找到流量最小的目的CPU,此时如果放在收包CPU上与目的CPU上,差距不超过1/4,则继续调度在收包CPU上。
核心流量标准:高于最大流量的1/512,都属于核心流量,只调度核心流量,且最多只调度流数为 转发核数量的4倍。
流量计量指标:单播包数+4*多播包数+包长增益(平均包长/256 * 1/8,即平均包长每多256,指标增加1/8),尽量综合多播和包长的影响。
转发表:每转发核1个表,便于记录和区分每个核上的流量,表项1024个,按照rss % 1024定位到对应槽位。调度时收集的时所有转发核的流数据,统一进行调度。

方案缺陷:
1.调度需要一定时间,当前是默认100ms,新流量过来如果100ms内突发较高,则仍会丢包,理论上调度周期可以更小,但是调度会过于频繁,所以默认100ms。
2.调度基于数据包rss,转发表每核hash到1024个表项,有概率存在多条流算到同一条流中。
3.最大调度流数为转发核数的4倍,4核就是16条流,为了更高效,没有调度所有流量。
4.为了性能考虑,转发表没有加锁,统计时可能会有细微误差,但在100ms的尺度上基本可以忽略。