DOCA SNAP-4 服务指南
本指南提供关于在 NVIDIA® BlueField®-3 DPU 上使用 DOCA SNAP-4 服务的说明。
NVIDIA® BlueField® SNAP 和 virtio-blk SNAP(存储定义网络加速处理)技术支持本地存储的硬件加速虚拟化。NVMe/virtio-blk SNAP 将网络存储呈现为本地块存储设备(例如,SSD),模拟 PCIe 总线上的本地驱动器。主机操作系统或虚拟机监控程序使用其标准存储驱动程序,不知道通信不是与物理驱动器进行的,而是与 NVMe/virtio-blk SNAP 框架进行的。在通过基于 Fabric 的网络将请求和/或数据重定向到远程或本地存储目标之前,任何逻辑都可以应用于 I/O 请求或通过 NVMe/virtio-blk SNAP 框架的数据。
NVMe/virtio-blk SNAP 基于 NVIDIA® BlueField® DPU 系列技术,并将独特的软件定义硬件加速存储虚拟化与 DPU 的高级网络和可编程功能相结合。NVMe/virtio-blk SNAP 与 BlueField DPU 一起,为解决存储和网络效率及性能的应用开辟了广阔前景。

从主机到达仿真 PCIe 设备的流量被重定向到在 mlnx_snap
服务上打开的匹配存储控制器。
控制器实现设备规范,并可能相应地公开后端设备(在本用例中,SPDK 用作公开后端设备的存储堆栈)。当收到命令时,控制器会执行它。
管理命令通常立即响应,而 I/O 命令则被重定向到后端设备进行处理。
请求处理管道是完全异步的,并且工作负载分布在所有 Arm 核心(分配给 SPDK 应用程序)上,以实现最佳性能。
以下是 SNAP 的关键概念
在 Fabric/传输/协议方面具有完全的灵活性(例如 NVMe-oF/iSCSI/其他、RDMA/TCP、ETH/IB)
NVMe 和 virtio-blk 仿真支持
可编程性
轻松的数据操作
允许从远程存储到主机的零拷贝 DMA
使用 Arm 核心进行数据路径
用于 NVIDIA® BlueField®-2 DPU 的 BlueField SNAP 是许可软件。用户必须为每个 BlueField-2 DPU 购买许可证才能使用它们。
NVIDIA® BlueField®-3 DPU 运行 BlueField SNAP 没有许可证要求。
SNAP 作为容器
在这种方法中,可以从 NVIDIA NGC 下载容器,并可以轻松地将其部署在 DPU 上。
yaml 文件包含与最新 spdk.nvda
版本对齐的 SNAP 二进制文件。在这种情况下,SNAP 源代码不可用,并且无法修改 SNAP 以支持不同的 SPDK 版本(SNAP 作为 SDK 软件包应用于此)。
SNAP 4.x 未预安装在 BFB 上,但可以根据需要手动下载。
有关如何安装 SNAP 容器的说明,请参阅“SNAP 容器部署”。
SNAP 作为软件包
SNAP 开发包(自定义)旨在为希望根据自己的环境自定义 SNAP 服务的用户提供,通常用于使用专有的 bdev 而不是 spdk.nvda
版本。这允许用户完全访问服务代码和 lib 标头,从而能够编译他们的更改。
SNAP 仿真库
这包括协议库以及与固件/硬件 (PRM) 的交互,以及
普通共享对象 (
*.so
)静态存档 (
*.a
)pkgconfig 定义 (
*.pc
)包含文件 (
*.h
)
SNAP 服务源
这包括以下管理器
仿真设备管理器
仿真管理器 – 管理设备仿真、功能发现和功能事件
热插拔管理器 – 管理设备仿真热插拔和热拔出
配置管理器 – 处理通用配置和 RCP(非协议特定的)
服务基础设施管理器
内存管理器 – 处理 SNAP 内存池,当主机和远程目标之间不使用零拷贝时,该内存池用于复制到 Arm 内存中
线程管理器 – 处理 SPDK 线程
协议特定的控制路径管理器
NVMe 管理器 – 处理 NVMe 子系统、NVMe 控制器和命名空间功能
VBLK 管理器 – 处理 virtio-blk 控制器功能
IO 管理器
实现常规和优化流程的 IO 路径(RDMA ZC 和 TCP XLIO ZC)
处理 bdev 创建和功能
SNAP 服务依赖项
SNAP 服务依赖于以下库
SPDK – 依赖于 bdev 和 SPDK 资源,例如 SPDK 线程、SPDK 内存和 SPDK RPC 服务
XLIO(用于 NVMeTCP 加速)
SNAP 服务流程

IO 流程
RDMA 零拷贝读/写 IO 流程示例

RDMA 非零拷贝读取 IO 流程示例

