热迁移后虚拟机卡死、CPU 100%,往往是时间处理出了问题。用 crash 看 dump 能确认死循环卡在内核路径,再用 perf 在虚拟机内和宿主机上对比,就能定位到时间中断处理异常。
现象
把一台虚拟机从 Ubuntu 20 宿主机热迁移到 Ubuntu 22 宿主机后,虚拟机卡死了。
从宿主机看,virsh migrate 是成功的,虚拟机处于 running 状态,qemu 进程也正常。但被迁移的虚拟机 CPU 占用 100%,完全无响应。
排查
用 crash 分析 virsh dump 出来的内存转储,发现内核一直在刷下面的报错,看起来是死循环卡在了调度进程 swapper 上:
[7686175795.936645] Call Trace:
[7686175795.936647] <IRQ>
[7686175795.936651] run_timer_softirq+0x2a/0x50
[7686175795.936655] __do_softirq+0xd1/0x2c1
[7686175795.936658] irq_exit+0xae/0xb0
[7686175795.936660] smp_apic_timer_interrupt+0x7b/0x140
[7686175795.936662] apic_timer_interrupt+0xf/0x20
[7686175795.936662] </IRQ>
[7686175795.936664] RIP: 0010:native_safe_halt+0xe/0x10
[7686175823.935201] watchdog: BUG: soft lockup - CPU#0 stuck for 23s! [swapper/0:0]
0 号进程就是 swapper 进程,也就是调度进程。
这种 CPU 100% 表现出来的卡死,必然是因为某种原因让 CPU 进入了死循环——CPU 被占住放不出来,无法执行其他进程,所以表现出来就是"卡死"。
这种问题必须用 perf 来分析,分别在虚拟机内和宿主机上进行分析对比:
# 虚拟机内
perf record -e msr:write_msr -g -a
perf script
根因
大概原因是:
- 虚拟机没配置 CPU 的情况下,是从一个精度很低的时间设备上维持高精度计时器;
- 如果虚拟机配置了 CPU,则是通过特定的指令集
rdtsc指令从物理 CPU 读取计数器。
在热迁移过程中,时间处理这方面存在问题,导致迁移完成后,虚拟机卡死在时间中断处理流程上。
小结
迁移和扩容,时间处理都是常出问题的地方。
这类问题排查的关键在于:
- 用
crash分析 dump,确认是不是死循环、卡在哪个内核路径上; - 用
perf在虚拟机内和宿主机上对比分析,定位时间处理相关的异常; - 热迁移涉及 CPU 状态、时间源、中断等底层细节,任何一个环节不一致都可能引发迁移后的"假死"。