一句话
gpu 是 max2 上管理「显卡归宿主还是归虚拟机」的全局命令。它只写状态文件,重启后由开机服务落实驱动绑定 —— 因为显卡驱动在开机时就被图形栈拿走了,运行期不可能热切换。
命令速查
| 命令 | 作用 | 何时生效 |
|---|---|---|
gpu | 打印帮助 | — |
gpu list | 列出显示类设备(含同 IOMMU 组设备),每块卡下列出它的显示器 | 只读 |
gpu pass <卡> | 把该卡及其同组设备标记为「直通」 | 重启后 |
gpu host <卡> | 把该卡及其同组设备标记为「宿主」 | 重启后 |
gpu apply | 按状态文件落实绑定(开机由服务调用,平时不用手跑) | 立即 |
gpu bpc <卡> <显示器> <值> | 运行时改该屏色深上限并立即生效(值=6|8|10|12;只支持 AMD 卡) | 立即 |
gpu bpc pins / gpu bpc clear <卡> <显示器> | 列出全部手动 pin / 清除该屏的 pin(回到自动默认 6) | 立即(clear 只删记录,当前流不变) |
<卡> 的写法:BDF(01:00.0 或 0000:01:00.0),或 gpu list 里显示类设备的序号(0 起)。
<显示器> 的写法:X 输出名(如 DisplayPort-8,见 list 下挂行),或内核连接器名(DP-8),或 EDID 名片段(如 T24h;命中多台时必须用 X 输出名精确指定)。
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 = 展示名补一行即可。
list 下挂的显示器行
显示器(AMD Radeon 780M · 0000:65:00.0):
└ DisplayPort-9[LEN T24h-20]2560x1440@59.95 bpc上限6
└ DisplayPort-8[T24h-10]2560x1440@59.95 bpc上限6
└ DisplayPort-6[T24h-10]2560x1440@59.95 bpc上限6
└ eDP[GPD1001H]?@? bpc上限16- 每行 = X 输出名[EDID 名]分辨率@刷新率 bpc上限。bpc 列是 X 的
max bpc属性(起流时取的就是这个上限,≈实际色深)。 - X 输出名和内核连接器名编号对不上(
DisplayPort-8vscard1-DP-9)—— 脚本内部用 EDID 字节比对跨过去,显示给你的是 X 名(重设模式/截图时用的就是它)。 - 内屏 eDP 未启用时显示
?@?(无模式),这是正常的,只表示当前没点亮。 - X 没起(纯 TTY)时降级显示内核信息:
内核:DP-10[LEN T24h-20]已连接(图形会话未运行,无模式信息),不编刷新率。 - 双胞胎同型号屏 EDID 完全一样时按顺序认领(第一台拿第一个 X 名),两台的值永远一致,不会串。
gpu bpc(运行时改色深,立即生效)
起因:三屏 2K 菊花链在 8bpc 下超 HBR2 带宽黑屏,必须 6bpc;而 output_bpc 在 6.18 内核只读、X 的 max bpc 属性范围钉死 8..16 —— 唯一能把 6 写进去的口子是 debugfs 的 max_bpc,且写完必须重设一次同分辨率模式(modeset 会重读镜像,不重设就不生效)。这条命令把「写 + 重设 + 回读确认」做成一步。
| 命令 | 作用 |
|---|---|
sudo gpu bpc 0 DisplayPort-8 6 | 把该屏色深上限改为 6 并立即生效(自动重设同分辨率模式,约闪屏 1 秒) |
sudo gpu bpc clear 0 DisplayPort-8 | 清除该屏的手动 pin(当前流不变,下次事件后回到自动默认 6) |
gpu bpc pins | 列出全部手动 pin(不需要 sudo) |
真机往返实测(2026-10-23,三屏 2K 菊花链):bpc 0 DisplayPort-8 8 写 8(仅落盘,未重设);bpc 0 DisplayPort-8 6 触发 modeset,回读 bpc 上限 8 → 6,三屏全程有画面。
- pin 落点:
/var/lib/amdgpu-bpc/<EDID稳定id>,内容就是6/8/10/12其中一个数字。id 形如LEN_61F0_944134476(厂商_产品_序列号),跨连接器重编号稳定;双胞胎同 EDID(含序列号都一样)时共用一个 pin —— 值本来就该一样,不算问题。 - 和自动 bpc 机制不打架:
amdgpu-mst-bpc(开机/udev/唤醒三钩)改成了「有 pin 按 pin 写、无 pin 写默认 6」。udev 事件、睡眠唤醒、重启后重放的是你的 pin 值,手动值不会被吃掉;关自动机制不需要,pin 文件本身跨重启(/var/lib)。 - 判据:命令末行回读
bpc 上限 旧值 → 新值;gpu bpc pins见到记录;自动脚本日志default 0x6, pins honored。 - 边界:NVIDIA 卡带的屏报不支持(NVIDIA 无对等 knob);纯 TTY 下改不了(要重设模式,必须有 X);值只能是 6|8|10|12,其它一律拒绝;显示器 sink 上限更低时以回读为准(命令会明确提示)。
机制:为什么不能热切换
- 不用内核参数(
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 里)。 - bpc:
output_bpc在 6.18 内核是只读(写啥都是 EINVAL),别把它当 knob;X 的max bpc属性范围钉死 8..16,xrandr --set写不进 6 —— 必须走 debugfsmax_bpc+ 重设模式,gpu bpc已经包好了,手写容易漏第二步。 - bpc:sudo 下 DISPLAY 会丢 —— 脚本从会话进程的 environ 里找回 DISPLAY/XAUTHORITY;如果报「找不到可用的 X 会话」,先确认图形会话在,再贴
loginctl show-session查。
出处
- 实现:
/etc/nixos/modules/hardware/gpu-mode.nix+gpu-mode.sh+gpu-mode-names.conf;bpc 自动侧:gpu-amd-780m-mst-bpc.nix(三钩 + pin 优先,pin 目录/var/lib/amdgpu-bpc/由 tmpfiles 建) - 台账:
hermes-nixos-workspace/docs/gpu-passthrough-mode-switch.md§4(list 改版)/ §5.1(bind 分支首次真机成功)/ §6;docs/egpu-4060-weekend-runbook.md§10.7;bpc 子命令与 pin 机制见docs/gpu-bpc-subcommand.md