数据路径提供程序
SNAP 方便用户配置提供程序,以协助从主机卸载数据路径应用程序。这些包括设备仿真、IO 密集型操作和 DMA 操作。
DPA 提供程序 – DPA(数据路径加速器)是嵌入在 BlueField 中的多核和多执行单元 RISC-V 处理器的集群
DPU 提供程序 – 使用 BlueField CPU 处理来自主机的数据路径应用程序。此模式可改善 IO 延迟并减少崩溃恢复期间的 SNAP 停机时间。
对于 NVMe 和 virtio-blk,DPA 是 SNAP 中的默认提供程序。
virtio-blk 仅支持 DPU 模式。要设置 DPU 模式,请使用环境变量 VIRTIO_EMU_PROVIDER=dpu
修改 YAML 上的变量。有关更多信息,请参阅“SNAP 环境变量”页面。
本节介绍如何将 SNAP 部署为容器。
SNAP 未预装在 BFB 中。
在 DPU 上安装完整 DOCA 镜像
要安装 NVIDIA® BlueField®-3 BFB
[host] sudo bfb-install --rshim <rshimN> --bfb <image_path.bfb>
有关更多信息,请参阅 Linux 版 DOCA 安装指南 中的“在 DPU 上安装完整 DOCA 镜像”部分。
固件安装
[dpu] sudo /opt/mellanox/mlnx-fw-updater/mlnx_fw_updater.pl --force-fw-update
有关更多信息,请参阅 Linux 版 DOCA 安装指南 中的“升级固件”部分。
固件配置
固件配置可能会公开新的仿真 PCI 功能,这些功能稍后可供主机操作系统使用。因此,用户必须确保所有公开的 PCI 功能(静态/热插拔 PF、VF)都由支持的 SNAP SW 配置支持,否则这些功能将保持故障状态,并且主机行为将是未定义的。
在实施所需配置之前,清除固件配置
[dpu] mst start [dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 reset
查看固件配置
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 query
输出示例
mlxconfig -d /dev/mst/mt41692_pciconf0 -e query | grep NVME Configurations: Default Current Next Boot * NVME_EMULATION_ENABLE False(0) True(1) True(1) * NVME_EMULATION_NUM_VF 0 125 125 * NVME_EMULATION_NUM_PF 1 2 2 NVME_EMULATION_VENDOR_ID 5555 5555 5555 NVME_EMULATION_DEVICE_ID 24577 24577 24577 NVME_EMULATION_CLASS_CODE 67586 67586 67586 NVME_EMULATION_REVISION_ID 0 0 0 NVME_EMULATION_SUBSYSTEM_VENDOR_ID 0 0 0
其中输出提供 5 列
非默认配置标记 (*)
固件配置名称
默认固件值
当前固件值
重启后的固件值 – 显示等待系统重启的配置更新
要启用存储仿真选项,必须将第一个 DPU 设置为在内部 CPU 模型中工作
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s INTERNAL_CPU_MODEL=1
要启用具有 virtio-blk 仿真 PF 的固件配置
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s VIRTIO_BLK_EMULATION_ENABLE=1 VIRTIO_BLK_EMULATION_NUM_PF=1
要启用具有 NVMe 仿真 PF 的固件配置
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s NVME_EMULATION_ENABLE=1 NVME_EMULATION_NUM_PF=1
有关 SNAP 固件配置选项的完整列表,请参阅附录“DPU 固件配置”。
需要断电重启才能应用固件配置更改。
RDMA/RoCE 固件配置
BlueField OS 的默认接口(名为 ECPF,通常为 mlx5_0
和 mlx5_1
)阻止 RoCE 通信。如果需要 RoCE 流量,则必须添加额外的网络功能,即可扩展功能 (SF),这些功能支持 RoCE 传输。
要启用 RDMA/RoCE
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s PER_PF_NUM_SF=1
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s PF_SF_BAR_SIZE=8 PF_TOTAL_SF=2
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0.1 s PF_SF_BAR_SIZE=8 PF_TOTAL_SF=2
通过 TCP 或 InfiniBand 上的 RDMA 工作时,不需要这样做。
SR-IOV 固件配置
SNAP 在 NVMe 上最多支持 512 个 VF,在 virtio-blk 上最多支持 2000 个 VF。VF 可以分布在最多 4 个 virtio-blk PF 或 2 个 NVMe PF 之间。
以下示例仅供参考。有关参数范围的完整详细信息,请参阅附录“DPU 固件配置”。
常见示例
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s SRIOV_EN=1 PER_PF_NUM_SF=1 LINK_TYPE_P1=2 LINK_TYPE_P2=2 PF_TOTAL_SF=1 PF_SF_BAR_SIZE=8 TX_SCHEDULER_BURST=15
注意当使用 64KB 页大小的操作系统时,应配置
PF_SF_BAR_SIZE=10
(而不是 8)。Virtio-blk 250 个 VF 示例(每个 VF 2 个队列)
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s VIRTIO_BLK_EMULATION_ENABLE=1 VIRTIO_BLK_EMULATION_NUM_VF=125 VIRTIO_BLK_EMULATION_NUM_PF=2 VIRTIO_BLK_EMULATION_NUM_MSIX=2 VIRTIO_BLK_EMULATION_NUM_VF_MSIX=2
Virtio-blk 1000 个 VF 示例(每个 VF 2 个队列)
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s VIRTIO_BLK_EMULATION_ENABLE=1 VIRTIO_BLK_EMULATION_NUM_VF=250 VIRTIO_BLK_EMULATION_NUM_PF=4 VIRTIO_BLK_EMULATION_NUM_MSIX=2 VIRTIO_NET_EMULATION_ENABLE=0 NUM_OF_VFS=0 PCI_SWITCH_EMULATION_ENABLE=0 VIRTIO_BLK_EMULATION_NUM_VF_MSIX=2
NVMe 250 个 VF 示例(每个 VF 2 个 IO 队列)
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s NVME_EMULATION_ENABLE=1 NVME_EMULATION_NUM_VF=125 NVME_EMULATION_NUM_PF=2 NVME_EMULATION_NUM_MSIX=2 NVME_EMULATION_NUM_VF_MSIX=2
热插拔固件配置
一旦启用 PCIe 交换机仿真,BlueField 最多可以支持 31 个热插拔 NVMe/Virtio-blk 功能。“PCI_SWITCH_EMULATION_NUM_PORT
-1”个热插拔 PCIe 功能。这些插槽在所有 DPU 用户和应用程序之间共享,并且可以容纳 NVMe、virtio-blk、virtio-fs 或其他类型(例如,virtio-net)的热插拔设备。
要启用 PCIe 交换机仿真并确定要使用的热插拔端口数
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s PCI_SWITCH_EMULATION_ENABLE=1 PCI_SWITCH_EMULATION_NUM_PORT=32
PCI_SWITCH_EMULATION_NUM_PORT
等于 1 + 热插拔 PCIe 功能的数量。
有关热插拔设备的更多信息,请参阅“热插拔 PCIe 功能管理”部分。
不能保证热插拔在 AMD 机器上工作。
启用PCI_SWITCH_EMULATION_ENABLE可能会影响 Intel 和 AMD 机器上的 SR-IOV 功能。
目前,热插拔 PF 不支持 SR-IOV。
UEFI 固件配置
要使用存储仿真作为启动设备,建议使用 DPU 嵌入的 UEFI 扩展 ROM 驱动程序供 UEFI 使用,而不是原始供应商的 BIOS 驱动程序。
要启用 UEFI 驱动程序
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s EXP_ROM_VIRTIO_BLK_UEFI_x86_ENABLE=1 EXP_ROM_NVME_UEFI_x86_ENABLE=1
DPU 配置
DPA 核心掩码
数据路径加速器 (DPA) 是一个包含 16 个核心的集群,每个核心有 16 个执行单元 (EU)。
只有 EU 0-170 可用于 SNAP。
SNAP 支持为 NVMe 或 virtio-blk 控制器预留 DPA EU。默认情况下,所有可用的 EU 0-170 在系统上的 NVMe、virtio-blk 和其他 DPA 应用程序(例如,virtio-net)之间共享。
要分配特定的 EU 集,请设置以下环境变量
对于 NVMe
dpa_nvme_core_mask=0x<EU_mask>
对于 virtio-blk
dpa_helper_core_mask=0x<EU_mask>
核心掩码必须包含有效的十六进制数字(从右向左解析)。例如,dpa_virtq_split_core_mask=0xff00
设置 8 个 EU(即 EU 8-16)。
每个 DPA EU 的硬件限制为 128 个队列(线程)。
SNAP 容器部署
SNAP 容器在 DOCA SNAP NVIDIA NGC 目录页面上可用。
在 BlueField DPU 之上部署 SNAP 容器需要以下步骤
容器部署的设置准备和 SNAP 资源下载。有关详细信息,请参阅“准备步骤”部分。
如果需要高级配置,请根据“调整 YAML 配置”部分调整
doca_snap.yaml
。部署容器。镜像会自动从 NGC 中拉取。有关详细信息,请参阅“生成 SNAP 容器”部分。
以下是 SNAP 容器设置的示例。

准备步骤
步骤 1:分配 Hugepages
通用
根据 DPU OS 的 Hugepagesize
值,为 SNAP 容器分配 4GiB
hugepages
查询
Hugepagesize
值[dpu] grep Hugepagesize /proc/meminfo
在 Ubuntu 中,该值应为 2048KB。在 CentOS 8.x 中,该值应为 524288KB。
将以下行附加到
/etc/sysctl.conf
文件的末尾对于 Ubuntu 或 CentOS 7.x 设置(即,
Hugepagesize
= 2048 kB)vm.nr_hugepages = 2048
对于 CentOS 8.x 设置(即,
Hugepagesize
= 524288 kB)vm.nr_hugepages = 8
运行以下命令
[dpu] sysctl --system
如果在此部署中使用了实时升级,则有必要为升级后的容器分配两倍于上面列出的资源量。
如果其他应用程序在设置中同时运行并消耗 hugepages,请确保为这些应用程序分配超出本节描述数量的额外 hugepages。
当以高连接规模(即,500 个或更多磁盘)部署 SNAP 时,hugepages 的默认分配 (4GiB) 变得不足。可以通过 SNAP 和 SPDK 层中的错误消息来识别这种 hugepages 短缺。这些错误消息通常指示在创建或修改 QP 或其他对象时发生故障。
步骤 2:创建 nvda_snap 文件夹
容器使用 /etc/nvda_snap
文件夹在部署后进行自动配置。
下载 YAML 配置
SNAP 容器的 .yaml
文件配置为 doca_snap.yaml
。.yaml
文件的下载命令可以在 DOCA SNAP NGC 页面上找到。
下载 SNAP 资源需要互联网连接。要在没有互联网连接的 DPU 上部署容器,请参阅附录“在没有互联网连接的设置上部署容器”。
调整 YAML 配置
可以轻松编辑 .yaml
文件以进行高级配置。
SNAP
.yaml
文件默认配置为通过使用 hugepages-2Mi 来支持 Ubuntu 设置(即,Hugepagesize
= 2048 kB)。要支持其他设置,请根据 DPU OS 的相关
Hugepagesize
值编辑 hugepages 部分。例如,要支持 CentOS 8.x,请将Hugepagesize
配置为 512MBlimits: hugepages-512Mi: "<number-of-hugepages>Gi"
注意当部署具有大量控制器(500 个或更多)的 SNAP 时,hugepages 的默认分配 (4GB) 可能会变得不足。可以通过错误消息来识别这种 hugepages 短缺,这些错误消息通常指示在创建或修改 QP 或其他对象时发生故障。在这些情况下,需要更多 hugepages。
以下示例编辑
.yaml
文件,为 SNAP 容器请求 16 个 CPU 核心和 4Gi 内存以及 4Gi hugepagesresources: requests: memory: "2Gi" hugepages-2Mi: "4Gi" cpu: "8" limits: memory: "4Gi" hugepages-2Mi: "4Gi" cpu: "16" env: - name: APP_ARGS value: "-m 0xffff"
注意如果请求了所有 BlueField-3 核心,则用户必须验证没有其他容器在 CPU 资源上发生冲突。
注意当使用大量核心运行 Virtio-fs 服务时,有必要增加 SPDK 中 IO 缓冲区的数量。例如,要使用 16 个核心运行,大型 IO 缓冲池的大小必须设置为至少 4095。这可以通过将 RPC 命令
iobuf_set_options --large-pool-count 4095
添加到/etc/nvda_snap
下的spdk_rpc_init.conf
来配置。根据使用的规模和 SPDK 子系统,可能需要调整其他 SPDK 配置参数。有关更多详细信息,请参阅 SPDK 文档。要在部署时自动配置 SNAP 容器
在
/etc/nvda_snap/
下添加spdk_rpc_init.conf
文件。文件示例bdev_malloc_create 64 512
在
/etc/nvda_snap
/
下添加snap_rpc_init.conf
文件。Virtio-blk 文件示例
virtio_blk_controller_create --pf_id 0 --bdev Malloc0
NVMe 文件示例
nvme_subsystem_create --nqn nqn.2022-10.io.nvda.nvme:0 nvme_namespace_create -b Malloc0 -n 1 --nqn nqn.2022-10.io.nvda.nvme:0 --uuid 16dab065-ddc9-8a7a-108e-9a489254a839 nvme_controller_create --nqn nqn.2022-10.io.nvda.nvme:0 --ctrl NVMeCtrl1 --pf_id 0 --suspended nvme_controller_attach_ns -c NVMeCtrl1 -n 1 nvme_controller_resume -c NVMeCtrl1
相应地编辑
.yaml
文件(取消注释)env: - name: SPDK_RPC_INIT_CONF value: "/etc/nvda_snap/spdk_rpc_init.conf" - name: SNAP_RPC_INIT_CONF value: "/etc/nvda_snap/snap_rpc_init.conf"
注意用户有责任确保 SNAP 配置与固件配置匹配。也就是说,必须在所有现有(静态/热插拔)仿真 PCIe 功能上打开仿真控制器(通过自动或手动配置)。没有支持控制器的 PCIe 功能被认为是故障的,并且主机行为是异常的。
生成 SNAP 容器
运行 Kubernetes 工具
[dpu] systemctl restart containerd
[dpu] systemctl restart kubelet
[dpu] systemctl enable kubelet
[dpu] systemctl enable containerd
将更新后的 doca_snap.yaml
文件复制到 /etc/kubelet.d
目录。
Kubelet 自动从 YAML 文件中描述的 NGC 拉取容器镜像,并生成一个执行容器的 pod。
cp doca_snap.yaml /etc/kubelet.d/
SNAP 服务立即开始初始化,这可能需要几秒钟。要验证 SNAP 是否正在运行
在日志中查找消息“SNAP 服务成功运行”
发送
spdk_rpc.py spdk_get_version
以确认 SNAP 是否正在运行或仍在初始化
调试和日志
查看当前活动的 pod 及其 ID(pod 可能需要长达 20 秒才能启动)
crictl pods
输出示例
POD ID CREATED STATE NAME
0379ac2c4f34c About a minute ago Ready snap
查看当前活动的容器及其 ID
crictl ps
查看现有容器及其 ID
crictl ps -a
检查给定容器的日志(SNAP 日志)
crictl logs <container_id>
如果某些内容未按预期工作,请检查 kubelet 日志
journalctl -u kubelet
容器日志文件由 Kubelet 自动保存在 /var/log/containers
下。
有关更多日志记录信息,请参阅“RPC 日志历史”部分。
停止、启动、重启 SNAP 容器
SNAP 二进制文件在 Docker 容器中部署为 SNAP 服务,该服务作为 supervisorctl 服务进行管理。Supervisorctl 为各种部署选项提供控制和配置层。
如果 SNAP 崩溃或重启,supervisorctl 会检测到该操作并等待退出的进程释放其资源。然后,它在同一容器内部署一个新的 SNAP 进程,该进程启动恢复流程以替换终止的进程。
如果容器崩溃或重启,kubeletclt 会检测到该操作并等待退出的容器释放其资源。然后,它部署一个带有新 SNAP 进程的新容器,该进程启动恢复流程以替换终止的进程。
在容器崩溃或退出后,kubelet 会以指数退避延迟(10 秒、20 秒、40 秒等)重启它们,延迟上限为五分钟。一旦容器在没有问题的情况下运行了 10 分钟,kubelet 就会重置该容器的重启退避计时器。在不重启容器的情况下重启 SNAP 服务有助于避免发生退避延迟。
不同的 SNAP 终止选项
容器终止
要终止容器,请从
/etc/kubelet.d/
中删除.yaml
文件。要启动容器,请将.yaml
文件cp
回到同一路径cp doca_snap.yaml /etc/kubelet.d/
要使用
crictl
重启容器(使用 sig-term),请使用-t
(超时)选项crictl stop -t 10 <container-id>
SNAP 进程终止
要在不重启容器的情况下重启 SNAP 服务,请终止 DPU 上的 SNAP 服务进程。不同的信号可用于不同的终止选项。例如
pkill -9 -f snap
SNAP 服务终止可能需要时间,因为它会释放所有已分配的资源。持续时间取决于用例的规模以及任何其他与 SNAP 共享资源的应用程序。
SNAP 源码包部署
系统准备
根据 DPU OS 的 Hugepagesize
值,为 SNAP 容器分配 4GiB
hugepages
查询
Hugepagesize
值[dpu] grep Hugepagesize /proc/meminfo
在 Ubuntu 中,该值应为 2048KB。在 CentOS 8.x 中,该值应为 524288KB。
将以下行附加到
/etc/sysctl.conf
文件的末尾对于 Ubuntu 或 CentOS 7.x 设置(即,
Hugepagesize
= 2048 kB)vm.nr_hugepages = 2048
对于 CentOS 8.x 设置(即,
Hugepagesize
= 524288 kB)vm.nr_hugepages = 8
运行以下命令
[dpu] sysctl --system
如果在此部署中使用了实时升级,则有必要为升级后的容器分配两倍于上面列出的资源量。
如果其他应用程序在设置中同时运行并消耗 hugepages,请确保为这些应用程序分配超出本节描述数量的额外 hugepages。
当以高连接规模(即,500 个或更多磁盘)部署 SNAP 时,hugepages 的默认分配 (4GiB) 变得不足。可以通过 SNAP 和 SPDK 层中的错误消息来识别这种 hugepages 短缺。这些错误消息通常指示在创建或修改 QP 或其他对象时发生故障。
安装 SNAP 源码包
安装软件包
对于 Ubuntu,运行
dpkg -i snap-sources_<version>_arm64.*
对于 CentOS,运行
rpm -i snap-sources_<version>_arm64.*
构建、编译和安装源码
要使用自定义 SPDK 构建 SNAP,请参阅“替换 BFB SPDK”部分。
移动到源码文件夹。运行
cd /opt/nvidia/nvda_snap/src/
构建源码。运行
meson /tmp/build
编译源码。运行
meson compile -C /tmp/build
安装源码。运行
meson install -C /tmp/build
配置 SNAP 环境变量
要配置 SNAP 的环境变量,请运行
source /opt/nvidia/nvda_snap/src/scripts/set_environment_variables.sh
运行 SNAP 服务
/opt/nvidia/nvda_snap/bin/snap_service
替换 BFB SPDK(可选)
首先安装 SPDK。
对于旧版 SPDK 版本(例如,SPDK 19.04),请参阅附录“安装旧版 SPDK”。
要使用自定义 SPDK 构建 SNAP,而不是按照基本构建步骤,请执行以下操作
移动到源码文件夹。运行
cd /opt/nvidia/nvda_snap/src/
构建启用 spdk-compat 的源码,并提供自定义 SPDK 的路径。运行
meson setup /tmp/build -Denable-spdk-compat=true -Dsnap_spdk_prefix=</path/to/custom/spdk>
编译源码。运行
meson compile -C /tmp/build
安装源码。运行
meson install -C /tmp/build
配置 SNAP 环境变量并运行 SNAP 服务,如“配置 SNAP 环境变量”和“运行 SNAP 服务”部分中所述。
启用调试打印进行构建(可选)
而不是基本构建步骤,请执行以下操作
移动到源码文件夹。运行
cd /opt/nvidia/nvda_snap/src/
使用
buildtype=debug
构建源码。运行meson --buildtype=debug /tmp/build
编译源码。运行
meson compile -C /tmp/build
安装源码。运行
meson install -C /tmp/build
配置 SNAP 环境变量并运行 SNAP 服务,如“配置 SNAP 环境变量”和“运行 SNAP 服务”部分中所述。
自动化 SNAP 配置(可选)
脚本 run_snap.sh
自动化 SNAP 部署。用户必须修改以下文件以与其设置对齐。如果用户使用了不同的目录,则必须相应地对 run_snap.sh
进行编辑
在以下位置编辑 SNAP 环境变量
/opt/nvidia/nvda_snap/bin/set_environment_variables.sh
编辑 SPDK 初始化 RPC 调用
/opt/nvidia/nvda_snap/bin/spdk_rpc_init.conf
编辑 SNAP 初始化 RPC 调用
/opt/nvidia/nvda_snap/bin/snap_rpc_init.conf
运行脚本
/opt/nvidia/nvda_snap/bin/run_snap.sh
支持的环境变量
名称 | 描述 | 默认值 |
| 启用/禁用 RDMA 零拷贝传输类型。 有关更多信息,请参阅“零拷贝 (SNAP-direct)”部分。 | 1(已启用) |
| 建议从同一远程目标发现的命名空间不要由不同的 PCIe 仿真共享。如果希望这样做,用户应将变量 警告
通过这样做,用户必须确保 SPDK bdev 始终在合理的时间内完成 IO(无论是成功还是失败)。否则,系统可能会停顿,直到所有 IO 返回。 | 1(已启用) |
| 启用/禁用使用共享内存文件的 virtio-blk 恢复。这允许在不使用 | 1(已启用) |
| 配置为具有仿真管理功能的 RDMA 设备的名称。 如果未定义变量(默认),SNAP 将搜索所有可用设备以查找仿真管理器(这可能会减慢初始化过程)。除非另行配置,否则 SNAP 选择第一个 ECPF(即“mlx5_0”)作为仿真管理器。 | NULL(未配置) |
YAML 配置
要更改 SNAP 环境变量,请将以下内容添加到 doca_snap.yaml
并从“调整 YAML 配置”部分继续。
env:
- name: VARIABLE_NAME
value: "VALUE"
例如
env:
- name: SNAP_RDMA_ZCOPY_ENABLE
value: "1"
源码包配置
要更改 SNAP 环境变量
在
scripts/set_environment_variables.sh
下添加/修改配置。重新运行
source scripts/set_environment_variables.sh
重新运行 SNAP。
远程过程调用 (RPC) 协议用于控制 SNAP 服务。NVMe/virtio-blk SNAP 与其他标准 SPDK 应用程序一样,支持基于 JSON 的 RPC 协议命令,以控制任何资源并轻松地从 CLI 创建、删除、查询或修改命令。
除了扩展的 SNAP 特定命令集外,SNAP 还支持所有标准 SPDK RPC 命令。SPDK 标准命令由 spdk_rpc.py
工具执行,而 SNAP 特定命令集扩展由 snap_rpc.py
工具执行。
完整的 spdk_rpc.py
命令集文档可以在 SPDK 官方文档站点中找到。
完整的 snap_rpc.py
扩展命令在本章后面会详细介绍。
使用基于 JSON 的 RPC 协议
基于 JSON 的 RPC 协议可以通过 SNAP 容器内的 snap_rpc.py
脚本和 crictl
工具使用。
SNAP 容器与 CRI 兼容。
要查询活动的容器 ID
crictl ps -s running -q --name snap
要使用
crictl
将 RPC 发布到容器crictl exec <container-id> snap_rpc.py <RPC-method>
例如
crictl exec 0379ac2c4f34c snap_rpc.py emulation_function_list
此外,可以使用别名
alias snap_rpc.py="crictl ps -s running -q --name snap | xargs -I{} crictl exec -i {} snap_rpc.py " alias spdk_rpc.py="crictl ps -s running -q --name snap | xargs -I{} crictl exec -i {} spdk_rpc.py "
要打开容器的 bash shell,可用于发布 RPC
crictl exec -it <container-id> bash
日志管理
snap_log_level_set
SNAP 允许使用 snap_log_level_set
动态更改记录器后端的日志级别。将显示请求级别下的任何日志。
参数 | 必填? | 类型 | 描述 |
| 是 | 数字 | 日志级别
|
PCIe 功能管理
仿真的 PCIe 功能通过称为仿真管理器的 IB 设备进行管理。仿真管理器是普通的 IB 设备,具有控制 PCIe 通信和面向主机操作系统的设备仿真的特殊权限。
SNAP 查询支持请求的功能集的仿真管理器。
仿真管理器保存其控制的仿真 PCIe 功能的列表。PCIe 功能稍后可以通过 3 种方式访问
vuid
– 建议使用,因为它保证保持不变(有关详细信息,请参阅附录“PCIe BDF 到 VUID 转换”)vhca_id
功能索引(即,
pf_id
或vf_id
)
emulation_function_list
emulation_function_list
列出所有现有功能。
以下是 emulation_function_list
命令的示例响应
[
{
"hotplugged": true,
"hotplug state": "POWER_ON",
"emulation_type": "VBLK",
"pf_index": 0,
"pci_bdf": "87:00.0",
"vhca_id": 5,
"vuid": "MT2306XZ009TVBLKS1D0F0",
"ctrl_id": "VblkCtrl1",
"num_vfs": 0,
"vfs": []
}
]
使用 -a
或 --all
,以显示所有非活动 VF 功能。
SNAP 支持 2 种类型的 PCIe 功能
静态功能 – 在固件配置阶段配置的 PCIe 功能(物理和虚拟)。有关更多信息,请参阅附录“DPU 固件配置”。
热插拔功能 – 在运行时动态配置的 PCIe 功能。用户可以添加可分离的功能。有关更多信息,请参阅“热插拔 PCIe 功能管理”部分。
热插拔 PCIe 功能管理
热插拔 PCIe 功能在运行时使用 RPC 动态配置。一旦新的 PCIe 功能被热插拔,它将出现在主机的 PCIe 设备列表中,并保持持久存在,直到被显式拔出或系统进行冷重启。重要的是,即使 SNAP 进程终止,这种持久性仍然存在。因此,建议不要在自动初始化脚本(例如,snap_rpc_init.conf
)中包含热插拔/热拔出操作。
热插拔 PF 不支持 SR-IOV。
两步 PCIe 热插拔
以下 RPC 命令用于在 DPU 应用程序中动态添加或删除 PCIe PF(即热插拔功能)。
一旦 PCIe 功能被创建(通过 virtio_blk_function_create
),它在 DPU 应用程序中是可访问和可管理的,但不会立即对主机 OS/内核可见。这与传统的 API 不同,在传统 API 中,创建和主机暴露是同时发生的。相反,向主机 OS 暴露或隐藏 PCIe 功能由单独的 RPC 命令(virtio_blk_controller_hotplug
和 virtio_blk_controller_hotunplug
)管理。热拔出后,可以安全地从 DPU 中移除该功能(使用 virtio_blk_function_destroy
)。
这种方法的一个主要优点是能够在功能上预配置控制器,使其在暴露给主机驱动程序后立即为其服务。事实上,用户必须创建一个控制器才能使用 virtio_blk_controller_hotplug
API,这是使功能对主机 OS 可见所必需的。
命令 | 描述 |
创建一个新的 virtio-blk 模拟功能 | |
向主机 OS 暴露(热插拔)模拟功能 | |
从主机 OS 中移除(热拔出)模拟功能 | |
删除现有的 virtio-blk 模拟功能 |
virtio_blk_function_create
创建一个新的 virtio-blk 模拟功能。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 字符串 | 用于管理热插拔功能的模拟管理器(未使用) |
virtio_blk_function_destroy
删除现有的 virtio-blk 模拟功能。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 要删除的热插拔功能的标识符 |
virtio_blk_controller_hotplug
向主机 OS 暴露(热插拔)模拟功能。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 要暴露给主机 OS 的控制器 |
| 否 | 布尔值 | 阻塞直到主机发现并确认新命令 |
| 否 | 整数 | 放弃等待的时间(毫秒)。仅当使用 |
virtio_blk_controller_hotunplug
从主机 OS 中移除(热拔出)模拟功能。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 要暴露给主机 OS 的控制器 |
| 否 | 布尔值 | 阻塞直到主机识别并移除该功能 |
非传统 API 尚不支持 NVMe 协议。
当不使用 wait_for_done
方法时,用户有责任验证主机是否识别出新的热插拔功能。这可以通过查询 emulation_function_list
RPC 输出中的 pci_hotplug_state
参数来完成。
两步 PCIe 热插拔/热拔出示例
# Bringup
spdk_rpc.py bdev_nvme_attach_controller -b nvme0 -t rdma -a 1.1.1.1 -f ipv4 -s 4420 -n nqn.2022-10.io.nvda.nvme:swx-storage
snap_rpc.py virtio_blk_function_create
snap_rpc.py virtio_blk_controller_create --vuid MT2114X12200VBLKS1D0F0 --bdev nvme0n1
snap_rpc.py virtio_blk_controller_hotplug -c VblkCtrl1
# Cleanup
snap_rpc.py virtio_blk_controller_hotunplug -c VblkCtrl1
snap_rpc.py virtio_blk_controller_destroy -c VblkCtrl1
snap_rpc.py virtio_blk_function_destroy --vuid MT2114X12200VBLKS1D0F0
spdk_rpc.py bdev_nvme_detach_controller nvme0
(已弃用)旧版 API
热插拔传统命令
以下命令将新的 PCIe 功能热插拔到系统中。
在新的 PCIe 功能插入后,它会立即显示在主机的 PCIe 设备列表中,直到它被显式拔出或系统进行冷重启。因此,用户有责任在功能创建后立即打开一个控制器实例来管理新功能。保持热插拔功能而没有匹配的控制器来管理可能会导致主机 OS 驱动程序出现异常行为。
命令 | 描述 |
附加 virtio-blk 模拟功能 | |
附加 NVMe 模拟功能 |
virtio_blk_emulation_device_attach
附加 virtio-blk 模拟功能。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 数字 | 设备 ID |
| 否 | 数字 | 供应商 ID |
| 否 | 数字 | 子系统设备 ID |
| 否 | 数字 | 子系统供应商 ID |
| 否 | 数字 | 修订 ID |
| 否 | 数字 | 类代码 |
| 否 | 数字 | MSI-X 表大小 |
| 否 | 数字 | 允许的最大 VF 数量 |
| 否 | 字符串 | 用作后端的块设备 |
| 否 | 数字 | IO 队列的数量(默认 1,范围 1-62)。 注意
实际队列数受硬件支持的队列数限制。
提示
建议 MSIX 的数量大于 IO 队列的数量(1 个用于配置中断)。 |
| 否 | 数字 | 队列深度(默认 256,范围 1-256) 注意
只有在驱动程序未加载时才能修改队列深度。 |
| 否 | 布尔值 | 过渡设备支持。有关更多详细信息,请参阅“Virtio-blk 过渡设备支持”部分。 |
| 否 | 布尔值 | 不适用 – 不支持 |
nvme_emulation_device_attach
附加 NVMe 模拟功能。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 数字 | 设备 ID |
| 否 | 数字 | 供应商 ID |
| 否 | 数字 | 子系统设备 ID |
| 否 | 数字 | 子系统供应商 ID |
| 否 | 数字 | 修订 ID |
| 否 | 数字 | 类代码 |
| 否 | 数字 | MSI-X 表大小 |
| 否 | 数字 | 允许的最大 VF 数量 |
| 否 | 数字 | IO 队列的数量(默认 31,范围 1-31)。 注意
实际队列数受硬件支持的队列数限制。
提示
建议 MSIX 的数量大于 IO 队列的数量(1 个用于配置中断)。 |
| 否 | 字符串 | 规范版本(目前仅支持 |
热拔出传统命令
以下命令分两步从系统中热拔出 PCIe 功能
命令 | 描述 | |
1 | 准备要分离的模拟功能 | |
2 | 分离模拟功能 |
emulation_device_detach_prepare
这是分离模拟设备的第一步。它准备系统分离热插拔的模拟功能。如果成功,主机的热插拔设备状态会发生变化,您可以安全地继续执行 emulation_device_detach
命令。
连接到模拟功能的控制器必须在执行此命令时创建并处于活动状态。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 数字 | PCIe 功能的 vHCA ID |
| 否 | 字符串 | PCIe 设备 VUID |
| 否 | 字符串 | 控制器 ID |
必须至少提供一个标识符来描述要分离的 PCIe 功能。
emulation_device_detach
这是完成热插拔模拟功能分离的第二步。如果 分离准备 超时,您可以使用 --force
命令执行意外拔出。
驱动程序必须卸载,否则可能会发生错误。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 数字 | PCIe 功能的 vHCA ID |
| 否 | 字符串 | PCIe 设备 VUID |
| 否 | 布尔值 | 在准备失败的情况下分离 |
必须至少提供一个标识符来描述要分离的 PCIe 功能。
Virtio-blk 热插拔/热拔出示例
// Bringup
spdk_rpc.py bdev_nvme_attach_controller -b nvme0 -t rdma -a 1.1.1.1 -f ipv4 -s 4420 -n nqn.2022-10.io.nvda.nvme:swx-storage
snap_rpc.py virtio_blk_emulation_device_attach
snap_rpc.py virtio_blk_controller_create --vuid MT2114X12200VBLKS1D0F0 --bdev nvme0n1
// Cleanup
snap_rpc.py emulation_device_detach_prepare --vuid MT2114X12200VBLKS1D0F0
snap_rpc.py virtio_blk_controller_destroy -c VblkCtrl1
snap_rpc.py emulation_device_detach --vuid MT2114X12200VBLKS1D0F0
spdk_rpc.py bdev_nvme_detach_controller nvme0
(已弃用)SPDK Bdev 管理
以下 RPC 已弃用,不再支持
spdk_bdev_create
spdk_bdev_destroy
bdev_list
这些 RPC 是可选的。如果不执行,SNAP 将自动生成 SNAP 块设备 (bdev)。
Virtio-blk 模拟管理
Virtio-blk 模拟是一种存储协议,属于 virtio 设备系列。这些设备在虚拟环境中找到,但从设计上来看,对于虚拟机内的用户来说,它们看起来像物理设备。
每个暴露给主机的 virtio-blk 设备(例如,virtio-blk PCIe 条目),无论是 PF 还是 VF,都必须由 virtio-blk 控制器支持。
Virtio-blk 限制
在主机上探测 virtio-blk 驱动程序时,如果没有已经正常工作的 virtio-blk 控制器,可能会导致主机挂起,直到成功打开这样的控制器(不存在超时机制)。
在创建 virtio-blk 控制器时,后端设备必须已经存在。
Virtio-blk 仿真管理命令
命令 | 描述 |
创建新的基于 SNAP 的 virtio-blk 控制器 | |
销毁 virtio-blk SNAP 控制器 | |
暂停 virtio-blk SNAP 控制器 | |
恢复 virtio-blk SNAP 控制器 | |
将 bdev 附加到 virtio-blk SNAP 控制器 | |
从 virtio-blk SNAP 控制器分离 bdev | |
Virtio-blk SNAP 控制器列表 | |
Virtio-blk 控制器参数修改 | |
获取 virtio-blk SNAP 控制器 IO 统计信息 | |
获取 virtio-blk SNAP 控制器调试统计信息 | |
保存已暂停的 virtio-blk SNAP 控制器的状态 | |
恢复已暂停的 virtio-blk SNAP 控制器的状态 | |
将 virtio-blk SNAP 控制器 VF 的 MSIX 回收至空闲 MSIX 池。仅对 PF 有效。 |
virtio_blk_controller_create
在主机上的特定 PCIe 功能上创建新的基于 SNAP 的 virtio-blk 控制器。要指定在其上打开控制器的 PCIe 功能,必须按照“PCIe 功能管理”部分所述提供。
vuid
(推荐,因为它保证保持不变)。vhca_id。
功能索引 –
pf_id
、vf_id
。
可以通过运行 emulation_function_list
查询 pci_index
的映射。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 字符串 | PCIe 设备 VUID |
| 否 | 数字 | PCIe 功能的 vHCA ID |
| 否 | 数字 | 要在其上启动模拟的 PCIe PF 索引 |
| 否 | 数字 | 要在其上启动模拟的 PCIe VF 索引(如果控制器要在 VF 上打开) |
| 否 | 字符串 | PCIe 设备 BDF |
| 否 | 字符串 | 控制器 ID |
| 否
| 数字 | IO 队列的数量(默认 1,范围 1-64)。 |
| 否 | 数字 | 队列深度(默认 256,范围 1-256) |
| 否 | 数字 | 最大 SGE 数据传输大小(默认 4096,范围 1– |
| 否 | 数字 | 最大 SGE 列表长度(默认 1,范围 1- |
| 否 | 字符串 | 用作后端的 SNAP SPDK 块设备 |
| 否 | 字符串 | 控制器的序列号 |
| 否 | 0/1 | 启用实时迁移和 NVIDIA vDPA |
| 否 | 0/1 | 此 PF 上 SR-IOV VF 的动态 MSIX。仅对 PF 有效。 |
| 否 | 数字 | 控制要与此控制器关联的 MSIX 表的数量。仅对 VF 有效(其父 PF 控制器是使用 注意
当使用 |
| 否 | 0/1 | 支持 virtio-blk 崩溃恢复。将此参数设置为 1 可能会影响 virtio-blk 性能(默认为 0)。有关更多信息,请参阅“Virtio-blk 崩溃恢复”部分。 |
| 否 | 0/1 | 启用控制器 virt-queues 的间接描述符支持。 注意
当使用 virtio-blk 内核驱动程序时,如果启用了间接描述符,驱动程序始终会使用它。对于大多数 IO 流量模式,对所有 IO 流量模式使用间接描述符可能会损害性能。 |
| 否 | 0/1 | 创建只读 virtio-blk 控制器。 |
| 否 | 0/1 | 在暂停状态下创建控制器。 |
| 否 | 0/1 | 创建具有通过 IPC 侦听实时更新通知功能的控制器。 |
| 否 | 0/1 | 不适用 – 不支持 |
| 否 | 0/1 | 不适用 – 不支持 |
示例响应
{
"jsonrpc": "2.0",
"id": 1,
"result": "VblkCtrl1"
}
virtio_blk_controller_destroy
销毁先前创建的 virtio-blk 控制器。可以通过从 virtio_blk_controller_create()
获取的控制器名称唯一标识控制器。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
force | 否 | 布尔值 | 强制销毁 SR-IOV 的 VF 控制器 |
virtio_blk_controller_suspend
暂停后,控制器停止接收来自主机驱动程序的新请求,仅完成处理已在进行中的请求。所有暂停的请求(如果有)将在 恢复 后处理。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
virtio_blk_controller_resume
在控制器停止接收来自主机驱动程序的新请求(即,已暂停)并且仅完成处理已在进行中的请求后,恢复命令将恢复控制器对 IO 的处理。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
virtio_blk_controller_bdev_attach
将指定的 bdev 附加到 virtIO-blk SNAP 控制器。如果附加了新的 bdev,则可以更改序列 ID(使用 vblk_id
参数)。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
| 是 | 字符串 | 块设备名称 |
| 否 | 字符串 | 控制器的序列号 |
virtio_blk_controller_bdev_detach
您可以替换 virtio-blk 控制器的 bdev。首先,您应该从控制器分离 bdev。当 bdev 分离后,控制器停止接收来自主机驱动程序的新请求(即,已暂停),并且仅完成处理已在进行中的请求。
此时,您可以附加新的 bdev 或销毁控制器。
当附加新的 bdev 时,控制器恢复处理所有未完成的 I/O。
如果驱动程序已加载,则无法更改块大小。
如果驱动程序未加载,则可以将 bdev 替换为不同的块大小。
没有附加 bdev 的控制器被认为是临时状态,在这种状态下,控制器未完全运行,并且可能不会响应驱动程序请求的某些操作。
如果没有立即调用 virtio_blk_controller_bdev_attach
的意图,建议附加 none
bdev 代替。例如
snap_rpc.py virtio_blk_controller_bdev_attach -c VblkCtrl1 --bdev none --dbg_bdev_type null
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
virtio_blk_controller_list
列出 virtio-blk SNAP 控制器。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 字符串 | 控制器名称 |
示例响应
{
"ctrl_id": "VblkCtrl2",
"vhca_id": 38,
"num_queues": 4,
"queue_size": 256,
"seg_max": 32,
"size_max": 65536,
"bdev": "Nvme1",
"plugged": true,
"indirect_desc": true,
"num_msix": 2,
"min configurable num_msix": 2,
"max configurable num_msix": 32
}
virtio_blk_controller_modify
此功能允许用户在控制器已创建后实时修改控制器的某些参数。
修改只能在模拟功能处于空闲状态时完成 - 因此没有驱动程序与其通信。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 字符串 | 控制器名称 |
num_queues | 否 | 整数 | 控制器的队列数 |
num_msix | 否 | 整数 | 要用于控制器的 MSIX 数量。 仅与 VF 控制器相关(当启用动态 MSIX 功能时)。 |
标准 virtio-blk 内核驱动程序当前不支持 PCI FLR。因此,
virtio_blk_controller_dbg_io_stats_get
调试计数器是每个控制器的 I/O 统计信息,可以帮助了解控制器不同队列之间的 I/O 分布以及控制器上接收的总 I/O。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
示例响应
"ctrl_id": "VblkCtrl2",
"queues": [
{
"queue_id": 0,
"core_id": 0,
"read_io_count": 19987068,
"write_io_count": 6319931,
"flush_io_count": 0
},
{
"queue_id": 1,
"core_id": 1,
"read_io_count": 9769556,
"write_io_count": 3180098,
"flush_io_count": 0
}
],
"read_io_count": 29756624,
"write_io_count": 9500029,
"flush_io_count": 0
}
virtio_blk_controller_dbg_debug_stats_get
调试计数器是每个控制器的调试统计信息,可以帮助了解控制器和队列的健康状况和状态。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
示例响应
{
"ctrl_id": "VblkCtrl1",
"queues": [
{
"qid": 0,
"state": "RUNNING",
"hw_available_index": 6,
"sw_available_index": 6,
"hw_used_index": 6,
"sw_used_index": 6,
"hw_received_descs": 13,
"hw_completed_descs": 13
},
{
"qid": 1,
"state": "RUNNING",
"hw_available_index": 2,
"sw_available_index": 2,
"hw_used_index": 2,
"sw_used_index": 2,
"hw_received_descs": 6,
"hw_completed_descs": 6
},
{
"qid": 2,
"state": "RUNNING",
"hw_available_index": 0,
"sw_available_index": 0,
"hw_used_index": 0,
"sw_used_index": 0,
"hw_received_descs": 4,
"hw_completed_descs": 4
},
{
"qid": 3,
"state": "RUNNING",
"hw_available_index": 0,
"sw_available_index": 0,
"hw_used_index": 0,
"sw_used_index": 0,
"hw_received_descs": 3,
"hw_completed_descs": 3
}
]
}
virtio_blk_controller_state_save
保存已暂停的 virtio-blk SNAP 控制器的状态。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
| 是 | 字符串 | 要将状态保存到的文件名 |
virtio_blk_controller_state_restore
恢复已暂停的 virtio-blk SNAP 控制器的状态。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
| 是 | 字符串 | 要将状态保存到的文件名 |
virtio_blk_controller_vfs_msix_reclaim
将 virtio-blk SNAP 控制器 VF 的 MSIX 回收至空闲 MSIX 池。仅对 PF 有效。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
Virtio-blk 配置示例
单控制器的 Virtio-blk 配置
spdk_rpc.py bdev_nvme_attach_controller -b nvme0 -t rdma -a 1.1.1.1 -f ipv4 -s 4420 -n nqn.2022-10.io.nvda.nvme:swx-storage
snap_rpc.py virtio_blk_controller_create --vuid MT2114X12200VBLKS1D0F0 --bdev nvme0n1
单控制器的 Virtio-blk 清理
snap_rpc.py virtio_blk_controller_destroy -c VblkCtrl1
spdk_rpc.py bdev_nvme_detach_controller nvme0
125 个 VF 的 Virtio-blk 动态配置
按照“SR-IOV 固件配置”部分所述更新固件配置。
重启主机。
运行
[dpu] spdk_rpc.py bdev_nvme_attach_controller -b nvme0 -t rdma -a 1.1.1.1 -f ipv4 -s 4420 -n nqn.2022-10.io.nvda.nvme:swx-storage [dpu] snap_rpc.py virtio_blk_controller_create --vuid MT2114X12200VBLKS1D0F0 [host] modprobe -v virtio-pci && modprobe -v virtio-blk [host] echo 125 > /sys/bus/pci/devices/0000:86:00.3/sriov_numvfs [dpu] for i in `seq 0 124`; do snap_rpc.py virtio_blk_controller_create --pf_id 0 --vf_id $i --bdev nvme0n1; done;
注意当启用 SR-IOV 时,建议使用以下方法而不是
virito_blk_controller_destroy
RPC 命令来销毁 VF 上的 virtio-blk 控制器[host] echo 0 > /sys/bus/pci/devices/0000:86:00.3/sriov_numvfs
要销毁单个 virtio-blk 控制器,请运行
[dpu] ./snap_rpc.py -t 1000 virtio_blk_controller_destroy -c VblkCtrl5 –f
Virtio-blk 暂停、恢复示例
[host] // Run fio
[dpu] snap_rpc.py virtio_blk_controller_suspend -c VBLKCtrl1
[host] // IOs will get suspended
[dpu] snap_rpc.py virtio_blk_controller_resume -c VBLKCtrl1
[host] // fio will resume sending IOs
Virtio-blk Bdev 附加、分离示例
[host] // Run fio
[dpu] snap_rpc.py virtio_blk_controller_bdev_detach -c VBLKCtrl1
[host] // Bdev will be detached and IOs will get suspended
[dpu] snap_rpc.py virtio_blk_controller_bdev_attach -c VBLKCtrl1 --bdev null2
[host] // The null2 bdev will be attached into controller and fio will resume sending IOs
注释
Virtio-blk 协议控制器仅支持一个后端设备
Virtio-blk 协议不支持添加后端的管理命令。因此,所有后端属性都通过 PCIe BAR 传达给主机 virtio-blk 驱动程序,并且在驱动程序探测期间必须可访问。因此,只有当 PCIe 功能未被任何主机存储驱动程序使用时,才能更改后端。
NVMe 仿真管理
NVMe 子系统
NVMe 子系统如 NVMe 规范中所述,是一个逻辑实体,它封装了 NVMe 后端(或命名空间)和连接(或控制器)的集合。当使用多个 NVMe 控制器,尤其是在使用 NVMe VF 时,NVMe 子系统非常有用。每个 NVMe 子系统在创建后都由其序列号 (SN)、型号 (MN) 和合格名称 (NQN) 定义。
本节列出的 RPC 控制 NVMe 子系统的创建和销毁。
NVMe 命名空间
NVMe 命名空间是本地/远程存储中连续 LBA 范围的代表。每个命名空间都必须链接到一个子系统,并在整个 NVMe 子系统中具有唯一的标识符 (NSID)(例如,即使两个命名空间链接到不同的控制器,它们也不能共享相同的 NSID)。
创建后,NVMe 命名空间可以附加到控制器。
SNAP 目前不支持不同控制器之间的共享命名空间。因此,每个命名空间应附加到单个控制器。
SNAP 应用程序使用 SPDK 块设备框架作为其 NVMe 命名空间的后端。因此,应提前配置它们。有关 SPDK 块设备的更多信息,请参阅 SPDK bdev 文档 和 附录 SPDK 配置。
NVMe 控制器
每个暴露给主机的 NVMe 设备(例如,NVMe PCIe 条目),无论是 PF 还是 VF,都必须由 NVMe 控制器支持,该控制器负责与主机驱动程序的所有协议通信。
每个新的 NVMe 控制器也必须链接到 NVMe 子系统。创建后,可以使用其名称(例如,“Nvmectrl1”)或其子系统 NQN 和控制器 ID 来寻址 NVMe 控制器。
将 NVMe 命名空间附加到 NVMe 控制器
在同一子系统下创建 NVMe 控制器和 NVMe 命名空间后,使用以下方法将命名空间附加到控制器。
NVMe 仿真管理命令
命令 | 描述 |
创建 NVMe 子系统 | |
销毁 NVMe 子系统 | |
NVMe 子系统列表 | |
创建 NVMe 命名空间 | |
销毁 NVMe 命名空间 | |
暂停 NVMe 控制器 | |
恢复 NVMe 控制器 | |
将 NVMe 控制器的快照保存到文件 | |
NVMe 命名空间列表 | |
创建新的 NVMe 控制器 | |
销毁 NVMe 控制器 | |
NVMe 控制器列表 | |
NVMe 控制器参数修改 | |
将 NVMe 命名空间附加到控制器 | |
从控制器分离 NVMe 命名空间 | |
将 NVMe SNAP 控制器 VF 的 MSIX 回收至空闲 MSIX 池。仅对 PF 有效。 | |
获取 NVMe 控制器 IO 调试统计信息 |
nvme_subsystem_create
创建一个新的 NVMe 子系统,由一个或多个 NVMe SNAP 控制器控制。NVMe 子系统包括一个或多个控制器、零个或多个命名空间以及一个或多个端口。NVMe 子系统可能包括非易失性存储介质以及 NVMe 子系统中的控制器与非易失性存储介质之间的接口。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 子系统合格名称 |
| 否 | 字符串 | 子系统序列号 |
| 否 | 字符串 | 子系统型号 |
| 否 | 数字 | 子系统中允许的最大命名空间 ID(默认 0xFFFFFFFE;范围 1-0xFFFFFFFE) |
| 否 | 数字 | 子系统中允许的最大命名空间数量(默认 1024;范围 1-0xFFFFFFFE) |
示例请求
{
"jsonrpc": "2.0",
"id": 1,
"method": "nvme_subsystem_create",
"params": {
"nqn": "nqn.2022-10.io.nvda.nvme:0"
}
}
nvme_subsystem_destroy
销毁(先前创建的)NVMe SNAP 子系统。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 子系统合格名称 |
| 否 | 布尔值 | 强制删除子系统下的所有控制器和命名空间 |
nvme_subsystem_list
列出 NVMe 子系统。
nvme_namespace_create
创建新的 NVMe 命名空间,这些命名空间代表先前配置的 bdev 中的连续 LBA 范围。每个命名空间都必须链接到一个子系统,并在整个 NVMe 子系统中具有唯一的标识符 (NSID)。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 子系统合格名称 |
| 是 | 字符串 | 用作后端的块设备 |
| 是 | 数字 | 命名空间 ID |
| 否 | 数字 | 命名空间 UUID 注意
为了安全地分离/附加命名空间,应提供 UUID 以强制 UUID 保持持久性。 |
| 否 | string | 要与控制器附加的 Bdev 插件。有关更多信息,请参阅“Bdev”部分。 |
nvme_namespace_destroy
销毁先前创建的 NVMe 命名空间。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 子系统合格名称 |
| 是 | 数字 | 命名空间 ID |
nvme_namespace_list
列出 NVMe SNAP 命名空间。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 字符串 | 子系统合格名称 |
nvme_controller_create
在主机上的特定 PCIe 功能上创建一个新的基于 SNAP 的 NVMe blk 控制器。
要指定在其上打开控制器的 PCIe 功能,必须提供 pci_index
。
可以通过运行 emulation_function_list
查询 pci_index
的映射。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 子系统合格名称 |
| 否 | 数字 | PCIe 功能的 VUID |
| 否 | 数字 | 要在其上启动模拟的 PCIe PF 索引 |
| 否 | 数字 | 要在其上启动模拟的 PCIe VF 索引(如果控制器注定要在 VF 上打开) |
| 否 | 字符串 | 要在其上启动模拟的 PCIe BDF |
| 否 | 数字 | PCIe 功能的 vHCA ID |
| 否 | 数字 | 控制器 ID |
| 否 | 数字 | IO 队列的数量(默认 1,范围 1-31)。 注意
实际队列数受硬件支持的队列数限制。
提示
建议 MSIX 的数量与 IO 队列的数量相匹配或更大。 |
| 否 | 数字 | MDTS(默认 7,范围 1-7) |
| 否 | 数字 | 最大固件槽数(默认 4) |
| 否 | 0/1 | 启用 |
| 否 | 0/1 | 设置控制器中 |
| 否 | 0/1 | 设置控制器中 注意
在崩溃恢复期间,所有 compare 和 write 命令都应失败。 |
| 否 | 0/1 | 设置控制器中 |
| 否 | 0/1 | 在暂停状态下打开控制器(需要额外调用 注意
如果预期进行 NVMe 恢复或者在驱动程序已加载时创建控制器,则这是必需的。因此,建议在所有场景中使用它。 要在附加命名空间后恢复控制器,请使用 |
| 否 | 字符串 | 从快照文件路径创建控制器。快照是先前使用 |
| 否 | 0/1 | 为此控制器启用动态 MSIX 管理(默认 0)。仅适用于 PF。 |
| 否 | 数字 | 控制要与此控制器关联的 MSIX 表的数量。仅对 VF 有效(其父 PF 控制器是使用 注意
当使用 |
| 否 | 0/1 | 创建仅具有管理队列的 NVMe 控制器(即,没有 IO 队列) |
| 否 | 数字 | 用于支持不符合 NVMe 规范的错误驱动程序的位掩码。
有关更多详细信息,请参阅“OS 问题”部分。 |
如果未设置,则 SNAP NVMe 控制器仅在其加载驱动程序时附加到它的所有命名空间都支持可选 NVMe 命令时才支持该命令。要绕过此功能,您可以显式设置 NVMe 可选命令支持位,方法是使用其对应的标志。
例如,使用 –-compare 0
创建的控制器将不支持可选的 compare
NVMe 命令,无论其附加的命名空间如何。
示例请求
{
"jsonrpc": "2.0",
"id": 1,
"method": "nvme_controller_create",
"params": {
"nqn": "nqn.2022-10.io.nvda.nvme:0",
"pf_id": 0,
"num_queues": 8,
}
}
nvme_controller_destroy
销毁先前创建的 NVMe 控制器。可以通过从 nvme_controller_create
获取的控制器名称唯一标识控制器。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
| 否 | 1/0 | 将 MSIX 释放回空闲池。仅适用于 VF。 |
nvme_controller_suspend
暂停后,控制器停止处理来自主机驱动程序的新请求。所有挂起的请求(如果有)将在 恢复 后处理。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
| 否 | 数字 | 暂停超时 注意
如果 I/O 在 bdev 层(或远程目标)中处于挂起状态,则操作将失败,并在超时后恢复。 如果未提供 |
| 否 | 0/1 | 即使存在正在进行的 I/O,也强制挂起 |
| 否 | 0/1 | 仅挂起管理队列 |
| 否 | 0/1 | 通过 IPC 发送实时更新通知 |
nvme_controller_resume
resume 命令继续(先前已挂起的)控制器处理驱动程序发送的新请求。如果控制器在挂起模式下创建,则 resume 也用于启动与主机驱动程序的初始通信。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
| 否 | 0/1 | 实时更新恢复 |
nvme_controller_snapshot_get
拍摄控制器当前状态的快照并将其转储到文件中。此文件可用于基于此快照创建控制器。为了保证快照的一致性,用户应仅在控制器挂起时调用此函数(请参阅 nvme_controller_suspend
RPC)。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
| 是 | 字符串 | 文件路径 |
nvme_controller_vfs_msix_reclaim
将所有 VF 的 MSIX 回收至 PF 的可用 MSIX 池。
此函数只能应用于 PF,并且只能在主机端未设置 SR-IOV 时运行(即,sriov_numvfs = 0
)。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
nvme_controller_list
提供所有活动的(已创建的)NVMe 控制器的列表及其特性。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 字符串 | 子系统合格名称 |
| 否 | 字符串 | 仅搜索特定的控制器 |
nvme_controller_modify
此功能允许用户在控制器已创建后实时修改控制器的某些参数。
修改只能在模拟功能处于空闲状态时完成 - 因此没有驱动程序与其通信。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 字符串 | 控制器名称 |
num_queues | 否 | 整数 | 控制器的队列数 |
num_msix | 否 | 整数 | 要用于控制器的 MSIX 数量。 仅与 VF 控制器相关(当启用动态 MSIX 功能时)。 |
nvme_controller_attach_ns
将先前创建的 NVMe 命名空间附加到同一子系统下的给定 NVMe 控制器。
响应对象中的结果对于成功返回 true
,对于失败返回 false
。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
| 是 | 数字 | 命名空间 ID |
nvme_controller_detach_ns
从 NVMe 控制器分离先前附加的具有给定 NSID 的命名空间。
响应对象中的结果对于成功返回 true
,对于失败返回 false
。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
| 是 | 数字 | 命名空间 ID |
nvme_controller_dbg_io_stats_get
响应对象中的结果对于成功返回 true
,对于失败返回 false
。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 控制器名称 |
"ctrl_id": "NVMeCtrl2",
"queues": [
{
"queue_id": 0,
"core_id": 0,
"read_io_count": 19987068,
"write_io_count": 6319931,
"flush_io_count": 0
},
{
"queue_id": 1,
"core_id": 1,
"read_io_count": 9769556,
"write_io_count": 3180098,
"flush_io_count": 0
}
],
"read_io_count": 29756624,
"write_io_count": 9500029,
"flush_io_count": 0
}
NVMe 配置示例
单控制器 NVMe 配置
在 DPU 上
spdk_rpc.py bdev_nvme_attach_controller -b nvme0 -t rdma -a 1.1.1.1 -f ipv4 -s 4420 -n nqn.2022-10.io.nvda.nvme:swx-storage
snap_rpc.py nvme_subsystem_create --nqn nqn.2022-10.io.nvda.nvme:0
snap_rpc.py nvme_namespace_create -b nvme0n1 -n 1 --nqn nqn.2022-10.io.nvda.nvme:0 --uuid 263826ad-19a3-4feb-bc25-4bc81ee7749e
snap_rpc.py nvme_controller_create --nqn nqn.2022-10.io.nvda.nvme:0 --pf_id 0 --suspended
snap_rpc.py nvme_controller_attach_ns -c NVMeCtrl1 -n 1
snap_rpc.py nvme_controller_resume -c NVMeCtrl1
必须在挂起状态下创建控制器。之后,可以附加命名空间,然后才应使用 nvme_controller_resume
RPC 恢复控制器。
为了安全地分离/附加命名空间,必须提供 UUID 以强制 UUID 保持持久。
单控制器 NVMe 清理
snap_rpc.py nvme_controller_detach_ns -c NVMeCtrl2 -n 1
snap_rpc.py nvme_controller_destroy -c NVMeCtrl2
snap_rpc.py nvme_namespace_destroy -n 1 --nqn nqn.2022-10.io.nvda.nvme:0
snap_rpc.py nvme_subsystem_destroy --nqn nqn.2022-10.io.nvda.nvme:0
spdk_rpc.py bdev_nvme_detach_controller nvme0
单控制器 NVMe 和热插拔清理
snap_rpc.py nvme_controller_detach_ns -c NVMeCtrl1 -n 1
snap_rpc.py emulation_device_detach_prepare --vuid MT2114X12200VBLKS1D0F0
snap_rpc.py nvme_controller_destroy -c NVMeCtrl1
snap_rpc.py emulation_device_detach --vuid MT2114X12200VBLKS1D0F0
snap_rpc.py nvme_namespace_destroy -n 1 --nqn nqn.2022-10.io.nvda.nvme:0
snap_rpc.py nvme_subsystem_destroy --nqn nqn.2022-10.io.nvda.nvme:0
spdk_rpc.py bdev_nvme_detach_controller nvme0
125 个 VF SR-IOV 的 NVMe 配置
按照“SR-IOV 固件配置”部分所述更新固件配置。
重启主机。
在父 PF 上创建虚拟控制器
[dpu] # snap_rpc.py nvme_subsystem_create --nqn nqn.2022-10.io.nvda.nvme:0 [dpu] # snap_rpc.py nvme_controller_create --nqn nqn.2022-10.io.nvda.nvme:0 --ctrl NVMeCtrl1 --pf_id 0 --admin_only
创建 125 个 Bdev(远程或本地)、125 个 NS 和 125 个控制器
[dpu] for i in `seq 0 124`; do \ # spdk_rpc.py bdev_null_create null$((i+1)) 64 512; # snap_rpc.py nvme_namespace_create -b null$((i+1)) -n $((i+1)) --nqn nqn.2022-10.io.nvda.nvme:0 --uuid 3d9c3b54-5c31-410a-b4f0-7cf2afd9e$((i+100)); # snap_rpc.py nvme_controller_create --nqn nqn.2022-10.io.nvda.nvme:0 --ctrl NVMeCtrl$((i+2)) --pf_id 0 --vf_id $i --suspended; # snap_rpc.py nvme_controller_attach_ns -c NVMeCtrl$((i+2)) -n $((i+1)); # snap_rpc.py nvme_controller_resume -c NVMeCtrl$(i+2); done
加载驱动程序并配置 VF
[host] # modprobe -v nvme [host] # echo 125 > /sys/bus/pci/devices/0000\:25\:00.2/sriov_numvfs
环境变量管理
snap_global_param_list
snap_global_param_list
列出所有现有的环境变量。
以下是 snap_global_param_list
命令的示例响应
[
"SNAP_ENABLE_POLL_SKIP : set : 0 ",
"SNAP_POLL_CYCLE_SIZE : not set : 16 ",
"SNAP_RPC_LOG_ENABLE : set : 1 ",
"SNAP_MEMPOOL_SIZE_MB : set : 1024",
"SNAP_MEMPOOL_4K_BUFFS_PER_CORE : not set : 1024",
"SNAP_RDMA_ZCOPY_ENABLE : set : 1 ",
"SNAP_TCP_XLIO_ENABLE : not set : 1 ",
"SNAP_TCP_XLIO_TX_ZCOPY : not set : 1 ",
"MLX5_SHUT_UP_BF : not set : 0 ",
"SNAP_SHARED_RX_CQ : not set : 1 ",
"SNAP_SHARED_TX_CQ : not set : 1 ",
...
RPC 日志历史
RPC 日志历史记录(默认启用)记录发送到 SNAP 应用程序的所有 RPC 请求(来自 snap_rpc.py
和 spdk_rpc.py
)以及每个 RPC 请求的 RPC 响应,记录在一个专用的日志文件 /var/log/snap-log/rpc-log
中。此文件在容器外部可见(即,DPU 上的日志文件路径也为 /var/log/snap-log/rpc-log
)。
可以使用 SNAP_RPC_LOG_ENABLE
环境变量来启用 (1
) 或禁用 (0
) 此功能。
SPDK 版本 spdk23.01.2-12 及更高版本支持 RPC 日志历史记录。
启用 RPC 日志历史记录后,SNAP 应用程序会持续(以追加模式)将 RPC 请求和响应消息写入 /var/log/snap-log/rpc-log
。请注意此文件的大小。如果文件过大,请在启动 SNAP pod 之前删除 DPU 上的该文件。
SR-IOV
SR-IOV 的配置取决于内核版本,需要仔细考虑以确保最佳的设备可见性和系统稳定性。
对于具有多个虚拟设备的部署,必须禁用自动探测 (autoprobe),以通过在 /sys/bus/pci/devices/<BDF>/
中设置 sriov_drivers_autoprobe=0
来确保可靠的设备发现。未能这样做可能会导致以下问题
设备可见性不完整
虚拟磁盘丢失
设备初始化期间可能发生系统挂起
大量 VF(>100)的不可靠行为
大规模部署所需的配置
禁用自动探测 (autoprobe)
echo
0 > /sys/bus/pci/devices/<BDF>/sriov_drivers_autoprobe根据需要使用特定的驱动程序绑定工具(例如,sysfs 中的
driverctl
或bind/unbind
)手动绑定 VF。
对于小规模部署(少于 100 个 VF),您可以使用 sriov_totalvfs
sysfs 条目来设置 VF 的数量
echo
<number_of_vfs> > /sys/bus/pci/devices/<BDF>/sriov_totalvfs
将此配置应用于更大规模的部署。
完成 SR-IOV 配置后,默认情况下不会在 hypervisor 中公开任何磁盘。只有在使用虚拟化管理器(例如,libvirt、VMware 等)将关联的 PCIe VF 分配给 VM 后,磁盘才会出现在 VM 中。如果您需要直接从 hypervisor 使用设备,请手动将 PCIe VF 绑定到所需的驱动程序。
热插拔 PF 不支持 SR-IOV。
建议在创建超过 127 个 VF 时,将 pci=assign-busses
添加到启动命令行。
如果没有此选项,主机可能会出现以下错误,并且 virtio 驱动程序将不会探测这些设备
pci 0000
:84
:00.0
: [1af4:1041
] type 7f class
0xffffff
pci 0000
:84
:00.0
: unknown header type 7f, ignoring device
零拷贝 (SNAP-direct)
SPDK 21.07 及更高版本支持零拷贝。
SNAP-direct 允许 SNAP 应用程序直接将数据从主机内存传输到远程存储,而无需在 DPU 内部使用任何暂存缓冲区。
仅当针对 SPDK NVMe-oF RDMA 块设备工作时,SNAP 才根据 SPDK BDEV 配置启用此功能。
要启用零拷贝,请设置环境变量(默认情况下已启用)
SNAP_RDMA_ZCOPY_ENABLE=1
更多信息请参考 "SNAP 环境变量" 部分。
NVMe/TCP XLIO 零拷贝
NVMe/TCP 零拷贝在 SPDK NVMe initiator 中作为自定义的 NVDA_TCP
传输实现,并且它基于新的 XLIO socket 层实现。
Tx 和 Rx 的实现方式不同
NVMe/TCP Tx 零拷贝在 RDMA 和 TCP 之间是相似的,因为数据直接从主机内存发送到网络线路,而无需中间复制到 Arm 内存。
NVMe/TCP Rx 零拷贝允许在 Rx 流上实现部分零拷贝,方法是消除从 socket 缓冲区 (XLIO) 到应用程序缓冲区 (SNAP) 的复制。但是数据仍然必须从 Arm DMA 到主机内存。
要启用 NVMe/TCP 零拷贝,请使用 SPDK v22.05.nvda --with-xlio
(v22.05.nvda
或更高版本)。
有关 XLIO 的更多信息,包括限制和错误修复,请参阅 NVIDIA 加速 IO (XLIO) 文档。
要启用 SNAP TCP XLIO 零拷贝
SNAP 容器:在 YAML 文件中设置环境变量和资源
resources: requests: memory: "4Gi" cpu: "8" limits: hugepages-2Mi: "4Gi" memory: "6Gi" cpu: "16" ## Set according to the local setup env: - name: APP_ARGS value: "--wait-for-rpc" - name: SPDK_XLIO_PATH value: "/usr/lib/libxlio.so"
SNAP 源代码:在相关脚本中设置环境变量和资源
在
run_snap.sh
中,编辑APP_ARGS
变量以使用 SPDK 命令行参数--wait-for-rpc
run_snap.sh
APP_ARGS="--wait-for-rpc"
在
set_environment_variables.sh
中,取消注释SPDK_XLIO_PATH
环境变量set_environment_variables.sh
export SPDK_XLIO_PATH="/usr/lib/libxlio.so"
NVMe/TCP XLIO 需要 4Gi 的 BlueField Arm OS 巨页大小。有关配置巨页的信息,请参阅 "步骤 1:分配巨页" 和 "调整 YAML 配置" 部分。
在大规模部署中,即使会导致高内存消耗,也需要使用全局变量 XLIO_RX_BUFS=4096
。使用 XLIO_RX_BUFS=1024
需要较低的内存消耗,但会限制扩展工作负载的能力。
更多信息请参考 "SNAP 环境变量" 部分。
建议将 NVMe/TCP XLIO 配置为将传输 ack 超时选项增加到 12。
[dpu] spdk_rpc.py bdev_nvme_set_options --transport-ack-timeout 12
其他 bdev_nvme
选项可以根据需求进行调整。
通过在远程 SPDK 目标上使用 TCP 传输类型,公开具有一个命名空间的 NVMe-oF 子系统。
[dpu] spdk_rpc.py sock_set_default_impl -i xlio
[dpu] spdk_rpc.py framework_start_init
[dpu] spdk_rpc.py bdev_nvme_set_options --transport-ack-timeout 12
[dpu] spdk_rpc.py bdev_nvme_attach_controller -b nvme0 -t nvda_tcp -a 3.3.3.3 -f ipv4 -s 4420 -n nqn.2023-01.io.nvmet
[dpu] snap_rpc.py nvme_subsystem_create --nqn nqn.2023-01.com.nvda:nvme:0
[dpu] snap_rpc.py nvme_namespace_create -b nvme0n1 -n 1 --nqn nqn. 2023-01.com.nvda:nvme:0 --uuid 16dab065-ddc9-8a7a-108e-9a489254a839
[dpu] snap_rpc.py nvme_controller_create --nqn nqn.2023-01.com.nvda:nvme:0 --ctrl NVMeCtrl1 --pf_id 0 --suspended --num_queues 16
[dpu] snap_rpc.py nvme_controller_attach_ns -c NVMeCtrl1 -n 1
[dpu] snap_rpc.py nvme_controller_resume -c NVMeCtrl1 -n 1
[host] modprobe -v nvme
[host] fio --filename /dev/nvme0n1 --rw randrw --name=test-randrw --ioengine=libaio --iodepth=64 --bs=4k --direct=1 --numjobs=1 --runtime=63 --time_based --group_reporting --verify=md5
有关 XLIO 的更多信息,请参考 XLIO 文档。
加密
SNAP 附带的 SPDK 版本支持硬件加密/解密卸载。要启用 AES/XTS,请按照 "修改 SF 信任级别以启用加密" 部分下的说明进行操作。
零拷贝 (SNAP-direct) 与加密
SNAP 为使用 RDMA 传输的 bdev_nvme
提供对零拷贝和加密的支持。
如果使用另一个 bdev_nvme
传输或 NVMe 以外的基础 bdev,则不支持零拷贝流,并且会执行从主机到 BlueField Arm 的额外 DMA 操作。
请参考 "SPDK 加密示例" 部分,了解如何配置具有 AES_XTS 卸载的零拷贝流。
命令 | 描述 |
| 接受用于加密操作的设备列表 |
| 创建加密密钥 |
| 构建 NVMe 块设备 |
| 创建一个虚拟块设备,该设备加密写入 IO 命令并解密读取 IO 命令 |
mlx5_scan_accel_module
接受在 --allowed-devs
参数中提供的用于加密操作的设备列表。如果未指定任何设备,则使用第一个支持加密的设备。
为了获得最佳性能,建议使用具有最大 InfiniBand MTU (4096) 的设备。可以使用 ibv_devinfo
命令验证 MTU 大小(查找 max 和 active MTU 字段)。通常,mlx5_2
设备预计具有 4096 的 MTU,应将其用作允许的加密设备。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 数字 | QP 大小 |
| 否 | 数字 | 共享请求池的大小 |
| 否 | 字符串 | 以逗号分隔的允许设备名称列表(例如,“mlx5_2”) 注意
确保选择用于 RDMA 流量的设备以支持零拷贝。 |
| 否 | 布尔值 | 启用 accel_mlx5 平台驱动程序。允许 AES_XTS RDMA 零拷贝。 |
accel_crypto_key_create
创建加密密钥。一个密钥可以由多个 bdev 共享。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 数字 | 加密协议 (AES_XTS) |
| 是 | 数字 | 密钥 |
| 是 | 数字 | 密钥 2 |
| 是 | 字符串 | 密钥名称 |
bdev_nvme_attach_controller
创建 NVMe 块设备。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | NVMe 控制器的名称,每个 bdev 名称的前缀 |
| 是 | 字符串 | NVMe-oF 目标 trtype(例如,rdma、pcie) |
| 是 | 字符串 | NVMe-oF 目标地址(例如,IP 地址或 BDF) |
| 否 | 字符串 | NVMe-oF 目标 trsvcid(例如,端口号) |
| 否 | 字符串 | NVMe-oF 目标 adrfam(例如,ipv4、ipv6) |
| 否 | 字符串 | NVMe-oF 目标 subnqn |
bdev_crypto_create
此 RPC 创建一个虚拟加密块设备,该设备为基础块设备添加加密。
命令参数
参数 | 必填? | 类型 | 描述 |
| 是 | 字符串 | 基础 bdev 的名称 |
| 是 | 字符串 | 加密 bdev 名称 |
| 是 | 字符串 | 使用 |
SPDK 加密示例
以下是在 bdev_nvme
之上创建加密虚拟块设备的配置示例,该配置具有 RDMA 传输和零拷贝支持
[dpu] # spdk_rpc.py mlx5_scan_accel_module --allowed-devs "mlx5_2" --enable-driver
[dpu] # spdk_rpc.py framework_start_init
[dpu] # spdk_rpc.py accel_crypto_key_create -c AES_XTS -k 00112233445566778899001122334455 -e 11223344556677889900112233445500 -n test_dek
[dpu] # spdk_rpc.py bdev_nvme_attach_controller -b nvme0 -t rdma -a 1.1.1.1 -f ipv4 -s 4420 -n nqn.2016-06.io.spdk:cnode0
[dpu] # spdk_rpc.py bdev_crypto_create nvme0n1 crypto_0 -n test_dek
[dpu] # snap_rpc.py spdk_bdev_create crypto_0
[dpu] # snap_rpc.py nvme_subsystem_create --nqn nqn.2023-05.io.nvda.nvme:0
[dpu] # snap_rpc.py nvme_controller_create --nqn nqn.2023-05.io.nvda.nvme:0 --pf_id 0 --ctrl NVMeCtrl0 --suspended
[dpu] # snap_rpc.py nvme_namespace_create –nqn nqn.2023-05.io.nvda.nvme:0 --bdev_name crypto_0 –-nsid 1 -–uuid 263826ad-19a3-4feb-bc25-4bc81ee7749e
[dpu] # snap_rpc.py nvme_controller_attach_ns –-ctrl NVMeCtrl0 --nsid 1
[dpu] # snap_rpc.py nvme_controller_resume –-ctrl NVMeCtrl0
Virtio-blk 实时迁移
实时迁移是 QEMU 支持的标准流程,它允许系统管理员在实时运行的系统中在虚拟机之间传递设备。有关更多信息,请参阅 QEMU VFIO 设备迁移文档。
SNAP virtio-blk 设备支持实时迁移。可以使用具有适当支持的驱动程序激活它(例如,NVIDIA 的专有 vDPA 实时迁移解决方案)。
snap_rpc.py virtio_blk_controller_create --dbg_admin_q …
SNAP 容器实时升级
实时升级允许更新容器使用的 SNAP 镜像,而不会导致 SNAP 容器停机。
虽然较新的 SNAP 版本可能会引入其他内容,这可能会在升级期间导致行为差异,但该过程旨在确保向后兼容性。同一子版本(例如,4.0.0-x 到 4.0.0-y)内的版本更新应顺利进行。
但是,跨不同主要版本或次要版本的更新可能需要更改系统组件(例如,固件、BFB),这可能会影响向后兼容性,并需要在更新后完全重启。在这些情况下,实时更新是不必要的。
实时升级先决条件
要启用实时升级,请执行以下修改
为目标容器和源容器分配双倍巨页。
确保请求的 CPU 核心数量可用。
默认 YAML 配置将容器设置为请求 8-16 个 CPU 核心的范围。这意味着如果可用核心少于 8 个,则不会部署容器;如果有 16 个空闲核心,则容器将使用所有 16 个核心。
例如,如果一个容器当前正在使用所有 16 个核心,并且在实时升级期间,部署了一个额外的 SNAP 容器。在这种情况下,每个容器在升级过程中使用 8 个核心。一旦源容器终止,目标容器开始使用所有 16 个核心。
注意对于 8 核 DPU,必须将
.yaml
编辑为 4-8 个 CPU 核心的范围。更改描述目标容器的
doca_snap.yaml
文件的名称(例如,doca_snap_new.yaml
),以避免覆盖正在运行的容器.yaml
。在第 16 行中更改新的
.yaml
pod 的名称(例如,snap-new
)。通过将新的 yaml 文件(例如,
doca_snap_new.yaml
)复制到 kubelet 来部署目标容器。
部署目标容器后,在实时更新过程完成之前,避免通过 RPC 进行任何配置更改。具体来说,不要创建或销毁热插拔功能。
在实时更新期间在目标容器中恢复控制器时,建议使用与源容器中最初用于控制器创建的参数相同的参数。
实时升级流程
实时升级 SNAP 镜像的方法是在不同容器之间移动 SNAP 控制器和 SPDK 块设备,同时最大限度地减少主机 VM 影响的持续时间。

源容器 – 实时升级前正在运行的容器
目标容器 – 实时升级后正在运行的容器
SNAP 容器实时升级步骤
按照 "实时升级先决条件" 部分中的步骤,并使用修改后的
yaml
文件部署目标 SNAP 容器。查询源容器和目标容器
crictl ps -r
检查目标容器的日志中是否出现
SNAP started successfully
,然后将实时更新从容器复制到您的环境。[dpu] crictl logs -f <dest-container-id> [dpu] crictl exec <dest-container-id> cp /opt/nvidia/nvda_snap/bin/live_update.py /etc/nvda_snap/
运行
live_update.py
脚本,将所有活动对象从源容器移动到目标容器[dpu] cd /etc/nvda_snap [dpu] ./live_update.py -s <source-container-id> -d <dest-container-id>
删除源容器。
注意要发布 RPC,请使用 crictl 工具
crictl exec -it <container-id X> snap_rpc.py <RPC-method> crictl exec -it <container-id Y> spdk_rpc.py <RPC-method>
注意为了自动化 SNAP 配置(例如,在失败或重启后),如 "自动化 SNAP 配置(可选)" 部分所述,
spdk_rpc_init.conf
和snap_rpc_init.conf
不得包含任何作为实时升级一部分的配置。然后,一旦完成到新容器的转换,就可以使用所需的配置修改spdk_rpc_init.conf
和snap_rpc_init.conf
。
SNAP 容器实时升级命令
实时更新工具旨在支持快速实时更新。它迭代可用的仿真功能,并为每个功能执行以下操作
在源容器上
snap_rpc.py virtio_blk_controller_suspend --ctrl [ctrl_name] --events_only
在目标容器上
spdk_rpc.py bdev_nvme_attach_controller ... snap_rpc.py virtio_blk_controller_create ... --suspended --live_update_listener
在源容器上
snap_rpc.py virtio_blk_controller_destroy --ctrl [ctrl_name] spdk_rpc.py bdev_nvme_detach_controller [bdev_name]
SR-IOV 动态 MSIX 管理
消息信号中断扩展 (MSIX) 是一种中断机制,允许设备使用多个中断向量,从而提供比传统中断机制(如共享中断)更高效的中断处理。在 Linux 中,内核支持 MSIX,并且 MSIX 通常用于高性能设备,例如网络适配器、存储控制器和图形卡。MSIX 具有诸如降低 CPU 利用率、提高设备性能和更好的可伸缩性等优点,使其成为现代硬件的常用选择。
然而,MSIX 中断的正确配置和管理可能具有挑战性,并且需要仔细调整才能实现最佳性能,尤其是在 SR-IOV 等多功能环境中。
默认情况下,BlueField 在所有虚拟 PCIe 功能 (VF) 之间均匀分配 MSIX 向量。这种方法不是最佳的,因为用户可以选择将 VF 附加到不同的 VM,每个 VM 具有不同数量的资源。动态 MSIX 管理允许用户独立地手动控制每个 VF 提供的 MSIX 向量的数量。
所有仿真类型(特别是 NVMe 和 virtio-blk)的配置和行为都相似。
动态 MSIX 管理由几个配置步骤构建而成
此时,以及将来任何时候未打开 VF 控制器(
sriov_numvfs=0
)时,所有 PF 相关的 MSIX 向量都可以从 VF 回收到 PF 的可用 MSIX 池。用户必须在 VF 控制器创建期间从可用池中获取一些 MSIX,并将它们提供给特定的 VF。
销毁 VF 控制器时,用户可以选择将其 MSIX 释放回池中。
配置完成后,MSIX 到 VF 的链接将保持持久性,并且仅在以下情况下可能会更改
用户在控制器销毁期间明确请求将 VF MSIX 返回到池中。
PF 明确地将所有 VF MSIX 回收到池中。
发生了 Arm 重启(FE 重置/冷启动)。
为了强调,以下情况不会更改 MSIX 配置
应用程序重启/崩溃。
在没有动态 MSIX 支持的情况下关闭和重新打开 PF/VF。
以下是动态 MSIX 配置步骤的 NVMe 示例(类似的配置也适用于 virtio-blk)
将所有 VF 的 MSIX 回收至 PF 的可用 MSIX 池
snap_rpc.py nvme_controller_vfs_msix_reclaim <CtrlName>
查询控制器列表以获取有关 PF 资源约束的信息
# snap_rpc.py nvme_controller_list -c <CtrlName> … 'free_msix': 100, … 'free_queues': 200, … 'vf_min_msix': 2, … 'vf_max_msix': 64, … 'vf_min_queues': 0, … 'vf_max_queues': 31, …
其中
free_msix
表示 PF 的可用池中可用的 MSIX 总数,通过参数vf_num_msix
(<protocol>_controller_create
RPC 的参数)分配给 VF。free_queues
表示 PF 的可用池中可用的队列(或“门铃”)总数,通过参数num_queues
(<protocol>_controller_create
RPC 的参数)分配给 VF。vf_min_msix
和vf_max_msix
共同定义了vf_num_msix
参数值的可用可配置范围,该参数值可以在<protocol>_controller_create
RPC 中为每个 VF 传递。vf_min_queues
和vf_max_queues
共同定义了num_queues
参数值的可用可配置范围,该参数值可以在<protocol>_controller_create
RPC 中为每个 VF 传递。
在 VF 的创建过程中,考虑到 PF 的限制,在 VF 之间分配 MSIX
snap_rpc.py nvme_controller_create_ --vf_num_msix <n> --num_queues <m> …
注意强烈建议在 VF 控制器创建时同时提供
vf_num_msix
和num_queues
参数。仅提供其中一个值可能会导致 MSIX 和队列配置之间发生冲突,这反过来可能会导致控制器/驱动程序发生故障。提示在 NVMe 协议中,MSIX 由 NVMe CQ 使用。因此,建议为每个分配的队列从 PF 的全局池 (
free_msix
) 中分配 1 个 MSIX。在 virtio 协议中,MSIX 由 virtqueue 使用,并且 BAR 配置更改通知需要一个额外的 MSIX。因此,建议为每个分配的队列从 PF 的全局池 (
free_msix
) 中分配 1 个 MSIX,再分配一个作为配置 MSIX。总而言之,队列/MSIX 比率配置的最佳实践是
对于 NVMe –
num_queues
=vf_num_msix
对于 virtio –
num_queues
=vf_num_msix
-1
在 VF 拆卸时,将 MSIX 释放回可用池
snap_rpc.py nvme_controller_destroy_ --release_msix …
在主机驱动程序上设置 SR-IOV
echo <N> > /sys/bus/pci/devices/<BDF>/sriov_numvfs
注意强烈建议在将 VF 绑定到主机/客户机驱动程序之前,预先在 SNAP 中打开所有 VF 控制器。这样,例如,如果配置错误导致没有为所有 VF 留下足够的 MSIX,则配置仍然是可逆的,因为 MSIX 仍然是可修改的。否则,驱动程序可能会尝试在所有 VF 配置完成之前使用已配置的 VF,但将无法使用所有 VF(由于缺少 MSIX)。后一种情况可能会导致主机死锁,在最坏的情况下,只能通过冷启动恢复。
注意有几种方法可以安全地配置动态 MSIX(无需 VF 绑定)
禁用内核驱动程序自动将 VF 绑定到内核驱动程序
# echo 0 > /sys/bus/pci/devices/sriov_driver_autoprobe
在完成所有 VF 的 MSIX 配置后,可以将它们绑定到 VM,甚至绑定回 hypervisor
echo "0000:01:00.0" > /sys/bus/pci/drivers/nvme/bind
使用 VFIO 驱动程序(而不是内核驱动程序)进行 SR-IOV 配置。
例如
# echo 0000:af:00.2 > /sys/bus/pci/drivers/vfio-pci/bind # Bind PF to VFIO driver # echo 1 > /sys/module/vfio_pci/parameters/enable_sriov # echo <N> > /sys/bus/pci/drivers/vfio-pci/0000:af:00.2/sriov_numvfs # Create VFs device for it
恢复
NVMe 恢复
NVMe 恢复允许在 SNAP 应用程序关闭后(无论是正常关闭还是崩溃后,例如 kill -9
)恢复 NVMe 控制器。
要使用 NVMe 恢复,必须在挂起状态下重新创建控制器,并使用与崩溃前相同的配置(即,相同的 bdev、队列数和具有相同 uuid 的命名空间等)。
只有在附加所有 NS 后才能恢复控制器。
NVMe 恢复使用 BlueField 上 /dev/shm
下的文件来恢复控制器的内部状态。当 BlueField 重置时,共享内存文件将被删除。因此,BF 重置后不支持恢复。
Virtio-blk 崩溃恢复
以下选项可用于启用 virtio-blk 崩溃恢复。
使用 --force_in_order 的 Virtio-blk 崩溃恢复
对于使用 --force_in_order
的 virtio-blk 崩溃恢复,禁用 VBLK_RECOVERY_SHM
环境变量,并使用 --force_in_order
参数创建控制器。
在 virtio-blk SNAP 中,应用程序不能保证在突然崩溃(例如 kill -9
)后正确恢复。
要启用 virtio-blk 崩溃恢复,请设置以下内容
snap_rpc.py virtio_blk_controller_create --force_in_order …
将 force_in_order
设置为 1 可能会影响 virtio-blk 性能,因为它将按顺序处理命令。
如果未使用 --force_in_order
,由于 Linux 内核 virtio-blk 驱动程序中的支持有限,SNAP 或驱动程序中的任何故障或意外拆卸都可能导致异常行为。
Virtio-blk 无需 --force_in_order 的崩溃恢复
对于无需 --force_in_order
的 virtio-blk 崩溃恢复,启用 VBLK_RECOVERY_SHM
环境变量,并创建一个不带 --force_in_order
参数的控制器。
Virtio-blk 恢复允许在 SNAP 应用程序关闭后(无论是正常关闭还是崩溃后,例如 kill -9
)恢复 virtio-blk 控制器。
要使用无需 --force_in_order
标志的 virtio-blk 恢复,必须启用 VBLK_RECOVERY_SHM
,并且必须使用与崩溃前相同的配置(即相同的 bdev、队列数等)重新创建控制器。
当启用 VBLK_RECOVERY_SHM
时,virtio-blk 恢复使用 BlueField 上 /dev/shm
中的文件来恢复控制器的内部状态。当 BlueField 重置时,共享内存文件将被删除。因此,BlueField 重置后不支持恢复。
SNAP 配置恢复
SNAP 配置恢复机制目前支持 virtio-blk,并允许使用已保存的配置文件恢复 SNAP 的状态。此功能允许 SNAP 在重启时重新加载其先前的状态,而无需通过 RPC 重新配置。
此恢复过程不能用于 SNAP 的初始配置。
热插拔仿真功能在 SNAP 运行之间(而不是跨 BlueField 重置)持续存在,并且仅需要在初始配置期间设置。在这些功能上创建的控制器将被保存和恢复。
配置恢复步骤
设置环境变量
为环境变量
SNAP_RPC_INIT_CONF_JSON
定义一个目录路径。该目录应该是临时的,并在 BlueField 重置后清除(例如,
/dev/shm
),因为配置在重置后将失效。如果在重启 SNAP 后需要新配置,请删除此目录中现有的
snap_config.json
文件。
为
SPDK_RPC_INIT_CONF_JSON
设置文件路径。对于首次运行,这应指向现有的配置文件export
SPDK_RPC_INIT_CONF_JSON=<spdk_config.json>
创建并保存所需的配置
使用 SPDK/SNAP RPC 来配置所需的状态。
保存配置
spdk_rpc.py save_config > <spdk_config.json>
这确保仅在所有更改都已成功应用后才保存配置。
使用已保存的配置重启 SNAP
重启 SNAP 后,它将自动从
SNAP_RPC_INIT_CONF_JSON
文件加载配置。在初始配置之后,无需重新运行 SNAP RPC 或在初始化文件中设置 RPC。
此方法通过避免重新配置的需要,显著提高了恢复时间。
确保在对控制器或功能配置进行任何更改期间,驱动程序保持卸载状态。如果配置更改未成功完成,则不这样做可能会导致恢复过程失败。
如果在重启 SNAP 后配置发生显著更改,请删除现有的配置文件 (snap_config.json
) 并设置一个新的配置文件,以避免无效的配置。
提高 SNAP 恢复时间
下表概述了旨在加速 SNAP 初始化和终止后恢复过程的功能。
功能 | 描述 | 如何操作? |
SPDK JSON-RPC 配置文件 | 可以为 SNAP 中 SPDK 配置指定初始配置。配置文件是一个 JSON 文件,其中包含所需配置的所有 SPDK JSON-RPC 方法调用。从发布 RPC 到 JSON 文件可以缩短启动时间。 信息
有关更多信息,请查看 SPDK JSON-RPC 文档。 | 要基于当前配置生成 JSON-RPC 文件,请运行
注意
如果 SPDK 在处理 JSON 配置文件时遇到错误,则初始化阶段将失败,导致 SNAP 以错误代码退出。 |
禁用 SPDK accel 功能 | 当使用 NVMe TCP 功能时,SPDK accel 功能是必需的。如果未使用 NVMe TCP,则应手动禁用 accel 以减少 SPDK 启动时间,否则可能需要几秒钟。要禁用所有 accel 功能,请编辑标志 | 按如下所示编辑配置文件
|
提供仿真管理器名称 | 如果未定义 | 使用 |
virtio-blk 的 DPU 模式 | DPU 模式仅在使用 virtio-blk 时受支持。DPU 模式减少了崩溃恢复期间的 SNAP 停机时间。 | 设置 |
virtio-blk 的 SNAP 配置恢复 | SNAP 配置恢复使恢复 SNAP 状态成为可能,而无需重新发布 SNAP RPC。通过从发布单个 RPC 转移到使用预先保存的 JSON 配置文件,启动时间得到了显著提高。 | 将 |
SNAP ML 优化器
SNAP ML 优化器是一个旨在微调 SNAP 轮询器参数的工具,可根据特定环境和工作负载增强 SNAP I/O 处理性能并提高控制器吞吐量。
在工作负载执行期间,优化器迭代地调整配置(操作)并评估其对性能(奖励)的影响。通过预测下一个要测试的最佳配置,它可以有效地缩小到最佳设置,而无需探索每种可能的组合。
一旦确定了最佳配置,就可以将其应用于目标系统,从而提高类似条件下的性能。目前,该工具支持“IOPS”作为奖励指标,其目标是最大化 IOPS。
SNAP ML 优化器准备步骤
机器要求
设备应能够 SSH 连接到 BlueField
Python 3.10 或更高版本
至少 6 GB 的可用存储空间
设置 SNAP ML 优化器
要设置 SNAP ML 优化器
将
snap_ml
文件夹从容器复制到共享的nvda_snap
文件夹,然后再复制到请求的机器crictl exec -it $(crictl ps -s running -q --name snap) cp -r /opt/nvidia/nvda_snap/bin/snap_ml /etc/nvda_snap/
将目录更改为
snap_ml
文件夹cd tools/snap_ml
为 SNAP ML 优化器创建一个虚拟环境。
python3 -m venv snap_ml
这确保所需的依赖项安装在隔离的环境中。
激活虚拟环境以开始在此隔离环境中工作
source snap_ml/bin/activate
安装 Python 包要求
pip3 install --no-cache-dir -r requirements.txt
这可能需要一些时间,具体取决于您的系统性能。
运行 SNAP ML 优化器。
python3 snap_ml.py --help
使用
--help
标志查看可用选项和用法信息--version Show the version and exit. -f, --framework <TEXT> Name of framework (Recommended: ax , supported: ax, pybo). -t, --total-trials <INTEGER> Number of optimization iterations. The recommended range is 25-60. --filename <TEXT> where to save the results (default: last_opt.json). --remote <TEXT> connect remotely to the BlueField card, format: <bf_name>:<username>:<password> --snap-rpc-path <TEXT> Snap RPC prefix (default: container path). --log-level <TEXT> CRITICAL | ERROR | WARN | WARNING | INFO | DEBUG --log-dir <TEXT> where to save the logs.
SNAP ML 优化器相关 RPC
snap_actions_set
snap_actions_set
命令用于动态调整 SNAP 参数(称为“操作”),这些参数控制轮询行为。此命令是 SNAP-AI 工具的核心功能,可实现特定环境和工作负载的自动优化,以及轮询参数的手动调整。
命令参数
参数 | 必填? | 类型 | 描述 |
| 否 | 数字 | SNAP 在单个轮询周期中传递的最大 IO 数量(整数;1-256) |
| 否 | 数字 | SNAP 轮询周期发生的速率(浮点数;0< |
| 否 | 数字 | 每个核心的最大飞行中 IO 数量(整数;1-65535) |
| 否 | 数字 | 最大公平性批处理大小(整数;1-4096) |
| 否 | 数字 | 在单个轮询周期中处理的最大新 IO 数量(整数;1-4096) |
snap_reward_get
snap_reward_get
命令检索性能计数器,特别是完成计数器(或“奖励”),优化器使用这些计数器来监视和增强 SNAP 性能。
此命令不需要参数。
为 ML 优化器优化 SNAP 参数
要优化 SNAP 的参数以适应您的环境,请使用以下命令
python3 snap_ml.py --framework ax --total-trials 40 --filename example.json --remote <bf_hostname>:<username>:<password> --log-dir <log_directory>
结果和优化后操作
优化过程完成后,该工具会自动应用优化的参数。这些参数也以 example.json
文件格式保存在以下位置
{
"poll_size": 30,
"poll_ratio": 0.6847347955107689,
"max_inflights": 32768,
"max_iog_batch": 512,
"max_new_ios": 32
}
此外,该工具还在名为 example_<timestamp>.json
的带时间戳的文件中记录所有迭代,包括所采取的操作和收到的奖励。
手动应用优化的参数
用户可以通过显式调用 snap_actions_set
RPC 并使用优化的参数,在 SNAP 服务的新实例上应用优化的参数,如下所示
snap_rpc.py snap_actions_set –poll_size 30 –poll_ratio 0.6847 --max_inflights 32768 –max_iog_batch 512 –max_new_ios 32
仅当预期系统行为与使用 SNAP ML 优化器的系统相似时,才建议使用优化的参数。
停用 Python 环境
用户完成 SNAP ML 优化器的使用后,可以通过运行以下命令停用 Python 虚拟环境
deactivate
插件
插件是模块化组件或附加组件,可增强 SNAP 应用程序的功能。它们与主软件无缝集成,允许添加其他功能,而无需更改核心代码库。插件设计为仅与源代码包一起使用,因为它允许在构建过程中进行自定义,例如根据需要启用或禁用插件。
在容器化环境中,SNAP 应用程序作为具有固定配置的预构建二进制文件交付。由于容器中的二进制文件是预编译的,因此无法添加或删除插件。容器化软件仅支持在其构建期间包含的插件。对于需要插件灵活性的环境(例如添加自定义插件),必须使用源代码包。
要构建带有插件的 SNAP 源代码包,请执行以下操作,而不是遵循 基本构建步骤
移动到源码文件夹。运行
cd /opt/nvidia/nvda_snap/src/
构建要启用插件的源代码。运行
meson setup /tmp/build -Denable-bdev-null=true -Denable-bdev-malloc=true
编译源码。运行
meson compile -C /tmp/build
安装源码。运行
meson install -C /tmp/build
配置 SNAP 环境变量并运行 SNAP 服务,如“配置 SNAP 环境变量”和“运行 SNAP 服务”部分中所述。
Bdev
SNAP 支持各种类型的块设备 (bdev),在与存储后端交互时提供灵活性和可扩展性。这些 bdev 插件提供不同的存储仿真选项,允许自定义,而无需修改核心软件。
SPDK
SPDK 是 SNAP 使用的默认插件。如果未显式指定特定插件,SNAP 将默认使用 SPDK 进行块设备操作。
有关更多信息,请参阅 spdk_bdev。
Malloc
Malloc 插件仅用于性能分析和调试目的;它不适合生产环境使用
它通过在内存中分配缓冲区并将其作为块设备公开来创建内存支持的块设备
由于数据存储在内存中,因此在系统关闭时会丢失
可以使用
enable-bdev-malloc
构建选项启用此插件
Malloc 配置示例
创建 Malloc bdev 并将其与 NVMe 控制器一起使用
# snap_rpc.py snap_bdev_malloc_create --bdev test 64 512 # snap_rpc.py nvme_subsystem_create -s nqn.2020-12.mlnx.snap # snap_rpc.py nvme_namespace_create -s nqn.2020-12.mlnx.snap -t malloc -b test -n 1 # snap_rpc.py nvme_controller_create --pf_id=0 -s nqn.2020-12.mlnx.snap --mdts=7 # snap_rpc.py nvme_controller_attach_ns -c NVMeCtrl1 -n 1
删除 Malloc bdev
# snap_rpc.py snap_bdev_malloc_destroy test
调整 Malloc bdev 大小
# snap_rpc.py snap_bdev_malloc_resize test 32
这将删除现有的 bdev 并创建一个具有指定大小的新 bdev。现有 bdev 上的数据将丢失。
NULL
NULL 插件专为性能分析和调试目的而设计,不适用于生产环境。
它充当虚拟块设备,接受 I/O 请求并仿真块设备,而无需执行实际的 I/O 操作。
它适用于不涉及真实存储设备的测试或基准测试场景。
该插件消耗最少的系统资源。
可以使用
enable-bdev-null
构建选项启用它。
NULL 配置示例
创建 NULL bdev 并将其与 NVMe 控制器一起使用
# snap_rpc.py snap_bdev_null_create_dbg test 1 512 # snap_rpc.py nvme_subsystem_create -s nqn.2020-12.mlnx.snap # snap_rpc.py nvme_namespace_create -s nqn.2020-12.mlnx.snap -t malloc -b test -n 1 # snap_rpc.py nvme_controller_create --pf_id=0 -s nqn.2020-12.mlnx.snap --mdts=7 # snap_rpc.py nvme_controller_attach_ns -c NVMeCtrl1 -n 1
删除 NULL bdev
# snap_rpc.py snap_bdev_null_destroy_dbg test
在配置 SNAP 之前,用户必须确保满足所有固件配置要求。默认情况下,SNAP 处于禁用状态,必须通过运行常见的 SNAP 配置以及额外的协议特定配置来启用,具体取决于应用程序的预期用途(例如,热插拔、SR-IOV、UEFI 启动等)。
配置完成后,必须对主机进行电源循环,以使更改生效。
要验证是否满足所有配置要求,用户可以运行以下命令查询当前/下一个配置
mlxconfig -d /dev/mst/mt41692_pciconf0 -e query
系统配置参数
参数 | 描述 | 可能的值 |
| 启用 BlueField 以在内部 CPU 模型中工作 注意
对于存储仿真,必须设置为 | 0/1 |
| 启用 SR-IOV | 0/1 |
| 为仿真的 PF 启用 PCI 交换机 | 0/1 |
| 热插拔仿真 PF 的最大数量,等于 注意
一个交换机端口保留给所有静态 PF。 | [0,2-32] |
SRIOV_EN
仅对静态 PF 有效。
RDMA/RoCE 配置
BlueField 的 RDMA/RoCE 通信被阻止用于 BlueField 的默认 OS 接口(命名为 ECPF,通常为 mlx5_0 和 mlx5_1)。如果需要 RoCE 流量,则必须添加支持 RDMA/RoCE 流量的附加网络功能(可扩展功能)。
通过 TCP 甚至 RDMA/IB 工作时,不需要以下内容。
要启用 RoCE 接口,请从 DPU 内运行以下命令
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s PER_PF_NUM_SF=1
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0 s PF_SF_BAR_SIZE=8 PF_TOTAL_SF=2
[dpu] mlxconfig -d /dev/mst/mt41692_pciconf0.1 s PF_SF_BAR_SIZE=8 PF_TOTAL_SF=2
NVMe 配置
参数 | 描述 | 可能的值 |
| 启用 NVMe 设备仿真 | 0/1 |
| 静态仿真 NVMe PF 的数量 | [0-4] |
| 分配给仿真 NVMe PF 的 MSIX 数量 注意
固件将此值视为尽力而为的值。分配给函数的有效 MSI-X 数量应作为 nvme_controller_list RPC 命令的一部分进行查询。 | [0-63] |
| 每个仿真 NVMe VF 的 MSIX 数量 注意
固件将此值视为尽力而为的值。分配给函数的有效 MSI-X 数量应作为 nvme_controller_list RPC 命令的一部分进行查询。
注意
此值应与通过 nvme_controller_create | [0-4095] |
| 每个仿真 NVMe PF 的 VF 数量 注意
如果非 0,则覆盖 | [0-256] |
| 启用 NVMe UEFI exprom 驱动程序 注意
用于 UEFI 启动过程。 | 0/1 |
Virtio-blk 配置
由于 virtio-blk 协议的限制,使用错误的配置与静态 virtio-blk PF 一起工作可能会导致主机服务器操作系统在启动时失败。
在继续之前,请确保您已配置
即使主机关闭,也可以访问 Arm 的工作通道。设置此类通道不在本文档的范围内。请参阅 NVIDIA BlueField DPU BSP 文档以获取更多详细信息。
将以下行添加到
/etc/nvda_snap/snap_rpc_init.conf
virtio_blk_controller_create –pf_id 0
有关更多信息,请参阅“Virtio-blk 仿真管理”部分。
参数 | 描述 | 可能的值 |
| 启用 virtio-blk 设备仿真 | 0/1 |
| 静态仿真 virtio-blk PF 的数量 注意
请参阅上面的警告。
| [0-4] |
| 分配给仿真 virtio-blk PF 的 MSIX 数量 注意
固件将此值视为尽力而为的值。分配给函数的有效 MSI-X 数量应作为 virtio_blk_controller_list RPC 命令的一部分进行查询。
| [0-63] |
| 每个仿真 virtio-blk VF 的 MSIX 数量 注意
固件将此值视为尽力而为的值。分配给函数的有效 MSI-X 数量应作为 virtio_blk_controller_list RPC 命令的一部分进行查询。
注意
此值应与通过 nvme_controller_create
| [0-4095] |
| 每个仿真 virtio-blk PF 的 VF 数量 注意
如果非 0,则覆盖
| [0-2000] |
| 启用 virtio-blk UEFI exprom 驱动程序 注意
用于 UEFI 启动过程。
| 0/1 |
要配置持久网络接口,以便它们在重启后不会丢失。在 /etc/sysconfig/network-scripts
下修改以下四个文件,如果不存在则创建它们,然后执行重启
# cd /etc/sysconfig/network-scripts/
# cat ifcfg-p0
NAME="p0"
DEVICE="p0"
NM_CONTROLLED="no"
DEVTIMEOUT=30
PEERDNS="no"
ONBOOT="yes"
BOOTPROTO="none"
TYPE=Ethernet
MTU=9000
# cat ifcfg-p1
NAME="p1"
DEVICE="p1"
NM_CONTROLLED="no"
DEVTIMEOUT=30
PEERDNS="no"
ONBOOT="yes"
BOOTPROTO="none"
TYPE=Ethernet
MTU=9000
# cat ifcfg-enp3s0f0s0
NAME="enp3s0f0s0"
DEVICE="enp3s0f0s0"
NM_CONTROLLED="no"
DEVTIMEOUT=30
PEERDNS="no"
ONBOOT="yes"
BOOTPROTO="static"
TYPE=Ethernet
IPADDR=1.1.1.1
PREFIX=24
MTU=9000
# cat ifcfg-enp3s0f1s0
NAME="enp3s0f1s0"
DEVICE="enp3s0f1s0"
NM_CONTROLLED="no"
DEVTIMEOUT=30
PEERDNS="no"
ONBOOT="yes"
BOOTPROTO="static"
TYPE=Ethernet
IPADDR=1.1.1.2
PREFIX=24
MTU=9000
SNAP 源代码包包含使用自定义 SPDK 构建容器所需的文件。
要构建容器
下载并安装 SNAP 源代码包
[dpu] # dpkg -i /path/snap-sources_<version>_arm64.deb
导航到
src
文件夹并将其用作开发环境[dpu] # cd /opt/nvidia/nvda_snap/src
将以下内容复制到容器文件夹
SNAP 源代码包 – 在容器内部安装 SNAP 所必需
自定义 SPDK – 到
container/spdk
。例如[dpu] # cp /path/snap-sources_<version>_arm64.deb container/ [dpu] # git clone -b v23.01.1 --single-branch --depth 1 --recursive --shallow-submodules https://github.com/spdk/spdk.git container/spdk
如果需要,修改
spdk.sh
文件,因为它用于编译 SDPK。要构建容器
对于 Ubuntu,运行
[dpu] # ./container/build_public.sh --snap-pkg-file=snap-sources_<version>_arm64.deb
对于 CentOS,运行
[dpu] # rpm -i snap-sources-<version>.el8.aarch64.rpm [dpu] # cd /opt/nvidia/nvda_snap/src/ [dpu] # cp /path/snap-sources_<version>_arm64.deb container/ [dpu] # git clone -b v23.01.1 --single-branch --depth 1 --recursive --shallow-submodules https://github.com/spdk/spdk.git container/spdk [dpu] # yum install docker-ce docker-ce-cli [dpu] # ./container/build_public.sh --snap-pkg-file=snap-sources_<version>_arm64.deb
将创建的映像从 Docker 工具传输到 crictl 工具。运行
[dpu] # docker save doca_snap:<version> doca_snap.tar [dpu] # ctr -n=k8s.io images import doca_snap.tar
注意要将容器映像传输到其他设置,请参阅附录“附录 - 在没有互联网连接的设置上部署容器”。
要验证映像,请运行
[DPU] # crictl images IMAGE TAG IMAGE ID SIZE docker.io/library/doca_snap <version> 79c503f0a2bd7 284MB
编辑
container/doca_snap.yaml
文件中的 image 字段。运行image: doca_snap:<version>
使用 YAML 文件部署容器。运行
[dpu] # cp doca_snap.yaml /etc/kubelet.d/
注意容器部署 准备步骤 是必需的。
当 DPU 上没有互联网连接时,Kubelet 在检测到 SNAP YAML 时会在本地扫描容器映像。用户可以在部署之前手动加载容器映像。
为了实现这一点,用户必须使用具有互联网连接的 DPU 下载必要的资源,然后将其传输并加载到缺少互联网连接的 DPU 上。
要下载
.yaml
文件[bf] # wget --content-disposition https://api.ngc.nvidia.com/v2/resources/nvidia/doca/doca_container_configs/versions/<path-to-yaml>/doca_snap.yaml
注意通过访问 https://catalog.ngc.nvidia.com/orgs/nvidia/teams/doca/containers/doca_snap,在 NGC 上访问最新的下载命令。SNAP 标签
doca_snap:4.1.0-doca2.0.2
在本节中用作示例。最新标签也可在 NGC 上找到。要下载 SNAP 容器映像
[bf] # crictl pull nvcr.io/nvidia/doca/doca_snap:4.1.0-doca2.0.2
要验证 SNAP 容器映像是否存在
[bf] # crictl images IMAGE TAG IMAGE ID SIZE nvcr.io/nvidia/doca/doca_snap 4.1.0-doca2.0.2 9d941b5994057 267MB k8s.gcr.io/pause 3.2 2a060e2e7101d 251kB
注意SNAP 容器需要
k8s.gcr.io/pause
映像。要将映像另存为
.tar
文件[bf] # mkdir images [bf] # ctr -n=k8s.io image export images/snap_container_image.tar nvcr.io/nvidia/doca/doca_snap:4.1.0-doca2.0.2 [bf] # ctr -n=k8s.io image export images/pause_image.tar k8s.gcr.io/pause:3.2
传输
.tar
文件并运行以下命令以将其加载到 Kubelet 中[bf] # sudo ctr --namespace k8s.io image import images/snap_container_image.tar [bf] # sudo ctr --namespace k8s.io image import images/pause_image.tar
现在,该映像已存在于工具中,可以进行部署了。
[bf] # crictl images IMAGE TAG IMAGE ID SIZE nvcr.io/nvidia/doca/doca_snap 4.1.0-doca2.0.2 9d941b5994057 267MB k8s.gcr.io/pause 3.2 2a060e2e7101d 251kB
要构建用于 SNAP 集成的 SPDK-19.04
Cherry-pick 用于 SPDK 共享库安装的关键修复程序(最初仅在 v19.07 以后的上游应用)。
[spdk.git] git cherry-pick cb0c0509
配置 SPDK
[spdk.git] git submodule update --init [spdk.git] ./configure --prefix=/opt/mellanox/spdk --disable-tests --without-crypto --without-fio --with-vhost --without-pmdk --without-rbd --with-rdma --with-shared --with-iscsi-initiator --without-vtune [spdk.git] sed -i -e 's/CONFIG_RTE_BUILD_SHARED_LIB=n/CONFIG_RTE_BUILD_SHARED_LIB=y/g' dpdk/build/.config
注意标志
--prefix
、--with-rdma
和--with-shared
是强制性的。制作 SPDK(和 DPDK 库)
[spdk.git] make && make install [spdk.git] cp dpdk/build/lib/* /opt/mellanox/spdk/lib/ [spdk.git] cp dpdk/build/include/* /opt/mellanox/spdk/include/
PCIe BDF(总线、设备、功能)是分配给连接到计算机的每个 PCIe 设备的唯一标识符。通过使用唯一的 BDF 编号标识每个设备,计算机的操作系统可以高效且有效地管理系统的资源。
PCIe BDF 值由主机操作系统确定,因此可能会在不同的运行之间甚至在单次运行中发生更改。因此,BDF 标识符不是永久配置的最佳选择。
为了克服这个问题,NVIDIA 设备向 PCIe 属性添加了一个扩展,称为 VUID。与 BDF 相比,VUID 在运行之间是持久的,这使其可用作 PCIe 功能标识符。
可以使用 lspci
命令从另一个中提取 PCI BDF 和 VUID
要从 BDF 中提取 VUID
[host] lspci -s <BDF> -vvv | grep -i VU | awk '{print $4}'
要从 VUID 中提取 BDF
[host] ./get_bdf.py <VUID> [host] cat ./get_bdf.py #!/usr/bin/python3 import subprocess import sys vuid = sys.argv[1] # Split the output into individual PCI function entries lspci_output = subprocess.check_output(['lspci']).decode().strip().split('\n') # Create an empty dictionary to store the results pci_functions = {} # Loop through each PCI function and extract the BDF and full info for line in lspci_output: bdf = line.split()[0] if vuid in subprocess.check_output(['lspci', '-s', bdf, '-vvv']).decode(): print(bdf) exit(0) print("Not Found")
本附录解释了 SNAP 如何消耗内存以及如何管理内存分配。
用户必须根据“步骤 1:分配 Hugepages”部分分配 DPA Hugepages 内存。可以按照“调整 YAML 配置”部分中的描述,在 SNAP 容器中使用部分 DPU 内存分配。此配置包括以下最小值和最大值
SNAP 容器消耗的最小分配
resources: requests: memory:
"4Gi"
SNAP 容器允许消耗的最大分配
resources: limits: hugepages-2Mi:
"4Gi"
Hugepage 内存由以下项使用
SPDK
mem-size
全局变量,用于控制 SPDK Hugepages 的消耗(可在 SPDK 中配置,默认为 1GB)SNAP
SNAP_MEMPOOL_SIZE_MB
– 与非 ZC 模式一起使用,作为 Arm 上的 IO 缓冲区暂存缓冲区。默认情况下,SNAP mempool 从 SPDKmem-size
Hugepages 分配中消耗 1G。可以使用SNAP_MEMPOOL_SIZE_MB
全局变量配置 SNAP mempool(最小值为 64 MB)。注意如果分配的值太低,使用非 ZC 可能会看到性能下降。
SNAP 和 SPDK 内部使用 – 默认应使用 1G。这可能会根据总体规模(即 VF/队列数/QD)而减少。
XLIO 缓冲区 – 仅在启用 NVMeTCP XLIO 时分配。
以下是 SNAP 容器允许使用的容器内存限制
resources:
limits:
memory: "6Gi"
这包括 Hugepages 限制(在本例中,额外的 2G 非 Hugepages 内存)。
当使用 NVMe 恢复时(在“NVMe 恢复”部分中描述),SNAP 容器还会消耗 DPU SHMEM 内存。此外,还使用了以下资源
limits:
memory:
在主机操作系统上使用 Linux 环境时,可能需要额外的内核启动参数来支持 SNAP 相关功能
要使用 SR-IOV
对于 Intel,必须添加
intel_iommu=on iommu=pt
对于 AMD,必须添加
amd_iommu=on iommu=pt
要使用 PCIe 热插拔,必须添加
pci=realloc
对于非内置的
virtio-blk
驱动程序或virtio-pci
驱动程序,使用modprobe.blacklist=virtio_blk,virtio_pci
要查看启动参数值,请运行
cat /proc/cmdline
建议将以下内容与 virtio-blk 一起使用
[dpu] cat /proc/cmdline BOOT_IMAGE … pci=realloc modprobe.blacklist=virtio_blk,virtio_pci
要启用 VF(virtio_blk/NVMe)
echo 125 > /sys/bus/pci/devices/0000\:27\:00.4/sriov_numvfs
英特尔服务器性能优化
cat /proc/cmdline
BOOT_IMAGE=(hd0,msdos1)/vmlinuz-5.15.0_mlnx root=UUID=91528e6a-b7d3-4e78-9d2e-9d5ad60e8273 ro crashkernel=auto resume=UUID=06ff0f35-0282-4812-894e-111ae8d76768 rhgb quiet iommu=pt intel_iommu=on pci=realloc modprobe.blacklist=virtio_blk,virtio_pci
AMD 服务器性能优化
cat /proc/cmdline
cat /proc/cmdline BOOT_IMAGE=(hd0,msdos1)/vmlinuz-5.15.0_mlnx root=UUID=91528e6a-b7d3-4e78-9d2e-9d5ad60e8273 ro crashkernel=auto resume=UUID=06ff0f35-0282-4812-894e-111ae8d76768 rhgb quiet iommu=pt amd_iommu=on pci=realloc modprobe.blacklist=virtio_blk,virtio_pci