NVMe 模拟
QEMU 通过 nvme、nvme-ns 和 nvme-subsys 设备提供 NVMe 模拟。
详细信息见以下各节:
添加 NVMe 设备
控制器模拟
控制器实现 NVM Express 1.4。全部强制功能均已实现,但有以下例外和限制:
断电重启后 SMART/Health 日志计数归零。
不支持中断合并,默认禁用。
在 PCI 总线上添加控制器的最简单方式:
-drive file=nvm.img,if=none,id=nvm
-device nvme,serial=deadbeef,drive=nvm
下面列出部分通用参数,完整列表见 -device nvme,help。
max_ioqpairs=UINT32(默认 64)最大 I/O 队列对数,替代已弃用的 num_queues。
msix_qsize=UINT16(默认 65)支持的 MSI-X 向量数。
mdts=UINT8(默认 7)最大数据传输大小。
use-intel-id(默认 off)自 5.2 起使用分配给 QEMU 的 Red Hat PCI 设备/厂商 ID。设为 on 可恢复 之前使用的未分配 Intel ID。
ocp(默认 off)Open Compute Project 的数据中心 NVMe SSD 规范在 NVMe 之上增加了数据 中心专用命令和行为。on 使 SMART/Health 扩展日志等 OCP 功能可用, 这里模拟该日志页的版本 5。
model(默认 QEMU NVMe Ctrl)覆盖报告的型号,以更接近某种设备,最长 40 字符。
firmware-version(默认当前 QEMU 版本)覆盖报告的固件版本,以更接近某种设备,最长 8 字符。
额外命名空间
最简方式只有 NSID 1 的单个命名空间。多命名空间和其他功能需要 nvme-ns。
-device nvme,id=nvme-ctrl-0,serial=deadbeef
-drive file=nvm-1.img,if=none,id=nvm-1
-device nvme-ns,drive=nvm-1
-drive file=nvm-2.img,if=none,id=nvm-2
-device nvme-ns,drive=nvm-2
nvme-ns 连接到最近定义的 nvme 设备创建的 nvme-bus,ID 从 1 自动分配。
可用参数:
nsid(默认 0)显式指定命名空间 ID。
uuid(默认自动生成)设置 UUID,在标识描述符列表中报告为 Namespace UUID。
nguid设置 NGUID,报告为 Namespace Globally Unique Identifier。必须是恰好 16 字节的十六进制字符串,或 auto 随机生成。可用连字符分组;不指定则全零。
eui64设置 EUI-64,报告为 IEEE Extended Unique Identifier。机器类型 6.1 起 未指定时使用非零默认值,更早机器默认 0。
bus多个控制器时,可指定连接到哪个 nvme,通过其 id 标识。
NVM 子系统
控制器关联 nvme-subsys 后,可启用共享命名空间和多路径 I/O 等功能。
-device nvme-subsys,id=nvme-subsys-0,nqn=subsys0
-device nvme,serial=deadbeef,subsys=nvme-subsys-0
-device nvme,serial=deadbeef,subsys=nvme-subsys-0
上例创建含两个控制器的子系统,并使 nvme-ns 支持:
shared(自 6.2 起默认 on)命名空间连接到子系统全部控制器,包括后续热插入的控制器。off 表示私有, 同时只能连接一个控制器。
detached(默认 off)on 表示命名空间在子系统中可用,但初始不连接任何控制器。共享命名空间 若开启此选项,也不会自动连接。
因此添加:
-drive file=nvm-1.img,if=none,id=nvm-1
-device nvme-ns,drive=nvm-1,nsid=1
-drive file=nvm-2.img,if=none,id=nvm-2
-device nvme-ns,drive=nvm-2,nsid=3,shared=off,detached=on
NSID 1 为共享且初始连接两控制器。NSID 3 因 shared=off 为私有,一次只能 连接一个;由于 detached=on,初始不连接,也不自动连接热插入控制器。
可选功能
控制器内存缓冲区
相关 nvme 参数:
cmb_size_mb=UINT32(默认 0)在 BAR 2 偏移 0 添加指定大小的控制器内存缓冲区。
legacy-cmb(默认 off)默认采用 1.4 方案,CMB 初始禁用,必须由主机显式启用。on 则采用 1.3 行为。
简单复制
支持 TP 4065 Simple Copy。nvme-ns 可通过以下参数限制复制命令:
mssrl=UINT16(默认 128)最大单个来源范围长度 MSSRL,即每个来源范围的最大逻辑块数。
mcl=UINT32(默认 128)最大复制长度 MCL,即一次命令全部来源范围的逻辑块总数上限。
msrc=UINT8(默认 127)最大来源范围数 MSRC,这是从零计数的值。
分区命名空间
可按 TP 4053 Zoned Namespaces 将 nvme-ns 设置为 zoned=on。
其他参数包括:
zoned.zone_size=SIZE(默认 128MiB)区域大小 ZSZE。
zoned.zone_capacity=SIZE(默认 0)区域容量 ZCAP,0 表示等于区域大小。
zoned.descr_ext_size=UINT32(默认 0)区域描述符扩展大小 ZDES,必须为 64 字节倍数。
zoned.cross_read=BOOL(默认 off)on 允许跨区域边界读取。
zoned.max_active=UINT32(默认 0)最大活动资源数 MAR,0 允许所有区域活动。
zoned.max_open=UINT32(默认 0)最大打开资源数 MOR,0 允许全部打开。若设置 max_active,此值不得超过它。
zoned.zasl=UINT8(默认 0)Zone Append 最大传输大小,与 mdts 一样以最小内存页 CAP.MPSMIN 为单位, 按 2^n 指定。0 表示继承 mdts。
灵活数据放置
在子系统设置 fdp=on,可支持 TP4146 Flexible Data Placement:
-device nvme-subsys,id=nvme-subsys-0,nqn=subsys0,fdp=on,fdp.nruh=16
子系统模拟单个 Endurance Group,并在其中支持 FDP。与规范略有不同: 只要子系统配置 FDP,控制器就始终启用 FDP Mode。
启用后可设置:
fdp.nrg(默认 1)回收组数量。
fdp.nruh(默认 0)回收单元句柄数量,必需且必须非零。
fdp.runs(默认 96M)回收单元标称大小,默认 96 MiB。
子系统内命名空间可请求回收单元句柄:
-device nvme-ns,drive=nvm-1,fdp.ruhs=RUHLIST
RUHLIST 是分号分隔列表,例如 0;1;2;3,可含范围,例如 0;8-15。未指定时, 控制器将其指定的回收单元句柄分配给放置句柄标识符 0。
元数据
命名空间支持独立的 MPTR 元数据和扩展 LBA。
ms=UINT16(默认 0)每 LBA 元数据字节数。
mset=UINT8(默认 0)1 启用扩展 LBA。
端到端数据保护
根据 mset,支持 DIF 或 DIX 保护信息。
pi=UINT8(默认 0)启用类型 1、2 或 3 的保护信息。
pil=UINT8(默认 0)控制保护信息在元数据中的位置。1 放在开头,否则放在末尾。
pif=UINT8(默认 0)默认 pif=0 使用 16 位 guard。2 启用 64 位 guard,需要至少 16 字节 元数据。目前不支持 pif=1 的 32 位 guard。
虚拟化增强与 SR-IOV(实验性支持)
nvme 支持单根 I/O 虚拟化与共享及虚拟化增强,SR-IOV 要求关联 nvme-subsys。
以下参数 可能变化:
sriov_max_vfs(默认 0)最大 PCIe VF 数量,非零使设备报告 SR-IOV 和 ARI 能力。VF 控制器本身 不报告 SR-IOV。
sriov_vq_flexible可分配给全部次级控制器的灵活队列资源总数,主控制器私有资源隐式为 max_ioqpairs - sriov_vq_flexible。
sriov_vi_flexible可分配的灵活中断资源总数,主控制器私有资源隐式为 msix_qsize - sriov_vi_flexible。
sriov_max_vi_per_vf(默认 0)单个次级控制器最大虚拟中断数,0 表示 sriov_vi_flexible / sriov_max_vfs。
sriov_max_vq_per_vf(默认 0)单个次级控制器最大虚拟队列数,0 表示 sriov_vq_flexible / sriov_max_vfs。
最简配置支持一个 VF,分配管理队列、I/O 队列和一个 MSI-X 中断:
-device nvme-subsys,id=subsys0
-device nvme,serial=deadbeef,subsys=subsys0,sriov_max_vfs=1,
sriov_vq_flexible=2,sriov_vi_flexible=1
配置可用次级控制器的最少步骤:
从主控制器解绑灵活资源
nvme virt-mgmt /dev/nvme0 -c 0 -r 1 -a 1 -n 0
nvme virt-mgmt /dev/nvme0 -c 0 -r 0 -a 1 -n 0
对主控制器执行功能级复位,真正释放资源
echo 1 > /sys/bus/pci/devices/0000:01:00.0/reset
启用 VF
echo 1 > /sys/bus/pci/devices/0000:01:00.0/sriov_numvfs
分配灵活资源并设为 ONLINE
nvme virt-mgmt /dev/nvme0 -c 1 -r 1 -a 8 -n 1
nvme virt-mgmt /dev/nvme0 -c 1 -r 0 -a 8 -n 2
nvme virt-mgmt /dev/nvme0 -c 1 -r 0 -a 9 -n 0
将 NVMe 驱动绑定到 VF
echo 0000:01:00.1 > /sys/bus/pci/drivers/nvme/bind