UPF 收包单队列解决方案
介绍
RSS 介绍
RSS
RSS(Receive Side Scaling 接收端报文缩放) 网卡分流机制
RSS 是一种网卡驱动技术,可在多处理器系统中的多个CPU之间有效分配网络接收处理;接收方缩放(RSS)也称为多队列接收,它在多个基于硬件的接收队列之间分配网络接收处理,从而允许多个CPU处理入站流量。RSS可用于缓解单个CPU过载导致的接收中断处理瓶颈,并减少网络延迟。
它的作用是在每个传入的数据包上发出带有预定义哈希键的哈希函数。哈希函数将数据包的IP地址,协议(UDP或TCP)和端口(5个元组)作为键并计算哈希值。哈希值的多个最低有效位(LSB)用于索引间接表RETA(Redirection Table)。
RSS RETA
间接表中的值用于将接收到的数据分配给CPU。(82599 RETA 为一个包含位宽 4 BITS
的 128
项的索引映射表,X710 PF RETA 为一个包含位宽6 BITS
的 256
项的索引映射表,位宽决定了 82599 最高支持 16 队列分流,X710 PF 最高支持 64 队列分流。)
以 82599 为例,RETA 表为 128 项的索引映射表,当设置入队列为 4 queues 时,将索引填充到 128 项表中即
[0,1,2,3,0,1,2,3,0,1,2,3,.....]
通过计算的 Hash 值的后 7 位索引来选择对应的 Queue;
针对不同的 Packet flow type 也可以划分不同的 Region 来绑定可用的 queue 范围,如 DPDK 基于 X710 中如下配置,将 flowtype 23 绑定对应的 10-25 Queue,flowtype 26 绑定到 1-8 Queue。然后根据计算的 Hash 值选择 Queue。
testpmd> set port 0 queue-region region_id 0 queue_start_index 1 queue_num 8
testpmd> set port 0 queue-region region_id 1 queue_start_index 10 queue_num 16
testpmd> set port 0 queue-region region_id 0 flowtype 26
testpmd> set port 0 queue-region region_id 1 flowtype 23
RSS 开关对比
当未开启 RSS 负载分流时,所有报文只会从一个硬件队列来收包。开启 RSS 负载分流时,RSS 根据报文的 L3 层 L4 层信息进行 Hash Function 计算 rss hash,rss hash 有效低位值映射到 RSS output index。无法解释的报文,rss hash 和 RSS output index 的设置为 0。
DPDK 场景下,为不同的收包队列绑定不同的 Worker CPU,通过将来自网卡的接收处理分配到多个 CPU 中来处理,减轻处理压力,有效减少 rx-miss 丢包问题。
工具介绍
testpmd
TestPMD 是一个使用 DPDK 软件包分发的参考应用程序。其主要目的是在网络接口的以太网端口之间转发数据包。此外,用户还可以用TestPMD尝试一些不同驱动程序的功能,例如RSS。
安装
下载
$ git clone -b v20.08 https://github.com/DPDK/dpdk.git
编译安装
$ cd dpdk/usertools
$ ./dpdk-setup.sh
$ 41 x86_64-native-linux-gcc
运行
$ ./dpdk-devbing.py -b vfio-pci 0000:00:03.0
$ cd dpdk/x86_64-native-linux-gcc
// rxq 和 txq 都需要
$ ./app/testpmd -c f -n 4 -- -i --pkt-filter-mode=perfect --port-topology=chained --txq=16 --rxq=16
scapy
Scapy 是一个 Python 程序,使用户能够发送,嗅探和剖析并伪造网络数据包。此功能允许构建可以探测,扫描或攻击网络的工具。Scapy 是一个功能强大的交互式数据包操作程序。它能够伪造或解码大量协议的数据包,通过线路发送,捕获它们,匹配请求和回复等等。Scapy 可以轻松处理大多数经典任务,如扫描,跟踪路由,探测,单元测试,攻击或网络发现。它可以取代 hping,arpspoof,arp-sk,arping,p0f 甚至是 Nmap,tcpdump 和tshark 的某些部分。
我们可以使用 scapy 构造 GTPU 报文,包括其中的 Extension Header,Teid 等内容。
安装
$ git clone https://github.com/secdev/scapy.git
安装
$ sudo python3 setup.py install
运行
$ scapy
问题描述
UPF N3 接口报文协议为 GTPU,单基站场景下,GTPU 隧道两端为固定的基站 IP 和 UPF N3 IP 和 2152 Port,无法根据内层信息进行 Hash 计算,所以当 UPF 收包时,RSS 通常计算的五元组得到的 RSS Hash 结果一样,选择的 Queue 一样,导致所有处理都到一个 CPU 上进行处理。
当高负载情况下,导致网卡收包出现大量 rx-miss 丢包现象。
解决方案
为避免外层五元组相同导致 RSS Hash 一样,我们选择计算 GTPU 的内层五元组或者与 UE 一一对应的 TEID。
Intel X710 可以通过使用 GTP DDP Profile 来支持计算 Field 的选择。下载地址
https://www.intel.com/content/www/us/en/download/19667/intel-ethernet-controller-x710-xxv710-xl710-adapters-dynamic-device-personalization-gtp-ext-package.html?wapkw=GTP
网卡通过加载该 profile 支持 GTP 报文解析,目前支持的 packet classification types 有
22: GTPU IPV4
23: GTPU IPV6
24: GTPU
25: IPV4 GTPC
38: IPV6 GTPC
12: IPV4 PFCP Session
13: IPV4 PFCP
14: IPV6 PFCP Session
15: IPV6 PFCP
16: GTPU GTPEH85 UL IPV4 // 上行带 Extenstion Header 的 GTPU
17: GTPU GTPEH85 UL IPV6
18: GTPU GTPEH85 IPV4
19: GTPU GTPEH85 IPV6
根据 《Dynamic_Device_Personalization_GTP_Ext_Headers_1_0.pdf》中 Fields 的定义
44-45 GTPU Teid
9-16 Inner IPv4
使用见下面的测试内容。
方案测试
测试场景如下,有对 i40e driver 和固件进行升级。
root@upfastri:~# ethtool -i ens3
driver: i40e
version: 2.17.4 // 升级
firmware-version: 7.10 0x80006459 1.2547.0 // 升级
expansion-rom-version:
bus-info: 0000:00:03.0
supports-statistics: yes
supports-test: yes
supports-eeprom-access: yes
supports-register-dump: yes
supports-priv-flags: yes
i40e 升级,下载 i40e-2.17.4.tar
https://www.intel.com/content/www/us/en/download/18026/intel-network-adapter-driver-for-pcie-40-gigabit-ethernet-network-connections-under-linux.html
$ tar -xvf i40e-2.17.4.tar
$ cd i40e-2.17.4/src
$ make install
$ modprobe i40e
使用 scapy 发送如下报文进行测试
Source IP 1.1.1.1
Destination IP 2.2.2.2
IP Protocol 17 (UDP)
GTP Source Port 2152
GTP Destination Port 2152
GTP Message type 0xFF
GTP Tunnel id 0x2-0xffffffff random
GTP PDU Type 1 (UL PDU Session)
GTP QFI 1
-- Inner IPv4 Configuration --------------
Source IP 3.3.3.1-255 random
Destination IP 4.4.4.1-255 random
IP Protocol 17 (UDP)
UDP Source Port 2000
UDP Destination Port 3000
未使用 DDP 时
testpmd 启动并配置所有 rss
$ ./app/testpmd -c f -n 4 -- -i --pkt-filter-mode=perfect --port-topology=chained --txq=16 --rxq=16
$ set verbose 1
$ port config all rss all
$ port start 0
$ start
scapy 启动并发包
$ scapy
// 支持 GTPU 和 SessionContainer
$ from scapy.contrib.gtp import GTP_U_Header as GTP_U_Header
$ from scapy.contrib.gtp import GTPPDUSessionContainer as GTPPDUSessionContainer
发送四个测试报文,其中内层源和目的 IP 不同,Teid 不同。
Source IP 1.1.1.1
Destination IP 2.2.2.2
IP Protocol 17 (UDP)
GTP Source Port 2152
GTP Destination Port 2152
GTP Message type 0xFF
GTP Tunnel id 0x2 0x3
GTP PDU Type 1 (UL PDU Session)
GTP QFI 1
-- Inner IPv4 Configuration --------------
Source IP 3.3.3.1 3.3.3.2
Destination IP 4.4.4.1 4.4.4.2
IP Protocol 17 (UDP)
UDP Source Port 2000
UDP Destination Port 3000
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0xa6675169 - RSS queue=0x9
只修改 TEID
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x3)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0xa6675169 - RSS queue=0x9
只修改内层源 IP
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.2',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0xa6675169 - RSS queue=0x9
只修改内层目的 IP
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.2')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0xa6675169 - RSS queue=0x9
可以看到,RSS Hash 的计算结果和 Queue 选择和 Teid 和 内层源目的 IP 无关,无法选择不同的 Queue。
使用 gtp-ext.pkg DDP
默认计算方法
testpmd 启动并计算 Hash
$ ./app/testpmd -c f -n 4 -- -i --pkt-filter-mode=perfect --port-topology=chained --txq=16 --rxq=16
$ set verbose 1
// 加载 DDP profile
$ ddp add 0 /root/gtp-ext.pkg,/root/gtp-ext-pkg.bak
// 关联 packet classification type 和 Flow type,16 pctype 为 GTPU GTPEH85 UL IPV4,23 flowtype 为 RTE_ETH_FLOW_GTPU; pctype 默认 hash TEID + 内层源 IP。
$ port config 0 pctype mapping update 16 23
$ port start 0
// 为 PORT 配置 flowtype 为 23 计算 RSS hash
$ port config all rss 23
$ start
scapy 启动并发包,见上节。
发送相同四个报文
Source IP 1.1.1.1
Destination IP 2.2.2.2
IP Protocol 17 (UDP)
GTP Source Port 2152
GTP Destination Port 2152
GTP Message type 0xFF
GTP Tunnel id 0x2 0x3
GTP PDU Type 1 (UL PDU Session)
GTP QFI 1
-- Inner IPv4 Configuration --------------
Source IP 3.3.3.1 3.3.3.2
Destination IP 4.4.4.1 4.4.4.2
IP Protocol 17 (UDP)
UDP Source Port 2000
UDP Destination Port 3000
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0x28017f79 - RSS queue=0x9
只修改 TEID
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x3)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0xf33b8a54 - RSS queue=0x4
只修改内层源 IP
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.2',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0x1ff44360 - RSS queue=0x0
只修改内层目的 IP
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.2')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0x28017f79 - RSS queue=0x9
可以看到,RSS Hash 的计算结果和 Queue 选择和 Teid 和 内层源 IP 有关,当修改 TEID 和内层源 IP 时可以获取到不同 Hash 值和 Queue。
根据 TEID 计算方法
testpmd 启动并配置 rss 根据 TEID 计算 Hash
$ ./app/testpmd -c f -n 4 -- -i --pkt-filter-mode=perfect --port-topology=chained --txq=16 --rxq=16
$ set verbose 1
// 加载 DDP profile
$ ddp add 0 /root/gtp-ext.pkg,/root/gtp-ext-pkg.bak
// 关联 packet classification type 和 Flow type,16 pctype 为 GTPU GTPEH85 UL IPV4,23 flowtype 为 RTE_ETH_FLOW_GTPU; hash_inset 为 teid (44-45)
$ port config 0 pctype mapping update 16 23
$ port config 0 pctype 16 hash_inset clear all
$ port config 0 pctype 16 hash_inset set field 44
$ port config 0 pctype 16 hash_inset set field 45
$ port start 0
// 为 PORT 配置 flowtype 为 23 计算 RSS hash
$ port config all rss 23
$ start
scapy 启动并发包,见上节。
发送相同四个报文
Source IP 1.1.1.1
Destination IP 2.2.2.2
IP Protocol 17 (UDP)
GTP Source Port 2152
GTP Destination Port 2152
GTP Message type 0xFF
GTP Tunnel id 0x2 0x3
GTP PDU Type 1 (UL PDU Session)
GTP QFI 1
-- Inner IPv4 Configuration --------------
Source IP 3.3.3.1 3.3.3.2
Destination IP 4.4.4.1 4.4.4.2
IP Protocol 17 (UDP)
UDP Source Port 2000
UDP Destination Port 3000
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0xed531408 - RSS queue=0x8
只修改 TEID
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x3)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0x37f53c19 - RSS queue=0x9
只修改内层源 IP
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.2',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0xed531408 - RSS queue=0x8
只修改内层目的 IP
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.2')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0xed531408 - RSS queue=0x8
可以看到,RSS Hash 的计算结果和 Queue 选择只和 Teid 有关,当修改 TEID 时可以获取到不同 Hash 值和 Queue。
根据内层源目的 IP 进行计算
testpmd 启动并配置 rss 根据内层源目的 IP 计算 Hash
$ ./app/testpmd -c f -n 4 -- -i --pkt-filter-mode=perfect --port-topology=chained --txq=16 --rxq=16
$ set verbose 1
// 加载 DDP profile
$ ddp add 0 /root/gtp-ext.pkg,/root/gtp-ext-pkg.bak
// 关联 packet classification type 和 Flow type,16 pctype 为 GTPU GTPEH85 UL IPV4,23 flowtype 为 RTE_ETH_FLOW_GTPU; hash_inset 为内层源目的 IP (9-16)
$ port config 0 pctype mapping update 16 23
$ port config 0 pctype 16 hash_inset clear all
$ port config 0 pctype 16 hash_inset set field 9
$ port config 0 pctype 16 hash_inset set field 10
$ port config 0 pctype 16 hash_inset set field 11
$ port config 0 pctype 16 hash_inset set field 12
$ port config 0 pctype 16 hash_inset set field 13
$ port config 0 pctype 16 hash_inset set field 14
$ port config 0 pctype 16 hash_inset set field 15
$ port config 0 pctype 16 hash_inset set field 16
$ port start 0
// 为 PORT 配置 flowtype 为 23 计算 RSS hash
$ port config all rss 23
$ start
scapy 启动并发包,见上节。
发送相同四个报文
Source IP 1.1.1.1
Destination IP 2.2.2.2
IP Protocol 17 (UDP)
GTP Source Port 2152
GTP Destination Port 2152
GTP Message type 0xFF
GTP Tunnel id 0x2 0x3
GTP PDU Type 1 (UL PDU Session)
GTP QFI 1
-- Inner IPv4 Configuration --------------
Source IP 3.3.3.1 3.3.3.2
Destination IP 4.4.4.1 4.4.4.2
IP Protocol 17 (UDP)
UDP Source Port 2000
UDP Destination Port 3000
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0x74cda611 - RSS queue=0x1
只修改 TEID
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x3)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0x74cda611 - RSS queue=0x1
只修改内层源 IP
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.2',dst='4.4.4.1')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0x2949adb2 - RSS queue=0x2
只修改内层目的 IP
$ p=Ether(src='11:11:22:22:33:33',dst='44:44:55:55:66:66')/IP(src='1.1.1.1',dst='2.2.2.2')/UDP(sport=2152,dport=2152)/GTP_U_Header(teid=0x2)/GTPPDUSessionContainer(QFI=1,type=1)/IP(src='3.3.3.1',dst='4.4.4.2')/UDP(sport=2000,dport=3000)/Raw('x'*20)
$ sendp(p, iface='ens3')
得到 RSS Hash 结算结果和 Queue 选择
src=11:11:22:22:33:33 - dst=44:44:55:55:66:66 - type=0x0800 - length=106 - nb_segs=1 - RSS hash=0x5b307da3 - RSS queue=0x3
可以看到,RSS Hash 的计算结果和 Queue 选择只和内层源目的 IP 有关,当修改内层源目的 IP 时可以获取到不同 Hash 值和 Queue。
VPP 集成配置 DDP 代码
DDP
配置对带拓展字段GTPU
报文内层IP
+TEID
进行RSS
testpmd> port stop 0 # 配置前停止 port
testpmd> set verbose 1 # 设置 log 登记
testpmd> ddp add 0 /root/gtp-ext.pkg,/root/gtp-ext-pkg.bak # 添加带 GTPU 拓展头模板文件
testpmd> port config 0 pctype mapping update 16 23 # 更新 hash 报文字段和 flow 对应 mapping,16 为报文GTPU-EXT,23 为 flow GTPU
testpmd> port start 0 # 启动 port
testpmd> port config all rss 23 # 配置对 GTPU RSS
testpmd> start # 运行 testpmd
入口函数
port stop 0 rte_eth_dev_stop
ddp add 0 /root/gtp-ext.pkg,/root/gtp-ext-pkg.bak cmd_ddp_add_parsed
port config 0 pctype mapping update 16 23 cmd_pctype_mapping_update_parsed
port start 0 rte_eth_dev_start
port config all rss 23 cmd_config_rss_parsed
VPP
集成
// 在 dpdk/lib/librte_cmdline/cmdline.c 添加配置 DDP 接口函数
uint8_t *
open_file(const char *file_path, uint32_t *size)
{
int fd = open(file_path, O_RDONLY);
off_t pkg_size;
uint8_t *buf = NULL;
int ret = 0;
struct stat st_buf;
if (size)
*size = 0;
if (fd == -1) {
printf("%s: Failed to open %s\n", __func__, file_path);
return buf;
}
if ((fstat(fd, &st_buf) != 0)){// || (!S_ISREG(st_buf.st_mode))) {
close(fd);
printf("%s: File operations failed\n", __func__);
return buf;
}
pkg_size = st_buf.st_size;
if (pkg_size < 0) {
close(fd);
printf("%s: File operations failed\n", __func__);
return buf;
}
buf = (uint8_t *)malloc(pkg_size);
if (!buf) {
close(fd);
printf("%s: Failed to malloc memory\n", __func__);
return buf;
}
ret = read(fd, buf, pkg_size);
if (ret < 0) {
close(fd);
printf("%s: File read operation failed\n", __func__);
close_file(buf);
return NULL;
}
if (size)
*size = pkg_size;
close(fd);
return buf;
}
int
save_file(const char *file_path, uint8_t *buf, uint32_t size)
{
FILE *fh = fopen(file_path, "wb");
if (fh == NULL) {
printf("%s: Failed to open %s\n", __func__, file_path);
return -1;
}
if (fwrite(buf, 1, size, fh) != size) {
fclose(fh);
printf("%s: File write operation failed\n", __func__);
return -1;
}
fclose(fh);
return 0;
}
int
close_file(uint8_t *buf)
{
if (buf) {
free((void *)buf);
return 0;
}
return -1;
}
int
eth_dev_info_get_print_err(uint16_t port_id,
struct rte_eth_dev_info *dev_info)
{
int ret;
ret = rte_eth_dev_info_get(port_id, dev_info);
if (ret != 0)
printf("Error during getting device (port %u) info: %s\n",
port_id, strerror(-ret));
return ret;
}
int cmdline_ddp(uint16_t port_id)
{
uint8_t *buff;
uint32_t size;
char *filepath;
char *file_fld[2];
int file_num;
int ret = -ENOTSUP;
#ifdef RTE_LIBRTE_I40E_PMD
/*port stop all*/
rte_eth_dev_stop((uint16_t)port_id);
/*ddp add 0 /root/gtp-ext.pkg,/root/gtp-ext-pkg.bak*/
filepath = strdup("/root/gtp-ext.pkg,/root/gtp-ext-pkg.bak");
if (filepath == NULL) {
return 0;
}
file_fld[0] = "/root/gtp-ext.pkg";
file_fld[1] = "/root/gtp-ext-pkg.bak";
file_num = 2;
buff = open_file(file_fld[0], &size);
if (!buff) {
free((void *)filepath);
return 0;
}
if (ret == -ENOTSUP)
ret = rte_pmd_i40e_process_ddp_package((uint16_t)port_id,
buff, size,
1);
if (ret == -EEXIST)
;
else if (ret < 0)
;
else if (file_num == 2)
save_file(file_fld[1], buff, size);
close_file(buff);
free((void *)filepath);
/*port config 0 pctype mapping update 16 23*/
struct rte_pmd_i40e_flow_type_mapping mapping;
unsigned int i;
unsigned int nb_item;
unsigned int pctype_list[64];
nb_item = 1;
pctype_list[0] = 16;
pctype_list[1] = 0;
mapping.flow_type = 23;
for (i = 0, mapping.pctype = 0ULL; i < nb_item; i++)
mapping.pctype |= (1ULL << pctype_list[i]);
ret = rte_pmd_i40e_flow_type_mapping_update(port_id,
&mapping,1,0);
switch (ret) {
case 0:
break;
case -EINVAL:
break;
case -ENODEV:
break;
case -ENOTSUP:
break;
default:
;
}
/*port start*/
rte_eth_dev_start(port_id);
/*port config all rss 23*/
struct rte_eth_rss_conf rss_conf = { .rss_key_len = 0, };
struct rte_eth_dev_info dev_info = { .flow_type_rss_offloads = 0, };
int all_updated = 1;
int diag;
rss_conf.rss_hf = 1ULL << 23;
rss_conf.rss_key = NULL;
struct rte_eth_rss_conf local_rss_conf;
ret = eth_dev_info_get_print_err(port_id, &dev_info);
if (ret != 0)
return;
local_rss_conf = rss_conf;
local_rss_conf.rss_hf = rss_conf.rss_hf &
dev_info.flow_type_rss_offloads;
if (local_rss_conf.rss_hf != rss_conf.rss_hf) {
//PMD_DRV_LOG(ERR,"Port %u modified RSS hash function based on hardware support,"
// "requested:%#"PRIx64" configured:%#"PRIx64"\n",
// port_id, rss_conf.rss_hf, local_rss_conf.rss_hf);
}
diag = rte_eth_dev_rss_hash_update(port_id, &local_rss_conf);
if (diag < 0) {
all_updated = 0;
//PMD_DRV_LOG(ERR,"Configuration of RSS hash at ethernet port %d "
// "failed with error (%d): %s.\n",
// port_id, -diag, strerror(-diag));
}
#endif
return 0;
}
// upf-astri.git/src/plugins/dpdkdevice/init.c
// 在 VPP 初始化 DPDK 时,添加调用配置 DPP
static clib_error_t *
dpdk_lib_init (dpdk_main_t * dm)
{
...
/* *INDENT-OFF* */
RTE_ETH_FOREACH_DEV(i)
{
...
/*set ddp hash func*/
if(devconf->num_rx_queues > 1)
{
cmdline_ddp(i);
}
...
}
/* *INDENT-ON* */
return 0;
}
验证:
root@upf:~# vppctl
uranus> show logging
2021/11/12 17:47:27:925 notice dpdk Device with port_id=1 already stopped
2021/11/12 17:47:27:925 notice dpdk Device with port_id=2 already stopped
2021/11/12 17:47:27:925 notice dpdk rte_pmd_i40e_process_ddp_package(): Profile already exists. # 第二次添加带 GTPU 拓展头模板文件,表明 DDP 已配置成功
2021/11/12 17:47:30:012 notice dpdk Device with port_id=1 already started
2021/11/12 17:47:30:012 notice dpdk Device with port_id=2 already started
注:DDP 在 82599 网卡不支持,在 X710 固件版本 6.0.1 以上可用,目前以上代码为 一汽现场版本临时修改,后续开发 DDP 正式版本,并提交到代码库
参考
https://www.cnblogs.com/dream397/p/13835075.html
<<332464-710_Series_Datasheet_v_3_9.pdf>>
https://www.intel.com/content/www/us/en/developer/articles/technical/dynamic-device-personalization-for-intel-ethernet-700-series.html
https://www.intel.com/content/www/us/en/download/19667/intel-ethernet-controller-x710-xxv710-xl710-adapters-dynamic-device-personalization-gtp-ext-package.html?wapkw=GTP