oculink显卡切换宿主驱动和直通KVM

一、总览

  • 客机里是 2 张显卡:一张半虚拟化显示控制器(QXL,无 3D,供远程查看)+ 一张直通的物理卡(承担全部 3D 算力)。并列关系,不是主备。
  • 因而有两条互不相干的输出路:路 A 把画面编码后送到宿主上的查看窗口;路 B 从直通卡的物理输出口直出。
  • 最省损耗的是路 B 接真显示器(零回传);什么都不接就只能吃跨卡回传的损耗,是三种形态里最差的。

计算链路

【计算链路】
┌─ 虚拟机内部(Windows)────────────────────────────────────────┐
│ 游戏 / 应用 ──DirectX/Vulkan──▶ 渲染调用 │
│ │ │
│ ┌────────────────┐ │ ┌──────────────────────────┐ │
│ │ 直通显卡 │◀──┼──▶│ QXL 虚拟显示控制器 │ │
│ │ (物理显卡) │ │ │ (半虚拟化,无 3D,仅 2D) │ │
│ └───────┬────────┘ │ └───────────┬──────────────┘ │
│ │ 显存/命令队列/中断 │ 帧缓冲 │
└───────────┼───────────────────────────┼───────────────────────┘
│ PCIe 直通(OCuLink 4.0 x4)│ 纯软件路径
▼ ▼
┌─ 宿主(Linux)────────────────────────────────────────────────┐
│ KVM / QEMU 进程 │
│ · vCPU 线程 ← 宿主 CPU 调度 · VM 内存 ← 宿主 DRAM │
│ · 系统盘 ← 宿主 NVMe 上的镜像 · 直通设备 ← VFIO 交 BAR/中断
│ ※ 一旦直通,宿主不再驱动它 ⇒ 宿主侧看不到这张卡 │
└────────────────────────────────────────────────────────────────┘

算力与访存由直通卡承担,宿主只负责调度与提供内存/磁盘;QXL 不参与计算。

显示链路

【显示链路】
应用渲染完成的画面
│
┌──────────────┴───────────────┐
▼ ▼
┌─ 路 A:虚拟显示(远程访问)─┐ ┌─ 路 B:直通显示(游戏直出)──┐
│ QXL 控制器 │ │ 直通显卡渲染 │
│ ▼ │ │ ▼ │
│ SPICE 编码 │ │ 显卡物理输出口 (DP/HDMI) │
│ ▼ │ │ ▼ │
│ 宿主上的查看窗口 │ │ 真显示器 │
│ (窗口里只有这一路) │ │ (不经宿主、零拷贝) │
└─────────────────────────────┘ └──────────────────────────────┘

· 路 A 与宿主接了什么显示器无关;路 B 只出现在「直通卡输出口实际连着的屏」上。
· 查看窗口里看不到路 B;路 B 也不会出现在窗口里。

二、画面走向:接哪根线、怎么判断

外接屏采用「一台显示器两根线」:一根进核显、一根进外置卡。切换屏上的输入源时看到什么,取决于那根线背后的卡此刻归谁用:

屏的输入源该卡此刻归谁看到什么
核显那一路始终归宿主宿主桌面本身 —— 路 A 只是宿主桌面上的一扇窗
外置卡那一路 · 宿主模式归宿主宿主桌面的一部分(该卡驱动的扩展屏/主屏)
外置卡那一路 · 直通模式归虚拟机客机画面(路 B)—— 客机桌面与游戏

三条推论:①切核显看宿主、切外置卡看客机(直通下),一台屏看两侧;②同一根线在两种模式下是两个世界;③直通下虚拟机未启动时那一路是黑的(卡已不在宿主手里),不是故障。

从哪看出画面走的是哪条路:

观察位置判据
客机「显示设置」能看到两个显示适配器/多台显示器;被设为主显示的那套接收桌面与游戏窗口
宿主查看窗口窗口内只有虚拟那一路;直通那一路永远不会出现(它走物理输出口)
宿主侧显卡与输出口直通后宿主看不到这张卡的输出口;仍能看到 ⇒ 说明并没有直通
物理显示器只有接在直通卡输出口上的那块会亮,与查看窗口互不影响

三、直通卡没接显示器时:回传与损耗

前提:显卡没有活动显示器 ≠ 不能渲染。驱动允许把它当纯渲染设备,成品帧由系统合成器跨适配器搬到「有显示的那张卡」再输出:

【回传路径】
直通卡渲染 ─▶ 成品帧 ─▶ 经 PCIe 回传到虚拟适配器 ─▶ 合成 ─▶ 编码 ─▶ 通道送出 ─▶ 查看窗口
(渲染快) (往返搬运:主要开销) (软件合成) (CPU 编码) (网络/USB 通道)
形态直通卡是否接真显示器画面如何被看到损耗
物理直出(最优)接直接看那块屏零回传损耗(渲染即输出)
假负载 + 采集接假负载,或在客机内装间接显示驱动造虚拟显示器用采集/串流抓走该输出再送到别处看渲染无损耗;采集与编码约数十毫秒延迟
跨卡回传(兜底)不接只能从查看窗口看跨适配器搬运是主要瓶颈(分辨率/刷新率越高越明显)+ 虚拟控制器只做 2D 合成 + 编码通道开销 ⇒ 帧率下降、延迟升高、可能撕裂跳帧,能玩但不适合竞技类

四、模式切换的设计:把「开关」从世代里拆出去

可行,且比「用系统重建管开关」合理得多。但「必须重启」绕不过 —— 它不在配置层而在驱动层;脚本能省掉的是世代堆积,不是重启。

