在 RISC-V 生态下将 PCIe NVMe 透传给 lkvm (kvmtool)
速览
核心流程与 x86/ARM64 一致(VFIO 解绑绑定 + --vfio-pci 参数 ),真正的瓶颈不在 kvmtool,而在 RISC-V 平台的 IOMMU 与 AIA 中断支持是否完备。
一、前提条件与支持现状
硬件要求
| 项 | 要求 |
|---|---|
| CPU | 支持 RISC-V Hypervisor 扩展(H 扩展) |
| 平台 | 具备 RISC-V IOMMU,PCIe 根复合体与 DMA 设备均受其管理 |
| 设备 | NVMe 所在的 PCIe 链路受 IOMMU 管控 |
软件支持进展
RISC-V 的 VFIO 透传经历了从无到有的过程:
| 时间 | 进展 |
|---|---|
| 2021 年 | kvmtool 为 RISC-V 添加 PCI host DT 节点,但当时明确说明"还不能用于透传"------缺少 IOMMU 与中断路由 |
| 2024 年底 | RISE 项目报告确认 KVM irqbypass 与 IOMMU 支持取得进展 |
| 2025 年 9 月 | 上游 patch 开始将 VFIO 模块加入 RISC-V defconfig |
| 2026 年 8 月 | LWN 报道 RISC-V IOMMU 支持二级域硬件脏页跟踪;测试中已通过 vfio-pci + iommufd 将 NVMe 与 e1000e 透传到 L2 guest,同时启用 KVM_VFIO 接口 |
结论:RISC-V 上的 NVMe VFIO 透传目前已经可用,但依赖较新的内核(建议 6.10+,最好使用包含完整 RISC-V IOMMU 支持的分支,如 Ventana 内核树)以及支持 AIA 的中断控制器。
二、具体操作步骤
步骤 1 · 确认硬件与内核能力
# 确认 CPU 支持 Hypervisor 扩展
grep -i isa /proc/cpuinfo # isa 串中应含小写 'h'
dmesg | grep -i "hypervisor"
# 确认 IOMMU 已启用并工作
dmesg | grep -i iommu
ls /sys/class/iommu/
# 确认 KVM 可用
ls -l /dev/kvm
dmesg | grep -i kvm
步骤 2 · 内核配置要求
Host 内核 .config 需启用以下选项:
CONFIG_KVM=y
CONFIG_KVM_VFIO=y
CONFIG_VFIO=y
CONFIG_VFIO_PCI=y
CONFIG_VFIO_PCI_CORE=y
CONFIG_RISCV_IOMMU=y
CONFIG_IOMMUFD=y
CONFIG_IRQ_BYPASS=y
CONFIG_KVM_GENERIC_HARDWARE_UNLOAD=y
建议追加内核启动参数:
iommu=pt
步骤 3 · 加载 VFIO 模块
sudo modprobe vfio
sudo modprobe vfio-pci
# 确认模块已加载
lsmod | grep vfio
步骤 4 · 定位 NVMe 设备并绑定到 vfio-pci
# 1. 找到 NVMe 设备的 PCI BDF 地址和厂商/设备 ID
lspci -nn | grep -i "Non-Volatile\|NVMe"
# 输出示例:
# 0000:01:00.0 Non-Volatile memory controller:
# Samsung Electronics Co Ltd NVMe SSD Controller [144d:a808]
# 2. 记录设备信息
PCI_BDF="0000:01:00.0"
VENDOR_ID="144d"
DEVICE_ID="a808"
# 3. 确认该设备当前使用的驱动
lspci -k -s $PCI_BDF
# 应显示 "Kernel driver in use: nvme"
# 4. 将设备从 nvme 驱动解绑
echo $PCI_BDF | sudo tee /sys/bus/pci/devices/$PCI_BDF/driver/unbind
# 5. 将厂商/设备 ID 写入 vfio-pci 的 new_id
echo "$VENDOR_ID $DEVICE_ID" | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id
# 6. 绑定到 vfio-pci
echo $PCI_BDF | sudo tee /sys/bus/pci/drivers/vfio-pci/bind
# 7. 验证绑定成功
lspci -k -s $PCI_BDF
# 应显示 "Kernel driver in use: vfio-pci"
# 8. 确认 VFIO 设备节点已生成
ls -l /dev/vfio/
# 应看到类似 /dev/vfio/NN 的组设备节点
⚠️ 注意:若 NVMe 是系统盘则不能直接解绑。需使用额外的 NVMe 卡,或从其他存储启动系统。
步骤 5 · 编译支持 RISC-V 的 kvmtool
# 安装依赖
sudo apt-get install libfdt-dev libssl-dev build-essential
# 克隆 kvmtool(建议使用包含 RISC-V 最新支持的分支)
git clone https://github.com/kvmtool/kvmtool.git
cd kvmtool
# 编译(指定 ARCH=riscv)
make ARCH=riscv
# 编译产物为 ./lkvm
./lkvm --help
步骤 6 · 启动虚拟机并透传 NVMe
kvmtool 使用 --vfio-pci 指定要透传的 PCI 设备:
sudo ./lkvm run \
-k /path/to/guest/Image \ # Guest 内核镜像
-d /path/to/guest/rootfs.ext4 \ # Guest 根文件系统(可选,也可从透传的 NVMe 启动)
-m 4096 \ # 内存大小 MB
-c 4 \ # vCPU 数量
--vfio-pci 0000:01:00.0 \ # 透传 NVMe 设备(关键参数)
--console serial \
-p "console=ttyS0 root=/dev/nvme0n1p1 rw" # Guest 内核参数,从透传的 NVMe 启动
--vfio-pci 可多次指定以透传多个设备:
--vfio-pci 0000:01:00.0 --vfio-pci 0000:02:00.0
步骤 7 · 在 Guest 中验证
# 查看 PCI 设备,应能看到透传的 NVMe 控制器
lspci -nn | grep -i nvme
# 查看 NVMe 块设备
lsblk
# 应看到 /dev/nvme0n1 等设备节点
# 测试读写
sudo fdisk -l /dev/nvme0n1
三、关键注意事项
1. 中断控制器要求
RISC-V 上的 PCIe MSI/MSI-X 中断透传需要 **AIA(Advanced Interrupt Architecture)** 支持,包括 APLIC 与 IMSIC。
若平台只有旧的 PLIC,MSI-X 可能无法正常工作,NVMe 只能退回传统 INTx 中断,性能较差。
2. IOMMU 组限制
VFIO 以 IOMMU 组为单位进行隔离。若 NVMe 与其他设备同组,需将整组设备透传给同一虚拟机,或借助 ACS(Access Control Services)拆分。
# 查看设备的 IOMMU 组
readlink /sys/bus/pci/devices/0000:01:00.0/iommu_group
# 查看同组所有设备
ls /sys/bus/pci/devices/0000:01:00.0/iommu_group/devices/
3. 内核版本建议
| 级别 | 版本 | 说明 |
|---|---|---|
| 最低 | Linux 6.8+ | 包含基础 RISC-V IOMMU 支持 |
| 推荐 | Linux 6.10+ | 或 Ventana Micro Systems 内核分支(含完整 KVM VFIO + irqbypass) |
| iommufd 路径 | Linux 6.2+ | 需启用 CONFIG_IOMMUFD |
4. 性能优化
- 启用
iommu=pt(passthrough 模式)以减少 DMA 映射开销 - 确保 Guest 内核同样启用了 NVMe 驱动与 PCIe 支持
- 使用 MSI-X 中断(依赖 AIA)而非 INTx
5. 常见问题排查
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
--vfio-pci 参数不识别 |
kvmtool 编译时未包含 VFIO 支持 | 检查编译配置,确保 vfio/pci.c 被编译进产物 |
| 设备绑定 vfio-pci 失败 | 设备正被占用 / IOMMU 未启用 | 确认设备未挂载,检查 dmesg | grep -i iommu |
| Guest 中看不到设备 | 中断路由问题 / PCI host bridge 未正确配置 | 检查 Guest 内核是否启用 PCI,查看 dmesg | grep -i pci |
| NVMe 识别但无法读写 | DMA 映射失败 / IOMMU 配置错误 | 检查 IOMMU 域配置,确认 iommu=pt 生效 |
| 性能很差 | 使用 INTx 而非 MSI-X | 确保平台支持 AIA,Guest 启用 MSI-X |
四、替代方案
若当前硬件/内核尚不满足 lkvm VFIO 透传条件,可考虑:
- QEMU + KVM --- RISC-V 上的 VFIO 支持更成熟,命令为
-device vfio-pci,host=0000:01:00.0 - virtio-blk 后端 --- 将 Host 上的 NVMe 作为 virtio-blk 暴露给 Guest(非透传,但兼容性好)
- OpenVMM --- 微软开源的 VMM,RISC-V 上亦有 VFIO 支持,文档称 NVMe 控制器可端到端工作
五、总结
在 RISC-V 上将 PCIe NVMe 透传给 lkvm 的核心流程与 x86/ARM64 类似(VFIO 解绑绑定 + --vfio-pci 参数),但关键瓶颈在于 RISC-V 平台的 IOMMU 和 AIA 中断支持是否完备。
建议做法:
- 在具备 RISC-V IOMMU 与 AIA 的硬件平台上,使用 6.10+ 内核尝试
- 遇到问题时,优先检查
dmesg中的 IOMMU 与 KVM 相关日志,确认底层依赖是否就绪