引言
在使用qemu-kvm时,有时候会遇到报“KVM internal error. Suberror: 1”或者“KVM internal error. Suberror: 3”错误,但是这类报错又没有其他报错信息辅助我们进行问题的定位和处理,而且kvm驱动中,也并未提供这类问题的debug信息调试手段。在kvm驱动中,将kvm emulation failure问题统一报“KVM internal error. Suberror: 1”或者“KVM internal error. Suberror: 3”错误。用户在错误时无法通过日志来甄别问题原因,只能知道原因是kvm外部原因(诸如启动kvm的配置选项,vcpu状态异常,cpu特性功能检测异常)。
其实报“KVM internal error. Suberror: 1”或者“KVM internal error. Suberror: 3”错误的原因最终都和cpu状态或者特性相关,kvm在启用vcpu时,如果检测到KVM_EXIT_INTERNAL_ERROR异常时就会报错并停止运行,例如-m 1配置下如下现象:
QEMU 4.2.0 monitor - type 'help' for more information
(qemu) KVM internal error. Suberror: 1
emulation failure
EAX=000082c6 EBX=0010ae38 ECX=000082d2 EDX=80ffffff
ESI=00100000 EDI=0000832a EBP=0007fff0 ESP=0007fff4
EIP=0010a000 EFL=00010086 [--S--P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00008280 00000027
IDT= 00000000 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000000
Code=02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 <00> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
异常触发流程
首先在qemu-kvm启动kvm时,它出现异常的过程如下:
|____kvm_cpu_exec
|_____trace_kvm_run_exit
|____case KVM_EXIT_INTERNAL_ERROR
|____kvm_handle_interal_error
kvm_cpu_exec->trace_kvm_run_exit-> case KVM_EXIT_INTERNAL_ERROR->kvm_handle_internal_error
1. static int kvm_handle_internal_error(CPUState *cpu, struct kvm_run *run)
2. {
3. //触发KVM internal error. Suberror(Suberror:1或者Suberror:3)
4. fprintf(stderr, "KVM internal error. Suberror: %d\n",
5. run->internal.suberror);
6.
7. // Suberror:3时会进这个条件,打印extra data[%d]信息
8. if (kvm_check_extension(kvm_state, KVM_CAP_INTERNAL_ERROR_DATA)) {
9. int i;
10.
11. for (i = 0; i < run->internal.ndata; ++i) {
12. fprintf(stderr, "extra data[%d]: %"PRIx64"\n",
13. i, (uint64_t)run->internal.data[i]);
14. }
15. }
16. //Suberror:3和Suberror:1时,打印当前寄存器信息
17. if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) {
18. fprintf(stderr, "emulation failure\n");
19. if (!kvm_arch_stop_on_emulation_error(cpu)) {
20. //通过cpu_synchronize_state获取CPU各寄存器运行环境,打印出来
21. cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
22. return EXCP_INTERRUPT;
23. }
24. }
25. /* FIXME: Should trigger a qmp message to let management know
26. * something went wrong.
27. */
28. return -1;
29. }
异常触发原因
而KVM_EXIT_INTERNAL_ERROR异常是由kvm驱动在模拟kvm时候传入的,而最常见的suberror错误码有1)KVM_INTERNAL_ERROR_EMULATION,对应suberror:1;2)KVM_CAP_INTERNAL_ERROR_DATA,对应suberror:3。kvm触发KVM_EXIT_INTERNAL_ERROR可以总结有以下场景,内核代码大致如下:
通过驱动代码层总结,发生suberror报错时,所有的场景都和cpu和内存异常相关。首先内存配置应该满足kvm的启动需求,如果-m 1时将会导致linux kvm出现suberror:1报错异常;其次配置vcpu特性时,应该注意cpu是否能够支持需要配置的cpu特性,如果cpu特性不支持,或驱动检测到cpu特性异常,也将出现suberror:1报错异常。详细总结如下:
No. | 场景描述 | 配置 | 是否可避免 | 说明 | 驱动测处理 |
1 | 内存配置过小 | -m 1时 | 是 | 事实上,当内存配置小于200M时,在启动linux kvm时,都必然将导致kvm启动异常,尤其内存配置为1009k-1064k时将必然导致KVM internal error. Suberror: 1 | arch/x86/kvm/x86.c代码中kvm_handle_memory_failure函数被调用,且内存异常不是由于page fault导致的,将会导致suberror:1异常 |
2 | 迁移场景中,配置了host无法支持的一些特性 | -cpu host,hv-vapic,hv-evmcs(此特性的配置将导致一些虚拟化特性不能使用) | 是 | 确保host能够支持该特性,且迁移时也要确定目标主机支持该特性(此问题可以通过增加内核打印信息判断是否被触发,内核打印语句pr_debug_ratelimited,对应打印级别为8,修改 /proc/sys/kernel/printk中的日志打印级别可以观察) | arch/x86/kvm/vmx/nested.c中调用以下两个接口时:1)nested_get_vmcs12_pages时在 vmcs12 中不支持 APIC 访问地址;2)调用vmx_get_nested_state_pages时由于hv_evmcs迁移后(L2 运行时)可能最终未映射,无法正确反映 vmcs12的更改导致“enlightened vmptrld failed”。 这两个场景都会最终导致suberror:1异常 |
3 | 配置了显卡直通,kvm休眠或待机唤醒时 | 显卡直通配置 | 是 | 尽量不用显卡直通配置,如需使用显卡直通需要确保host支持并开启了intel_iommu=on配置,如果host不支持或未开启iommu时,在使用过程中可能导致报错 | NA |
4 | 其它cpu特性异常场景 | NA | NA | 1.Kvm cupid 0x12异常时 2. X86_FEATURE_SGX2特性异常时 3. guest异常退出, emulation_required条件仍成立, vcpu处于pending状态 | arch/x86/kvm/x86.c代码中kvm_prepare_emulation_failure_exit在一些cpu异常场景下被调用,将会导致suberror:1异常。 |