virt-report虚拟化社区动态

虚拟 CPU 模型与迁移兼容性

本文由 OpenAI Codex (GPT-6-Astra) 辅助编写。

一、x86 CPU 能力差异与迁移兼容性

一台虚拟机在主机 A 上启动,应用发现 CPU 支持 AVX2,于是选择了向量计算实现。主机 B 也使用 x86 处理器,却不支持 AVX2。如果把内存与寄存器原样搬过去,程序继续执行时就可能遇到非法指令。目标能运行相同操作系统,并不意味着它能接着执行这台已经运行的虚拟机。

CPU 模型规定客户机看到的指令能力、部分控制接口和状态布局。迁移需要目标维持这些承诺,同时恢复已经产生的寄存器、扩展状态和时钟。下面先区分能力与状态,再用两台主机的例子说明模型选择、兼容检查和迁移边界。

配置从管理层向下落实:Libvirt 选择模型与特性,QEMU 展开模型并与加速器能力比较,KVM 按客户机定义处理 CPUID 和运行状态,客户机操作系统及应用据此选择代码路径。宿主 CPU 型号相同,也仍需比较这些软件层最终提供的能力。

源码版本:QEMU 10.2.0、Libvirt 12.0.0。

二、需要保持的三层 CPU 约定

层次主要内容迁移时的要求
可见能力CPUID 特性、拓扑、物理地址位数、可用控制接口目的端维持运行实例的 CPU 定义,不能临时拿掉已用能力
运行状态通用寄存器、控制寄存器、MSR、XSAVE 扩展状态、待处理事件迁移格式能表达,目的端 KVM 能接收并正确恢复
机器环境机器类型、设备模型、时钟配置及其他迁移状态与 CPU 一起构成可继续执行的系统,CPU 检查通过仍不足以证明整机兼容

CPUID 相当于客户机可以使用的接口目录,运行状态则是当前正在使用这些接口的结果。例如暴露 AVX 会带来向量寄存器和扩展状态管理要求。简单比较两份 CPU flags 列表,无法覆盖全部状态恢复和机器兼容约束。

因此,兼容性要在两个时刻检查:启动前选出目标池能够提供的能力;迁移前确认具体目的端仍能提供该运行实例的能力并恢复当前状态。前一次检查决定客户机可以做什么,后一次防止硬件或软件变化破坏已有约定。

KVM 路径不能普遍靠软件补齐缺少的指令特性。某些功能可由 KVM 或 QEMU 模拟,另一些必须由宿主硬件提供;应以目标加速器报告的能力为准。把 host 换成另一个型号名,也不能让不支持该指令的宿主自动具备它。

三、固定型号、host-model 与 host-passthrough

QEMU 的命名模型给出一个预定义基础,可以额外开启或关闭特性。Libvirt 的 custom 模式可明确选择模型、匹配策略与特性要求,适合维护一组迁移主机共同支持的配置。带版本的模型名,例如 Haswell-v4,比未固定版本的别名更容易复现;别名的解析还可能受机器类型影响。

host-model 由 Libvirt 根据当前宿主和 QEMU 能力构造接近宿主的命名模型及特性集合。进行实时迁移时,完整 CPU 定义会传给目的端,由目的端保持这次运行实例的定义;目的端依然必须支持它。在另一台机器上重新冷启动时,host-model 可以重新计算,不能把两次启动都使用这个配置名理解为能力永远相同。host-model 的启动与迁移语义

host-passthrough 尽量暴露宿主能力,适合优先利用本机特性的场合。迁移时更依赖双方硬件、内核、微码和 QEMU 环境的一致性。migratable='on' 主要筛掉已知不可迁移的特性,并不把任意两台宿主变成兼容目标。应结合宿主域能力及实际迁移验证选择模式。直通模式的限制

四、两台主机的共同 CPU 能力基线

用一个简化集合说明选择过程:A 可提供 {SSE4.2, AVX, AVX2},B 可提供 {SSE4.2, AVX}。若需要在 A、B 之间迁移,就在启动前选择双方支持的模型,并检查依赖关系、扩展状态和其他约束。这里的集合用于解释,真实配置不能只对三个 flags 求交集。

从宿主能力到迁移检查
运行前确定两端都能提供的客户机 CPU 定义;运行后保持该定义,并单独恢复 CPU 状态。目标缺少已经暴露的能力时,迁移应被拒绝。
运行前确定两端都能提供的客户机 CPU 定义;运行后保持该定义,并单独恢复 CPU 状态。目标缺少已经暴露的能力时,迁移应被拒绝。 打开原图
Mermaid 源码
flowchart TB
  accTitle: CPU 能力选择与迁移状态恢复
  accDescr: 源端与目的端在运行前共同限定客户机 CPU 能力。运行后目标必须保持这个定义,并恢复寄存器、扩展状态与时钟。缺少已暴露能力时拒绝迁移。
  A["主机 A<br/>SSE4.2 · AVX · AVX2"] --> C["启动前确定共同 CPU 定义<br/>模型版本 + 显式特性 + 依赖检查"]
  B["主机 B<br/>SSE4.2 · AVX"] --> C
  C --> V["VM 按固定定义运行<br/>生成寄存器与扩展状态"]
  V --> T{"目标能保持 CPU 定义<br/>并恢复状态?"}
  T -->|满足| R["恢复状态并继续执行"]
  T -->|缺少已暴露能力| X["拒绝迁移<br/>不能在线删去客户机能力"]
  classDef source fill:#eaf0f6,stroke:#8198ac,color:#263c4c;
  classDef guest fill:#e4f1ec,stroke:#438575,color:#173e34;
  classDef check fill:#fff4db,stroke:#b89a55,color:#59491e;
  class A,B source;
  class C,V,R guest;
  class T,X check;

