Skip to content

Commit e5c972c

Browse files
jepiobonzini
authored andcommitted
KVM: SVM: Flush Hyper-V TLB when required
The Hyper-V "EnlightenedNptTlb" enlightenment is always enabled when KVM is running on top of Hyper-V and Hyper-V exposes support for it (which is always). On AMD CPUs this enlightenment results in ASID invalidations not flushing TLB entries derived from the NPT. To force the underlying (L0) hypervisor to rebuild its shadow page tables, an explicit hypercall is needed. The original KVM implementation of Hyper-V's "EnlightenedNptTlb" on SVM only added remote TLB flush hooks. This worked out fine for a while, as sufficient remote TLB flushes where being issued in KVM to mask the problem. Since v5.17, changes in the TDP code reduced the number of flushes and the out-of-sync TLB prevents guests from booting successfully. Split svm_flush_tlb_current() into separate callbacks for the 3 cases (guest/all/current), and issue the required Hyper-V hypercall when a Hyper-V TLB flush is needed. The most important case where the TLB flush was missing is when loading a new PGD, which is followed by what is now svm_flush_tlb_current(). Cc: [email protected] # v5.17+ Fixes: 1e0c7d4 ("KVM: SVM: hyper-v: Remote TLB flush for SVM") Link: https://lore.kernel.org/lkml/[email protected]/ Suggested-by: Sean Christopherson <[email protected]> Signed-off-by: Jeremi Piotrowski <[email protected]> Reviewed-by: Vitaly Kuznetsov <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 9e347ba commit e5c972c

File tree

3 files changed

+54
-3
lines changed

3 files changed

+54
-3
lines changed

arch/x86/kvm/kvm_onhyperv.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ int hv_remote_flush_tlb_with_range(struct kvm *kvm,
1212
int hv_remote_flush_tlb(struct kvm *kvm);
1313
void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp);
1414
#else /* !CONFIG_HYPERV */
15+
static inline int hv_remote_flush_tlb(struct kvm *kvm)
16+
{
17+
return -EOPNOTSUPP;
18+
}
19+
1520
static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
1621
{
1722
}

arch/x86/kvm/svm/svm.c

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3729,7 +3729,7 @@ static void svm_enable_nmi_window(struct kvm_vcpu *vcpu)
37293729
svm->vmcb->save.rflags |= (X86_EFLAGS_TF | X86_EFLAGS_RF);
37303730
}
37313731

3732-
static void svm_flush_tlb_current(struct kvm_vcpu *vcpu)
3732+
static void svm_flush_tlb_asid(struct kvm_vcpu *vcpu)
37333733
{
37343734
struct vcpu_svm *svm = to_svm(vcpu);
37353735

@@ -3753,6 +3753,37 @@ static void svm_flush_tlb_current(struct kvm_vcpu *vcpu)
37533753
svm->current_vmcb->asid_generation--;
37543754
}
37553755

3756+
static void svm_flush_tlb_current(struct kvm_vcpu *vcpu)
3757+
{
3758+
hpa_t root_tdp = vcpu->arch.mmu->root.hpa;
3759+
3760+
/*
3761+
* When running on Hyper-V with EnlightenedNptTlb enabled, explicitly
3762+
* flush the NPT mappings via hypercall as flushing the ASID only
3763+
* affects virtual to physical mappings, it does not invalidate guest
3764+
* physical to host physical mappings.
3765+
*/
3766+
if (svm_hv_is_enlightened_tlb_enabled(vcpu) && VALID_PAGE(root_tdp))
3767+
hyperv_flush_guest_mapping(root_tdp);
3768+
3769+
svm_flush_tlb_asid(vcpu);
3770+
}
3771+
3772+
static void svm_flush_tlb_all(struct kvm_vcpu *vcpu)
3773+
{
3774+
/*
3775+
* When running on Hyper-V with EnlightenedNptTlb enabled, remote TLB
3776+
* flushes should be routed to hv_remote_flush_tlb() without requesting
3777+
* a "regular" remote flush. Reaching this point means either there's
3778+
* a KVM bug or a prior hv_remote_flush_tlb() call failed, both of
3779+
* which might be fatal to the guest. Yell, but try to recover.
3780+
*/
3781+
if (WARN_ON_ONCE(svm_hv_is_enlightened_tlb_enabled(vcpu)))
3782+
hv_remote_flush_tlb(vcpu->kvm);
3783+
3784+
svm_flush_tlb_asid(vcpu);
3785+
}
3786+
37563787
static void svm_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t gva)
37573788
{
37583789
struct vcpu_svm *svm = to_svm(vcpu);
@@ -4745,10 +4776,10 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
47454776
.set_rflags = svm_set_rflags,
47464777
.get_if_flag = svm_get_if_flag,
47474778

4748-
.flush_tlb_all = svm_flush_tlb_current,
4779+
.flush_tlb_all = svm_flush_tlb_all,
47494780
.flush_tlb_current = svm_flush_tlb_current,
47504781
.flush_tlb_gva = svm_flush_tlb_gva,
4751-
.flush_tlb_guest = svm_flush_tlb_current,
4782+
.flush_tlb_guest = svm_flush_tlb_asid,
47524783

47534784
.vcpu_pre_run = svm_vcpu_pre_run,
47544785
.vcpu_run = svm_vcpu_run,

arch/x86/kvm/svm/svm_onhyperv.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#ifndef __ARCH_X86_KVM_SVM_ONHYPERV_H__
77
#define __ARCH_X86_KVM_SVM_ONHYPERV_H__
88

9+
#include <asm/mshyperv.h>
10+
911
#if IS_ENABLED(CONFIG_HYPERV)
1012

1113
#include "kvm_onhyperv.h"
@@ -15,6 +17,14 @@ static struct kvm_x86_ops svm_x86_ops;
1517

1618
int svm_hv_enable_l2_tlb_flush(struct kvm_vcpu *vcpu);
1719

20+
static inline bool svm_hv_is_enlightened_tlb_enabled(struct kvm_vcpu *vcpu)
21+
{
22+
struct hv_vmcb_enlightenments *hve = &to_svm(vcpu)->vmcb->control.hv_enlightenments;
23+
24+
return ms_hyperv.nested_features & HV_X64_NESTED_ENLIGHTENED_TLB &&
25+
!!hve->hv_enlightenments_control.enlightened_npt_tlb;
26+
}
27+
1828
static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
1929
{
2030
struct hv_vmcb_enlightenments *hve = &vmcb->control.hv_enlightenments;
@@ -80,6 +90,11 @@ static inline void svm_hv_update_vp_id(struct vmcb *vmcb, struct kvm_vcpu *vcpu)
8090
}
8191
#else
8292

93+
static inline bool svm_hv_is_enlightened_tlb_enabled(struct kvm_vcpu *vcpu)
94+
{
95+
return false;
96+
}
97+
8398
static inline void svm_hv_init_vmcb(struct vmcb *vmcb)
8499
{
85100
}

0 commit comments

Comments
 (0)