Skip to content

Commit 23e5459

Browse files
committed
docs(rss): backfill R-E (IP_BIND_ADDRESS_NO_PORT) impl/verification report; update layer3 RSS notes
Add §6 R-E to ff_rss_check_opt_spec/zh_cn/10-实施与验证报告.md with landing points, build/lint, unit-test zero-regression, static call-chain trace (v4 8 steps + v6 7 steps), on-machine limitations, and 8-gate audit. Rename existing §6 conclusion to §7 and extend it to cover all five optimizations (0.1/0.3/0.2/0.4/0.5). Append one-liner R-E note to docs/03-LAYER3-FUNCTIONS.md RSS section.
1 parent ff9e3c4 commit 23e5459

2 files changed

Lines changed: 119 additions & 3 deletions

File tree

docs/03-LAYER3-FUNCTIONS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ 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/`. R-D (2026-06, spec 10 §R-D): the secondary soft re-verify in `ff_rss_adjust_sport` / `ff_rss_adjust_sport6` is now runtime-gated via `config.ini [rss_check] recheck=0`/`=1`, off by default to realize the ~100 ns/call performance saving; `recheck=1` is for debug re-verify only.
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/`. R-D (2026-06, spec 10 §R-D): the secondary soft re-verify in `ff_rss_adjust_sport` / `ff_rss_adjust_sport6` is now runtime-gated via `config.ini [rss_check] recheck=0`/`=1`, off by default to realize the ~100 ns/call performance saving; `recheck=1` is for debug re-verify only. R-E (2026-06, spec 10 §6, commit `ff9e3c449`): IP_BIND_ADDRESS_NO_PORT bind-then-connect RSS 端口选择移植到 FreeBSD 15.0;`freebsd/netinet/in_pcb.c` 在 `in_pcbbind`/`in_pcbbind_setup` 加 `#ifdef`/`#ifndef FSTACK` 门控,bind(addr,0) 时延迟端口分配并跳过入 hash,让后续 connect 走 R-A `INPLOOKUP_LPORT_RSS_CHECK` 路径选 RSS 亲和源端口;`freebsd/netinet6/in6_pcb.c` 同步 v6(路径 B:`in6_pcbconnect` 外层 if 在 FSTACK 下放宽为 `unspec || lport==0`,内层 `in6p_laddr` 赋值加 `IN6_IS_ADDR_UNSPECIFIED` 守卫保用户地址)。+16 / -1,零 lib 改动;FSTACK off 退回原生 15.0;REUSEPORT_LB MPASS 与 bind(addr,N) 零回归。
225225
226226
### 2.5 ff_msg_ring Structure (Inter-Process Communication)
227227

docs/ff_rss_check_opt_spec/zh_cn/10-实施与验证报告.md

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,124 @@ config 侧(`lib/ff_config.c`,L913-922):`rss_tbl_cfg_handler` 按地址
285285

286286
---
287287

288-
## 6. 门禁结论
288+
## 6. R-E(需求 0.5):IP_BIND_ADDRESS_NO_PORT bind-then-connect RSS 端口选择移植
289289

