4. NVMe-oF协议基础与DPU卸载切入点

NVMe-oF(NVMe over Fabrics)把本地 PCIe 上的 NVMe 命令语义搬到网络上,让主机像访问本地盘一样访问远端 SSD。理解它的关键不在于“把 NVMe 包一层网络”,而在于三件事:用哪条传输承载(TCP 还是 RoCEv2)、命令如何走完一次往返(命令流程)、队列如何映射到网络连接(队列机制)。DPU 的卸载价值,恰恰藏在这三件事的边界上。

一、两种承载:NVMe/TCP 与 NVMe/RoCEv2 的协议栈差异

NVMe-oF 定义了统一的 Fabrics 命令层(Capsule、Command、Response、Data),但底层传输可以不同。NVMe/TCP 把 Capsule 直接放进 TCP 字节流,依赖 TCP 的重传、拥塞控制和有序交付;NVMe/RoCEv2 则把 Capsule 封装进 RDMA 的 Send/Write/Read 语义,由 RoCEv2(UDP + IB BTH)承载,靠 PFC/ECN 做无损保障。

两者的栈结构差异决定了卸载切入点不同:

  • NVMe/TCP:NVMe → Fabrics Capsule → TCP → IP → Ethernet。主机侧需要处理 TCP 分段、重组、ACK、窗口与重传,CPU 开销集中在协议栈与内存拷贝。
  • NVMe/RoCEv2:NVMe → Fabrics Capsule → RDMA Send/Write/Read → RoCEv2(UDP+BTH) → Ethernet。主机侧需要处理 QP 状态、WQE 构造、CQ 轮询与内存注册(MR),开销集中在 verbs 路径与 doorbell。

一个常见的教学示例参数:MTU 1500、单次 I/O 4 KiB 随机读、队列深度 128。NVMe/TCP 下每个 I/O 至少涉及一次命令 Capsule 与一次数据回传,TCP 侧还要承担 ACK 与可能的合并;NVMe/RoCEv2 下数据可通过 RDMA Read 直接落到主机预注册缓冲区,减少一次拷贝。这里的数值仅为示例,用于说明路径差异,并非实测结果。

二、命令流程:一次 I/O 的完整往返

无论哪种承载,NVMe-oF 的命令流程都遵循“主机提交命令 → 目标执行 → 数据/状态回传”的骨架。以读 I/O 为例:

  1. 主机在提交队列(SQ)写入 NVMe Read 命令,更新 SQ Tail Doorbell。
  2. Fabrics 层把命令封装为 Capsule,经 TCP 或 RDMA Send 发往目标。
  3. 目标解析命令,从命名空间读取数据,按约定方式回传:TCP 下走独立数据流,RoCEv2 下可用 RDMA Write 或 Read Response。
  4. 目标写完成队列(CQ)条目,主机轮询 CQ 得到完成状态与 Phase Tag。

写 I/O 的差异在于数据方向:主机需先把数据送到目标缓冲区(TCP 内联或 RDMA Write),再发命令 Capsule,目标落盘后回完成。这个“数据先行还是命令先行”的顺序,是判断卸载点的重要线索。

NVMe-oF 读 I/O 命令流程与 DPU 卸载切入点 主机侧 (Host) 应用 / 文件系统 提交 4KiB 随机读 NVMe 驱动 SQ 写命令 + Doorbell Fabrics 层 Capsule 封装 传输层 TCP 或 RDMA verbs CQ 轮询 / Phase Tag 完成状态判读 网络承载 NVMe/TCP 路径 TCP 分段/重组 · ACK · 重传 Capsule 内联于字节流 NVMe/RoCEv2 路径 RDMA Send/Write/Read · QP PFC/ECN 无损保障 数据回传:TCP 数据流 或 RDMA Write/Read Response 队列映射:SQ/CQ ↔ 连接/QP 多队列并行,避免单连接瓶颈 目标侧 (Target) Fabrics 解析 Capsule 解封装 NVMe 子系统 命名空间 / 队列处理 SSD 访问 读取 4KiB 数据 数据回传 RDMA Write / TCP 发送 CQ 写完成条目 状态码 + Phase Tag DPU 卸载切入点:传输层 + Fabrics 层 + 队列管理

三、队列机制:SQ/CQ 如何映射到网络连接

NVMe 的队列模型是“多提交队列 + 多完成队列”,每个队列对(Queue Pair,此处指 NVMe 的 SQ/CQ 组合)独立工作。NVMe-oF 把这个模型映射到网络连接上:

  • NVMe/TCP:一个 TCP 连接可承载多个 NVMe 队列,通过 PDU 头中的 Queue ID 区分;也可以一个队列一个连接,换取更简单的流控与更低的队头阻塞。
  • NVMe/RoCEv2:每个 NVMe 队列通常绑定一个 RDMA QP,SQ 对应 Send Queue,CQ 对应 Completion Queue,命令与数据通过 WQE 描述。

队列深度(Queue Depth)与连接数的组合,直接决定并发能力。例如 4 个队列、每队列深度 128,理论可挂起 512 个未完成命令;若只用一个 TCP 连接承载全部队列,则 TCP 窗口与重传会成为瓶颈。这也是 DPU 卸载需要关注“队列到连接/QP 的映射策略”的原因。

四、DPU 可卸载的关键环节

把上面的流程拆开看,DPU 的卸载点可以落在四个位置:

  1. 传输层卸载:NVMe/TCP 的 TCP 分段、重组、ACK、重传、校验和计算;NVMe/RoCEv2 的 RoCEv2 封装、BTH 处理、PFC/ECN 响应。这些是纯协议处理,适合硬件或可编程流水线。
  2. Fabrics 层卸载:Capsule 的封装与解封装、PDU 头解析、命令与数据的关联。DPU 可在网卡侧完成 Capsule 组装,减少主机内存往返。
  3. 队列与连接管理:SQ/CQ 与 TCP 连接/QP 的映射、Doorbell 聚合、CQ 事件合并。DPU 可代主机维护队列状态,降低 doorbell 频率与中断次数。
  4. 数据搬运与内存注册:RDMA MR 管理、数据直接落盘或落内存、零拷贝路径。DPU 可承担数据缓冲区管理与地址翻译,避免主机侧重复拷贝。
判断一个环节是否值得卸载,可以问三个问题:它是否高频重复?它是否消耗主机 CPU 或内存带宽?它是否有明确的硬件友好接口(如描述符、队列、寄存器)?三者都满足时,DPU 卸载收益最大。

五、一个可参考的观察方法

在实验环境中,可以用 nvme-cli 连接远端目标,观察队列与连接状态:

nvme discover -t tcp -a 192.168.10.20 -s 4420
nvme connect -t tcp -n nqn.2024-01.com.example:subsys1 \
  -a 192.168.10.20 -s 4420
nvme list-subsys

执行后重点看 list-subsys 输出的传输类型、队列数量与状态。若把同样的目标换成 RoCEv2,传输类型变为 rdma,此时可用 ibv_devinfo 查看 QP 与端口状态。对比两种承载下 iostat -x 的 %system 与 await,可以直观看到协议栈开销的差异。这些命令与观察项属于通用方法,具体数值需以实际环境为准。

理解 NVMe-oF 的协议栈、命令流程与队列机制,是为了准确找到 DPU 的介入位置:传输层与 Fabrics 层是“协议搬运”,队列与内存管理是“状态与数据搬运”。把这两类工作从主机 CPU 上移走,才是 DPU 存储加速的真正起点。