4、RC服务核心概念:队列对(QP)类型、QP状态机、完成队列(CQ)、完成事件(CE)与工作请求(WR)的基本概念
RC(Reliable Connection,可靠连接)传输服务是InfiniBand中功能最完整、语义最丰富的传输模式。要真正理解RC的工作机制,不能只停留在“发送-接收”的抽象层面,而必须深入到驱动与HCA(Host Channel Adapter)硬件交互的具体对象上。本节将围绕五个核心概念展开:队列对(QP)、QP状态机、完成队列(CQ)、完成事件(CE)以及工作请求(WR)。这些概念构成了RC服务的“骨架”,所有可靠传输行为——包括报文重传、应答匹配、错误处理——都建立在这套机制之上。
4.1 队列对(QP)类型与RC的定位
队列对(Queue Pair)是InfiniBand节点之间通信的基本端点。每个QP由两部分组成:
- 发送队列(Send Queue, SQ):承载用户下发的发送请求,描述“要发什么数据、发给谁”。
- 接收队列(Receive Queue, RQ):承载用户预置的接收缓冲区,描述“收到的数据放到哪里”。
InfiniBand规范定义了四种QP类型,RC只是其中之一。下表对比了各类型的关键差异:
| QP类型 | 连接方式 | 可靠性 | 报文顺序 | 典型用途 |
|---|---|---|---|---|
| RC(可靠连接) | 1对1,需建立连接 | 可靠(ACK/NACK重传) | 严格保序 | RDMA读写、大规模HPC |
| UC(不可靠连接) | 1对1,需建立连接 | 不可靠(无重传) | 尽力保序 | 低延迟、容忍丢包场景 |
| UD(不可靠数据报) | 1对多,无需连接 | 不可靠 | 不保证 | 管理报文、多播 |
| XRC(扩展可靠连接) | 1对多共享接收队列 | 可靠 | 按连接保序 | 多对一通信优化 |
RC的“可靠”并非凭空而来,它依赖QP内部的发送序号(PSN)、接收序号匹配以及重传定时器等机制。每个RC QP在建立连接时,双方会交换初始PSN,之后每个报文都携带递增的PSN,接收方据此检测丢包和乱序。
4.2 QP状态机:连接生命周期的核心
每个RC QP在生命周期内会经历多个状态,状态转换由软件(驱动)或硬件事件驱动。理解状态机是排查RC连接故障的基础。
状态转换的关键路径如下:
- RESET → INIT:通过
modify_qp设置QP的物理端口、QP号等基础属性。 - INIT → RTR(Ready to Receive):配置接收侧参数,包括目的QP号、初始PSN、路径MTU等。此时QP可以接收对端报文,但不能发送。
- RTR → RTS(Ready to Send):配置发送侧参数(如超时时间、重传次数),QP进入全双工状态。
- RTS → SQD(Send Queue Drained):驱动主动暂停发送队列,等待已发出的报文全部确认后,再修改QP属性。
- 任何状态 → ERR:发生不可恢复错误(如重传超时、PSN失配、接收缓冲区缺失)时,QP直接进入ERR状态,所有未完成WR被丢弃。
4.3 工作请求(WR)与完成队列(CQ)的协作
工作请求(Work Request, WR)是应用程序与QP之间的“任务单”。用户通过post_send和post_recv接口将WR提交到SQ或RQ。每个WR描述一次数据传输操作,包括:
- WR ID:用户自定义的标识符,用于关联完成事件。
- SGE(Scatter/Gather Element)列表:指向数据缓冲区的地址、长度和LKey。
- 操作类型:如
IBV_WR_SEND、IBV_WR_RDMA_WRITE、IBV_WR_RDMA_READ等。
完成队列(Completion Queue, CQ)则负责收集已完成的WR状态。每个CQ包含多个完成队列条目(CQE),当QP中的WR执行完毕(成功或失败),HCA会生成一个CQE写入CQ。CQE中记录了WR ID、状态码、字节数等信息。
完成事件(Completion Event, CE)是CQ的异步通知机制。当CQ从空变为非空时,HCA可以触发一个完成事件,驱动通过事件回调通知应用程序“有新的完成条目”。这避免了应用程序频繁轮询CQ的开销。
典型工作流程:
1. 创建QP和CQ(ibv_create_qp / ibv_create_cq)
2. 将QP状态迁移至RTS
3. 在RQ上预置接收WR(post_recv)
4. 在SQ上提交发送WR(post_send)
5. HCA硬件执行DMA传输
6. 传输完成 → 生成CQE → 写入CQ
7. 应用程序通过轮询(ibv_poll_cq)或事件(ibv_get_cq_event)获取完成信息
8. 解析CQE中的状态,判断传输是否成功
4.4 案例:RC QP建立与一次RDMA写操作
假设节点A要向节点B的某段内存执行RDMA写操作,完整流程如下:
| 步骤 | 节点A(发起方) | 节点B(响应方) |
|---|---|---|
| 1 | 创建QP_A,状态RESET | 创建QP_B,状态RESET |
| 2 | 修改QP_A至INIT | 修改QP_B至INIT |
| 3 | 修改QP_A至RTR | 修改QP_B至RTR |
| 4 | 修改QP_A至RTS | 修改QP_B至RTS |
| 5 | post_recv(预置接收WR) | post_recv(预置接收WR) |
| 6 | post_send(RDMA_WRITE WR) | — |
| 7 | HCA发送RDMA写请求报文 | HCA解析报文,将数据写入指定内存 |
| 8 | 收到ACK,生成CQE | 生成完成通知(可选) |
注意:RDMA写操作不需要在接收方预置接收WR,因为数据直接写入接收方预先注册的内存区域(通过RKey授权)。这是RC服务中“单边操作”的典型特征。
4.5 误区与复盘:QP与CQ的常见理解偏差
误区1:一个QP必须对应一个CQ。实际上,多个QP可以共享同一个CQ。CQ是“完成信息的汇聚点”,与QP无一一对应关系。驱动在创建QP时,会指定该QP的发送完成队列和接收完成队列,但多个QP可以指向同一个CQ。
误区2:CQE生成后,WR就立即释放了。WR的释放时机取决于用户是否调用ibv_poll_cq获取了CQE。如果CQ积压了大量CQE而未轮询,HCA的CQ缓冲区可能溢出,导致IBV_WC_CQ_OVERFLOW错误。因此,应用程序必须及时消费CQE。
复盘建议:在排查RC连接故障时,建议按以下顺序检查:
- 确认QP状态是否为RTS(使用
ibv_query_qp)。 - 检查CQ中是否有错误CQE(状态码非SUCCESS)。
- 查看QP的发送/接收计数器(通过
ibv_query_qp获取)是否异常增长。 - 确认接收侧是否预置了足够的接收WR(对于SEND操作)。
理解QP、CQ、WR和CE之间的关系,是掌握RC服务的关键一步。QP负责“传输通道”,CQ负责“结果反馈”,WR是“任务描述”,CE是“异步通知”。四者协同,构成了RC服务高效、可靠的基础。在实际编程中,合理配置CQ深度、及时轮询完成队列、正确管理QP状态,是保证InfiniBand应用性能与稳定性的核心技能。