OSDN Git Service

KVM: nVMX: Free only guest_mode (L2) roots on INVVPID w/o EPT
authorSean Christopherson <seanjc@google.com>
Wed, 9 Jun 2021 23:42:29 +0000 (16:42 -0700)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 17 Jun 2021 17:09:53 +0000 (13:09 -0400)
When emulating INVVPID for L1, free only L2+ roots, using the guest_mode
tag in the MMU role to identify L2+ roots.  From L1's perspective, its
own TLB entries use VPID=0, and INVVPID is not requied to invalidate such
entries.  Per Intel's SDM, INVVPID _may_ invalidate entries with VPID=0,
but it is not required to do so.

Cc: Lai Jiangshan <laijs@linux.alibaba.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210609234235.1244004-10-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/mmu/mmu.c
arch/x86/kvm/vmx/nested.c

index d866bfe..a92d565 100644 (file)
@@ -1684,6 +1684,7 @@ int kvm_mmu_unprotect_page(struct kvm *kvm, gfn_t gfn);
 void __kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu);
 void kvm_mmu_free_roots(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
                        ulong roots_to_free);
+void kvm_mmu_free_guest_mode_roots(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu);
 gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access,
                           struct x86_exception *exception);
 gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva,
index 894b9a4..f4fea68 100644 (file)
@@ -3212,6 +3212,33 @@ void kvm_mmu_free_roots(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
 }
 EXPORT_SYMBOL_GPL(kvm_mmu_free_roots);
 
+void kvm_mmu_free_guest_mode_roots(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu)
+{
+       unsigned long roots_to_free = 0;
+       hpa_t root_hpa;
+       int i;
+
+       /*
+        * This should not be called while L2 is active, L2 can't invalidate
+        * _only_ its own roots, e.g. INVVPID unconditionally exits.
+        */
+       WARN_ON_ONCE(mmu->mmu_role.base.guest_mode);
+
+       for (i = 0; i < KVM_MMU_NUM_PREV_ROOTS; i++) {
+               root_hpa = mmu->prev_roots[i].hpa;
+               if (!VALID_PAGE(root_hpa))
+                       continue;
+
+               if (!to_shadow_page(root_hpa) ||
+                       to_shadow_page(root_hpa)->role.guest_mode)
+                       roots_to_free |= KVM_MMU_ROOT_PREVIOUS(i);
+       }
+
+       kvm_mmu_free_roots(vcpu, mmu, roots_to_free);
+}
+EXPORT_SYMBOL_GPL(kvm_mmu_free_guest_mode_roots);
+
+
 static int mmu_check_root(struct kvm_vcpu *vcpu, gfn_t root_gfn)
 {
        int ret = 0;
index 8e2487f..13a4acc 100644 (file)
@@ -5481,8 +5481,8 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
 
        /*
         * Sync the shadow page tables if EPT is disabled, L1 is invalidating
-        * linear mappings for L2 (tagged with L2's VPID).  Free all roots as
-        * VPIDs are not tracked in the MMU role.
+        * linear mappings for L2 (tagged with L2's VPID).  Free all guest
+        * roots as VPIDs are not tracked in the MMU role.
         *
         * Note, this operates on root_mmu, not guest_mmu, as L1 and L2 share
         * an MMU when EPT is disabled.
@@ -5490,8 +5490,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
         * TODO: sync only the affected SPTEs for INVDIVIDUAL_ADDR.
         */
        if (!enable_ept)
-               kvm_mmu_free_roots(vcpu, &vcpu->arch.root_mmu,
-                                  KVM_MMU_ROOTS_ALL);
+               kvm_mmu_free_guest_mode_roots(vcpu, &vcpu->arch.root_mmu);
 
        return nested_vmx_succeed(vcpu);
 }