virtio-nvgpu 在 KVM 客户机中实现接近原生的 NVIDIA GPU 性能
在 KVM 客户机内接近原生的 NVIDIA GPU 访问
要点: virtio-nvgpu 在驱动 ABI 级别在主机和客户机之间转发 NVIDIA 内核驱动 ioctl,使 Linux KVM 客户机能够运行未经修改的 NVIDIA 用户态驱动,并实现与裸机相比在 2% 以内的渲染性能,且几乎没有额外的 CPU 开销。
工作原理
- 客户机内核驱动(GPL) 注册常规的
/dev/nvidia*节点。在每次ioctl()时,它将原始请求序列化到 virtqueue 上;在mmap()时,它将共享内存窗口直接映射到客户机进程。 - 设备 crate(Apache-2.0,Rust) 在 VMM 中运行,接收这些 virtqueue 消息,转换嵌入的文件描述符和指针,并将 ioctl 转发到主机的 NVIDIA 驱动。缓冲区簿记在此处进行。
- 事件 virtqueue 在 GPU 完成工作时通知客户机,消除了忙轮询。
- 隔离计划——未来的每客户机沙箱辅助进程(isolate 组件)将持有真实的设备文件描述符;目前 VMM 进程直接持有它们。
该架构与 chromeos/virtio-media 类似:GPL 客户机驱动旁边是一个宽松许可的、与 VMM 无关的设备 crate,所有 VMM 交互都通过 trait 表达。
性能结果(RTX 3060,驱动 595.99.02)
| 指标 | 客户机(virtio-nvgpu) | 裸机主机 |
|---|---|---|
| 典型游戏绘制的帧时间(≥2 毫秒) | -0.4% 到 +1.7%(在噪声范围内) | — |
| 超轻量绘制的帧时间(≤0.5 毫秒) | 最高 +40.8%(等待成本占主导) | — |
| 100 fps 非节流运行(12 秒)的 CPU 使用率 | 0.37 秒 | 0.40 秒 |
| 每帧主机-客户机穿越次数 | ≈0.02(每约 59 帧一次穿越) | 数千次(Venus) |
| 多客户机扩展(一台 RTX 3060 上 4 个客户机) | 总计 103.7 fps,每个约 25.8 fps,与单客户机性能相同 | — |
解读: 对于 GPU 密集型工作负载,客户机与裸机无法区分;唯一可测量的性能损失出现在帧预算小于 GPU 唤醒延迟(约 0.02 毫秒)时。
支持的使用场景
- Vulkan 和 OpenGL 渲染(无头 EGL 可用)
- 客户机内的 Wayland 合成器,呈现屏幕外帧
- CUDA 内存分配以及与 Vulkan/GL 的零拷贝互操作
- 直接从客户机侧 CUDA 缓冲区进行 NVENC 编码(H.264/H.265 输出)
该设计明确排除了物理扫描输出、MIG/SR-IOV 和完整的统一虚拟内存(cudaMallocManaged)。
驱动 ABI 处理
NVIDIA 的内核驱动 ABI 在不同版本之间会变化。virtio-nvgpu 提供了明确的 ABI 配置文件:
| 配置文件 | 覆盖范围 |
|---|---|
535.129.03 |
535.129.03 直到下一个配置文件 |
580.178.04 |
580.178.04 直到下一个配置文件 |
595.71.05 |
595.71.05 及更新版本 |
任何早于 535.129.03 的驱动都会被拒绝。较新的驱动在最后一个配置文件下被接受,直到添加新的配置文件。这些配置文件是从 NVIDIA 的 open-gpu-kernel-modules 源码机械生成的,而非手工编写。
与其他 GPU 虚拟化方法的比较
| 方法 | 客户机驱动 | API 转换 | GPU 共享 | NVENC 支持 | 隔离性 |
|---|---|---|---|---|---|
| VFIO 直通 | 主机驱动绑定到客户机 PCI 设备 | 无(原生) | 每个 GPU 一个虚拟机(或专用显卡) | 可用(原生) | 强(IOMMU 隔离客户机内存) |
| virtio-gpu + Venus | 客户机 Mesa 驱动(virgl) | 每次绘制调用序列化与重放 | 多个虚拟机共享 GPU | 不可行(缓冲区位于主机) | 中等 |
| virtio-nvgpu(本项目) | 未经修改的 NVIDIA 用户态驱动 | 在驱动 ABI 级别转发 ioctl(每帧约 1 次穿越) | 多个虚拟机共享一张显卡(已测试最多 4 个) | 可用,零拷贝 | 弱——所有 ioctl 都被转发;主机驱动在 TCB 中 |
关键见解: 通过将转换边界从图形 API 移动到内核驱动,
virtio-nvgpu消除了 Venus 每次绘制调用的巨大开销,同时仍允许多个客户机共享 GPU。
安全与隔离考虑
- 没有 IOMMU 边界将客户机 GPU 工作与主机内存分离;主机 NVIDIA 驱动运行在主机的 IOMMU 域中,实际上属于可信计算基的一部分。
- 当前实现转发 所有 驱动 ioctl(除了 ABI 配置文件明确拒绝的)。一个宽松标志(
--permissive-abi)可以进一步放宽此限制以用于调试。 - 未来的工作(
isolate/)旨在将真实的设备文件描述符沙箱化到每客户机辅助进程中,但尚未实现。 - 与 VFIO 相比,攻击面更大:被攻破的客户机可能发出任何允许的 ioctl,类似于在主机上运行多个共享 GPU 的进程。
社区反馈(Hacker News 亮点)
- @refibrillator 指出了三种主要的 GPU 共享方法(VFIO、virtio-gpu/Venus、virtio-nvgpu),并指出
virtio-nvgpu提供最弱的隔离,因为主机驱动在 TCB 中。 - @orphereus 询问为什么不直接使用常规 GPU 直通;答案是直通无法在虚拟机之间共享 GPU。
- @markasoftware 询问这与 gVisor 的
nvproxy有何不同;README 将nvproxy列为直接灵感来源,但没有详细说明差异。 - @ericd 对无需 VFIO 复杂性的高性能游戏虚拟机表示兴奋。
- @majorchord 询问 Windows 客户机支持;当前项目仅针对 Linux 客户机。
仓库布局
| 目录 | 许可证 | 用途 |
|---|---|---|
driver/ |
GPL-2.0 | 客户机内核模块,通过 virtqueue 转发 ioctl 和 mmap |
device/ |
Apache-2.0 | 与 VMM 无关的 Rust crate,实现 virtio 设备和 ABI 转换 |
isolate/ |
Apache-2.0 | 未来每客户机沙箱辅助进程的设计说明(尚无代码) |
gen/ |
— | 从 NVIDIA 源码自动生成的 ABI 表 |
protocol/ |
BSD-3-Clause 或 GPL-2.0+ | 共享的线上格式和 ABI 定义 |
快速开始
- 构建客户机驱动(
driver/)并在 KVM 客户机内加载。 - 通过实现所需的 trait(描述符链 I/O、事件处理、客户机/主机内存映射),将
device/crate 集成到你的 VMM 中。 - 在客户机内运行 Vulkan 或 Wayland 应用程序;README 中的基准测试脚本(
BENCHMARKS.md)验证接近原生的性能。
限制与未来工作
- 已在单个 RTX 3060 上测试最多 四个 客户机;更多客户机未经测试。
- 仅测试了驱动版本 595.99.02(RTX 3060)和 615.71.09(RTX A2000);其他显卡未经测试。
- CUDA 功能被转发,但除枚举外未进行彻底验证。
- 尚不支持 Windows 客户机;项目仅针对 Linux。
- 隔离改进(每客户机沙箱、seccomp 过滤器)已计划但尚未实现。
许可证
仓库包含三个许可区域:客户机驱动为 GPL-2.0,设备 crate 和 isolate 设计为 Apache-2.0,共享协议头文件为 BSD-3-Clause(或 GPL-2.0+)。
总之,virtio-nvgpu 证明了在 ABI 级别转发 NVIDIA 驱动 ioctl 可以为 KVM 客户机提供接近裸机的 GPU 性能,同时允许多个虚拟机共享一张显卡,尽管隔离性比 VFIO 直通弱。