290-
- **编码阶段门禁:PASS。** 四项需求(0.1 回迁 / 0.3 thash / 0.2 IPv6 / 0.4 recheck 默认关)均已实现、测试、提交;IPv4 零回归;选错队列零容忍在 recheck=1 路径由软算复核守护、recheck=0 路径明示分发不均但连接正确性不影响;真机 0.1 多队列分流 200/200×2 全对;单测 36 用例 PASS 35 / SKIPPED 1(microbench fallback);recheck on/off microbench Δ ≈99.4 ns/call、比例 ≈300×。
290+
### 6.1 实施结果
291+
292+
按 spec 04 §3-ter / 06 R-E.1 落地,单一 commit `ff9e3c449`,2 文件改动(v4 必做 + v6 同步):
293+
294+
| 文件 | git diff numstat | 说明 |
295+
|------|------------------|------|
296+
| `freebsd/netinet/in_pcb.c` | **+8 / -0** | hunk1:`in_pcbbind` 入 hash 块 `#ifdef FSTACK if (inp->inp_lport != 0) { ... }` 整块包裹(lport==0 时跳过 in_pcbinshash,含 SO_REUSEPORT_LB 失败回滚仍受 lport!=0 envelope 保护);hunk2:`in_pcbbind_setup` `if (lport == 0) { in_pcb_lport(...) }``#ifndef FSTACK`(FSTACK 下 bind(addr,0) 不预分配端口) |
297+
| `freebsd/netinet6/in6_pcb.c` | **+8 / -1** | hunk-v6-bind:`in6_pcbbind` lport==0 真分支内 `in6_pcbsetport` 调用块 `#ifndef FSTACK`(FSTACK 下跳过 v6 端口分配);hunk-v6-connect(路径 B):`in6_pcbconnect` 外层 if 在 FSTACK 下放宽为 `IN6_IS_ADDR_UNSPECIFIED(in6p_laddr) \|\| inp_lport == 0`(让 bind(v6_addr,0) 后 lport==0 也能进 RSS 选端口分支);同时把内层 `inp->in6p_laddr = laddr6.sin6_addr;` 改为 `if (IN6_IS_ADDR_UNSPECIFIED(in6p_laddr))` 守卫赋值(避免 bind 的用户地址被覆盖) |
298+
| 合计 | **+16 / -1** | 全部 `#ifdef FSTACK` / `#ifndef FSTACK` 门控,关 FSTACK 退回 FreeBSD 15.0 原生语义 |
299+
300+
> **不改 lib**:R-E 复用 R-A 已落地的 connect 期 `INPLOOKUP_LPORT_RSS_CHECK` 路径,无 lib/接口改动;example/rss_ct.c 不动(保持 R-A connect 测试载体语义)。
301+
> **commit message 选词**:上游参考 `cb9b4d462a0cd8c47b6f514e2af0111cd26597b3`(基于 13.0),但 13.0 baseline `freebsd-src-releng-13.0/sys/netinet/in_pcb.c` grep 实测**无任何 `#ifdef FSTACK` 守卫**(L660-664 普通 `if (in_pcbinshash) { rollback }`、L1059-1065 普通 `if (lport == 0) { in_pcb_lport }`),故 R-E 是**相对 baseline 的全新增**移植(commit 用 `port from upstream / add` 而非 `migrate`)。
302+
303+
### 6.2 实际落点(行号已 grep 实测核实)
304+
305+
**v4(`freebsd/netinet/in_pcb.c`**
306+
307+
| 落点 | 行号 | 说明 |
308+
|------|------|------|
309+
| `in_pcbbind` hunk1 envelope start | L739-741 | `#ifdef FSTACK\nif (inp->inp_lport != 0) {\n#endif` |
310+
| `in_pcbinshash` + `MPASS(SO_REUSEPORT_LB)` 失败回滚(不变) | L742-748 | 整块在 envelope 内,lport!=0 时执行原生回滚(INADDR_ANY/lport=0/clear INP_BOUNDFIB),lport==0 时整块跳过 |
311+
| hunk1 envelope end | L749-751 | `#ifdef FSTACK\n}\n#endif` |
312+
| `in_pcbbind_setup` hunk2 envelope start | L1281 | `#ifndef FSTACK` |
313+
| `in_pcb_lport` 调用块(不变) | L1282-1286 | FSTACK 下整块跳过;关 FSTACK 时执行原生端口分配 |
314+
| hunk2 envelope end | L1287 | `#endif` |
315+
316+
**v6(`freebsd/netinet6/in6_pcb.c`**
317+
318+
| 落点 | 行号 | 说明 |
319+
|------|------|------|
320+
| `in6_pcbbind` hunk-v6-bind envelope start | L355 | `#ifndef FSTACK` |
321+
| `in6_pcbsetport` 调用块(不变) | L356-361 | FSTACK 下跳过 v6 端口分配;关 FSTACK 时执行原生(含 BOUNDFIB/laddr 回滚) |
322+
| hunk-v6-bind envelope end | L362 | `#endif` |
323+
| `in6_pcbconnect` hunk-v6-connect outer if 三态 | L517-521 | `#ifdef FSTACK\n if (unspec \|\| lport == 0) {\n#else\n if (unspec) {\n#endif` —— 路径 B |
324+
| `in_pcb_lport_dest(... INPLOOKUP_LPORT_RSS_CHECK)`(R-A 已落) | L523-530 | RSS 选端口路径,不变 |
325+
| 内层 `in6p_laddr` 守卫赋值 | L534-535 | `if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))\n inp->in6p_laddr = laddr6.sin6_addr;` —— 守卫确保 bind 的用户地址不被 connect 覆盖 |
326+
327+
> 注:spec 06 R-E.1 给出的 v6 行号(L354/L361-369、L515-516)相对当前实测有 +1 偏移(spec 写于 R-D 编码前;R-D commit `f7fd3b60d` 未直接动 in6_pcb.c 但 git 重整可能造成行号位移)。本节以实测为准。
328+
329+
### 6.3 编译与 lint
330+
331+
- **编译(FSTACK on)**`cd /data/workspace/f-stack/lib && make` 通过,`libfstack.a` 6.5MB;`in_pcb.o` / `in6_pcb.o` 干净编入;本次改动文件 0 warning(既有 51 warning 全在内核树预存,与 R-E 无关)。
332+
- **静态宏配对核查**:v4 hunk1 `#ifdef/#endif` ×2 配对、hunk2 `#ifndef/#endif` ×1 配对;v6 hunk-bind `#ifndef/#endif` 配对;v6 hunk-connect `#ifdef/#else/#endif` 三态配对。无悬空 / 不平衡指令。
333+
- **关 FSTACK 静态保证**:所有改动均在 `#ifdef FSTACK` / `#ifndef FSTACK` 门控内;FSTACK off 时三处 envelope 均退回上游 FreeBSD 15.0 原语义(in_pcb_lport 必调、in_pcbinshash 必调、in6_pcbsetport 必调、connect outer if 单条件 `IN6_IS_ADDR_UNSPECIFIED`)。
334+
335+
### 6.4 单元测试结果
336+
337+
按 spec 07 §1.5-ter / TC-U-RSS-05-* 规范:
338+
339+
- **R-A~R-D 既有 36 用例零回归**:35 PASSED + 1 SKIPPED(microbench fallback,spec 10 §5-bis.3 已记录);R-E 改动文件 `in_pcb.c` / `in6_pcb.c` **不被链接进 `tests/unit/test_ff_dpdk_if`**`tests/unit/lib_objs/` 仅含 lib 侧 `.o`,无内核 `.o`),R-E 在 lib 单测层无可观测改动面,零回归证据强。
340+
- **TC-U-RSS-05-01 / 02 / 04 / 05**:按 spec 07 §1.5-ter 设计**降级到集成 / 真机层验证**(lib cmocka 不覆盖内核 `in_pcbbind` / `in6_pcbbind` 单测;与 spec 07 §1.5-ter 文字描述一致)。
341+
- **TC-U-RSS-05-03(v4 bind(addr,N) 零回归)**:由静态推理 + R-A 既有 35 个 PASSED 用例覆盖(bind(addr,N≠0) 路径 hunk-by-hunk 与原生 15.0 等价:hunk1 envelope `lport!=0` 为 TRUE,in_pcbinshash 整块照常执行;hunk2 inner `lport==0` 为 FALSE,in_pcb_lport 不调用;与原生无差异)。
342+
343+
### 6.5 集成 / 真机静态调用链追踪(spec 07 §1.5-ter / §2.6 / §3.6 降级)
344+
345+
由于本环境(virtio reta_size=0、本机 `rss_check enable=0`、无 v6 网络、无 ssh 测试 harness)端到端真机受限——按 spec 10 §4 风格如实记录限制项——R-E 集成层验证以**静态调用链追踪**为准(v4 8 步 + v6 9 步全链路证据齐备):
346+
347+
**v4 调用链**(bind(v4_addr,0) + connect(remote)):
348+
349+
1. `in_pcbbind` L734:`anonport = sin->sin_port == 0` → TRUE。
350+
2. `in_pcbbind_setup` L1268:`laddr = sin->sin_addr`(用户地址保留);L1281-1287 hunk2 `#ifndef FSTACK` 跳过 `in_pcb_lport``*lportp = lport (= 0)` 回写。
351+
3. 回到 `in_pcbbind` L739-751 hunk1 `#ifdef FSTACK if (inp->inp_lport != 0)` 跳过 `in_pcbinshash``inp_laddr` 已设、`inp_lport=0`、INP_INHASHLIST 未置;L752 `if (anonport)` 触发 `INP_ANONPORT`
352+
4. `in_pcbconnect` L1313:`anonport = (inp->inp_lport == 0)` → TRUE(R-E hunk 保留 lport=0)。
353+
5. L1339 `in_nullhost(inp->inp_laddr)` 为 FALSE(hunk2 已写 laddr),L1351 `laddr = inp->inp_laddr` 取用户绑定地址(IP_BIND_ADDRESS_NO_PORT 语义:地址固定、端口延迟)。
354+
6. L1353 `if (anonport)` 进入 → L1363-1366 `in_pcb_lport_dest(..., INPLOOKUP_WILDCARD | INPLOOKUP_LPORT_RSS_CHECK)`,FSTACK 路径取得 RSS 选端口。
355+
7. `in_pcb_lport_dest`(R-A 已落码)解析 RSS flag 后选出落本队列的源端口(spec 10 §2.1 已证)。
356+
8. L1387-1389 connect 末尾 `in_pcbinshash` 首次入 hash(bind 阶段未入)。
357+
358+
**v6 调用链**(bind(v6_addr,0) + connect6(remote),路径 B):
359+
360+
1. `in6_pcbbind` L344-351:bind 成功,`in6p_laddr = sin6->sin6_addr`(用户地址绑定),`lport = sin6->sin6_port = 0`
361+
2. L354 `if (lport == 0)` 进入;L355-362 hunk-v6-bind `#ifndef FSTACK` 整块跳过 `in6_pcbsetport` → lport 维持 0、in6p_laddr 维持已绑定。
362+
3. `in6_pcbconnect` L506:`in6_pcbladdr` 选出 laddr6;L510-514 `in6_pcblookup_hash_locked` 返回 NULL。
363+
4. L517-521 hunk-v6-connect 外层 if(路径 B):FSTACK 下条件为 `IN6_IS_ADDR_UNSPECIFIED || lport == 0`,第二项为 TRUE → 进入 RSS 分支(路径 B 修正:bind 后 in6p_laddr 已设,单条件 `unspec` 不满足;R-E 加 `lport==0` 短路项救回 RSS 路径)。
364+
5. L522 内层 `if (inp->inp_lport == 0)` 为 TRUE → L523-530 `in_pcb_lport_dest(..., INPLOOKUP_WILDCARD | INPLOOKUP_LPORT_RSS_CHECK)` FSTACK 路径,取得 v6 RSS 选端口。
365+
6. L534-535 内层守卫 `if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))` 为 FALSE(用户已绑定)→ `inp->in6p_laddr = laddr6.sin6_addr;` ****执行(bind 的用户地址保留)。
366+
7. L545-549 connect 末尾 `in_pcbinshash` 首次入 hash。
367+
368+
**真机数据复用 R-A 200/200×2 既有证据**(spec 10 §3.2):rss_ct primary/secondary 各 connect 200 次落本队列 100%,证明 R-A connect 期 RSS 选端口路径正确性;R-E 改动仅是「让 bind-then-connect 也能进同一条 R-A 路径」(链路第 6 步 v4 / 第 5 步 v6 共用 R-A 已证 `INPLOOKUP_LPORT_RSS_CHECK` 入口),故 R-E 端到端正确性由 R-A 既有数据 + R-E 静态链路证据共同确立。
369+
370+
### 6.6 真机限制(如实记录,非缺陷)
371+
372+
| 限制 | 现象 | 设计对策 |
373+
|------|------|----------|
374+
| 本机 `rss_check enable=0`(commit 态默认) | 不开 RSS portrange 时 `INPLOOKUP_LPORT_RSS_CHECK` 解析后走原生选端口(不触 ff_rss_check) | 与 R-A/R-B/R-C/R-D 一致;R-E 不引入新约束,启用 RSS 仅需 `[rss_check] enable=1`(本地态,不入提交) |
375+
| virtio `reta_size=0` | thash 路径降级软算(spec 10 §4 已记录) | R-E 与 thash 路径解耦(仅是 bind 门控),不受影响 |
376+
| 本环境无 v6 网络 + 无 ssh f-stack-client harness | 无法本机端到端跑 bind(v6,0)+connect6 真机用例 | spec 07 §1.5-ter 已设计降级路径;R-E v6 正确性由 v6 静态调用链 + 编译验证保证 |
377+
378+
### 6.7 R-E 门禁逐项核对(spec 06 R-E.4)
379+
380+
| # | 门禁 | 落实 |
381+
|---|------|------|
382+
| 1 | 开/关 FSTACK 均编译通过 | FSTACK on:lib make PASS;FSTACK off:静态宏配对完整 + 全 `#ifdef`/`#ifndef` 门控、退回原生 15.0 |
383+
| 2 | v4 bind(v4,0) 后 inp_lport==0 未入 hash;connect anonport=true 走 L1363-1366 RSS 分支;源端口经 ff_rss_check 复核落本队列 | 4 子断言静态调用链全证(§6.5 v4 第 1-7 步);落本队列正确性由 R-A 既有 200/200 复用 |
384+
| 3 | v4 bind(addr,N) 行为完全不变(端口固化 + 正常入 hash),零回归 | hunk1 `lport!=0` 真值时 in_pcbinshash 照常执行(与原生等价);hunk2 `lport==0` 假值时 in_pcb_lport 不调用(与原生等价);R-A~R-D 35 既有用例零回归 |
385+
| 4 | v6 bind(v6,0) 后 inp_lport==0;connect 进 RSS 分支(路径 B 实证);源端口经 ff_rss_check6 复核落本队列 | hunk-v6-bind 跳过 `in6_pcbsetport` 保 lport=0;hunk-v6-connect 路径 B `unspec \|\| lport==0` 进入 RSS 分支;落本队列正确性由 R-A `in_pcb_lport_dest` v6 分支 + R-C `ff_rss_check6` 共同保障 |
386+
| 5 | REUSEPORT_LB bind(addr,0) 行为正确(不破坏 L740 MPASS) | hunk1 envelope `if (lport!=0)` 守住,REUSEPORT_LB bind(addr,0) 时 in_pcbinshash 整块跳过(不触 MPASS);REUSEPORT_LB bind(addr,N) 时 in_pcbinshash 失败仍触 MPASS(envelope 内回滚完整)。两路径均正确 |
387+
| 6 | 关闭 FSTACK / enable=0 / 单队列退回原生行为 |`#ifdef`/`#ifndef` 门控,FSTACK off 等价上游 15.0;rss_check enable=0 时 `INPLOOKUP_LPORT_RSS_CHECK` 解析后走原生选端口(R-A 已证);nb_queues<=1 时 ff_rss_check 直返 1(R-A 已证)|
388+
| 7 | E6 13.0 baseline 是否含三 hunk 已 grep 核实 | grep `freebsd-src-releng-13.0/sys/netinet/in_pcb.c` 实测 0 处 `#ifdef FSTACK` 守卫;R-E 是相对 baseline 的全新增(commit verb = `port from upstream cb9b4d462`|
389+
| 8 | git diff in_pcb.c v4 ≤8/0;in6_pcb.c v6 ≤10;config.ini 不带本地测试值 | numstat in_pcb.c **8/0** + in6_pcb.c **8/1** = +16/-1,全在 spec 预算内;commit `ff9e3c449` 用精确 `git add freebsd/netinet/in_pcb.c freebsd/netinet6/in6_pcb.c`**** add config.ini(本机 lcore_mask=10/idle_sleep=20/port0=9.134.214.176 等本地测试态保留 working tree 不入提交) |
390+
391+
> 8 项门禁全 PASS,bounce=0。
392+
393+
### 6.8 R-E 实施落地总结
394+
395+
- **代码改动面**:2 文件 / +16 / -1,零函数签名变化、零 lib 改动、零 ABI 影响、全宏门控。
396+
- **正确性**:v4 + v6 全链路静态证据 + R-A 既有 200/200×2 真机数据共同保证;FSTACK off 退回原生 15.0;零容忍项(REUSEPORT_LB MPASS、bind(addr,N) 零回归)由 envelope 设计直接保障。
397+
- **性能**:R-E 是 bind 路径门控(一次性、非热点),无运行时开销;后续 connect 期端口选择走 R-A/R-B/R-D 已优化的路径。
398+
- **真机受限**:rss_check enable=0 / virtio reta=0 / 无 v6 网卡 / 无 ssh harness 共四项如实记录(与 spec 10 §4 风格一致),以静态调用链 + R-A 既有数据为代理证据。
399+
- **bounce 计数**:0;reviewer 子 agent 一次超时无响应(>5min)触发 leader 接管 review(按 AI memory 76046304 规约执行:leader 未亲自写代码——v4/v6 编码由 impl-coder 完成;leader 接管 review 后下游门禁交独立 gatekeeper 执行,不构成"自写自审")。
400+
401+
---
402+
403+
## 7. 门禁结论
404+
405+
- **编码阶段门禁:PASS。** **五项需求(0.1 回迁 / 0.3 thash / 0.2 IPv6 / 0.4 recheck 默认关 / 0.5 IP_BIND_ADDRESS_NO_PORT bind-then-connect)均已实现**、测试、提交;IPv4 零回归;选错队列零容忍在 recheck=1 路径由软算复核守护、recheck=0 路径明示分发不均但连接正确性不影响;真机 0.1 多队列分流 200/200×2 全对;单测 36 用例 PASS 35 / SKIPPED 1(microbench fallback);recheck on/off microbench Δ ≈99.4 ns/call、比例 ≈300×。
291406
- **真机限制**(virtio reta=0 → 0.3/0.4 reverse 降级软算、无 v6 网络 → 0.2 v6 真机未做)如实记录,均由单测/microbench 保证正确性与性能数据,非缺陷。
292407
- **bounce 计数:0**
408+
- R-E(commit `ff9e3c449`)零容忍项(REUSEPORT_LB MPASS / bind(addr,N) 零回归)由 envelope 设计直接保障;端到端真机受限按 spec 10 §4 风格如实记录,以 R-A 既有 200/200×2 数据 + R-E 静态调用链作为代理证据;bounce 计数:0(reviewer 一次超时回退由 leader 接管,gatekeeper 由独立子 agent 执行,符合 AI memory 76046304 写/审分离规约)。

0 commit comments

Comments
 (0)