一句话
gpu 是 max2 上管理「显卡归宿主还是归虚拟机」的全局命令。它只写状态文件,重启后由开机服务落实驱动绑定 —— 因为显卡驱动在开机时就被图形栈拿走了,运行期不可能热切换。
命令速查
| 命令 | 作用 | 何时生效 |
|---|---|---|
gpu | 打印帮助 | — |
gpu list | 列出显示类设备(含同 IOMMU 组设备) | 只读 |
gpu pass <卡> | 把该卡及其同组设备标记为「直通」 | 重启后 |
gpu host <卡> | 把该卡及其同组设备标记为「宿主」 | 重启后 |
gpu apply | 按状态文件落实绑定(开机由服务调用,平时不用手跑) | 立即 |
<卡> 的写法:BDF(01:00.0 或 0000:01:00.0),或 gpu list 里显示类设备的序号(0 起)。
gpu list 的列
# 类型 设备 PCI 位置 IOMMU 组 当前驱动 期望 一致
0 VGA AMD Radeon 780M · 引导显卡 0000:65:00.0 18 amdgpu 宿主 ✓- 序号:就是
gpu pass/host的入参(0 起)。序号会随硬件在位情况变化 —— 没插 eGPU 时 0 号就是 780M,插上后 4060 通常排前面 ⇒ 脚本里禁止写死序号。 - 设备:由 udev 的
pci.ids动态派生;引导显卡加· 引导显卡后缀;同 IOMMU 组的音频等功能行用└缩进。 - 当前驱动 / 期望 / 一致:
一致为✓表示已经是期望状态;待重启表示状态文件已改、但驱动还没换(要重启)。 - pci.ids 只有代号、没有营销名时,在
/etc/gpu-mode-names.conf按vendor:device = 展示名补一行即可。
机制:为什么不能热切换
- 不用内核参数(
vfio-pci.ids=…):那会把开关焊进「配置基线」,改一次就得改配置重建。改用每设备自带的driver_override接口 —— 启动期按状态文件决定绑谁。 - 时机卡在图形会话之前:
gpu-mode-apply.service(oneshot,wantedBy=multi-user.target、before=display-manager.service、after=local-fs.target)。晚一步图形栈就把显卡拿走了。 - 直通必须整组:显卡和它的音频功能同属一个 IOMMU 组,只切一半会被虚拟机拒绝。
- 改状态不产生 nix 世代:状态写在
/var/lib/gpu-mode/,不属于声明式配置。
数据落点
/var/lib/gpu-mode/<BDF>.mode
vendor=0x10de # 身份字段:防 BDF 漂移后误操作别的设备
device=0x2882
mode=passthrough # host | passthrough三个字段都要有。apply 时会比对 vendor/device:不符就跳过该设备(典型场景:没接 eGPU,那个 BDF 上换成了别的设备)。
怎么确认生效(判据)
gpu list # 期望列=直通、一致列=✓ 即已落实
basename $(readlink -f /sys/bus/pci/devices/0000:01:00.0/driver) # 期望 vfio-pci
nvidia-smi # 直通后应报 Failed to initialize NVML(预期,不是故障)
ls /sys/class/drm/ # 4060 的 card 消失,只剩 780M
journalctl -u gpu-mode-apply -b | tailapply 的日志会逐设备打印 <BDF> <旧驱动> → vfio-pci(直通)或交回内核探测(宿主)。
本机现状(2026-10-19,办公室没接 eGPU)
| 项 | 实况 |
|---|---|
gpu list | 只有一行:0 VGA AMD Radeon 780M · 引导显卡 0000:65:00.0 组18 amdgpu 宿主 ✓,结论行「全部设备已处于期望模式」 |
| 状态文件 | 仍是 4060 的 mode=passthrough(vendor=0x10de device=0x2882)—— 留着不动,回家插上 eGPU 重启即按直通生效 |
| 本次开机 apply | ⚠ 状态文件身份不符(0000:01:00.0 现在是 0x1c5c:0x174a,记录是 0x10de:0x2882)—— 跳过;0000:01:00.1 不在总线上 —— 跳过;服务状态 Finished |
2026-10-19 修掉的坑:身份不符原本会 rc=1,导致没接 eGPU 时每次 nixos-rebuild 都被 gpu-mode-apply 的失败中断。现在改为「跳过、不置错」—— 设备不在位属预期状态,不是失败。真正的写失败(driver_override / bind 失败)仍会让服务失败。
坑与注意
- BDF 会漂移:eGPU 接上时是
0000:01:00.0+01:00.1;没接时同一 BDF 可能是别的设备 ⇒ 靠状态文件里的 vendor/device 兜住,永远别只看数字。 - 直通后宿主会失去这张卡的输出:显示布局可能变化(尤其那台显示器还在用 4060 那根线时)。
- 回宿主必须两步:
sudo gpu host 0+ 重启。没有「关掉虚拟机就自动回宿主」这回事(详见服务笔记wK8ClezMHQJh)。 - 回宿主那条
drivers_probe分支在本机还没实跑过一次(项目台账记的待办)—— 第一次真回宿主时留意日志里是否出现「交回内核探测失败」。 - 直通切换后记得重挂 USB 鼠标:
kvm usb 5(USB 设备同样不写死在 XML 里)。
出处
- 实现:
/etc/nixos/modules/hardware/gpu-mode.nix+gpu-mode.sh+gpu-mode-names.conf - 台账:
hermes-nixos-workspace/docs/gpu-passthrough-mode-switch.md§4(list 改版)/ §5.1(bind 分支首次真机成功)/ §6;docs/egpu-4060-weekend-runbook.md§10.7