Unraid 虚拟机里 perf 计数为 0

在 Unraid 的 Ubuntu 虚拟机里跑 perf stat,task-clock 有数,cycles 和 instructions 却一直是 0。加了 sudo,换成持续计算的 Python 程序,结果也一样。

这次的环境是 i3-12100、Unraid 7.3.2,宿主内核 6.18.38-Unraid,Ubuntu 内核 7.0.0-34-generic。虚拟 CPU 用的是 host-passthrough,宿主的 enable_pmu 也是 Y。

先用计算任务确认问题,命令在 Ubuntu 内执行:

sudo taskset -c 0 perf stat \
  -e task-clock,cpu_core/cycles/,cpu_core/instructions/ -- \
  python3 -c 'sum(range(100000000))'

输出中的关键三行:

552.10 msec task-clock
     0      cpu_core/cycles/
     0      cpu_core/instructions/

程序确实消耗了 CPU 时间,硬件计数却没有增长。

先查环境和 PMU

在 Ubuntu 虚拟机内查看内核、perf 版本和虚拟化类型:

uname -r
perf --version
systemd-detect-virt

三条命令依次返回:

7.0.0-34-generic
perf version 7.0.14
kvm

这确认了来宾内核、工具版本以及 KVM 环境。也可以检查来宾暴露了哪些事件,以及普通用户的访问限制:

perf list
cat /proc/sys/kernel/perf_event_paranoid

这两条属于补充检查,本次没有保留返回结果。事件出现在列表里,不代表一定能计数。这次使用 sudo 后依然为 0,需要继续查虚拟 PMU。

在 Unraid 宿主机终端查看宿主版本、物理 CPU 和 KVM 的 PMU 开关:

cat /etc/unraid-version
uname -r
lscpu | grep -E 'Model name|型号名称'
cat /sys/module/kvm/parameters/enable_pmu

对应输出为:

version="7.3.2"
6.18.38-Unraid
Model name:                              12th Gen Intel(R) Core(TM) i3-12100
Y

最后的 Y 是 enable_pmu 的结果,只说明宿主允许 PMU 虚拟化;还要检查这台虚拟机的配置:

virsh list --all
virsh dumpxml 'Ubuntu' |
  sed -n '/<features>/,/<\/features>/p; /<cpu /,/<\/cpu>/p'

Ubuntu 是列表中的虚拟机名称,使用时换成自己的。运行中的虚拟机会显示当前生效的配置;查看下次启动使用的持久配置,可以给 dumpxml 加上 --inactive。

虚拟机列表只摘录目标行:

Id   Name            State
12   Ubuntu          running

原 XML 的相关部分是:

<features>
  <acpi/>
  <apic/>
</features>
<cpu mode='host-passthrough' check='none' migratable='off'>
  <topology sockets='1' dies='1' clusters='1' cores='4' threads='2'/>
  <cache mode='passthrough'/>
</cpu>

这里确认了 CPU 直通和 4 核 8 线程拓扑。没有 <pmu> 项,只能说明没有显式设置,不能直接判定 PMU 已关闭。

回到 Ubuntu 虚拟机内,读取 PMU 初始化日志和寄存器异常:

sudo dmesg | grep -iE -A 12 'performance events|PMU|unsupported.*CPU'
sudo dmesg | grep -iE -B 3 -A 20 'unchecked MSR|WRMSR|check_exception'

两次 dmesg 查询的关键字段摘录如下,省略了时间戳和无关调用栈:

Performance Events: Alderlake Hybrid events, full-width counters, Intel PMU driver.
core: cpu_core PMU driver:
... version:                   2
... generic counters:          8
... fixed-purpose counters:    3
... global_ctrl mask:          00010007000000ff
unchecked MSR access error: WRMSR to 0x38f
(tried to write 0x00010007000000ff)

调用栈落在 __intel_pmu_enable_all。来宾识别出了 Alder Lake,按对应驱动初始化 PMU,但写控制寄存器时失败了。Linux 开发者也报告过同类 KVM 问题。

如果 dmesg 中已经没有启动时的记录,可以改查 journal。本次没有使用这条命令的返回结果:

sudo journalctl -k -b --no-pager |
  grep -iE -B 3 -A 20 'performance events|PMU|unchecked MSR|WRMSR'

host-passthrough 暴露了宿主的 CPU 身份和特性,性能计数器却仍由 KVM 虚拟化。来宾驱动想用的能力,虚拟 PMU 未必完整支持。

当输出中出现 cpu_core、cpu_atom 时,还可以在来宾查询它们对应的 CPU 列表:

cat /sys/bus/event_source/devices/cpu_core/cpus
cat /sys/bus/event_source/devices/cpu_atom/cpus
查询路径 本次输出
cpu_core/cpus 0-7
cpu_atom/cpus 空,无 CPU 编号

文件不存在时表示没有暴露对应名称的 PMU,不能单凭这一点认定故障;换成 Broadwell 后也不必再依赖这两个路径。

换模型,还要显式开 PMU

先在 Unraid 上确认可用模型:

virsh domcapabilities --virttype kvm | grep -E "model.*usable=.yes."

列表中选用的这一行是:

<model usable='yes' vendor='Intel' canonical='Broadwell-v4'>Broadwell-noTSX-IBRS</model>

usable='yes' 表示宿主支持这个模型,不代表已经验证过 PMU。正常关闭虚拟机、备份 XML 后,把原来的整个 <cpu> 段换成:

<cpu mode='custom' match='exact' check='partial'>
  <model fallback='forbid'>Broadwell-noTSX-IBRS</model>
  <topology sockets='1' dies='1' clusters='1' cores='4' threads='2'/>
</cpu>

这里的拓扑沿用原来的 4 核 8 线程。第一次只换模型,启动后查询 dmesg,并用通用的 cycles,instructions 重跑相同计算负载,得到:

Performance Events: unsupported CPU family 6 model 61 no PMU driver, software events only.

         569.70 msec task-clock
<not supported>      cycles
<not supported>      instructions

此时连硬件事件都不可用了,来宾只启用了软件事件。

还差一步:在已有的 <features> 里加入 <pmu state='on'/>,保留其他配置:

<features>
  <acpi/>
  <apic/>
  <pmu state='on'/>
</features>

从 Unraid 重新启动虚拟机,在 Ubuntu 内确认驱动和错误日志,再测计数:

sudo dmesg | grep -iE 'Performance Events|unchecked MSR|WRMSR'
sudo taskset -c 0 perf stat -e task-clock,cycles,instructions -- \
  python3 -c 'sum(range(100000000))'

这次加载的是 Broadwell PMU 驱动,计数恢复了:

Performance Events: Broadwell events, Intel PMU driver.

578.77 msec task-clock
2344786616 cycles
13450293438 instructions

会不会变慢

程序仍在 i3-12100 上通过 KVM 执行,频率和物理缓存不会变成 Broadwell 的规格。不过,命名 CPU 模型会改变来宾可见的特性,部分程序可能因此选择不同的实现路径。

我没有做实际业务的重复对照测试,所以暂时只能确认基础计数恢复,不能说性能没有损失。Broadwell 专属事件、缓存指标和 Top-down 也没有验证。这套配置先解决了当前机器上的问题,不能直接套到所有虚拟机上。