Skip to content

Commit e62afc5

Browse files
author
zc-read-spec-leader
committed
docs(zc_read): FSTACK_ZC_RECV implementation spec (spec-only, zh_cn)
Adds the implementation-grade spec set (docs/zc_read_spec/zh_cn/10-19) for zero-copy receive, building on the feasibility study (00-09). Contents (all code-grounded, gatekeeper spot-check 7/7): - 10 overview + FF_ZC_RECV/FSTACK_ZC_RECV switch (mirror of FF_ZC_SEND) - 11 layered architecture + data flow - 12 kernel patch spec: new kern_zc_recvit passing soreceive mp0 (recv path uipc_syscalls.c:948; read path soo_read sys_socket.c:133), soreceive core unchanged, gated by FSTACK_ZC_RECV - 13 user API contract: ff_zc_recv / ff_zc_mbuf_read(rewrite) / ff_zc_recv_free - 14 mbuf ownership state machine (INV1-4: no UAF/leak/double-free) - 15 boundary & fallback matrix (split/PEEK/WAITALL/DONTWAIT/OOB/SCM/TLS/UDP) - 16 CMocka test spec (unit + integration, aligned with tests/ framework) - 17 acceptance criteria + M0-M5 milestones - 19 gatekeeper review (all PASS) Spec/design only; no implementation code. Recommended approach: reuse FreeBSD-native soreceive mp0 out-parameter.
1 parent 875532e commit e62afc5

10 files changed

Lines changed: 517 additions & 0 deletions

