RDMA 编程中,常用的通信操作有 Read/Write 和 Send/Recv。前者是单向操作,即发起端可以直接读写远端的内存,不需要远端的 CPU 参与;后者是双向操作,发起端 Send 需要搭配远端 Recv,类似 TCP 通信。Read/Write 使用上会更简单,但在发起操作前需要知道远端内存的地址,所以正常情况下无法单独使用,一般需要搭配 Send/Recv 实现的控制流,才能完成 Read/Write 实现的数据流。
所以实现高性能的 RDMA 通信仍需要先实现高性能的 Send/Recv。但与 TCP 通信不同的是,RDMA Send 一定需要远端已经发起了 Recv 操作,否则 Send 操作会直接失败。这就需要实现一套良好的收发控制策略,使得 Send 操作可以高效且安全地被提交。
整理 Send 相关的限制条件:
max_send_wr
,限制提交到该发送队列的 WR 的最大数量,WR 的类型包含 Send/Read/Write 操作max_recv_wr
,以及提交的未完成的 Recv 操作数量max_cqe
,如果完成的 WR 累积在完成队列中没有被消费也会引发错误限制条件简化:
max_cqe
= max_send_wr
+ max_recv_wr
,这样即使当前所有提交的任务都完成了,也不会使得完成队列溢出max_send_wr
进行容量上的拆分,分为 max_send_data_wr
、max_send_imm_wr
、max_read_wr
和 max_write_wr
,max_send_data_wr
用于发送数据,max_send_imm_wr
用于发送立即数,并且 max_recv_wr
= max_send_data_wr
+ max_send_imm_wr
,这样即使提交所有 Send 操作,也不会使得远端的接收队列溢出max_recv_wr
分析一下本地 Send、远端 Recv 的场景:
设计策略如下:
send_data_local_remain
和 send_imm_local_remain
,即本地限制下还可以提交多少个 Send(data) 和 Send(ack) 操作,初始值分别为 max_send_data_wr
和 max_send_imm_wr
,提交 WR 前消耗计数,对应的 WR 完成时恢复计数send_data_remote_remain
和 send_imm_remote_remain
,即远端限制下还可以提交多少个 Send(data) 和 Send(ack) 操作,初始值同样分别为 max_send_data_wr
和 max_send_imm_wr
,提交 WR 前消耗计数,直到收到 Ack 后恢复计数local_remain
和 remote_remain
是否还有余量,如果没有余量则暂存到队列中等待,直到 WR 完成或者收到 Ack 更新计数后再进行发送send_data_remote_remain += a
,send_imm_remote_remain += b
。阈值可以设定为初试容量的一半