判据(本机实测)实测意义
IOMMU已启用,设备有独立分组直通前提已满足
直通所需内核模块三个模块都在本机内核里无需为此再声明
按设备指定期望驱动的接口支持:每个 PCI 设备都有 driver_override(当前为空)关键:绑定决策可以不写内核参数,因而能移出世代
目标驱动占用情况引用计数非零,被显示相关子模块持有不可热切换的直接证据 —— 卸载它等于停掉整个图形栈,实际等同重启

注意:外置卡与其音频功能同属一个分组,直通必须整组给。

机制世代开销评价
A. 写内核参数参数里写死直通哪些设备,改一次重建一次每次一个世代开关被焊进「配置基线」
B. 期望驱动 + 启动期应用(推荐)系统层只搭机制:模块可用 + 一个启动期服务;开关值由脚本写进状态文件,服务开机读取并应用零开关是状态不是配置,与不可热切换的事实天然一致

两层共同点:都要重启。差别只在「切换动作会不会污染版本基线」。

【三层分工】
系统声明层:IOMMU / 模块 / 启动期应用服务 / 显示栈容错 ← 不含具体哪张卡,不随开关变
状态层 :每张卡的「期望模式」标记(系统状态目录,不在配置树里)
应用层 :开机读状态 ⇒ 写期望驱动并绑定/解绑,必须早于图形会话

命令行工具(运行时,零世代) 状态文件 启动期服务
───────────────────────── ────────────── ───────────────────
gpu 帮助
gpu list/devices ─▶ 列出显卡:当前驱动 + 期望模式 + 是否一致
gpu pass <卡> ─▶ 写「期望=直通」──┐
gpu host <卡> ─▶ 写「期望=宿主」──┤
▼
重启 ─▶ 服务读状态 ─▶ 应用绑定 ─▶ 图形会话启动

五、落地要点

代价表现缓解 / 铁律
引入非声明式状态「实际绑定」不再能只从配置推出list 并列给出「当前驱动 / 期望模式 / 是否一致」
时序敏感服务晚于图形会话 ⇒ 切换静默失效显式早于图形会话启动 + 在 list 里回读实际绑定自证
两处可能打架系统层若又写死绑定参数,会与状态文件冲突铁律:系统声明层永不写具体设备的绑定参数
调试面变宽要查 状态文件 → 服务日志 → 驱动绑定 三处查询面收敛为 list 一个入口

最容易漏掉的一处 —— 显示栈容错:卡被交给虚拟机后宿主就失去它。若宿主图形配置硬编码了「这张卡负责哪些输出」,开机就会因找不到设备而报错或掉回兜底布局。这属于系统声明层必须一起搭好的结构,脚本补救不了 —— 应让显示配置按「实际存在的设备」自适应,或启动期按状态选配置。

落点:归拢到硬件域(modules/hardware)职责
声明文件IOMMU 与模块可用性 + 启动期应用服务(读状态→应用绑定)+ 显示栈容错
脚本文件运行时工具本体(很薄:list 读设备树、pass/host 写状态)

一句话:系统层持久化「环境基础 + 脚本」,脚本管理「运行时」,重启生效。

切换生效后,光切到一个含直通设备的模具还不够,还有三处必须一起满足:

#必须项不满足会怎样
1直通设备整组给显卡与音频同组,只给一半 ⇒ 启动被拒(分组不可用)
2模具含反虚拟化隐藏项客机显卡驱动识别出虚拟化环境,拒绝工作或报错
3启动虚拟机之前已处于直通模式模式不符 ⇒ 管理器可能自动抢绑(与状态层打架)或启动失败

另两处属「配套而非模具」:宿主图形栈要容忍「这张卡不见了」;客机里仍需保留虚拟显示控制器,否则查看窗口没画面。因此虚拟机侧两种做法都成立:手动切直通模具,或按当前显卡模式自动挑模具(一一对应时更省心)。

六、实测现状与边界

判据实测
外置卡当前归谁归宿主显卡驱动,未交给虚拟机
直通设备占用无任何设备处于直通绑定状态
宿主可见的输出口外置卡两个口「已连接」;核显侧内屏与另一外接口「已连接」⇒ 天然具备物理直出通路
现有虚拟机方案的显卡形态全部是「虚拟控制器 + 远程显示通道」,无一含直通设备
显示拓扑外接屏「一台两根线」分接核显与外置卡;另有一台屏走该屏的菊花链下游

落地要动三处:①把卡交给虚拟机(宿主随即失去它,显示布局需提前规划);②虚拟机的显示配置(保留虚拟控制器 + 加直通设备);③输出通路二选一(真显示器 / 客机内造虚拟显示器 + 采集),都不做就只能吃跨卡回传的损耗。

边界:全文属配置推演 —— 本机尚未执行直通,故所有直通形态的结论均按机制推出,未实测;「跨适配器回传的帧率损失」也未量化,只给构成与定性排序。多屏串联时下级屏显示什么取决于上级屏向下转发哪一路(本文按「多数显示器只转发 DP 输入」推演),切核显时下级屏可能无信号,需现场核对。半虚拟化显示控制器型号随方案可选,本文按「无 3D 的 2D 控制器」描述。本次为只读梳理:未改动任何配置,未启停任何虚拟机。

七、整体结论

  • 架构判断成立:开关交给运行时工具、机制留在系统声明层,前提是不写内核参数,改用按设备的期望驱动接口。
  • 「重启生效」是物理事实不是设计缺陷:绑定发生在内核探测期,且驱动被图形栈持有。要区分切换成本(消不掉)与版本污染(本方案消掉)。
  • 两个子命令就够:list 一屏给出设备/当前驱动/期望模式/是否一致;pass/host 只写状态;真正逻辑全在启动期服务里。
  • 落地前必须先解决显示栈容错,否则第一次直通就会把宿主桌面搞挂。
  • 冷插拔禁忌不变:外置通路切换全程断电,不因脚本化而放松。