Skip to content

Commit 183054d

Browse files
committed
Modify rss doc.
1 parent 8feb4a9 commit 183054d

9 files changed

Lines changed: 1209 additions & 7 deletions

File tree

config.ini

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[dpdk]
22
# Hexadecimal bitmask of cores to run on.
3-
lcore_mask=1
3+
lcore_mask=10
44

55
# Number of memory channels.
66
channel=4
@@ -27,11 +27,11 @@ vlan_strip=1
2727
# Set [vlanN]'s addrs like [portN] later
2828
# the format is same as port_list
2929
# Set vlan filter id, to enable L3/L4 RSS below vlan hdr is not enable after f-stack-1.22.
30-
vlan_filter=1,2,4-6
30+
#vlan_filter=1,2,4-6
3131

3232
# sleep when no pkts incomming
3333
# unit: microseconds
34-
idle_sleep=0
34+
idle_sleep=20
3535

3636
# sent packet delay time(0-100) while send less than 32 pkts.
3737
# default 100 us.
@@ -125,10 +125,10 @@ kernel_coexist=0
125125
# Port config section
126126
# Correspond to dpdk.port_list's index: port0, port1...
127127
[port0]
128-
addr=192.168.1.2
129-
netmask=255.255.255.0
130-
broadcast=192.168.1.255
131-
gateway=192.168.1.1
128+
addr=9.134.214.176
129+
netmask=255.255.248.0
130+
broadcast=9.134.215.255
131+
gateway=9.134.208.1
132132
# set interface name, Optional parameter.
133133
#if_name=eno7
134134

@@ -265,6 +265,9 @@ gateway=192.168.1.1
265265
enable=0
266266
# Debug only: 1=verify thash result via ff_rss_check (default 0 for performance).
267267
recheck=0
268+
# thash reverse-calc + NIC RSS key sync switch (independent of enable):
269+
# 1 (default) = enable thash adjust path in multi-queue; 0 = soft scan only.
270+
thash_adjust=1
268271
rss_tbl=0 192.168.1.1 192.168.2.1 80;0 192.168.1.1 192.168.2.1 443
269272

270273
# Kni config: if enabled and method=reject,

docs/ff_rss_check_opt_spec/zh_cn/05-接口设计.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,19 @@ int ff_rss_adjust_sport6(void *softc, const uint8_t *saddr6,
328328
- `freebsd/netinet/in_pcb.c` / `in_pcb.h` / `in6_pcb.c`**0 改动**(0.4 是用户态运行时开关,内核侧不感知)。
329329
- R-A 回迁的 `INPLOOKUP_LPORT_RSS_CHECK` 透传不变;`in_pcb.c:904` 软算分支不变。
330330

331+
## 3-ter. 配置项变更:`thash_adjust` 开关(R-F,与 `enable` 解耦)
332+
333+
- **配置项**`[rss_check]` 段新增 `thash_adjust``struct ff_rss_check_cfg.thash_adjust`),默认值 `1`
334+
- **语义**:thash 反算 + NIC RSS key 同步的**独立开关**`1`(默认)= 启用路线①(取回改写 key、同步全局 rsskey、`rss_hash_update` 上传 NIC,多队列下生效);`0` = 路线②,仅走内核侧软扫描。
335+
- **门控范围**`thash_adjust` 门控以下调用,**`rss_check.enable` 解耦**
336+
- `ff_rss_thash_build_key``init_port_start`,多队列 KEY_FINAL 构造);
337+
- `ff_rss_thash_ctx_init`(thash diag readback,移出 `enable` 块,独立由 `thash_adjust` 门控);
338+
- `ff_rss_adjust_sport` / `ff_rss_adjust_sport6` 的 route② 守卫。
339+
- `ff_rss_tbl_init` / `ff_rss_tbl6_init` 仍归 `rss_check.enable`,不受 `thash_adjust` 影响。
340+
- **空指针语义**`ff_global_cfg.dpdk.rss_check_cfgs == NULL` 时按 `1` 处理(视为开),与历史默认行为一致。
341+
- **解析与默认**`rss_check_cfg_handler` 首次 `calloc` 后显式置 `rcc->thash_adjust = 1;`,再由 `thash_adjust=` 行覆盖。
342+
- **提交约束**:config.ini 仅提交新增 `thash_adjust=1` 行 + 注释,不提交本地测试值(与 §3.3 一致)。
343+
331344
---
332345

