Skip to content

Commit a77896e

Browse files
kevinloughlinsean-jc
authored andcommitted
KVM: SEV: Prefer WBNOINVD over WBINVD for cache maintenance efficiency
AMD CPUs currently execute WBINVD in the host when unregistering SEV guest memory or when deactivating SEV guests. Such cache maintenance is performed to prevent data corruption, wherein the encrypted (C=1) version of a dirty cache line might otherwise only be written back after the memory is written in a different context (ex: C=0), yielding corruption. However, WBINVD is performance-costly, especially because it invalidates processor caches. Strictly-speaking, unless the SEV ASID is being recycled (meaning the SNP firmware requires the use of WBINVD prior to DF_FLUSH), the cache invalidation triggered by WBINVD is unnecessary; only the writeback is needed to prevent data corruption in remaining scenarios. To improve performance in these scenarios, use WBNOINVD when available instead of WBINVD. WBNOINVD still writes back all dirty lines (preventing host data corruption by SEV guests) but does *not* invalidate processor caches. Note that the implementation of wbnoinvd() ensures fall back to WBINVD if WBNOINVD is unavailable. In anticipation of forthcoming optimizations to limit the WBNOINVD only to physical CPUs that have executed SEV guests, place the call to wbnoinvd_on_all_cpus() in a wrapper function sev_writeback_caches(). Signed-off-by: Kevin Loughlin <kevinloughlin@google.com> Reviewed-by: Mingwei Zhang <mizhang@google.com> Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com> Link: https://lore.kernel.org/r/20250201000259.3289143-3-kevinloughlin@google.com [sean: tweak comment regarding CLFUSH] Cc: Francesco Lavra <francescolavra.fl@gmail.com> Link: https://lore.kernel.org/r/20250522233733.3176144-8-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 7e00013 commit a77896e

File tree

1 file changed

+26
-19
lines changed

1 file changed

+26
-19
lines changed

arch/x86/kvm/svm/sev.c

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ static int sev_flush_asids(unsigned int min_asid, unsigned int max_asid)
117117
*/
118118
down_write(&sev_deactivate_lock);
119119

120+
/* SNP firmware requires use of WBINVD for ASID recycling. */
120121
wbinvd_on_all_cpus();
121122

122123
if (sev_snp_enabled)
@@ -708,6 +709,18 @@ static void sev_clflush_pages(struct page *pages[], unsigned long npages)
708709
}
709710
}
710711

712+
static void sev_writeback_caches(void)
713+
{
714+
/*
715+
* Ensure that all dirty guest tagged cache entries are written back
716+
* before releasing the pages back to the system for use. CLFLUSH will
717+
* not do this without SME_COHERENT, and flushing many cache lines
718+
* individually is slower than blasting WBINVD for large VMs, so issue
719+
* WBNOINVD (or WBINVD if the "no invalidate" variant is unsupported).
720+
*/
721+
wbnoinvd_on_all_cpus();
722+
}
723+
711724
static unsigned long get_num_contig_pages(unsigned long idx,
712725
struct page **inpages, unsigned long npages)
713726
{
@@ -2694,12 +2707,7 @@ int sev_mem_enc_unregister_region(struct kvm *kvm,
26942707
goto failed;
26952708
}
26962709

2697-
/*
2698-
* Ensure that all guest tagged cache entries are flushed before
2699-
* releasing the pages back to the system for use. CLFLUSH will
2700-
* not do this, so issue a WBINVD.
2701-
*/
2702-
wbinvd_on_all_cpus();
2710+
sev_writeback_caches();
27032711

27042712
__unregister_enc_region_locked(kvm, region);
27052713

@@ -3089,30 +3097,29 @@ static void sev_flush_encrypted_page(struct kvm_vcpu *vcpu, void *va)
30893097

30903098
/*
30913099
* VM Page Flush takes a host virtual address and a guest ASID. Fall
3092-
* back to WBINVD if this faults so as not to make any problems worse
3093-
* by leaving stale encrypted data in the cache.
3100+
* back to full writeback of caches if this faults so as not to make
3101+
* any problems worse by leaving stale encrypted data in the cache.
30943102
*/
30953103
if (WARN_ON_ONCE(wrmsrq_safe(MSR_AMD64_VM_PAGE_FLUSH, addr | asid)))
3096-
goto do_wbinvd;
3104+
goto do_sev_writeback_caches;
30973105

30983106
return;
30993107

3100-
do_wbinvd:
3101-
wbinvd_on_all_cpus();
3108+
do_sev_writeback_caches:
3109+
sev_writeback_caches();
31023110
}
31033111

31043112
void sev_guest_memory_reclaimed(struct kvm *kvm)
31053113
{
31063114
/*
31073115
* With SNP+gmem, private/encrypted memory is unreachable via the
3108-
* hva-based mmu notifiers, so these events are only actually
3109-
* pertaining to shared pages where there is no need to perform
3110-
* the WBINVD to flush associated caches.
3116+
* hva-based mmu notifiers, i.e. these events are explicitly scoped to
3117+
* shared pages, where there's no need to flush caches.
31113118
*/
31123119
if (!sev_guest(kvm) || sev_snp_guest(kvm))
31133120
return;
31143121

3115-
wbinvd_on_all_cpus();
3122+
sev_writeback_caches();
31163123
}
31173124

31183125
void sev_free_vcpu(struct kvm_vcpu *vcpu)
@@ -3876,9 +3883,9 @@ void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
38763883
* From this point forward, the VMSA will always be a guest-mapped page
38773884
* rather than the initial one allocated by KVM in svm->sev_es.vmsa. In
38783885
* theory, svm->sev_es.vmsa could be free'd and cleaned up here, but
3879-
* that involves cleanups like wbinvd_on_all_cpus() which would ideally
3880-
* be handled during teardown rather than guest boot. Deferring that
3881-
* also allows the existing logic for SEV-ES VMSAs to be re-used with
3886+
* that involves cleanups like flushing caches, which would ideally be
3887+
* handled during teardown rather than guest boot. Deferring that also
3888+
* allows the existing logic for SEV-ES VMSAs to be re-used with
38823889
* minimal SNP-specific changes.
38833890
*/
38843891
svm->sev_es.snp_has_guest_vmsa = true;
@@ -4874,7 +4881,7 @@ void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)
48744881

48754882
/*
48764883
* SEV-ES avoids host/guest cache coherency issues through
4877-
* WBINVD hooks issued via MMU notifiers during run-time, and
4884+
* WBNOINVD hooks issued via MMU notifiers during run-time, and
48784885
* KVM's VM destroy path at shutdown. Those MMU notifier events
48794886
* don't cover gmem since there is no requirement to map pages
48804887
* to a HVA in order to use them for a running guest. While the

0 commit comments

Comments
 (0)