<div dir="ltr"><div>Ah, I see. Yes, just the single patch for N and O. I will send it as 1/1 next time for different series.</div><div><br></div><div>Thanks,<br></div><div>Matthew</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Wed, 8 Jan 2025 at 22:29, Juerg Haefliger <<a href="mailto:juerg.haefliger@canonical.com">juerg.haefliger@canonical.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Patch 3/3 of but it's only a single patch for N and O?<br>
<br>
...Juerg<br>
<br>
On Wed,  8 Jan 2025 16:53:33 +1300<br>
Matthew Ruffell <<a href="mailto:matthew.ruffell@canonical.com" target="_blank">matthew.ruffell@canonical.com</a>> wrote:<br>
<br>
> From: Sean Christopherson <<a href="mailto:seanjc@google.com" target="_blank">seanjc@google.com</a>><br>
> <br>
> BugLink: <a href="https://bugs.launchpad.net/bugs/2093146" rel="noreferrer" target="_blank">https://bugs.launchpad.net/bugs/2093146</a><br>
> <br>
> Snapshot the output of CPUID.0xD.[1..n] during kvm.ko initiliaization to<br>
> avoid the overead of CPUID during runtime.  The offset, size, and metadata<br>
> for CPUID.0xD.[1..n] sub-leaves does not depend on XCR0 or XSS values, i.e.<br>
> is constant for a given CPU, and thus can be cached during module load.<br>
> <br>
> On Intel's Emerald Rapids, CPUID is *wildly* expensive, to the point where<br>
> recomputing XSAVE offsets and sizes results in a 4x increase in latency of<br>
> nested VM-Enter and VM-Exit (nested transitions can trigger<br>
> xstate_required_size() multiple times per transition), relative to using<br>
> cached values.  The issue is easily visible by running `perf top` while<br>
> triggering nested transitions: kvm_update_cpuid_runtime() shows up at a<br>
> whopping 50%.<br>
> <br>
> As measured via RDTSC from L2 (using KVM-Unit-Test's CPUID VM-Exit test<br>
> and a slightly modified L1 KVM to handle CPUID in the fastpath), a nested<br>
> roundtrip to emulate CPUID on Skylake (SKX), Icelake (ICX), and Emerald<br>
> Rapids (EMR) takes:<br>
> <br>
>   SKX 11650<br>
>   ICX 22350<br>
>   EMR 28850<br>
> <br>
> Using cached values, the latency drops to:<br>
> <br>
>   SKX 6850<br>
>   ICX 9000<br>
>   EMR 7900<br>
> <br>
> The underlying issue is that CPUID itself is slow on ICX, and comically<br>
> slow on EMR.  The problem is exacerbated on CPUs which support XSAVES<br>
> and/or XSAVEC, as KVM invokes xstate_required_size() twice on each<br>
> runtime CPUID update, and because there are more supported XSAVE features<br>
> (CPUID for supported XSAVE feature sub-leafs is significantly slower).<br>
> <br>
>  SKX:<br>
>   CPUID.0xD.2  = 348 cycles<br>
>   CPUID.0xD.3  = 400 cycles<br>
>   CPUID.0xD.4  = 276 cycles<br>
>   CPUID.0xD.5  = 236 cycles<br>
>   <other sub-leaves are similar><br>
> <br>
>  EMR:<br>
>   CPUID.0xD.2  = 1138 cycles<br>
>   CPUID.0xD.3  = 1362 cycles<br>
>   CPUID.0xD.4  = 1068 cycles<br>
>   CPUID.0xD.5  = 910 cycles<br>
>   CPUID.0xD.6  = 914 cycles<br>
>   CPUID.0xD.7  = 1350 cycles<br>
>   CPUID.0xD.8  = 734 cycles<br>
>   CPUID.0xD.9  = 766 cycles<br>
>   CPUID.0xD.10 = 732 cycles<br>
>   CPUID.0xD.11 = 718 cycles<br>
>   CPUID.0xD.12 = 734 cycles<br>
>   CPUID.0xD.13 = 1700 cycles<br>
>   CPUID.0xD.14 = 1126 cycles<br>
>   CPUID.0xD.15 = 898 cycles<br>
>   CPUID.0xD.16 = 716 cycles<br>
>   CPUID.0xD.17 = 748 cycles<br>
>   CPUID.0xD.18 = 776 cycles<br>
> <br>
> Note, updating runtime CPUID information multiple times per nested<br>
> transition is itself a flaw, especially since CPUID is a mandotory<br>
> intercept on both Intel and AMD.  E.g. KVM doesn't need to ensure emulated<br>
> CPUID state is up-to-date while running L2.  That flaw will be fixed in a<br>
> future patch, as deferring runtime CPUID updates is more subtle than it<br>
> appears at first glance, the benefits aren't super critical to have once<br>
> the XSAVE issue is resolved, and caching CPUID output is desirable even if<br>
> KVM's updates are deferred.<br>
> <br>
> Cc: Jim Mattson <<a href="mailto:jmattson@google.com" target="_blank">jmattson@google.com</a>><br>
> Cc: <a href="mailto:stable@vger.kernel.org" target="_blank">stable@vger.kernel.org</a><br>
> Signed-off-by: Sean Christopherson <<a href="mailto:seanjc@google.com" target="_blank">seanjc@google.com</a>><br>
> Message-ID: <<a href="mailto:20241211013302.1347853-2-seanjc@google.com" target="_blank">20241211013302.1347853-2-seanjc@google.com</a>><br>
> Signed-off-by: Paolo Bonzini <<a href="mailto:pbonzini@redhat.com" target="_blank">pbonzini@redhat.com</a>><br>
> (cherry picked from commit 1201f226c863b7da739f7420ddba818cedf372fc)<br>
> Signed-off-by: Matthew Ruffell <<a href="mailto:matthew.ruffell@canonical.com" target="_blank">matthew.ruffell@canonical.com</a>><br>
> ---<br>
>  arch/x86/kvm/cpuid.c | 31 ++++++++++++++++++++++++++-----<br>
>  arch/x86/kvm/cpuid.h |  1 +<br>
>  arch/x86/kvm/x86.c   |  2 ++<br>
>  3 files changed, 29 insertions(+), 5 deletions(-)<br>
> <br>
> diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c<br>
> index ce1499732cb8..c4a369bb1444 100644<br>
> --- a/arch/x86/kvm/cpuid.c<br>
> +++ b/arch/x86/kvm/cpuid.c<br>
> @@ -36,6 +36,26 @@<br>
>  u32 kvm_cpu_caps[NR_KVM_CPU_CAPS] __read_mostly;<br>
>  EXPORT_SYMBOL_GPL(kvm_cpu_caps);<br>
>  <br>
> +struct cpuid_xstate_sizes {<br>
> +     u32 eax;<br>
> +     u32 ebx;<br>
> +     u32 ecx;<br>
> +};<br>
> +<br>
> +static struct cpuid_xstate_sizes xstate_sizes[XFEATURE_MAX] __ro_after_init;<br>
> +<br>
> +void __init kvm_init_xstate_sizes(void)<br>
> +{<br>
> +     u32 ign;<br>
> +     int i;<br>
> +<br>
> +     for (i = XFEATURE_YMM; i < ARRAY_SIZE(xstate_sizes); i++) {<br>
> +             struct cpuid_xstate_sizes *xs = &xstate_sizes[i];<br>
> +<br>
> +             cpuid_count(0xD, i, &xs->eax, &xs->ebx, &xs->ecx, &ign);<br>
> +     }<br>
> +}<br>
> +<br>
>  u32 xstate_required_size(u64 xstate_bv, bool compacted)<br>
>  {<br>
>       int feature_bit = 0;<br>
> @@ -44,14 +64,15 @@ u32 xstate_required_size(u64 xstate_bv, bool compacted)<br>
>       xstate_bv &= XFEATURE_MASK_EXTEND;<br>
>       while (xstate_bv) {<br>
>               if (xstate_bv & 0x1) {<br>
> -                     u32 eax, ebx, ecx, edx, offset;<br>
> -                     cpuid_count(0xD, feature_bit, &eax, &ebx, &ecx, &edx);<br>
> +                     struct cpuid_xstate_sizes *xs = &xstate_sizes[feature_bit];<br>
> +                     u32 offset;<br>
> +<br>
>                       /* ECX[1]: 64B alignment in compacted form */<br>
>                       if (compacted)<br>
> -                             offset = (ecx & 0x2) ? ALIGN(ret, 64) : ret;<br>
> +                             offset = (xs->ecx & 0x2) ? ALIGN(ret, 64) : ret;<br>
>                       else<br>
> -                             offset = ebx;<br>
> -                     ret = max(ret, offset + eax);<br>
> +                             offset = xs->ebx;<br>
> +                     ret = max(ret, offset + xs->eax);<br>
>               }<br>
>  <br>
>               xstate_bv >>= 1;<br>
> diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h<br>
> index 23dbb9eb277c..da4e23e32cff 100644<br>
> --- a/arch/x86/kvm/cpuid.h<br>
> +++ b/arch/x86/kvm/cpuid.h<br>
> @@ -32,6 +32,7 @@ int kvm_vcpu_ioctl_get_cpuid2(struct kvm_vcpu *vcpu,<br>
>  bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx,<br>
>              u32 *ecx, u32 *edx, bool exact_only);<br>
>  <br>
> +void __init kvm_init_xstate_sizes(void);<br>
>  u32 xstate_required_size(u64 xstate_bv, bool compacted);<br>
>  <br>
>  int cpuid_query_maxphyaddr(struct kvm_vcpu *vcpu);<br>
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c<br>
> index e36bf6162fc8..898b191954eb 100644<br>
> --- a/arch/x86/kvm/x86.c<br>
> +++ b/arch/x86/kvm/x86.c<br>
> @@ -13917,6 +13917,8 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_vmgexit_msr_protocol_exit);<br>
>  <br>
>  static int __init kvm_x86_init(void)<br>
>  {<br>
> +     kvm_init_xstate_sizes();<br>
> +<br>
>       kvm_mmu_x86_module_init();<br>
>       mitigate_smt_rsb &= boot_cpu_has_bug(X86_BUG_SMT_RSB) && cpu_smt_possible();<br>
>       return 0;<br>
<br>
</blockquote></div>