3. DPU存储加速的软件栈总览

Marvell DPU(如 OCTEON 10 DPU 系列)在存储加速场景中并不是“一块网卡加几个核”那么简单。它要同时承担 NVMe-oF 前端目标(Target)、后端本地 NVMe SSD 访问、数据搬运与卸载、以及主机侧虚拟化与用户态存储框架的对接。理解这套系统,关键是把它看成分层软件栈:每一层解决不同问题,层与层之间通过明确的接口传递命令、数据与完成事件。只有把“谁负责什么”分清楚,排障时才能快速定位是链路、固件、驱动还是用户态框架的问题。

一、分层结构:从下到上的职责边界

从最底层往上,DPU 存储加速软件栈大致可分为五层:

  • 固件与硬件抽象层:运行在 DPU 内部管理核或专用引擎上,负责 PCIe 枚举、NVMe 控制器初始化、队列创建、DMA 引擎配置、网络 MAC/PCS 初始化,以及安全启动与固件升级。它向上暴露的是寄存器、邮箱(Mailbox)和固件命令接口,而不是 Linux 设备节点。
  • SDK 与卸载引擎层:Marvell 提供的 DPU SDK(含 OCTEON 10 的 DPDK、NIX/NPA/SSO 等硬件加速抽象)把加密、压缩、校验、DMA、正则匹配等能力封装成可调用的 API。NVMe-oF 路径中,TCP 校验和卸载、TLS/IPSEC 加解密、数据完整性校验通常在这一层完成。
  • 内核驱动与块层:DPU 上运行的 Linux 内核包含 NVMe 主机驱动、NVMe Target 驱动、网络驱动以及 DPU 特有的 PCIe EP 驱动。它负责把固件/SDK 的能力映射成标准内核对象,如 /dev/nvme0n1、nvmet 子系统、rdma_cm 连接等。
  • 用户态框架与存储服务:SPDK、DPDK、NVMe-oF Target 用户态实现、存储虚拟化层(如 vhost-user、virtio-blk)运行在这一层。它们绕过内核块层,直接轮询队列,降低延迟并提高 IOPS。
  • 管理与编排面:包括 DPU 的 BMC/管理网口、Redfish、CLI、Kubernetes CSI 插件等,负责配置下发、健康监控、固件升级和存储卷生命周期管理。

这五层不是简单的“上下堆叠”,而是存在交叉调用:用户态 SPDK 可以绕过内核驱动直接访问 DPU 的 DMA 引擎;内核 NVMe Target 也可以调用 SDK 的加密卸载接口。因此,理解每一层在 NVMe-oF 路径中的具体职责,比记住分层名称更重要。

DPU 存储加速软件栈分层与 NVMe-oF 路径职责 管理与编排面 Redfish / CLI / Kubernetes CSI / 固件升级 / 健康监控 / 卷生命周期 配置下发 · 监控 用户态框架与存储服务 SPDK / DPDK / NVMe-oF Target 用户态实现 / vhost-user / virtio-blk 职责:轮询队列、零拷贝数据面、QoS 限速、多租户隔离 数据面 · 低延迟 内核驱动与块层 NVMe Host 驱动 / NVMe Target 驱动 / 网络驱动 / PCIe EP 驱动 职责:设备枚举、队列映射、块设备注册、标准 ioctl/sysfs 接口 控制面 · 标准接口 SDK 与卸载引擎层 DPDK / NIX / NPA / SSO / 加密引擎 / 压缩引擎 / DMA 引擎 职责:TCP 校验和卸载、TLS/IPSEC 加解密、数据完整性校验、批量搬运 加速 · 卸载 固件与硬件抽象层 PCIe 枚举 / NVMe 控制器初始化 / 队列创建 / DMA 配置 / MAC-PCS 初始化 职责:安全启动、固件升级、寄存器与邮箱接口、硬件资源管理 硬件 · 初始化

二、NVMe-oF 路径中各层的实际职责

