Skip to content

Commit 35ef80e

Browse files
dmatlacksean-jc
authored andcommitted
KVM: x86/mmu: Batch TLB flushes when zapping collapsible TDP MMU SPTEs
Set SPTEs directly to SHADOW_NONPRESENT_VALUE and batch up TLB flushes when zapping collapsible SPTEs, rather than freezing them first. Freezing the SPTE first is not required. It is fine for another thread holding mmu_lock for read to immediately install a present entry before TLBs are flushed because the underlying mapping is not changing. vCPUs that translate through the stale 4K mappings or a new huge page mapping will still observe the same GPA->HPA translations. KVM must only flush TLBs before dropping RCU (to avoid use-after-free of the zapped page tables) and before dropping mmu_lock (to synchronize with mmu_notifiers invalidating mappings). In VMs backed with 2MiB pages, batching TLB flushes improves the time it takes to zap collapsible SPTEs to disable dirty logging: $ ./dirty_log_perf_test -s anonymous_hugetlb_2mb -v 64 -e -b 4g Before: Disabling dirty logging time: 14.334453428s (131072 flushes) After: Disabling dirty logging time: 4.794969689s (76 flushes) Skipping freezing SPTEs also avoids stalling vCPU threads on the frozen SPTE for the time it takes to perform a remote TLB flush. vCPUs faulting on the zapped mapping can now immediately install a new huge mapping and proceed with guest execution. Signed-off-by: David Matlack <dmatlack@google.com> Link: https://lore.kernel.org/r/20240823235648.3236880-3-dmatlack@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 8ccd51c commit 35ef80e

1 file changed

Lines changed: 10 additions & 45 deletions

File tree

arch/x86/kvm/mmu/tdp_mmu.c

Lines changed: 10 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -583,48 +583,6 @@ static inline int __must_check tdp_mmu_set_spte_atomic(struct kvm *kvm,
583583
return 0;
584584
}
585585

586-
static inline int __must_check tdp_mmu_zap_spte_atomic(struct kvm *kvm,
587-
struct tdp_iter *iter)
588-
{
589-
int ret;
590-
591-
lockdep_assert_held_read(&kvm->mmu_lock);
592-
593-
/*
594-
* Freeze the SPTE by setting it to a special, non-present value. This
595-
* will stop other threads from immediately installing a present entry
596-
* in its place before the TLBs are flushed.
597-
*
598-
* Delay processing of the zapped SPTE until after TLBs are flushed and
599-
* the FROZEN_SPTE is replaced (see below).
600-
*/
601-
ret = __tdp_mmu_set_spte_atomic(iter, FROZEN_SPTE);
602-
if (ret)
603-
return ret;
604-
605-
kvm_flush_remote_tlbs_gfn(kvm, iter->gfn, iter->level);
606-
607-
/*
608-
* No other thread can overwrite the frozen SPTE as they must either
609-
* wait on the MMU lock or use tdp_mmu_set_spte_atomic() which will not
610-
* overwrite the special frozen SPTE value. Use the raw write helper to
611-
* avoid an unnecessary check on volatile bits.
612-
*/
613-
__kvm_tdp_mmu_write_spte(iter->sptep, SHADOW_NONPRESENT_VALUE);
614-
615-
/*
616-
* Process the zapped SPTE after flushing TLBs, and after replacing
617-
* FROZEN_SPTE with 0. This minimizes the amount of time vCPUs are
618-
* blocked by the FROZEN_SPTE and reduces contention on the child
619-
* SPTEs.
620-
*/
621-
handle_changed_spte(kvm, iter->as_id, iter->gfn, iter->old_spte,
622-
SHADOW_NONPRESENT_VALUE, iter->level, true);
623-
624-
return 0;
625-
}
626-
627-
628586
/*
629587
* tdp_mmu_set_spte - Set a TDP MMU SPTE and handle the associated bookkeeping
630588
* @kvm: KVM instance
@@ -1596,13 +1554,16 @@ static void zap_collapsible_spte_range(struct kvm *kvm,
15961554
gfn_t end = start + slot->npages;
15971555
struct tdp_iter iter;
15981556
int max_mapping_level;
1557+
bool flush = false;
15991558

16001559
rcu_read_lock();
16011560

16021561
for_each_tdp_pte_min_level(iter, root, PG_LEVEL_2M, start, end) {
16031562
retry:
1604-
if (tdp_mmu_iter_cond_resched(kvm, &iter, false, true))
1563+
if (tdp_mmu_iter_cond_resched(kvm, &iter, flush, true)) {
1564+
flush = false;
16051565
continue;
1566+
}
16061567

16071568
if (iter.level > KVM_MAX_HUGEPAGE_LEVEL ||
16081569
!is_shadow_present_pte(iter.old_spte))
@@ -1630,11 +1591,15 @@ static void zap_collapsible_spte_range(struct kvm *kvm,
16301591
if (max_mapping_level < iter.level)
16311592
continue;
16321593

1633-
/* Note, a successful atomic zap also does a remote TLB flush. */
1634-
if (tdp_mmu_zap_spte_atomic(kvm, &iter))
1594+
if (tdp_mmu_set_spte_atomic(kvm, &iter, SHADOW_NONPRESENT_VALUE))
16351595
goto retry;
1596+
1597+
flush = true;
16361598
}
16371599

1600+
if (flush)
1601+
kvm_flush_remote_tlbs_memslot(kvm, slot);
1602+
16381603
rcu_read_unlock();
16391604
}
16401605

0 commit comments

Comments
 (0)