如果 VM 已在 A 上暴露了 AVX2,不能在迁移中途删掉它来适配 B。客户机可能已选择实现、建立扩展状态或生成对应机器码。应更换满足要求的目标;若要降低模型,通常需要停止虚拟机,在下一次启动前调整配置,并确认操作系统和应用的最低要求。

在源、目的宿主上,分别连接对应 QEMU 的 QMP,查看各自加速器下的模型定义与展开结果。新建的直接 QMP 连接先发送 qmp_capabilities 完成协商,再执行下面两条查询;这些请求在宿主管理通道执行,不是客户机中的命令:

{"execute":"query-cpu-definitions"}
{"execute":"query-cpu-model-expansion","arguments":{
  "type":"full","model":{"name":"Haswell-v4"}
}}

第一条的 unavailable-features 反映该加速器缺少的要求;第二条返回模型展开属性,便于核对 A、B 两端是否得到相同定义。展开和可运行性检查是两个步骤,QEMU 在 realize 阶段另行过滤能力,启用 enforce 时将缺少要求作为错误。结果为空的特性缺口也只覆盖 CPU 检查,设备与整机迁移仍有其他条件。模型加载、展开与过滤的分工

这里的 Haswell-v4 是待检查的候选模型,并未表示前面的 A、B 都能使用它。若 B 报告候选模型缺少 AVX2,就回到启动前的基线选择;若源 VM 已经按含 AVX2 的定义运行,则改选能够接收它的目的端。这两条查询描述候选模型及加速器能力,运行实例实际采用的 CPU 定义还需与管理配置核对。

使用 Libvirt 时,domcapabilities、CPU baseline/compare 相关接口有助于在实际机器和虚拟机类型下筛选配置。比较时还要记录明确的机器类型、QEMU 版本和特性策略;“两端都选了同一个简称”不足以代替比较。

五、源码中的模型展开与状态恢复

  1. 加载定义x86 CPU 对象从模型定义和用户属性构造初始能力;命名模型的版本决定默认属性。
  2. 展开能力x86_cpu_expand_features() 补齐依赖于宿主或加速器的内容,处理用户指定特性。
  3. 检查可运行性x86_cpu_filter_features() 将请求与加速器支持比较,记录不能提供的特性;realize 根据 enforce 等配置决定失败或告警。
  4. 建立并运行 vCPUQEMU 设置 KVM 的 CPUID 和状态,客户机按已确定的定义执行。
  5. 迁移恢复状态vmstate_x86_cpu 及其子节描述寄存器和扩展状态;加载回调检查部分状态条件,再交给目标 KVM 恢复。

vmstate_x86_cpu 包含基本字段和按条件出现的子节。CPU 模型定义与这份状态流各负其责:模型限定可用接口,迁移流保存某一时刻的值。目的端不能只认得流里的字段名,还要能提供它们要求的语义。

时钟是容易遗漏的边界。TSC 频率、缩放支持和客户机可见的时钟承诺会影响迁移;两台 CPU 都支持相同向量指令,不代表它们的时钟环境已经匹配。cpu_post_load() 检查显式 TSC 频率约束,具体时间连续性见KVM 虚拟时钟与定时器。

六、硬件与软件演进中的兼容基线

较保守的通用模型方便兼容旧宿主,但可能达不到新操作系统或编译产物要求的 x86-64 能力等级。按硬件代际命名的模型让选择更清楚,模型版本进一步固定默认特性;安全缓解相关能力又使微码、内核与型号选择联系起来。基线过低会牺牲新指令与缓解能力,基线过高则缩小迁移范围。

可将宿主按共同能力划成迁移池:池内维持可验证的 CPU 定义,扩容和升级时重新检查;需要新特性的业务进入较新的池。这样比全环境永久采用最低能力更灵活,但管理系统需要记录每台 VM 的运行时 CPU 定义,不能只保存一个笼统的池标签。

验证应覆盖启动前能力比较、迁移中的扩展状态保存,以及恢复后的持续执行。例如使用向量运算的负载,要在源端实际写入相应向量状态,迁移后继续计算并核对结果;同时检查时钟和业务延迟。一次空闲 VM 能成功迁移,只验证了那次执行所触及的状态,不能代表所有扩展能力。

七、参考资料