docs/zc_read_spec/zh_cn/10-plan.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# FSTACK_ZC_RECV 零拷贝收包 实现级 Spec — Plan
2+
3+
> 状态:PLAN-READY →(执行)BUILDING → FINISHED
4+
> 前置:可行性调研已通过(docs/zc_read_spec/zh_cn/00-09,结论"可行",commit 875532e35)
5+
> 本阶段范围:产出**实现级规格文档**(spec),指导后续编码;**本阶段仍不写实现代码**
6+
> 文档语言:仅中文(docs/zc_read_spec/zh_cn/)
7+
> 方法:harness 工程 + spec 驱动 + agent team(leader + 架构探测/文档编写/spec 审核门禁子 agent)
8+
> 铁律:所有规格条目以**实测代码为准**,外网/文档仅启发;不一致以代码为准;严禁未执行即给结论
9+
10+
---
11+
12+
## §1 目标
13+
基于可行性结论(推荐方案A:复用 soreceive 的 `mp0` 出参),产出可直接指导实现的零拷贝收包规格:架构设计、内核 patch 详规、用户态 API 契约、mbuf 生命周期状态机、边界与回退矩阵、CMocka 测试规格、验收标准与里程碑。
14+
15+
## §2 Phase 0 spec 级实测补充(已完成)
16+
> 在可行性调研(01-09)基础上,本阶段新增以下实测依据:
17+
18+
|| 实测 | 用途 |
19+
|---|---|---|
20+
| 编译开关范式 | `lib/Makefile:210-212 ifdef FF_ZC_SEND → CFLAGS+=-DFSTACK_ZC_SEND` | 对称定义 `FF_ZC_RECV→-DFSTACK_ZC_RECV` |
21+
| kern_recvit 签名 | `uipc_syscalls.c:895 kern_recvit(td,s,mp,fromseg,controlp)`;soreceive 第4参=NULL(L948)| 内核 patch 点 |
22+
| read() 路径 | `sys_socket.c:122 soo_read → soreceive(so,0,uio,0,0,0)`(L133,mp0=0)| 内核 patch 点(read 亦需)|
23+
| 示例调用序列 | `example/main_zc.c`:ff_zc_mbuf_get→ff_zc_mbuf_write(可多次)→ff_zc_send(L185-220)| 对称设计 recv 示例规格 |
24+
| 测试现状 | tests/ 下**** ff_zc_* 测试 | 测试规格为 greenfield,需新建 |
25+
26+
(其余实测见 01-05,本 plan 不重复。)
27+
28+
## §3 Spec 文档清单(docs/zc_read_spec/zh_cn/,新增 10-19 系列)
29+
| 文档 | 内容 | 负责子 agent |
30+
|---|---|---|
31+
| `10-spec-overview.md` | 范围/术语/目标/与可行性结论衔接/编译开关 FSTACK_ZC_RECV | leader |
32+
| `11-architecture-design.md` | 分层架构(用户态 API ↔ 内核 mp 贯通 ↔ soreceive mp0 ↔ ext-mbuf)+ 数据流图 | spec-writer-arch |
33+
| `12-kernel-patch-spec.md` | 内核改动详规:kern_recvit/kern_zc_recvit 变体 + soo_read 透传 + FSTACK_ZC_RECV 宏;改动点/前后对比/不破坏现有 recv 的约束 | spec-writer-kernel |
34+
| `13-userspace-api-spec.md` | API 契约:ff_zc_recv / ff_zc_mbuf_read 重写 / ff_zc_recv_free;struct ff_zc_mbuf 复用语义;错误码/参数/返回值 | spec-writer-api |
35+
| `14-mbuf-lifecycle-spec.md` | mbuf 所有权状态机(sockbuf→APP→release)+ refcnt 契约 + 泄漏防护 + 时序图 | spec-writer-life |
36+
| `15-boundary-and-fallback-spec.md` | 边界矩阵详规(整/split/PEEK/WAITALL/DONTWAIT/OOB/SCM/TLS/UDP)+ 回退策略 | spec-writer-arch |
37+
| `16-test-spec.md` | CMocka 测试规格(参考 c-unittest-expert 方法论,API 换 CMocka):用例命名/setup-teardown/断言粒度/边界覆盖/mock 策略 | spec-writer-test |
38+
| `17-acceptance-and-milestones.md` | 验收标准(功能/性能/内存安全/兼容)+ M0-M5 里程碑 + DoD | leader |
39+
| `19-spec-review.md` | spec 审核门禁(4 维 + 可实现性 + 自洽性 + 引用真实性)| gatekeeper |
40+
41+
## §4 Agent Team 拓扑
42+
| 角色 | 职责 | 模式 |
43+
|---|---|---|
44+
| **leader(主 agent)** | 统筹/分发/汇总/门禁裁决/落盘 10/17/收尾 commit | 主 agent |
45+
| **probe-spec**(架构探测·async code-explorer)| 补充 spec 级实测(kern_recvit 调用方、soo_read、msghdr 流、m_freem、mbuf flags),确保规格条目可溯源 | 只读 |
46+
| **spec-writer-kernel** | 编写 12 内核 patch 详规 | 串行 |
47+
| **spec-writer-api** | 编写 13 用户态 API 规格 | 串行 |
48+
| **spec-writer-life** | 编写 14 生命周期状态机 | 串行 |
49+
| **spec-writer-arch** | 编写 11 架构 + 15 边界矩阵 | 串行 |
50+
| **spec-writer-test** | 编写 16 CMocka 测试规格(参考 c-unittest-expert)| 串行 |
51+
| **gatekeeper** | 19 审核门禁:引用真实性/行号吻合/可实现性/自洽性/无臆测,任一不过打回重写 | 串行 |
52+
53+
**失败回滚 SOP**:gatekeeper 不通过 → 打回对应 spec-writer 重写;发现与代码矛盾 → 以代码为准并标注。
54+
55+
## §5 Phase Schedule
56+
- **P0** spec 级实测补充(✅ 见 §2)
57+
- **P1** 文档骨架落盘(10-19 系列骨架)
58+
- **P2** 架构探测补证(probe-spec,确保 12/13/14 的内核/调用引用可溯源)
59+
- **P3** 串行编写 11/12/13/14/15/16(各 spec-writer)
60+
- **P4** 落盘 10 overview + 17 acceptance/milestones
61+
- **P5** gatekeeper 审核门禁(19);不过回滚 P3
62+
- **P6** 收尾 commit(简洁英文 message)
63+
64+
## §6 验收门禁(本 spec 的 acceptance gate)
65+
| Gate | 内容 |
66+
|---|---|
67+
| G-SPEC-1 | 所有内核/代码引用 file:line:symbol 经实测核对,gatekeeper 抽检 100% 命中 |
68+
| G-SPEC-2 | 内核 patch 详规给出 read()+recv() 两条路径的改动点 + 不破坏现有 recv 的约束论证 |
69+
| G-SPEC-3 | API 契约完整(签名/参数/返回/错误码/调用序列/误用防护)|
70+
| G-SPEC-4 | mbuf 生命周期状态机闭环(无 use-after-free / 无泄漏 / 无双重 free 的状态论证)|
71+
| G-SPEC-5 | 边界矩阵 + 回退策略覆盖全场景 |
72+
| G-SPEC-6 | CMocka 测试规格可落地(命名/setup-teardown/断言/边界/mock)|
73+
| G-SPEC-7 | 编译开关 FSTACK_ZC_RECV 与现有 FSTACK_ZC_SEND 范式一致 |
74+
| G-SPEC-8 | 仅中文;无臆测(凡条目溯源到代码或标注"实现期验证")|
75+
76+
## §7 范围与约束
77+
**In-scope**:实现级规格文档(架构/内核/API/生命周期/边界/测试/验收)。
78+
**Out-of-scope(本期不做)**:实现代码、内核 patch 落地、实际测试编译运行、性能压测、英文文档。
79+
**工作区规约**:rm→rm_tmp_file.sh;kill→kill_process.sh;chmod→chmod_modify.sh;make install 等非直接 chmod 可执行。commit message 英文。
80+
**测试方法论**:CMocka,参考 c-unittest-expert(Unity-based 思路通用,API 换 CMocka:assert_int_equal 等)。
81+
82+
## §8 风险
83+
| 风险 | 缓解 |
84+
|---|---|
85+
| spec 条目脱离实际代码 | 每条内核/API 规格强制 file:line 溯源;gatekeeper 抽检 |
86+
| read()/recv() 两路径遗漏 | 12 强制覆盖 soo_read(sys_socket.c:133) + kern_recvit(uipc_syscalls.c:948) |
87+
| 生命周期状态机不闭环 | 14 用状态机 + 时序图 + 03 实测 refcnt 链支撑 |
88+
| 测试规格不可落地 | 16 对齐现有 tests/unit 框架(cmocka + Makefile per-target 范式)|
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# 10 · Spec 总览(FSTACK_ZC_RECV 零拷贝收包)
2+
3+
> 实现级规格。衔接可行性结论(00-09,结论"可行",推荐方案A 复用 soreceive mp0)。
4+
5+
## 1. 范围
6+
为 f-stack 增加零拷贝收包能力 FSTACK_ZC_RECV:让 APP 直接获得 socket 接收缓冲区中的 BSD mbuf 链(其数据已零拷贝指向 DPDK mbuf),消除 `soreceive→uiomove` 的 mbuf→用户 buffer 拷贝。本规格指导后续编码,**本阶段不写实现代码**
7+
8+
## 2. 术语
9+
| 术语 | 含义 |
10+
|---|---|
11+
| ZC-RECV | 零拷贝收包(本特性)|
12+
| mp0 | soreceive 第 4 形参 `struct mbuf **mp0`,非 NULL 时以 mbuf 链返回、避免拷贝(FreeBSD 原生)|
13+
| ext-mbuf | external mbuf:BSD mbuf 头 + 指向 DPDK mbuf 数据的外部存储(EXT_DISPOSABLE)|
14+
| release 契约 | APP 读完后必须归还 mbuf 链(m_freem),否则 mempool 泄漏 |
15+
16+
## 3. 目标与非目标
17+
- **目标**:TCP(含 stream)大块数据零拷贝收取;正确的所有权移交与释放;不破坏现有 recv/read 语义。
18+
- **非目标(本特性)**:MSG_OOB / MSG_PEEK / KERN_TLS 零拷贝(回退拷贝);UDP 走 dgram 回退;小包优化。
19+
20+
## 4. 编译开关(与 SEND 对称)
21+
- 现有:`lib/Makefile:210-212 ifdef FF_ZC_SEND → CFLAGS+=-DFSTACK_ZC_SEND`
22+
- 新增:`ifdef FF_ZC_RECV → CFLAGS+=-DFSTACK_ZC_RECV`
23+
- 哨兵宏 FSTACK_ZC_MAGIC(send 用)**不复用于 recv**(方向相反,见 01 §5 / 11)。
24+
25+
## 5. 文档导航
26+
11 架构 / 12 内核 patch / 13 用户态 API / 14 生命周期 / 15 边界回退 / 16 测试 / 17 验收里程碑 / 19 审核。
27+
28+
## 6. 关键实测依据(溯源)
29+
- soreceive 原型与 mp0 分派:uipc_socket.c:3661-3671
30+
- recv 链:sys_recvfrom/sys_recvmsg → recvit(uipc_syscalls.c:1049) → kern_recvit(:895) → soreceive(...,NULL,...)(:948)
31+
- read 链:read → dofileread(sys_generic.c:345) → fo_read → soo_read(sys_socket.c:121) → soreceive(so,0,uio,0,0,0)(:133)
32+
- 返回字节:kern_recvit:967 `td_retval[0]=len-auio.uio_resid`
33+
- ext-mbuf:ff_veth.c:374 m_extadd(...EXT_DISPOSABLE);释放链 ff_veth.c:300/1106 → rte_pktmbuf_free_seg
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# 11 · 架构设计
2+
3+
> 实现级架构。所有内核/代码锚点经实测(见 10 §6)。
4+
5+
## 1. 分层总览
6+
```
7+
┌─────────────────────────────────────────────────────────────┐
8+
│ APP(用户业务) │
9+
│ ff_zc_recv(fd, &zm, n) → 遍历 ff_zc_mbuf_read/segment → ff_zc_recv_free │
10+
├─────────────────────────────────────────────────────────────┤
11+
│ 用户态 API 层(lib/ff_api.h, lib/ff_veth.c, lib/ff_syscall_wrapper.c)│
12+
│ - ff_zc_recv:构造 msghdr/uio,经新内核入口透传 mbuf 出参 mp │
13+
│ - ff_zc_mbuf_read(重写):从 zm->bsd_mbuf 链顺序读出/遍历 │
14+
│ - ff_zc_recv_free:m_freem(zm->bsd_mbuf) 归还整链 │
15+
├─────────────────────────────────────────────────────────────┤
16+
│ 内核桥接层(freebsd/kern/uipc_syscalls.c / sys_socket.c) │
17+
│ - kern_zc_recvit 变体 / soo_read 透传:把 soreceive mp0 由 NULL 改为 &mp │
18+
├─────────────────────────────────────────────────────────────┤
19+
│ soreceive mp0 引擎(freebsd/kern/uipc_socket.c,FreeBSD 原生) │
20+
│ - mp!=NULL:sbfree + *mp=m 直交(零拷贝) │
21+
│ - split:m_copym 回退 │
22+
├─────────────────────────────────────────────────────────────┤
23+
│ ext-mbuf(ff_veth.c m_extadd EXT_DISPOSABLE)→ DPDK rte_mbuf │
24+
│ - 数据零拷贝指向 DPDK mbuf;m_ext refcnt 驱动 rte_pktmbuf_free_seg │
25+
└─────────────────────────────────────────────────────────────┘
26+
```
27+
28+
## 2. 数据流(ZC-RECV 成功路径)
29+
1. NIC→DPDK mbuf→`ff_veth_input``ff_mbuf_gethdr`(m_extadd EXT_DISPOSABLE)→ ext-mbuf 入 sockbuf(已零拷贝,现状)。
30+
2. APP `ff_zc_recv`:构造 uio(uio_resid=n)+ 传 mbuf 出参 mp。
31+
3. 内核桥接:`soreceive(so, psa, uio, &mp, ...)`(mp 非 NULL)。
32+
4. `soreceive` mp!=NULL 分支:`sbfree`(记账)+ `*mp=m` + `sb_mb` 前移 → **不 uiomove**,整段 mbuf 交出。
33+
5. 返回:`td_retval[0]=len-uio_resid`;zm->bsd_mbuf = 交出的链首。
34+
6. APP 遍历读数据(直接访问 mbuf 数据,零拷贝),用完 `ff_zc_recv_free``m_freem`→逐段 ff_mbuf_ext_free→rte_pktmbuf_free_seg 归还 DPDK seg。
35+
36+
## 3. 与 ZC-SEND 的架构差异(关键)
37+
| 维度 | SEND | RECV |
38+
|---|---|---|
39+
| 方向 | 用户构造 mbuf→内核接管 | 内核交出 mbuf→用户消费 |
40+
| 触发 | uio_offset=FSTACK_ZC_MAGIC(m_uiotombuf)| mp0 出参(soreceive 原生)|
41+
| 释放 | 内核接管后自行管理 | **APP 负责 release(新增契约)** |
42+
| 改动 | m_uiotombuf 加 magic 分支 | kern_recvit/soo_read 透传 mp0(不改 soreceive 核心)|
43+
44+
## 4. 设计原则
45+
- **不改 soreceive 核心逻辑**:仅打通 mp0 通道(风险最小,升级友好)。
46+
- **不破坏现有 recv/read**:新增独立入口(kern_zc_recvit),现有 kern_recvit 保持 mp0=NULL 不变。
47+
- **回退优先正确性**:不可零拷贝场景(split/PEEK/OOB/TLS/UDP)回退拷贝路径,语义与现状一致。
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# 12 · 内核 Patch 详规
2+
3+
> 所有锚点经实测。改动包裹于 `#ifdef FSTACK_ZC_RECV`,默认不启用,不影响现有路径。
4+
5+
## 1. 改动总表
6+
| # | 文件 | 锚点 | 改动 | 风险 |
7+
|---|---|---|---|---|
8+
| K1 | freebsd/kern/uipc_syscalls.c | kern_recvit:895 / soreceive:948 | 新增 `kern_zc_recvit` 变体,把 soreceive 第4参由 NULL 改为 `&mp` 透传 | 低(新增函数,不改原 kern_recvit)|
9+
| K2 | freebsd/kern/sys_socket.c | soo_read:121 / soreceive:133 | read() 路径 ZC 支持(可选,见 §4):经 fp 标志或新 fileop 透传 mp ||
10+
| K3 | lib/Makefile | 210-212 | 新增 `ifdef FF_ZC_RECV → -DFSTACK_ZC_RECV` ||
11+
| K4 | lib/ff_syscall_wrapper.c | ff_recvfrom:1319 / ff_recvmsg:1359 | 新增 `ff_zc_recv` 入口,调 kern_zc_recvit;普通 recv 不变 ||
12+
13+
## 2. K1 — kern_zc_recvit(推荐核心改动)
14+
现状(uipc_syscalls.c:948,**保持不变**):
15+
```c
16+
error = soreceive(so, &fromsa, &auio, NULL,
17+
(mp->msg_control || controlp) ? &control : NULL, &mp->msg_flags);
18+
```
19+
新增变体(伪代码,`#ifdef FSTACK_ZC_RECV`):
20+
```c
21+
int
22+
kern_zc_recvit(struct thread *td, int s, struct msghdr *mp,
23+
enum uio_seg fromseg, struct mbuf **controlp, struct mbuf **mp0 /* 新增出参 */)
24+
{
25+
/* 与 kern_recvit 同样构造 auio(uio_segflg/uio_rw/uio_resid,:928-941)*/
26+
struct mbuf *zc_chain = NULL;
27+
error = soreceive(so, &fromsa, &auio, &zc_chain /* mp0 非 NULL */,
28+
(mp->msg_control || controlp) ? &control : NULL, &mp->msg_flags);
29+
/* 返回字节同 kern_recvit:967: td_retval[0] = len - auio.uio_resid */
30+
if (mp0 != NULL) *mp0 = zc_chain; /* 把零拷贝链交出 */
31+
/* 地址/控制消息回填逻辑复用 kern_recvit 现有代码 */
32+
}
33+
```
34+
**约束**:
35+
- 必须复用 kern_recvit 现有的 auio 构造(:928-941)、返回值计算(:967)、fromsa/control 回填,保持语义一致。
36+
- mp0!=NULL 时 soreceive 仅用 uio_resid(FreeBSD 手册,04 §1),故 auio 仍需正确设置 uio_resid。
37+
- 调用方(ff_zc_recv)负责把 zc_chain 存入 struct ff_zc_mbuf 并最终 m_freem。
38+
39+
## 3. soreceive 侧(**不改**)
40+
soreceive(uipc_socket.c:3661) 的 mp0 分支为 FreeBSD 原生,**无需改动**:
41+
- mp!=NULL 整段直交(sbfree + *mp=m + sb_mb 前移);
42+
- split → m_copym 回退;
43+
- mp==NULL → uiomove(现有 recv 不受影响)。
44+
TCP 默认 pr_soreceive=soreceive_generic(支持 mp0);开启 soreceive_stream 亦支持。
45+
46+
## 4. K2 — read() 路径(可选,分期)
47+
read→soo_read→`soreceive(so,0,uio,0,0,0)`(sys_socket.c:133)写死 mp0=0。read() 接口本身无 mbuf 出参语义,ZC-recv **优先经 ff_zc_recv(recv 系)实现**;read() 路径 ZC 列为可选/后续(需经 file 标志或新 fileop 传 mp,改动面更大,M4 评估)。
48+
49+
## 5. 不破坏现有 recv 的论证
50+
- 原 kern_recvit / soo_read **零改动**,mp0 仍 NULL → 现有 recv/read 行为完全不变。
51+
- 新增 kern_zc_recvit 仅在 FSTACK_ZC_RECV 编译 + APP 显式调 ff_zc_recv 时生效。
52+
- soreceive 核心未改 → 协议层/记账/锁语义不变(02 §4 已验证 mp0 分支记账自洽)。
53+
54+
## 6. FSTACK_ZC_RECV 宏门控范围
55+
- lib/ff_api.h:ff_zc_recv/ff_zc_mbuf_read/ff_zc_recv_free 声明
56+
- lib/ff_veth.c:ff_zc_mbuf_read 重写 + ff_zc_recv_free
57+
- lib/ff_syscall_wrapper.c:ff_zc_recv
58+
- freebsd/kern/uipc_syscalls.c:kern_zc_recvit
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# 13 · 用户态 API 规格
2+
3+
> 全部包裹 `#ifdef FSTACK_ZC_RECV`。对称于 SEND 的 get/write/send,RECV 为 recv→read/segment→free 三段。
4+
5+
## 1. struct ff_zc_mbuf(复用,ff_api.h:347)
6+
```c
7+
struct ff_zc_mbuf {
8+
void *bsd_mbuf; /* ZC-RECV: 内核交出的 mbuf 链首(m_freem 的对象)*/
9+
void *bsd_mbuf_off; /* ZC-RECV: 遍历游标(当前读到的 mbuf)*/
10+
int off; /* ZC-RECV: 已读累计偏移,APP 不应改 */
11+
int len; /* ZC-RECV: 链总有效字节(=本次 recv 返回字节)*/
12+
};
13+
```
14+
语义复用:SEND 时 off/len 是"已写/容量",RECV 时是"已读/总有效"。
15+
16+
## 2. ff_zc_recv —— 零拷贝收取
17+
```c
18+
ssize_t ff_zc_recv(int fd, struct ff_zc_mbuf *zm, size_t nbytes);
19+
```
20+
| 项 | 规格 |
21+
|---|---|
22+
| 入参 | fd:socket;zm:出参容器(非 NULL);nbytes:期望最大字节(映射 uio_resid)|
23+
| 行为 | 经 kern_zc_recvit 透传 mp0,取回零拷贝 mbuf 链;填 zm->bsd_mbuf=链首、bsd_mbuf_off=链首、off=0、len=返回字节 |
24+
| 返回 | >0:实际收取字节(=len);0:对端关闭;-1:错误(errno)|
25+
| 错误码 | EINVAL(zm==NULL/nbytes>INT_MAX)、EAGAIN(非阻塞无数据)、ECONNRESET 等沿用 soreceive |
26+
| 误用防护 | 返回 >0 后 APP **必须**最终调用 ff_zc_recv_free(否则 mempool 泄漏)|
27+
28+
## 3. ff_zc_mbuf_read —— 读出/遍历(重写空 stub)
29+
现状空 stub(ff_veth.c:359,签名 `const char *data` 与读出矛盾)。重设计:
30+
```c
31+
/* 方案1:拷贝读出到用户 buf(data 去 const 作 OUT)*/
32+
int ff_zc_mbuf_read(struct ff_zc_mbuf *zm, char *out, int len);
33+
/* 方案2(推荐,真零拷贝遍历):返回当前段指针+长度,游标后移 */
34+
int ff_zc_mbuf_segment(struct ff_zc_mbuf *zm, void **seg_data, int *seg_len);
35+
```
36+
|| 规格(方案2)|
37+
|---|---|
38+
| 行为 | 从 bsd_mbuf_off 取当前 mbuf 的 mtod(m)/m_len,*seg_data/*seg_len 返回,游标 m_next 后移,off+=seg_len |
39+
| 返回 | >0:本段字节;0:链已读完;-1:错误 |
40+
| 零拷贝 | seg_data 直接指向 mbuf 数据(指向 DPDK mbuf),APP 在 free 前可安全访问 |
41+
42+
## 4. ff_zc_recv_free —— 归还整链
43+
```c
44+
void ff_zc_recv_free(struct ff_zc_mbuf *zm);
45+
```
46+
| 项 | 规格 |
47+
|---|---|
48+
| 行为 | `m_freem((struct mbuf*)zm->bsd_mbuf)`;逐段触发 ff_mbuf_ext_free→rte_pktmbuf_free_seg 归还 DPDK seg;清零 zm |
49+
| 幂等 | bsd_mbuf==NULL 时 no-op |
50+
| 约束 | 调用后 zm 不可再用于 read/segment(需重新 ff_zc_recv)|
51+
52+
## 5. 调用序列(对称 main_zc.c SEND)
53+
```c
54+
struct ff_zc_mbuf zm;
55+
ssize_t n = ff_zc_recv(clientfd, &zm, sizeof_expect);
56+
if (n > 0) {
57+
void *seg; int slen;
58+
while (ff_zc_mbuf_segment(&zm, &seg, &slen) > 0) {
59+
/* 零拷贝处理 seg[0..slen)(如转发/解析)*/
60+
}
61+
ff_zc_recv_free(&zm); /* 必须!*/
62+
}
63+
```
64+
65+
## 6. 与现有 API 的关系
66+
- 普通 ff_read/ff_recv/ff_recvfrom/ff_recvmsg **完全不变**(mp0 仍 NULL,拷贝路径)。
67+
- ff_zc_recv 与普通 recv 互斥使用于同一次读取;混用不破坏正确性(各自独立 soreceive 调用)。

0 commit comments

Comments
 (0)