|
| 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