/* 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,
                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;
 }
        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))
        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;
 
 
        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;