Skip to content

Commit 9fe2cc2

Browse files
committed
docs(rss): add ff_rss_check optimization impl/verification report; update layer3 RSS notes
Records R-A/R-B/R-C implementation (commits, functions, line numbers), 31 unit tests with v4/v6 full-loop 100% queue landing, and on-machine verification (q0/q1 200/200 land on owning queue). Notes virtio reta_size=0 thash soft-fallback and no-IPv6-net real-machine limits, both covered by unit tests.
1 parent 80f6391 commit 9fe2cc2

5 files changed

Lines changed: 250 additions & 0 deletions

File tree

docs/03-LAYER3-FUNCTIONS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ struct ff_rss_tbl_type {
221221
int ff_rss_tbl_init(void);
222222
```
223223
224+
> **RSS lport optimization (see `ff_rss_check_opt_spec`)**: the connect-side RSS source-port selection has been extended with three optimizations — (0.1) IPv4 kernel-side port-range hooks migrated back to FreeBSD 15.0 (`freebsd/netinet/in_pcb.c`), (0.3) a dynamic fast path that reverse-calculates the source port via `rte_thash_adjust_tuple` with a forced soft re-verify (`ff_rss_thash_ctx_init` / `ff_rss_adjust_sport`), and (0.2) an independent IPv6 path (`ff_rss_check6` / `ff_rss_tbl6_init` / `ff_rss_tbl6_set/get_portrange` / `ff_rss_adjust_sport6`) that leaves the IPv4 structures/signatures untouched. A read-only helper `ff_rss_self_queue_info()` exposes the current process's queue id / nb_queues / reta_size. Details and verification: `docs/ff_rss_check_opt_spec/zh_cn/`.
225+
224226
### 2.5 ff_msg_ring Structure (Inter-Process Communication)
225227
226228
> **Note**: `ff_msg_send()` is not a public API; it does not exist in either `ff_api.h` or `ff_api.symlist`. Inter-process communication is implemented through the `ff_msg` message queue (`lib/ff_msg.h`), used by F-Stack internal tools (knictl/sysctl, etc.). Application-level code does not need to call it directly.

docs/ff_rss_check_opt_spec/zh_cn/00-总览索引.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
| `07-测试规格.md` | M4 | 14 单元 + 5 集成 + 4 真机 + 9 零回归判据 + 验收矩阵;mock 策略 |
2424
| `08-性能基线方案.md` | M4 | 静态表/软算/thash 三路径对比口径、TSC 打点、QPS、真机(9.134.214.176)步骤、通过标准 |
2525
| `09-spec评审门禁.md` | M5 | 19 断言逐条 PASS/FAIL(回代码核实) + 18 条待确认总表 + CONDITIONAL PASS 结论 |
26+
| `10-实施与验证报告.md` | 编码 | 三项落地 commit/函数:行号、设计要点、单测 31 用例 + 真机 200×2 结果、真机限制、F1-F18 落实对照、编码门禁 PASS |
2627

2728
## 需求 ↔ 里程碑 ↔ 关键用例 导航
2829
| 需求 | 编码里程碑 | 核心用例 |
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# 10 实施与验证报告 —— ff_rss_check 三项优化
2+
3+
> 角色:spec-writer(编码完成后的文档同步)。本报告记录 0.1 / 0.3 / 0.2 三项优化的**实际落地结果**,所有函数/行号经 `grep`/读码核实(以代码为准),实测数据以 leader 真机/单测复核为准。
4+
> 实现分支:`feature/1.26`。落地 commit:`22462f58d`(R-A 0.1)、`39f61e05e`(R-B 0.3)、`fe7d190af`(R-C 0.2)、`80f6391ad`(真机载体)。
5+
> 涉及文件:`lib/ff_dpdk_if.c``lib/ff_config.c`/`.h``lib/ff_host_interface.h`/`ff_api.h``freebsd/netinet/in_pcb.c``freebsd/netinet6/in6_pcb.c``example/rss_ct.c``tests/unit/test_ff_dpdk_if.c`
6+
7+
---
8+
9+
## 1. 实施概览
10+
11+
| 里程碑 | 需求 | commit | 主要落点 | 状态 |
12+
|--------|------|--------|----------|------|
13+
| R-A | 0.1 IPv4 内核侧 RSS 选端口钩子回迁 15.0 | `22462f58d` | `freebsd/netinet/in_pcb.c` | 已实现/已测/已提交 |
14+
| R-B | 0.3 `rte_thash` 动态优化(IPv4) | `39f61e05e` | `lib/ff_dpdk_if.c` | 已实现/已测/已提交 |
15+
| R-C | 0.2 IPv6 全链路(方案 A,v6 独立) | `fe7d190af` | `lib/ff_dpdk_if.c` + `ff_config.c` + `in_pcb.c` + `in6_pcb.c` | 已实现/已测/已提交 |
16+
| 载体 | 真机自检 connect 程序 + 只读队列信息接口 | `80f6391ad` | `example/rss_ct.c` + `lib/ff_dpdk_if.c` | 已实现/已提交 |
17+
18+
- 单元测试:`tests/unit/test_ff_dpdk_if.c`**31** 个 cmocka 用例(`grep -c "cmocka_unit_test("` = 31),全 PASS。
19+
- 编码顺序遵循 spec 06 R-A→R-B→R-C,且每一步对 IPv4 既有路径零回归。
20+
21+
---
22+
23+
## 2. 各需求实现要点
24+
25+
### 2.1 需求 0.1(R-A):IPv4 内核侧 RSS 选端口钩子回迁 15.0
26+
27+
13.0→15.0 升级时 `in_pcb_lport_dest` 内的 RSS 选端口消费逻辑未移植(仅保留 `INPLOOKUP_LPORT_RSS_CHECK``#define`,见 09 E 类与 M3-brief)。R-A 在 15.0 重新接回,并适配 15.0 内核差异。
28+
29+
实际落点(`freebsd/netinet/in_pcb.c`,行号已核实):
30+
31+
| 落点 | 行号 | 说明 |
32+
|------|------|------|
33+
| `in_pcb_lport_dest(const struct inpcb *inp, ...)` | L759-762 | 15.0 已 `const inpcb`;RSS 逻辑只改 `lookupflags` 值参与局部变量,不改 `inp` |
34+
| `INPLOOKUP_LPORT_RSS_CHECK` flag 解析 | L779 | `rss_check_flag = lookupflags & INPLOOKUP_LPORT_RSS_CHECK` |
35+
| flag 清除(不入后续 lookup) | L790 | `lookupflags &= ~INPLOOKUP_LPORT_RSS_CHECK`(沿用 13.0 行为) |
36+
| 静态表命中:`ff_rss_tbl_set/get_portrange` | L911-931 | 命中走静态 portrange 快路径 + 轮转(`rss_portrange[0]` 自增、越界回绕,L999-1009) |
37+
| 未命中软算:定位出口 `ifp` + `ff_rss_check` 复核 | L933-943 / L1075-1087 | 扫描循环中每个候选端口用 `ff_rss_check(ifp->if_softc, ...)` 确认落本队列,LOOPBACK 跳过 |
38+
| `in_pcblookup_local(..., RT_ALL_FIBS, ...)` | L961-963 / L1072-1073 | 适配 15.0 lookup 新增 `RT_ALL_FIBS` 入参 |
39+
| `in_pcbconnect` 对接 `ff_in_pcbladdr` | `in_pcb.c` L1342 | `in_nullhost(inp->inp_laddr)` 分支内、原生 `in_pcbladdr`(L1346) 之前插入 `ff_in_pcbladdr(AF_INET, &faddr, sin->sin_port, &laddr)` |
40+
| connect 传 flag | `in_pcb.c` L1363-1366 | `in_pcb_lport_dest(..., INPLOOKUP_WILDCARD \| INPLOOKUP_LPORT_RSS_CHECK)` |
41+
42+
15.0 适配要点:(a) `in_pcb_lport_dest` 形参 `const struct inpcb *`,RSS 逻辑不修改 `inp`;(b) 15.0 `in_pcbconnect_setup` 已合并入 `in_pcbconnect`,故 `ff_in_pcbladdr` 接在 `in_pcbconnect` 内;(c) lookup 系列统一带 `RT_ALL_FIBS`
43+
44+
### 2.2 需求 0.3(R-B):`rte_thash` 动态优化(IPv4)
45+
46+
在保留静态表快路径不变的前提下,对**未命中静态表的动态场景**`rte_thash_adjust_tuple()` 反算源端口,替代逐端口软扫描。
47+
48+
实际落点(`lib/ff_dpdk_if.c`):
49+
50+
| 符号 | 行号 | 说明 |
51+
|------|------|------|
52+
|`FF_RSS_THASH_V4_TUPLE_LEN`=12 / `FF_RSS_THASH_V4_SPORT_OFF`=64 | L141-142 | v4 tuple 12B(4 倍数)、sport 在 bit 64 |
53+
|`FF_RSS_THASH_SPORT_HELPER_LEN`=16 / `FF_RSS_THASH_ADJUST_ATTEMPTS`=16 | L143-144 | helper 长度(≥reta_sz_log2)、adjust 尝试次数 |
54+
| `ff_rss_thash_ctx_init(void)` | L2973 | init 期每端口建 thash ctx + add_helper("sport");`reta_size<2``continue`(标 `rss_thash_ready=0`,降级软算);`rte_thash_init_ctx`/`add_helper`/`get_helper` 任一失败亦置不 ready 并降级 |
55+
| `ff_rss_adjust_sport(softc, saddr, daddr, dport, *out_sport)` | L3053 | 动态反算 v4 源端口 |
56+
57+
`ff_rss_adjust_sport` 关键设计(L3053-3114):
58+
- **desired ∈ D(q) = { v∈[0,reta_size) | v%Q==q }**`desired = queueid + (arc4random()%ceil(R/Q))*nb_queues`(L3083),令反算 hash 的低位满足 `(hash&(R-1))%Q==q`(与 `ff_rss_check` 落队列式精确对齐)。
59+
- **强制软算复核兜底**`rte_thash_adjust_tuple` 成功后取出 sport,再用同一软 `ff_rss_check(softc, saddr, daddr, sport, dport)` 复核 ==1 才返回(L3104),否则丢弃。**选错队列零容忍由此守护**
60+
- **attempts 用尽回退**:循环 `ceil(R/Q)` 次、每次 `desired += nb_queues`(L3110);全失败返回 -1,调用方回退 R-A 软扫描。
61+
- **降级**`!rss_thash_ready[port] || ctx==NULL`(含 reta<2)直接返回 -1(L3068),等价 0.1 纯软算。
62+
- **接入点**`in_pcb.c` L955-969 未命中分支内、软扫描之前的快路径(仅 `AF_INET` 且非 LOOPBACK)。
63+
64+
静态表命中快路径(`ff_rss_tbl_get_portrange` 命中)保持 0.1 行为不变。
65+
66+
### 2.3 需求 0.2(R-C):IPv6 全链路(方案 A,v6 独立)
67+
68+
按 spec 04§2.2 决策 A:**全新增 v6 符号 + 不动 v4 结构/签名**,保证 IPv4 零回归。
69+
70+
lib 侧新增(`lib/ff_dpdk_if.c`):
71+
72+
| 符号 | 行号 | 说明 |
73+
|------|------|------|
74+
| `ff_in6_is_any` / `ff_in6_fold`(static inline) | L3118 / L3130 | v6 全零判定 / 16B 地址折叠为 32 位索引 |
75+
| `ff_rss_check6(softc, saddr6, daddr6, sport, dport)` | L3142 | v6 落队列判定,36B tuple(saddr6 16 + daddr6 16 + sport 2 + dport 2),`((hash&(R-1))%Q)==queueid` |
76+
| `ff_rss_tbl6_init` | L3174 | v6 静态表初始化 |
77+
| `ff_rss_tbl6_set_portrange` / `ff_rss_tbl6_get_portrange` | L3285 / L3343 | v6 静态 portrange 设置/查询(命中 0 / 未命中 -ENOENT) |
78+
| `ff_rss_adjust_sport6(softc, saddr6, daddr6, dport, *out_sport)` | L3391 | v6 动态反算源端口,tuple 36B、sport 在 bit 256(`FF_RSS_THASH_V6_SPORT_OFF`=256,L152),同样强制 `ff_rss_check6` 复核兜底(L3436) |
79+
| v6 thash ctx(`rss_thash6_*`| L3020-3039(在 `ff_rss_thash_ctx_init` 内) | v6 并行 ctx,sport helper offset=256bit;失败仅禁用本端口 v6 动态路径,v4 仍 ready |
80+
81+
config 侧(`lib/ff_config.c`,L913-922):`rss_tbl_cfg_handler` 按地址文本是否含 `:` 分派 family——含 `:``AF_INET6` + `inet_pton(AF_INET6, ...)``saddr6`/`daddr6`;否则 `AF_INET` 走原 v4 解析。结构字段 `family`/`daddr6[16]`/`saddr6[16]``ff_config.h` L236-238。**v4 解析逻辑逐行不变,仅被包入 `else` 分支**(git diff: v4 两行 `inet_pton(AF_INET)` 删除后原样移入 else)。
82+
83+
内核侧接入:
84+
85+
| 落点 | 行号 | 说明 |
86+
|------|------|------|
87+
| `in_pcb_lport_dest` v6 并行分支 | `in_pcb.c` L855-907 | `lsa->sa_family==AF_INET6` 走 v6 表/check6/adjust_sport6,与 v4 分支并列 |
88+
| 扫描循环 v6 复核 | `in_pcb.c` L991-996 / L1050-1062 | `ff_rss_check6` 复核 + v6 portrange 轮转 |
89+
| `in6_pcbconnect` 对接 `ff_in_pcbladdr(AF_INET6, ...)` | `in6_pcb.c` L421 | v6 connect 选源地址接入 |
90+
| `in6_pcbladdr` 传 flag | `in6_pcb.c` L517-521 | `in_pcb_lport_dest(..., INPLOOKUP_WILDCARD \| INPLOOKUP_LPORT_RSS_CHECK)` |
91+
92+
**IPv4 零回归证据**:R-C(`fe7d190af`)相对 R-B(`39f61e05e`)的 `git diff --numstat``in_pcb.c` = **+86 / -0**(纯新增 v6 分支,无任何 v4 行删除);`in6_pcb.c` = +16/-0;`ff_dpdk_if.c` = +385/-0;`ff_config.c` = +10/-2(2 删除为 v4 两行原样移入 else)。
93+
94+
### 2.4 真机自检载体(`80f6391ad`
95+
96+
- `lib/ff_dpdk_if.c::ff_rss_self_queue_info(proc_id, queueid, nb_queues, reta_size)`(L2895):只读返回本进程 RSS 队列信息(`lcore_conf`/`rss_reta_size`),不改任何状态;已导出 `ff_api.h`/`ff_api.symlist`
97+
- `example/rss_ct.c`:最小 F-Stack 应用,对 `--dst`/`--dst6` 发起 N(默认 200)次 `ff_connect` 并打印本地选中源端口 + `ff_rss_self_queue_info`,供部署方验证「每进程 connect 选中的源端口都 hash 到本进程 RSS 队列」。example/ 原仅有 server,本载体补上 connect 触发面(`in_pcb_lport_dest` RSS 选端口仅在 connect 触发)。
98+
99+
---
100+
101+
## 3. 测试结果
102+
103+
### 3.1 单元测试(31 用例全 PASS)
104+
105+
按里程碑分类(`tests/unit/test_ff_dpdk_if.c` 中 RSS 相关用例):
106+
107+
| 需求 | 用例 | 覆盖点 |
108+
|------|------|--------|
109+
| 0.1 portrange | `..._set_portrange_no_cfg/_disabled/_inverted_range``..._get_portrange_no_cfg/_disabled/_smoke``..._get_portrange_hit/_rotation/_miss` | set/get guard、命中、轮转、未命中 -ENOENT |
110+
| 0.3 thash | `..._adjust_sport_null``..._adjust_sport_degraded``..._adjust_sport_single_queue``..._thash_equivalence_hitrate` | NULL guard、降级(reta<2 不 ready)、单队列、reta=128/512 等价复核 |
111+
| 0.2 v6 | `..._check6_landing``..._check6_single_queue``..._tbl6_set_get``..._adjust_sport6_guard``..._thash6_equivalence_hitrate` | v6 落队列、单队列、v6 表 set/get、v6 guard/降级、reta=128/512 等价复核 |
112+
113+
等价/命中率数据(测试侧用 `default_rsskey_40bytes` 同款 key 独立复算 Toeplitz 交叉校验;属 go/no-go 数据,无硬阈值):
114+
115+
| 路径 | reta | 单候选等价率(per-candidate) | full-loop 最终落队列 |
116+
|------|------|------------------------------|----------------------|
117+
| 0.3 v4 | 128 | ~22-27% | **100%(`assert_int_equal(fok128, EQUIV_N)`** |
118+
| 0.3 v4 | 512 | ~22-27% | **100%** |
119+
| 0.2 v6 | 128 | ~22-27% | **100%(`assert_int_equal(fok128, EQUIV_N)`** |
120+
| 0.2 v6 | 512 | ~22-27% | **100%** |
121+
122+
硬质量门(代码 L959-962 / L1234):full-loop 必稳定落目标队列(由 `ff_rss_adjust_sport[6]` 强制 `ff_rss_check[6]` 复核保证),**零假阳性**内联断言。
123+
124+
### 3.2 真机测试(leader 亲测)
125+
126+
环境:`lcore_mask=0x30``nb_queues=2`)、`rss_check enable=1``rss_ct` 起 primary(queueid=0)与 secondary(queueid=1)各 connect 200。leader 用 lib 同款 `toeplitz` + `default_rsskey_40bytes` 复算落队列:
127+
128+
| 进程 | queueid | connect 数 | 落本队列 |
129+
|------|---------|-----------|----------|
130+
| primary | 0 | 200 | **200/200 落 queue0** |
131+
| secondary | 1 | 200 | **200/200 落 queue1** |
132+
133+
结论:**0.1 软算选端口的多队列分流端到端 100% 正确**
134+
135+
### 3.3 不回归
136+
137+
- 基础 v4 server:200 正常(IPv4 功能无回归)。
138+
- IPv4 代码零回归(见 §2.3 git diff 证据:in_pcb.c +86/-0)。
139+
140+
---
141+
142+
## 4. 真机限制与说明(如实记录,非缺陷)
143+
144+
| 限制 | 现象 | 设计行为 | 正确性保证依据 |
145+
|------|------|----------|----------------|
146+
| 本机网卡(virtio)`reta_size=0` | `ff_rss_thash_ctx_init``reta_size<2``continue``rss_thash_ready=0`,L2986-2989) | 0.3 thash 快路径真机**降级为纯软算**(设计的降级路径正确触发) | 0.3 thash 正确性由**单测** reta=128/512 full-loop 100% 落队列 + 零假阳性保证(§3.1) |
147+
| 本环境 port0 未配 IPv6 地址 | 无法在真机发起 v6 connect | 0.2 v6 真机 connect **未做** | 0.2 v6 正确性由**单测** v6 full-loop 100% + v6 表/guard 用例保证(§3.1) |
148+
149+
两项均为**环境能力限制**,非实现缺陷;降级路径本身是 0.3 设计的一部分(reta 不足/ctx 失败 → 回退软算),真机恰好触发并验证了降级路径可用。
150+
151+
---
152+
153+
## 5. 与 spec 09 编码期待确认项(F1-F18)落实对照
154+
155+
> 逐条核实代码后简述落实(以代码/实测为准)。
156+
157+
| # | 待确认项 | 编码期落实 |
158+
|---|----------|-----------|
159+
| F1 | `ff_in_pcbladdr``in_pcbconnect` 精确插入点 | 落实:`in_nullhost(inp->inp_laddr)` 分支内、`in_pcbladdr`(L1346) 前插入(`in_pcb.c` L1340-1342) |
160+
| F2 | connect 调用链 protosw 合并是否影响 flag 透传 | 落实:15.0 已合并入 `in_pcbconnect`,flag 在 L1366 直接传入,透传正常 |
161+
| F3 | `ff_rss_tbl_get_portrange` 返回语义 | 落实:命中 0 / 未命中 `-ENOENT`(L2844/2847);调用方按此处理(L920-922) |
162+
| F4 | portrange 端口轮转归属 | 落实:调用方在扫描循环内推进 `rss_portrange[0]``in_pcb.c` L999-1004),非函数内自增 |
163+
| F5 | IPv6 走统一 `in_pcb_lport_dest` 还是 in6 独立路径 | 落实:复用统一 `in_pcb_lport_dest`(含 `AF_INET6` 分支 L855-907);`in6_pcbladdr``in_pcb_lport_dest` 传 flag(`in6_pcb.c` L517-521) |
164+
| F6 | IPv6 网卡 RSS offload 能力 | 真机未确认(本环境无 v6 网络);默认 `RTE_ETH_RSS_PROTO_MASK` 含 v6,不支持则 v6 落单队列(硬件限制非 bug) |
165+
| F7 | rte_flow 硬编码 IPV4_TCP 是否在 0.2 范围 | 落实:不在 0.2 范围(未改 rte_flow 路径) |
166+
| F8 | v6 静态表容量宏 | 落实:沿用 v4 宏(`FF_RSS_TBL_MAX_*`,v6 表复用同容量索引方案) |
167+
| F9 | 0.3 `attempts` 收敛率/合理值 | 落实:`FF_RSS_THASH_ADJUST_ATTEMPTS`=16(L144);正确性由软算复核兜底,单测 full-loop 100% |
168+
| F10 | thash helper offset/len | 落实:v4 offset=64bit(L142)、v6 offset=256bit(L152)、len=16(≥reta_sz_log2,L143) |
169+
| F11 | static `rss_reta_size` 单测注入 | 落实:单测采用测试侧独立复算 Toeplitz + 自洽降级策略(§3.1),未注入 static |
170+
| F12 | `rss_tbl_cfg_handler` 链接属性 | 落实:文件级函数(无 static 前缀,`ff_config.c` L881) |
171+
| F13 | 新增函数落点行号 | 落实:见本报告 §2.2/§2.3 行号表(已回填核实值) |
172+
| F14 | desired_value 可观测断言 | 落实:经 full-loop 端到端等价用例覆盖(`..._thash_equivalence_hitrate`|
173+
| F15 | 主动 connect 客户端载体 | 落实:自备最小程序 `example/rss_ct.c``80f6391ad`|
174+
| F16 | 性能打点宏 | 未单独落地 perf 宏;性能非本轮硬门,正确性优先 |
175+
| F17 | wiki 量级值真机校准 | 真机以 200 connect/进程验证落队列正确性,量级值非硬门未单独校准 |
176+
| F18 | reta_size/nb_queues 真机取值 | 落实记录:真机 `nb_queues=2`、virtio `reta_size=0`(触发 0.3 降级,见 §4) |
177+
178+
> 说明:F6/F16/F17 属硬件能力/性能项,非正确性硬门,已如实记录现状;其余均已在代码/测试中落实。
179+
180+
---
181+
182+
## 6. 门禁结论
183+
184+
- **编码阶段门禁:PASS。** 三项需求(0.1 回迁 / 0.3 thash / 0.2 IPv6)均已实现、测试、提交;IPv4 零回归(diff -0 删除);选错队列零容忍由软算复核守护;真机 0.1 多队列分流 200/200×2 全对;单测 31 用例全 PASS(thash/v6 full-loop 100% 落队列)。
185+
- **真机限制**(virtio reta=0 → 0.3 降级软算、无 v6 网络 → 0.2 v6 真机未做)如实记录,均由单测保证正确性,非缺陷。
186+
- **bounce 计数:0**

0 commit comments

Comments
 (0)