第一部分 · 6 / 6
一次传输的全流程
把前面所有概念串起来。这一节走一遍一次 RC Send 的完整生命周期——记住这七步, 第二部分你会看到 gordma 如何把每一步封装成 Go 方法,第三部分则会看到 perftest 工具是怎么实操的。
七步生命周期
- 打开设备:枚举
Device,Open得到Context。 - 建资源:分配
PD,注册内存得到MR(拿到 lkey/rkey),创建CQ,再创建QP。 - 带外握手:通过 TCP 或 librdmacm 交换双方 QPN / PSN / GID / rkey / 远端地址。
- QP 状态迁移:INIT → RTR → RTS,填入对端参数。
- 投递工作请求:接收方先
post_recv,发送方post_send。 - 轮询完成队列:从
CQ取出完成项(WC),确认操作成功。 - 清理:销毁 QP / CQ / MR / PD,关闭 Context。
时序图
为什么接收方要先 post_recv
双边 Send 要求接收方预先挂好接收 WR(指向一块注册内存),发送方的数据才有落点。
若发来时对端没有可用 recv WR,会触发 RNR(Receiver Not Ready),靠重试机制兜底。
对应到 gordma 的方法
| 步骤 | 底层 gordma 调用 |
|---|---|
| ① 打开设备 | GetDeviceList() → dev.Open() |
| ② 建资源 | ctx.AllocPD() / pd.RegMRBuffer() / ctx.CreateCQ() / pd.CreateQP() |
| ③ 握手 | handshake 包(TCP)或 gordma.Dial/Listen(rdma_cm) |
| ④ 状态迁移 | qp.ModifyToInit/RTR/RTS() |
| ⑤ 投递 WR | SendWR / RecvWR + post(见 postwr.go) |
| ⑥ 轮询完成 | CQ 轮询 → WorkCompletion(检查 Status.OK()) |
| ⑦ 清理 | 各对象的 Close() |
完成项怎么读
第 ⑥ 步拿到的每个完成项是一个扁平值类型 WorkCompletion(cq.go),
关键是先看状态:
wc := /* 从 CQ 轮询得到 */
if !wc.Status.OK() { // Status != WCSuccess
// 失败:可能是 RNR 重试耗尽、长度错误、保护错误…
}
// wc.WRID 对回 post 时填的 id;wc.ByteLen 是收到的字节数
承上启下
第 2~6 步几乎全是样板代码,繁琐又容易出错。这正是 gordma 高层
rdmanet 包要替你做掉的事——
你只需要 Dial / SendMsg / RecvMsg。
进入 第二部分,看 gordma 库是怎么设计的。
gordma 教程