假设一台主机通过 NVMe-oF/TCP 访问 DPU 后端的本地 NVMe SSD,一次读命令的完整路径如下:

  1. 网络入口:主机发起 TCP 连接,DPU 网卡收到 NVMe-oF PDU。固件层完成 MAC/PCS 初始化,SDK 层通过 NIX 把报文分发到指定队列,并完成 TCP 校验和卸载。
  2. 协议解析:用户态 NVMe-oF Target(如 SPDK nvmf)从队列中取出 PDU,解析 NVMe 命令(如 Read 命令,LBA、长度、NSID)。此时不经过内核块层,避免上下文切换。
  3. 后端访问:Target 通过 SPDK 的 bdev 层访问本地 NVMe SSD。若 SSD 由内核驱动管理,则需通过 /dev/nvme0n1 或 io_uring 提交;若由 SPDK 接管,则直接操作 PCIe BAR 和队列。
  4. 数据搬运:SDK 的 DMA 引擎把 SSD 数据搬到 DPU 内存,再封装成 NVMe-oF 响应 PDU。若启用加密,加密引擎在搬运过程中完成 AES-XTS 加解密。
  5. 响应返回:用户态 Target 把响应 PDU 交给 SDK 发送队列,固件层通过 MAC 发出。主机侧收到完成事件。

可以看到,固件层负责“通不通”,SDK 层负责“快不快”,内核驱动负责“认不认”,用户态框架负责“稳不稳”,管理面负责“管不管”。任何一层出问题,现象都不同:固件问题表现为链路 down 或设备不识别;SDK 问题表现为吞吐上不去或校验错误;驱动问题表现为设备节点缺失;用户态问题表现为连接超时或队列卡死。

三、一个可参考的配置片段

以下示例展示在 DPU 上通过 SPDK 配置 NVMe-oF Target 的关键步骤,参数为教学示例,实际值需根据硬件和网络调整:

# 1. 绑定本地 NVMe SSD 到用户态驱动(示例 PCIe 地址)
dpdk-devbind.py -b vfio-pci 0000:01:00.0

# 2. 启动 SPDK NVMe-oF Target,使用 4 个核心,监听 192.168.10.1:4420
spdk/nvmf_tgt -m 0x0F -w 0x0F

# 3. 在 SPDK RPC 中创建传输层与子系统
rpc.py nvmf_create_transport -t TCP -u 8192 -p 4
rpc.py bdev_nvme_attach_controller -b Nvme0 -t pcie -a 0000:01:00.0
rpc.py nvmf_create_subsystem nqn.2024-06.io.marvell:dpunvme0 -a -s DPU0001
rpc.py nvmf_subsystem_add_ns nqn.2024-06.io.marvell:dpunvme0 Nvme0n1
rpc.py nvmf_subsystem_add_listener nqn.2024-06.io.marvell:dpunvme0 -t TCP -a 192.168.10.1 -s 4420

执行后,主机侧可用 nvme discover -t tcp -a 192.168.10.1 -s 4420 发现目标,再用 nvme connect 挂载。若发现失败,优先检查 DPU 固件是否已初始化网络端口、SDK 的 TCP 卸载是否使能、以及 SPDK 进程是否绑定到正确的核心和网卡队列。

四、分层排障的观察点

实际工程中,建议按以下顺序观察,避免盲目重启:

  • 固件层:dmesg | grep -i pcie 看链路速率与宽度;ethtool -S 看 MAC 统计是否有 CRC 错误。
  • SDK 层:dpdk-proc-info 查看 NIX/NPA 队列计数;openssl speed 对比加密卸载前后的 CPU 占用。
  • 内核驱动:lsblk、nvme list 确认设备节点;dmesg | grep nvme 看队列创建日志。
  • 用户态框架:SPDK 的 rpc.py nvmf_get_subsystems 查看子系统状态;perf 或 SPDK 自带统计看轮询核心利用率。
  • 管理面:Redfish 或 BMC 日志确认固件版本与告警。
分层理解的价值在于:当 NVMe-oF 性能不达预期时,先判断瓶颈在“网络卸载”还是“SSD 访问”还是“用户态轮询”。例如,若 TCP 校验和卸载未启用,CPU 会花大量周期在协议栈上,此时应检查 SDK 层配置,而不是盲目增加 SPDK 核心数。

总结来说,DPU 存储加速软件栈是一个“固件打底、SDK 加速、驱动对接、用户态提效、管理面兜底”的协作体系。每一层都有明确的输入输出和可观测指标,掌握这些边界,才能在 NVMe-oF 路径中快速定位问题并做出正确的调优决策。