]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
KVM: x86/mmu: Bail early from final #PF handling on spurious faults
authorSean Christopherson <sean.j.christopherson@intel.com>
Wed, 23 Sep 2020 22:04:25 +0000 (15:04 -0700)
committerPaolo Bonzini <pbonzini@redhat.com>
Mon, 28 Sep 2020 11:57:37 +0000 (07:57 -0400)
Detect spurious page faults, e.g. page faults that occur when multiple
vCPUs simultaneously access a not-present page, and skip the SPTE write,
prefetch, and stats update for spurious faults.

Note, the performance benefits of skipping the write and prefetch are
likely negligible, and the false positive stats adjustment is probably
lost in the noise.  The primary motivation is to play nice with TDX's
SEPT in the long term.  SEAMCALLs (to program SEPT entries) are quite
costly, e.g. thousands of cycles, and a spurious SEPT update will result
in a SEAMCALL error (which KVM will ideally treat as fatal).

Reported-by: Kai Huang <kai.huang@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Message-Id: <20200923220425.18402-5-sean.j.christopherson@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/mmu/mmu.c
arch/x86/kvm/mmu/paging_tmpl.h

index 2360d33a27b1eb07b324a3526551fdcc9141b24f..7ec3d057e8e0d251644f40b4890bfcae7c7c44ff 100644 (file)
@@ -2985,6 +2985,7 @@ static bool kvm_is_mmio_pfn(kvm_pfn_t pfn)
 /* Bits which may be returned by set_spte() */
 #define SET_SPTE_WRITE_PROTECTED_PT    BIT(0)
 #define SET_SPTE_NEED_REMOTE_TLB_FLUSH BIT(1)
+#define SET_SPTE_SPURIOUS              BIT(2)
 
 static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
                    unsigned int pte_access, int level,
@@ -3073,7 +3074,9 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
                spte = mark_spte_for_access_track(spte);
 
 set_pte:
-       if (mmu_spte_update(sptep, spte))
+       if (*sptep == spte)
+               ret |= SET_SPTE_SPURIOUS;
+       else if (mmu_spte_update(sptep, spte))
                ret |= SET_SPTE_NEED_REMOTE_TLB_FLUSH;
        return ret;
 }
@@ -3128,6 +3131,15 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
        if (unlikely(is_mmio_spte(*sptep)))
                ret = RET_PF_EMULATE;
 
+       /*
+        * The fault is fully spurious if and only if the new SPTE and old SPTE
+        * are identical, and emulation is not required.
+        */
+       if ((set_spte_ret & SET_SPTE_SPURIOUS) && ret == RET_PF_FIXED) {
+               WARN_ON_ONCE(!was_rmapped);
+               return RET_PF_SPURIOUS;
+       }
+
        pgprintk("%s: setting spte %llx\n", __func__, *sptep);
        trace_kvm_mmu_set_spte(level, gfn, sptep);
        if (!was_rmapped && is_large_pte(*sptep))
@@ -3364,6 +3376,9 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t gpa, int write,
        ret = mmu_set_spte(vcpu, it.sptep, ACC_ALL,
                           write, level, base_gfn, pfn, prefault,
                           map_writable);
+       if (ret == RET_PF_SPURIOUS)
+               return ret;
+
        direct_pte_prefetch(vcpu, it.sptep);
        ++vcpu->stat.pf_fixed;
        return ret;
index e1066226b8f0c1034e63c2fb44ac9494db1f14df..188ec2633bc56caff44ff957d940fc8be7f5c99d 100644 (file)
@@ -711,6 +711,9 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gpa_t addr,
 
        ret = mmu_set_spte(vcpu, it.sptep, gw->pte_access, write_fault,
                           it.level, base_gfn, pfn, prefault, map_writable);
+       if (ret == RET_PF_SPURIOUS)
+               return ret;
+
        FNAME(pte_prefetch)(vcpu, gw, it.sptep);
        ++vcpu->stat.pf_fixed;
        return ret;