KVM: x86/mmu: Don't flush TLBs when clearing Dirty bit in shadow MMU
authorSean Christopherson <seanjc@google.com>
Fri, 11 Oct 2024 02:10:37 +0000 (19:10 -0700)
committerSean Christopherson <seanjc@google.com>
Wed, 30 Oct 2024 21:46:46 +0000 (14:46 -0700)
Don't force a TLB flush when an SPTE update in the shadow MMU happens to
clear the Dirty bit, as KVM unconditionally flushes TLBs when enabling
dirty logging, and when clearing dirty logs, KVM flushes based on its
software structures, not the SPTEs.  I.e. the flows that care about
accurate Dirty bit information already ensure there are no stale TLB
entries.

Opportunistically drop is_dirty_spte() as mmu_spte_update() was the sole
caller.

Link: https://lore.kernel.org/r/20241011021051.1557902-6-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/mmu/mmu.c
arch/x86/kvm/mmu/spte.h

index 067b6bb7bfca31376cca70472e253f19b519cc18..cb4f0a8c52e02f28610f550190a23e5eadf806e6 100644 (file)
@@ -521,12 +521,15 @@ static u64 mmu_spte_update_no_track(u64 *sptep, u64 new_spte)
  * not whether or not SPTEs were modified, i.e. only the write-tracking case
  * needs to flush at the time the SPTEs is modified, before dropping mmu_lock.
  *
- * Remote TLBs also need to be flushed if the Dirty bit is cleared, as false
- * negatives are not acceptable, e.g. if KVM is using D-bit based PML on VMX.
- *
  * Don't flush if the Accessed bit is cleared, as access tracking tolerates
  * false negatives, and the one path that does care about TLB flushes,
- * kvm_mmu_notifier_clear_flush_young(), uses mmu_spte_update_no_track().
+ * kvm_mmu_notifier_clear_flush_young(), flushes if a young SPTE is found, i.e.
+ * doesn't rely on lower helpers to detect the need to flush.
+ *
+ * Lastly, don't flush if the Dirty bit is cleared, as KVM unconditionally
+ * flushes when enabling dirty logging (see kvm_mmu_slot_apply_flags()), and
+ * when clearing dirty logs, KVM flushes based on whether or not dirty entries
+ * were reaped from the bitmap/ring, not whether or not dirty SPTEs were found.
  *
  * Returns true if the TLB needs to be flushed
  */
@@ -537,8 +540,7 @@ static bool mmu_spte_update(u64 *sptep, u64 new_spte)
        if (!is_shadow_present_pte(old_spte))
                return false;
 
-       return (is_mmu_writable_spte(old_spte) && !is_mmu_writable_spte(new_spte)) ||
-              (is_dirty_spte(old_spte) && !is_dirty_spte(new_spte));
+       return is_mmu_writable_spte(old_spte) && !is_mmu_writable_spte(new_spte);
 }
 
 /*
index c81cac9358e02dc5ba2dbf8ff7af380e1467f665..574ca9a1fcab7c5638903ba5024c9b51de0df0df 100644 (file)
@@ -363,13 +363,6 @@ static inline bool is_accessed_spte(u64 spte)
                             : !is_access_track_spte(spte);
 }
 
-static inline bool is_dirty_spte(u64 spte)
-{
-       u64 dirty_mask = spte_shadow_dirty_mask(spte);
-
-       return dirty_mask ? spte & dirty_mask : spte & PT_WRITABLE_MASK;
-}
-
 static inline u64 get_rsvd_bits(struct rsvd_bits_validate *rsvd_check, u64 pte,
                                int level)
 {