From: Sean Christopherson <seanjc@google.com>
To: Thomas Gleixner <tglx@linutronix.de>,
Ingo Molnar <mingo@redhat.com>, Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
x86@kernel.org, Sean Christopherson <seanjc@google.com>,
Paolo Bonzini <pbonzini@redhat.com>
Cc: linux-kernel@vger.kernel.org, kvm@vger.kernel.org,
Zheyun Shen <szy0127@sjtu.edu.cn>,
Tom Lendacky <thomas.lendacky@amd.com>,
Kevin Loughlin <kevinloughlin@google.com>,
Mingwei Zhang <mizhang@google.com>
Subject: [PATCH 7/7] KVM: SVM: Flush cache only on CPUs running SEV guest
Date: Wed, 26 Feb 2025 17:48:58 -0800 [thread overview]
Message-ID: <20250227014858.3244505-8-seanjc@google.com> (raw)
In-Reply-To: <20250227014858.3244505-1-seanjc@google.com>
From: Zheyun Shen <szy0127@sjtu.edu.cn>
On AMD CPUs without ensuring cache consistency, each memory page
reclamation in an SEV guest triggers a call to do WBNOINVD/WBINVD on all
CPUs, thereby affecting the performance of other programs on the host.
Typically, an AMD server may have 128 cores or more, while the SEV guest
might only utilize 8 of these cores. Meanwhile, host can use qemu-affinity
to bind these 8 vCPUs to specific physical CPUs.
Therefore, keeping a record of the physical core numbers each time a vCPU
runs can help avoid flushing the cache for all CPUs every time.
Signed-off-by: Zheyun Shen <szy0127@sjtu.edu.cn>
Co-developed-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/svm/sev.c | 42 +++++++++++++++++++++++++++++++++++-------
arch/x86/kvm/svm/svm.h | 1 +
2 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 4238af23ab1b..b7a4cb728fba 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -447,6 +447,8 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
ret = sev_platform_init(&init_args);
if (ret)
goto e_free;
+ if (!zalloc_cpumask_var(&sev->have_run_cpus, GFP_KERNEL_ACCOUNT))
+ goto e_free;
/* This needs to happen after SEV/SNP firmware initialization. */
if (vm_type == KVM_X86_SNP_VM) {
@@ -706,16 +708,31 @@ static void sev_clflush_pages(struct page *pages[], unsigned long npages)
}
}
-static void sev_writeback_caches(void)
+static void sev_writeback_caches(struct kvm *kvm)
{
+ /*
+ * Note, the caller is responsible for ensuring correctness if the mask
+ * can be modified, e.g. if a CPU could be doing VMRUN.
+ */
+ if (cpumask_empty(to_kvm_sev_info(kvm)->have_run_cpus))
+ return;
+
/*
* Ensure that all dirty guest tagged cache entries are written back
* before releasing the pages back to the system for use. CLFLUSH will
* not do this without SME_COHERENT, and flushing many cache lines
* individually is slower than blasting WBINVD for large VMs, so issue
- * WBNOINVD (or WBINVD if the "no invalidate" variant is unsupported).
+ * WBNOINVD (or WBINVD if the "no invalidate" variant is unsupported)
+ * on CPUs that have done VMRUN, i.e. may have dirtied data using the
+ * VM's ASID.
+ *
+ * For simplicity, never remove CPUs from the bitmap. Ideally, KVM
+ * would clear the mask when flushing caches, but doing so requires
+ * serializing multiple calls and having responding CPUs (to the IPI)
+ * mark themselves as still running if they are running (or about to
+ * run) a vCPU for the VM.
*/
- wbnoinvd_on_all_cpus();
+ wbnoinvd_on_many_cpus(to_kvm_sev_info(kvm)->have_run_cpus);
}
static unsigned long get_num_contig_pages(unsigned long idx,
@@ -2766,7 +2783,7 @@ int sev_mem_enc_unregister_region(struct kvm *kvm,
goto failed;
}
- sev_writeback_caches();
+ sev_writeback_caches(kvm);
__unregister_enc_region_locked(kvm, region);
@@ -2914,6 +2931,7 @@ void sev_vm_destroy(struct kvm *kvm)
}
sev_asid_free(sev);
+ free_cpumask_var(sev->have_run_cpus);
}
void __init sev_set_cpu_caps(void)
@@ -3127,7 +3145,7 @@ static void sev_flush_encrypted_page(struct kvm_vcpu *vcpu, void *va)
return;
do_sev_writeback_caches:
- sev_writeback_caches();
+ sev_writeback_caches(vcpu->kvm);
}
void sev_guest_memory_reclaimed(struct kvm *kvm)
@@ -3140,7 +3158,7 @@ void sev_guest_memory_reclaimed(struct kvm *kvm)
if (!sev_guest(kvm) || sev_snp_guest(kvm))
return;
- sev_writeback_caches();
+ sev_writeback_caches(kvm);
}
void sev_free_vcpu(struct kvm_vcpu *vcpu)
@@ -3456,7 +3474,17 @@ void sev_es_unmap_ghcb(struct vcpu_svm *svm)
void pre_sev_run(struct vcpu_svm *svm, int cpu)
{
struct svm_cpu_data *sd = per_cpu_ptr(&svm_data, cpu);
- unsigned int asid = sev_get_asid(svm->vcpu.kvm);
+ struct kvm *kvm = svm->vcpu.kvm;
+ unsigned int asid = sev_get_asid(kvm);
+
+ /*
+ * To optimize cache flushes when memory is reclaimed from an SEV VM,
+ * track physical CPUs that enter the guest for SEV VMs and thus can
+ * have encrypted, dirty data in the cache, and flush caches only for
+ * CPUs that have entered the guest.
+ */
+ if (!cpumask_test_cpu(cpu, to_kvm_sev_info(kvm)->have_run_cpus))
+ cpumask_set_cpu(cpu, to_kvm_sev_info(kvm)->have_run_cpus);
/* Assign the asid allocated with this SEV guest */
svm->asid = asid;
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 5b159f017055..6ad18ce5a754 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -112,6 +112,7 @@ struct kvm_sev_info {
void *guest_req_buf; /* Bounce buffer for SNP Guest Request input */
void *guest_resp_buf; /* Bounce buffer for SNP Guest Request output */
struct mutex guest_req_mutex; /* Must acquire before using bounce buffers */
+ cpumask_var_t have_run_cpus; /* CPUs that have done VMRUN for this VM. */
};
struct kvm_svm {
--
2.48.1.711.g2feabab25a-goog
next prev parent reply other threads:[~2025-02-27 1:49 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-27 1:48 [PATCH 0/7] x86, KVM: Optimize SEV cache flushing Sean Christopherson
2025-02-27 1:48 ` [PATCH 1/7] KVM: SVM: Remove wbinvd in sev_vm_destroy() Sean Christopherson
2025-02-27 1:48 ` [PATCH 2/7] x86, lib: Drop the unused return value from wbinvd_on_all_cpus() Sean Christopherson
2025-02-27 19:59 ` Sean Christopherson
2025-02-27 1:48 ` [PATCH 3/7] x86, lib: Add WBNOINVD helper functions Sean Christopherson
2025-03-13 10:13 ` Huang, Kai
2025-02-27 1:48 ` [PATCH 4/7] KVM: SEV: Prefer WBNOINVD over WBINVD for cache maintenance efficiency Sean Christopherson
2025-02-27 1:48 ` [PATCH 5/7] KVM: x86: Use wbinvd_on_cpu() instead of an open-coded equivalent Sean Christopherson
2025-03-03 16:49 ` Tom Lendacky
2025-02-27 1:48 ` [PATCH 6/7] x86, lib: Add wbinvd and wbnoinvd helpers to target multiple CPUs Sean Christopherson
2025-02-27 12:45 ` Zheyun Shen
2025-03-13 10:19 ` Huang, Kai
2025-02-27 1:48 ` Sean Christopherson [this message]
2025-03-03 16:53 ` [PATCH 7/7] KVM: SVM: Flush cache only on CPUs running SEV guest Tom Lendacky
2025-03-03 16:46 ` [PATCH 0/7] x86, KVM: Optimize SEV cache flushing Tom Lendacky
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250227014858.3244505-8-seanjc@google.com \
--to=seanjc@google.com \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=kevinloughlin@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=mizhang@google.com \
--cc=pbonzini@redhat.com \
--cc=szy0127@sjtu.edu.cn \
--cc=tglx@linutronix.de \
--cc=thomas.lendacky@amd.com \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox