海光 CPU Windows libvirt 虚机蓝屏问题

May 3, 2024 13:00 · 951 words · 2 minute read Virtualization Linux KubeVirt

一句话描述现象:基于 KubeVirt 底座的 Windows 虚拟机在海光处理器物理节点上启动后蓝屏。

和 OpenStack 一样,KubeVirt 虚机本质上也是 libvirt - qemu - kvm 的模式。

真实原因

某些操作系统并不支持海光(Hygon)CPU:Windows 在读取到 CPU 供应商为海光后,会直接蓝屏。并不是完全不能跑,而是开发商(微软)不可能针对市面上所有的 CPU 开发与测试其操作系统,只会支持主流的处理器厂家的主流 CPU;为了避免用户在海光之类非主流国产厂商的处理器上使用 Windows 时可能遇到一些奇怪的问题,就采取了一刀切的方式。

Linux 上查看 CPU 详细信息的方式:

$ lscpu
Architecture:        x86_64
CPU op-mode(s):      32-bit, 64-bit
Byte Order:          Little Endian
CPU(s):              128
On-line CPU(s) list: 0-127
Thread(s) per core:  2
Core(s) per socket:  32
Socket(s):           2
NUMA node(s):        1
Vendor ID:           HygonGenuine
BIOS Vendor ID:      Chengdu Hygon
CPU family:          24
Model:               1
Model name:          Hygon C86 7285 32-core Processor
BIOS Model name:     Hygon C86 7285 32-core Processor

Vendor ID 一行表明 CPU 供应商为海光(Hygon),我们对比看一下英特尔的 CPU:

$ lscpu | grep "Vendor ID"
Vendor ID:           GenuineIntel
BIOS Vendor ID:      Intel(R) Corporation

如果不做处理,libvirt 在启动 qemu-kvm 时会直接使用宿主机的 CPU Vendor ID,即虚机内外保持一致:上述情景即是。

这点也可以在 qemu 的源码中证实:

https://gitlab.com/qemu-project/qemu/-/blob/v7.2.0/target/i386/cpu.c#L5097-5109

static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model)
{
    /* sysenter isn't supported in compatibility mode on AMD,
     * syscall isn't supported in compatibility mode on Intel.
     * Normally we advertise the actual CPU vendor, but you can
     * override this using the 'vendor' property if you want to use
     * KVM's sysenter/syscall emulation in compatibility mode and
     * when doing cross vendor migration
     */

    /*
     * vendor property is set here but then overloaded with the
     * host cpu vendor for KVM and HVF.
     */
    object_property_set_str(OBJECT(cpu), "vendor", def->vendor, &error_abort);
}

解决方案

因为海光 CPU 采用的是 AMD EPYC 架构,所以我们在 libvirt domain 中修改供应商,将其伪装成 AMD EPYC 处理器:

  <cpu mode='custom' match='exact' check='none'>
    <model fallback='forbid' vendor_id='AuthenticAMD'>EPYC</model>
  </cpu>

需要修改 KubeVirt Domain API 中的 CPU 字段定义实现:

type CPUModel struct {
    Fallback string `xml:"fallback,attr,omitempty"`
    VendorID string `xml:"vendor_id,attr,omitempty"`
    Value    string `xml:",chardata"`
}

type CPU struct {
    Check    string       `xml:"check,attr,omitempty"`
    Mode     string       `xml:"mode,attr,omitempty"`
    Model    *CPUModel    `xml:"model,omitempty"`
    Features []CPUFeature `xml:"feature"`
    Topology *CPUTopology `xml:"topology"`
    NUMA     *NUMA        `xml:"numa,omitempty"`
}

修改 virt-launcher 中的 converter,识别宿主机 CPU 供应商 ID:如果宿主机处理器为海光(Hygon),就将其伪装成 AMD EPYC。

OpenStack 或其他基于 libvirt 的 IaaS 系统中相同问题的处理方法也类似,可参考 https://hlyani.github.io/notes/openstack/openstack_hygon_patch.html

经过验证 Windows 虚机可成功启动,libvirt 启动 qemu 时带上了参数 -cpu EPYC,vendor=AuthenticAMD

$ ps -ef | grep qemu-kvm
/usr/libexec/qemu-kvm -name guest=mec-ba3abbb298f043fabaa0014644433b30_i-0778195e61b44584ab13,debug-threads=on -S -object {"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/run/kubevirt-private/libvirt/qemu/lib/domain-1-mec-ba3abbb298f043fa/master-key.aes"} -machine pc-q35-rhel9.2.0,usb=off,dump-guest-core=off -accel kvm -cpu EPYC,vendor=AuthenticAMD,monitor=off

在虚拟中查看硬件也显示为 AMD EPYC 处理器。

需要注意

如果物理机集群的计算节点中还存在其他供应商的服务器,那虚机热迁会有问题(从英特尔节点热迁至海光节点或从海光热迁至英特尔),需要额外考虑热迁时的调度;如果计算节点均为海光处理器,就没有问题。