基于内核的虚拟机 (KVM) 的“修复”并不经常值得注意,但今天是一个有趣的例外,在 Linux 6.13-rc3 标记之前发送的 KVM 修复中,开始处理影响最近几代 Intel 处理器的“滑稽/令人反感”的性能回归。这种性能回归在 Linux 6.14 之前不会完全解决,但至少在今天晚些时候合并代码后,有一个过渡步骤。
今天早上 Linux 6.13-rc3 的 KVM 更改引起了我的注意,这是唯一一个与 KVM x86 相关的更改:
“缓存 CPUID.0xD 模块初始化期间的 XSTATE 偏移量 + 大小 – 在 Intel 的 Emerald Rapids 上,CPUID 需要数百个周期,并且0xD下有很多叶子。在下一个版本中,计划在嵌套的 VM-Enter 和 VM-Exit 期间删除 CPUID,现在只需缓存它们:即使在 Skylake 上,速度也提高了 40%。
好吧,这很有趣……求助于 XSTATE 缓存来处理较新的 Intel Xeon Emerald Rapids CPU 比以前的处理器成本高得多。
Google 的 Sean Christopherson 一直在调查并使用更新的 Intel Xeon 处理器来承担这种更高的成本。在本周的补丁系列中,他旨在解决 xstate_required_size() 性能回归问题。他在补丁求职信中总结道:
“修复了 xstate_required_size() 中由于 CPUID host__in Emerald Rapids 上花费的时间比 Skylake 长 3 到 4 倍而弹出的滑稽/令人反感的性能回归(相对于老一代 CPU)。
这个问题在嵌套虚拟化转换中凸显出来,因为 KVM(不必要地)在每次转换中多次执行运行时 CPUID 更新,包括 XSAVE 大小。计算 XSAVE 大小,尤其是对于具有相当数量的受支持 XSAVE 功能和压缩格式支持的 vCPU,可以增加数千个周期。
要解决眼前的问题,请在 kvm.ko load 中缓存 CPUID 输出。对于给定的 CPU,该信息是静态的,即不需要每次都从硬件中重新读取。这是补丁 1,几乎消除了所有有意义的开销。
补丁 2 是一次小的清理,旨在尝试使代码更易于阅读。
补丁 3 修复了 CPUID 仿真中的一个疣,其中 KVM 进行中等成本(尽管与 CPUID 相比便宜,哈哈)的 MSR 查找,这对于绝大多数虚拟机来说可能是不必要的。
补丁 4 和 5 解决了 KVM 对每个嵌套的 VM-Enter 和 VM-Exit 多次进行运行时 CPUID 更新的问题,其中至少有一半是完全不必要的(CPUID 是 Intel 和 AMD 的强制拦截, 因此,在运行 L2 时确保动态 CPUID 位是最新的是没有意义的)。这个想法相当简单:通过延迟它们来延迟 CPUID 更新,直到某些东西可能真正消耗 guest 相关的
位。
…
也就是说,补丁 1 是最重要的,并被标记为稳定版,在 6.1、6.6 和 6.12 上完全适用(5.15 及更早版本的向后移植应该不会太可怕)。题外话,我不禁想知道 EMR 上的 CPUID 延迟是 CPU 还是 ucode 错误。对于许多叶子,KVM 可以比 CPUID 执行指令更快地模拟 CPUID。即,整个 VM-Exit => 模拟 => VM-Enter 序列花费的时间比在裸机上执行 CPUID 要少。这似乎绝对是疯狂的。但是,它不应该影响客人的表现,所以这是别人的问题,至少现在是这样。
该补丁系列的完整版本预计将在下一个周期 Linux 6.14 中推出,而对于 Linux 6.13,立即的“修复”是缓存方法。今天进入主线内核的补丁进一步总结了从 Emerald Rapids 开始的情况和更高的 Intel 成本:
“在 Intel 的 Emerald Rapids 上,CPUID 非常昂贵,以至于相对于使用缓存值,重新计算 XSAVE 偏移量和大小会导致嵌套 VM-Enter 和 VM-Exit 的延迟增加 4 倍(嵌套转换可以在每次转换中多次触发 xstate_required_size()。通过在触发嵌套过渡时运行 ‘perf top’ 来很容易看到这个问题: kvm_update_cpuid_runtime() 显示高达 50%。
通过 L2 的 RDTSC 测量(使用 KVM-Unit-Test 的 CPUID VM-Exit 测试和略微修改的 L1 KVM 来处理快速路径中的 CPUID),在 Skylake (SKX)、Icelake (ICX) 和 Emerald Rapids (EMR) 上模拟 CPUID 的嵌套往返需要:
SKX 11650
ICX 22350
EMR 28850使用缓存值,延迟降至:
SKX 6850
ICX 9000
EMR 7900根本问题是 CPUID 本身在 ICX 上很慢,在 EMR 上慢得可笑。在支持 XSAVES 和/或 XSAVEC 的 CPU 上,问题会更加严重,因为 KVM 在每次运行时 CPUID 更新时都会调用 xstate_required_size() 两次,并且因为有更多受支持的 XSAVE 功能(支持的 XSAVE 功能子叶的 CPUID 明显慢)。
随着 Intel Xeon Emerald Rapids 在一年前推出,令人惊讶的是,直到现在才开始研究解决方案来解决广泛使用的 KVM 的较高成本,KVM 是 Linux 开源虚拟化堆栈的重要组成部分。更重要的是,Google 工程师对此进行了深入研究,以便在 Google Cloud 中更好地处理,而不是来自 Intel。如果最新的 Sierra Forest 和 Granite Rapids 处理器也观察到昂贵的 CPUID 行为,则没有关于补丁的消息。