333346
## 4. 接口兼容性矩阵(IPv4 零回归核对)
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
# 0.2 根因独立仲裁报告(arbiter / 只读代码考据)
2+
3+
> 角色:独立仲裁员,全新视角,仅凭实际源码 + 严格位级推导裁决,禁止从众/臆测。
4+
> 源码版本:DPDK 23.11.5 / f-stack(/data/workspace)。
5+
> 争议:开启 `rss_check.enable``rte_thash_adjust_tuple` 反算的源端口经常不落本队列;
6+
> `rss_check.enable=0` 纯软算 `ff_rss_check` 正确。
7+
> H1(两位前序诊断员):根因=字节序不一致(be_to_cpu_32 翻字节),且 GFNI≠标量。
8+
> H2(leader):根因=key 不一致(add_helper 用 LFSR 改写了 ctx->hash_key),与字节序无关;GFNI=标量。
9+
10+
---
11+
12+
## 最终裁决(先给结论)
13+
14+
**H2 成立;H1 的"字节序"论断与"GFNI≠标量"论断均错误。**
15+
16+
1. **Q-A 字节序**:在【同 key、同 12 字节 tuple】下,`rte_softrss(be_to_cpu_32(bytes), key)``toeplitz_hash(key, bytes)` **逐位完全等价**`be_to_cpu_32` 不是 bug,恰恰是把"小端内存里的 uint32 字"还原成"MSB-first 数值",从而让 softrss 的按字处理与 toeplitz 的按字节流 MSB-first 处理一一对应。字节序**不是**根因。
17+
2. **Q-B key 改写**`rte_thash_add_helper → generate_subkey` 确实用 LFSR m-序列**原地改写了 `ctx->hash_key` 的 bit[64,110]**(v4,key 字节 8~13)。`adjust_tuple` 内部 `rte_thash_get_key(ctx)` 取的是**改写后**的 key;而 `ff_rss_check`/NIC 用的是**原始** `default_rsskey_40bytes`。三方 key 不同 → hash 系统性不同 → 反算端口落错队列。这是**真正根因**
18+
3. **Q-C GFNI vs 标量**:GFNI 矩阵由 `ctx->hash_key``rte_thash_complete_matrix` 生成,且 `generate_subkey` 改写 key 后会**重建矩阵**(rte_thash.c L430-432)。故 GFNI 分支与标量分支用的是**同一把(改写后的)key**,是同一 Toeplitz 函数的两种实现,对同输入给同结果。H1"二者口径不同/GFNI 与 toeplitz 一致而标量不一致"**自相矛盾、不成立**
19+
20+
---
21+
22+
## Q-A 【字节序】严格位级推导
23+
24+
### 被比较的两个函数
25+
26+
**A) `toeplitz_hash`**(f-stack `lib/ff_dpdk_if.c` L2588-2609),标准 FreeBSD/Toeplitz:
27+
28+
```c
29+
v = (key[0]<<24)+(key[1]<<16)+(key[2]<<8)+key[3]; // key 流前 32 bit 窗口
30+
for (i=0;i<datalen;i++)
31+
for (b=0;b<8;b++) {
32+
if (data[i] & (1<<(7-b))) hash ^= v; // data 按 MSB-first 取 bit
33+
v <<= 1;
34+
if ((i+4)<keylen && (key[i+4] & (1<<(7-b)))) v |= 1; // 从 key 流补下一 bit
35+
}
36+
```
37+
38+
- data 的全局比特序:字节 `i` 的 bit `(7-b)`(b=0→MSB),全局位置 `g = 8*i + b`。
39+
- 当 `data` 的全局第 g 个比特为 1:`hash ^=` 「key 比特流中从 bit g 开始连续 32 个比特组成的 uint32」(`v` 是滑动窗口,第 g 步时窗口起点恰为 key 流 bit g)。
40+
- 即 toeplitz:`hash = XOR_{g: data_bit[g]==1} KeyWindow32(g)`,其中 `KeyWindow32(g)=key 流 bit[g..g+31]`(MSB-first),key 流 = key 字节数组按 MSB-first 拼接。
41+
42+
**B) `rte_softrss(input, len, key)`**(DPDK `rte_thash.h` L175-190):
43+
44+
```c
45+
for (j=0;j<input_len;j++)
46+
for (map=input_tuple[j]; map; map&=(map-1)) {
47+
i = rte_bsf32(map); // i = LSB 计数的置位 bit
48+
ret ^= rte_cpu_to_be_32(key32[j]) << (31-i)
49+
| (uint32_t)((uint64_t)rte_cpu_to_be_32(key32[j+1]) >> (i+1));
50+
}
51+
```
52+
53+
`adjust_tuple`(rte_thash.c L812-817)喂给 softrss 的 input:
54+
55+
```c
56+
for (j=0;j<tuple_len/4;j++)
57+
tmp_tuple[j] = rte_be_to_cpu_32(*(uint32_t*)&tuple[j*4]); // 字节流 -> MSB-first 数值
58+
hash = rte_softrss(tmp_tuple, tuple_len/4, hash_key);
59+
```
60+
61+
### 逐位对应(核心)
62+
63+
**第一步:input_tuple[j] 的 bit 与 data 字节流位置的对应。**
64+
65+
`tmp_tuple[j] = be_to_cpu_32(tuple[4j..4j+3])`,无论本机字节序,`be_to_cpu_32` 的语义恒为"把 4 个字节按 MSB-first(tuple[4j] 为最高字节)解释为数值"。
66+
所以 `tmp_tuple[j]` 的(按 LSB 计数的)bit `i`,对应字节流中字节 `4j` 起的 MSB-first 第 `(31-i)` 个比特,即全局位置:
67+
68+
```
69+
g = 8*(4j) + (31 - i) = 32j + 31 - i
70+
```
71+
72+
这与 toeplitz 对 data 的全局比特定义 `g=8*i+b` 完全一致(同一个字节流、同一种 MSB-first 取位)。
73+
74+
**第二步:softrss 对该置位 bit 贡献的 key 窗口起点。**
75+
76+
softrss 贡献 = `be32(key32[j])<<(31-i) | (be32(key32[j+1])>>(i+1))`
77+
`be32(key32[j])` = key 字节 `4j,4j+1,4j+2,4j+3` 按 MSB-first 的 uint32(与 toeplitz 的 key 流定义一致)。
78+
79+
- `be32(key32[j]) << (31-i)`:取 key 流 bit `[32j .. 32j+i]`(共 i+1 个高位)放到结果高 (i+1) 位。
80+
- `be32(key32[j+1]) >> (i+1)`:取 key 流 bit `[32(j+1) .. 32(j+1)+30-i]`(共 31-i 个)放到结果低 (31-i) 位。
81+
- 拼接得到的 32 位 = key 流 bit `[32j+(31-i) .. 32j+(31-i)+31]` = `[g .. g+31]`(因 g=32j+31-i)。
82+
83+
即 softrss 对该置位比特贡献 `KeyWindow32(g)`**与 toeplitz 在同一 g 处贡献的 key 窗口逐位相同**
84+
85+
**第三步:求和集合相同。**
86+
87+
softrss 对 word j 遍历所有置位 bit i(`map&=(map-1)`),等价遍历 data 字节流中字节 `[4j,4j+4)` 区间内所有为 1 的全局比特 g;j 遍历全 word ⇒ 覆盖全部字节流比特。toeplitz 同样遍历全部字节流比特。两者求和集合与每项贡献逐位一致:
88+
89+
```
90+
rte_softrss(be_to_cpu_32(bytes), key) ≡ toeplitz_hash(key, bytes) (逐位恒等,与本机字节序无关)
91+
```
92+
93+
### Q-A 结论
94+
95+
**等价。** 字节序不是问题。`be_to_cpu_32` 是 softrss(按 uint32 字、要求字内 MSB-first 数值)与字节流 MSB-first Toeplitz 之间的**正确**桥接,不是 H1 所称的"字节翻转 bug"。H1 的字节序论断**错误**
96+
97+
(注:`rte_softrss_be` L205-219 才是"key 已预转换、直接吃机器序字"的变体;`adjust_tuple` 用的是普通 `rte_softrss` + `be_to_cpu_32`,配对正确。)
98+
99+
---
100+
101+
## Q-B 【key 改写】源码考据
102+
103+
### B1. add_helper 确实改写 ctx->hash_key
104+
105+
调用链:`rte_thash_add_helper`(rte_thash.c L571)→ 无重叠时走 L640-641 `generate_subkey(ctx, lfsr, start, end-1)``generate_subkey`(L402-435)内 `set_bit(ctx->hash_key, get_bit_lfsr(lfsr), i)`(L421-422)**原地写 ctx->hash_key**`set_bit`(L384-396)按 MSB-first 写第 pos 位。
106+
107+
### B2. 改写的确切 bit 范围(代入 v4)
108+
109+
f-stack 调用:`rte_thash_add_helper(ctx, "sport", len=FF_RSS_THASH_SPORT_HELPER_LEN=16, offset=FF_RSS_THASH_V4_SPORT_OFF=64)`(L2143-2144、L3002-3004)。
110+
`TOEPLITZ_HASH_LEN = 32`(rte_thash.c L17)。ctx flags=0(无 MINIMAL_SEQ)。
111+
112+
```
113+
end = offset + len + TOEPLITZ_HASH_LEN - 1 = 64 + 16 + 32 - 1 = 111 (L590)
114+
start = offset = 64 (L591-593, 无 MINIMAL_SEQ)
115+
generate_subkey(ctx, lfsr, start=64, end-1=110) -> 改写 bit[64, 110] (L641)
116+
```
117+
118+
**改写 bit[64,110]**:bit64 = key 字节 8 的 MSB,bit110 = key 字节 13 的 bit6(110/8=13 余 6)。即 **key 字节 8~13 被 LFSR m-序列覆盖**。这正是源端口(tuple offset 64,即第 9~10 字节 sport)参与 hash 的窗口区,目的是构造互补表使端口可反算 —— 但代价是 key 被改了。
119+
120+
### B3. ff_rss_thash_ctx_init 之后没有任何 key 回写/上传
121+
122+
`ff_rss_thash_ctx_init`(L2972-3043):
123+
- L2994 `rte_thash_init_ctx(name, rsskey_len, reta_log2, rsskey, 0)`:把**原始 rsskey** memcpy 进 ctx->hash_key(rte_thash.c L288-289)。
124+
- L3002 `rte_thash_add_helper(...)`**改写 ctx->hash_key bit[64,110]**
125+
- 之后仅 `rte_thash_get_helper` 取 helper 指针,**没有 `rte_thash_get_key`、没有更新全局 `rsskey`、没有 `rte_eth_dev_rss_hash_update`**
126+
127+
全工程检索确证:`grep rte_eth_dev_rss_hash_update | rte_thash_get_key | rte_thash_gfni``f-stack/lib/ff_dpdk_if.c` **0 命中**。NIC 的 RSS key(`port_conf.rx_adv_conf.rss_conf.rss_key = rsskey`,L742-743)始终是**原始 key**,从未被改写后的 key 替换。
128+
129+
### B4. 三方实际用的 key
130+
131+
| 使用方 | key | 出处 |
132+
|---|---|---|
133+
| **NIC 硬件 RSS** | 原始 `default_rsskey_40bytes` | L742-743 / L1042 / L1208,且无 hash_update 覆盖 |
134+
| **`ff_rss_check`(软算校验)** | 原始 `rsskey`(=default_rsskey_40bytes) | `toeplitz_hash(rsskey_len, rsskey, ...)` L2951 |
135+
| **`rte_thash_adjust_tuple`(反算)** | **改写后** `ctx->hash_key`(bit[64,110] 被 LFSR 覆盖) | L804 `hash_key = rte_thash_get_key(ctx)` = `ctx->hash_key` (rte_thash.c L684-688) |
136+
137+
### Q-B 结论
138+
139+
**adjust_tuple 用改写后 key,ff_rss_check 与 NIC 用原始 key,三方 key 不同。** 这是 0.2 的根本原因。
140+
反算时 adjust_tuple 在"改写 key 的 hash 空间"里求出落本队列的端口;但该端口拿回去给 **原始 key 的 NIC/ff_rss_check** 算,落点系统性偏移 → 经常不在本队列。recheck 用 ff_rss_check(原始 key)当然多次仍可能不通。
141+
142+
---
143+
144+
## Q-C 【GFNI vs 标量】
145+
146+
### C1. 两分支同一把 key
147+
148+
`rte_thash_adjust_tuple`(rte_thash.c L808-818):
149+
- `ctx->matrices != NULL`(GFNI 支持)→ `rte_thash_gfni(ctx->matrices, tuple, tuple_len)`
150+
- 否则 → `be_to_cpu_32 + rte_softrss(tmp_tuple, ..., hash_key=ctx->hash_key)`
151+
152+
矩阵来源:`rte_thash_init_ctx` L304-305 由 `ctx->hash_key``rte_thash_complete_matrix` 生成;而 `generate_subkey` 改写 key 后 L430-432 **重新调用 `rte_thash_complete_matrix(ctx->matrices, ctx->hash_key, key_len)` 重建矩阵**。所以 GFNI 矩阵反映的也是**改写后**的 ctx->hash_key。
153+
154+
⇒ GFNI 分支与标量分支 = **同一把改写后的 key、同一个 Toeplitz 函数的两种实现**,对同 tuple 必给**相同** hash。
155+
156+
### C2. H1"GFNI≠标量"为何不成立
157+
158+
H1 称"GFNI 字节流直算与 toeplitz 一致、标量 be_to_cpu_32 分支与 toeplitz 不一致"。但:
159+
- 由 Q-A,标量分支(be_to_cpu_32+softrss)在**同 key** 下与 toeplitz **逐位等价**
160+
- 由 C1,GFNI 与标量**同 key 同函数**,结果相同;
161+
- 故"GFNI 对、标量错"在数学上不可能成立——两者要么都对、要么都错,差别只在 key,不在实现口径。
162+
163+
H1 把"复现条件=真机无 GFNI"误解读成"实现口径差异"。真正的复现差异其实来自 **key 改写在 hash 空间的偏移**,与是否走 GFNI 无关;只要 adjust_tuple 用改写 key 而 NIC/check 用原始 key,无论 GFNI 还是标量都会错。(若某些场景"看起来"在支持 GFNI 的机器上不复现,那是 reta/队列分布的偶发掩盖,非口径差异。)
164+
165+
### Q-C 结论
166+
167+
**GFNI 与标量是同一函数同一 key 的两种实现,对同输入同结果。H1 的"二者口径不同"自相矛盾、不成立。**
168+
169+
---
170+
171+
## 0.2 根因表述的修正
172+
173+
**错误表述(H1,应废弃)**
174+
> "be_to_cpu_32 造成字节翻转,标量分支与 toeplitz 字节序不一致;GFNI 分支与 toeplitz 一致故仅真机复现。"
175+
176+
**正确表述(H2,应采纳)**
177+
> `rte_thash_add_helper` 为构造端口互补表,用 LFSR m-序列**原地改写了 `ctx->hash_key` 的 bit[64,110](key 字节 8~13)**`rte_thash_adjust_tuple` 据此**改写后的 key** 反算源端口,而 `ff_rss_check` 与 NIC 硬件仍用**原始 rsskey**。三方 key 不一致,导致反算端口在原始 key 的 hash 空间里系统性落错队列,recheck 多次仍不通。字节序(be_to_cpu_32)与 GFNI/标量分支差异均****根因。
178+
179+
---
180+
181+
## 修复方向(key 对齐)
182+
183+
核心:让"反算所用 key"与"校验/NIC 实际生效 key"三方一致。两条互斥路线:
184+
185+
**路线①(推荐:把改写后的 key 同步到 check + NIC)**
186+
1. `ff_rss_thash_ctx_init``rte_thash_add_helper` 之后,调 `rte_thash_get_key(ctx)` 取回改写后的 key。
187+
2. 用该 key 覆盖供 `ff_rss_check` 用的 key(即让 toeplitz_hash 也用改写后 key),并调 `rte_eth_dev_rss_hash_update(port_id, &rss_conf{key=改写后key})` 把改写 key 重新上传 NIC,使 NIC 实际 RSS 与之一致。
188+
3. 风险:`rss_hash_update` 部分网卡/驱动不支持或需重置队列;多端口需逐端口对齐;运行时改 NIC key 影响在途流量分布。需在初始化早期、流量前完成。
189+
190+
**路线②(不改 NIC:放弃 adjust_tuple,回退/坚持纯软扫描)**
191+
- 若不能改 NIC key,则 adjust_tuple 路线无法与原始 key 的 NIC 自洽,应直接走 `ff_rss_check` 软扫描端口(即 `rss_check.enable=0` 的正确路径),不启用 thash 反算。
192+
193+
> 倾向路线①(保留反算的性能收益),但必须验证目标真机 `rte_eth_dev_rss_hash_update` 可用且能在无流量窗口完成;否则退路线②。
194+
195+
---
196+
197+
## 与 0.1(多进程)修复方案的耦合 —— 关键约束
198+
199+
0.2 的修复(key 对齐)与 0.1(多进程共享)**强耦合**,必须一并设计,否则按下葫芦起瓢:
200+
201+
- 若每进程各自 `rte_thash_init_ctx` + `add_helper`,而 `add_helper` 内部 LFSR(`alloc_lfsr`/`get_bit_lfsr`)在无固定种子时**各进程产生不同 m-序列** → 各进程改写出的 ctx->hash_key **互不相同**
202+
-**一张 NIC 只能有一把 RSS key**。N 个进程 N 把不同改写 key,**无法同时与单一 NIC key 一致** → 路线①在"每进程独立 ctx"下从原理上无法成立。
203+
204+
**结论性建议**:0.1 应采用 **primary 进程创建、secondary 进程 `rte_thash_find_existing` 共享同一个 ctx**(DPDK 多进程标准用法,rte_thash 的 ctx 存于共享 tailq,见 rte_thash.c `rte_thash_find_existing` L324-348)。这样:
205+
1. 全进程共用同一把"改写后 key";
206+
2. 该唯一 key 上传 NIC 一次,三方(NIC / 所有进程的 check / 所有进程的 adjust_tuple)一致;
207+
3. 0.2 路线① 才有可落地前提。
208+
209+
即:**0.1 必须 primary-create + secondary-find_existing 共享单一 ctx;0.2 的 key 对齐必须基于这把共享 key 上传 NIC。两者不可分开实现。**
210+
211+
---
212+
213+
## 证据行号索引
214+
215+
- DPDK `rte_thash.c`:init_ctx 拷 key L288-289、GFNI 矩阵生成 L304-305;set_bit L384-396;generate_subkey 改写 hash_key L402-435(写 L421-422,重建矩阵 L430-432);add_helper start/end 计算 L590-593、generate_subkey 调用 L641;find_existing L324-348;rte_thash_get_key=return ctx->hash_key L684-688;adjust_tuple hash_key=get_key L804、GFNI/标量分支 L808-818。
216+
- DPDK `rte_thash.h`:rte_softrss L175-190;rte_softrss_be L205-219;TOEPLITZ_HASH_LEN=32 (rte_thash.c L17)。
217+
- f-stack `ff_dpdk_if.c`:default_rsskey_40bytes L92、rsskey 全局 L120-121;常量 V4_SPORT_OFF=64/HELPER_LEN=16/TUPLE_LEN=12 L141-144;NIC key 设置 L742-743/L1042/L1208;toeplitz_hash L2588-2609;ff_rss_check 用 rsskey L2918-2954(hash L2951);ff_rss_thash_ctx_init L2972-3043(init_ctx L2994、add_helper L3002,无 get_key/hash_update);ff_rss_adjust_sport L3052-3115。
218+
- 反证:`rte_eth_dev_rss_hash_update` / `rte_thash_get_key` / `rte_thash_gfni` 在 ff_dpdk_if.c **0 命中**
219+
220+
---
221+
222+
*裁决人:arbiter(独立只读考据)。结论仅依据上述实际源码与位级推导得出。*

0 commit comments

Comments
 (0)