From: Yosry Ahmed <yosry.ahmed@linux.dev>
To: Sean Christopherson <seanjc@google.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
Jim Mattson <jmattson@google.com>,
Maxim Levitsky <mlevitsk@redhat.com>,
Vitaly Kuznetsov <vkuznets@redhat.com>,
Rik van Riel <riel@surriel.com>,
Tom Lendacky <thomas.lendacky@amd.com>,
x86@kernel.org, kvm@vger.kernel.org,
linux-kernel@vger.kernel.org, Yosry Ahmed <yosry.ahmed@linux.dev>
Subject: [RFC PATCH 09/24] KVM: SEV: Generalize tracking ASID->vCPU with xarrays
Date: Wed, 26 Mar 2025 19:36:04 +0000 [thread overview]
Message-ID: <20250326193619.3714986-10-yosry.ahmed@linux.dev> (raw)
In-Reply-To: <20250326193619.3714986-1-yosry.ahmed@linux.dev>
Following changes will track ASID to vCPU mappings for all ASIDs, not
just SEV ASIDs. Using per-CPU arrays with the maximum possible number of
ASIDs would be too expensive. Use xarrays to generalize tracking the
mappings instead. The logic is also mostly moved outside the SEV code to
allow future changes to reuse it for normal SVM VMs.
Storing into an xarray is more expensive than reading/writing to an
array, but is only done on vCPU load and should be mostly uncontended.
Also, the size of the xarray should be O(# of VMs), so it is not
expected to be huge. In fact, the xarray will probably use less memory
than the normal array even for SEV on machines that only run a few VMs.
When a new ASID is allocated, reserve an entry for it on all xarrays on
all CPUs. This allows the memory allocations to happen in a more relaxed
context (allowing reclaim and accounting), and failures to be handled at
VM creation time. However, entries will be allocated even on CPUs that
never run the VM.
The alternative is relying on on-demand GFP_ATOMIC allocations with
xa_store() on vCPU load. These allocations are more likely to fail and
more difficult to handle since vCPU load cannot fail. Flushing the TLB
if the xa_store() fails is probably sufficient handling, but
preallocations are easier to reason about.
Signed-off-by: Yosry Ahmed <yosry.ahmed@linux.dev>
---
arch/x86/kvm/svm/sev.c | 25 ++++-----------------
arch/x86/kvm/svm/svm.c | 50 +++++++++++++++++++++++++++++++-----------
arch/x86/kvm/svm/svm.h | 7 +++---
3 files changed, 44 insertions(+), 38 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 1742f51d4c194..c11da3259c089 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -211,6 +211,9 @@ static int sev_asid_new(struct kvm_sev_info *sev)
goto e_uncharge;
}
+ if (!svm_register_asid(asid))
+ goto e_uncharge;
+
__set_bit(asid, sev_asid_bitmap);
mutex_unlock(&sev_bitmap_lock);
@@ -231,18 +234,10 @@ unsigned int sev_get_asid(struct kvm *kvm)
static void sev_asid_free(struct kvm_sev_info *sev)
{
- struct svm_cpu_data *sd;
- int cpu;
+ svm_unregister_asid(sev->asid);
mutex_lock(&sev_bitmap_lock);
-
__set_bit(sev->asid, sev_reclaim_asid_bitmap);
-
- for_each_possible_cpu(cpu) {
- sd = per_cpu_ptr(&svm_data, cpu);
- sd->sev_vcpus[sev->asid] = NULL;
- }
-
mutex_unlock(&sev_bitmap_lock);
sev_misc_cg_uncharge(sev);
@@ -3076,18 +3071,6 @@ void sev_hardware_unsetup(void)
misc_cg_set_capacity(MISC_CG_RES_SEV_ES, 0);
}
-int sev_cpu_init(struct svm_cpu_data *sd)
-{
- if (!sev_enabled)
- return 0;
-
- sd->sev_vcpus = kcalloc(nr_asids, sizeof(void *), GFP_KERNEL);
- if (!sd->sev_vcpus)
- return -ENOMEM;
-
- return 0;
-}
-
/*
* Pages used by hardware to hold guest encrypted state must be flushed before
* returning them to the system.
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index ce67112732e8c..b740114a9d9bc 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -694,7 +694,7 @@ static void svm_cpu_uninit(int cpu)
if (!sd->save_area)
return;
- kfree(sd->sev_vcpus);
+ xa_destroy(&sd->asid_vcpu);
__free_page(__sme_pa_to_page(sd->save_area_pa));
sd->save_area_pa = 0;
sd->save_area = NULL;
@@ -711,18 +711,11 @@ static int svm_cpu_init(int cpu)
if (!save_area_page)
return ret;
- ret = sev_cpu_init(sd);
- if (ret)
- goto free_save_area;
+ xa_init(&sd->asid_vcpu);
sd->save_area = page_address(save_area_page);
sd->save_area_pa = __sme_page_pa(save_area_page);
return 0;
-
-free_save_area:
- __free_page(save_area_page);
- return ret;
-
}
static void set_dr_intercepts(struct vcpu_svm *svm)
@@ -1557,6 +1550,7 @@ static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
unsigned int asid;
struct vcpu_svm *svm = to_svm(vcpu);
struct svm_cpu_data *sd = per_cpu_ptr(&svm_data, cpu);
+ struct kvm_vcpu *prev;
if (vcpu->scheduled_out && !kvm_pause_in_guest(vcpu->kvm))
shrink_ple_window(vcpu);
@@ -1573,13 +1567,13 @@ static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
if (sev_guest(vcpu->kvm)) {
/*
* Flush the TLB when a different vCPU using the same ASID is
- * run on the same CPU.
+ * run on the same CPU. xa_store() should always succeed because
+ * the entry is reserved when the ASID is allocated.
*/
asid = sev_get_asid(vcpu->kvm);
- if (sd->sev_vcpus[asid] != vcpu) {
- sd->sev_vcpus[asid] = vcpu;
+ prev = xa_store(&sd->asid_vcpu, asid, vcpu, GFP_ATOMIC);
+ if (prev != vcpu || WARN_ON_ONCE(xa_err(prev)))
kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
- }
}
}
@@ -5047,6 +5041,36 @@ static void svm_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)
sev_vcpu_deliver_sipi_vector(vcpu, vector);
}
+void svm_unregister_asid(unsigned int asid)
+{
+ struct svm_cpu_data *sd;
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ sd = per_cpu_ptr(&svm_data, cpu);
+ xa_erase(&sd->asid_vcpu, asid);
+ }
+}
+
+bool svm_register_asid(unsigned int asid)
+{
+ struct svm_cpu_data *sd;
+ int cpu;
+
+ /*
+ * Preallocate entries on all CPUs for the ASID to avoid memory
+ * allocations in the vCPU load path.
+ */
+ for_each_possible_cpu(cpu) {
+ sd = per_cpu_ptr(&svm_data, cpu);
+ if (xa_reserve(&sd->asid_vcpu, asid, GFP_KERNEL_ACCOUNT)) {
+ svm_unregister_asid(asid);
+ return false;
+ }
+ }
+ return true;
+}
+
static void svm_vm_destroy(struct kvm *kvm)
{
avic_vm_destroy(kvm);
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 3ab2a424992c1..4929b96d3d700 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -340,8 +340,7 @@ struct svm_cpu_data {
struct vmcb *current_vmcb;
- /* index = sev_asid, value = vcpu pointer */
- struct kvm_vcpu **sev_vcpus;
+ struct xarray asid_vcpu;
};
DECLARE_PER_CPU(struct svm_cpu_data, svm_data);
@@ -655,6 +654,8 @@ void set_msr_interception(struct kvm_vcpu *vcpu, u32 *msrpm, u32 msr,
void svm_set_x2apic_msr_interception(struct vcpu_svm *svm, bool disable);
void svm_complete_interrupt_delivery(struct kvm_vcpu *vcpu, int delivery_mode,
int trig_mode, int vec);
+bool svm_register_asid(unsigned int asid);
+void svm_unregister_asid(unsigned int asid);
/* nested.c */
@@ -793,7 +794,6 @@ void sev_vm_destroy(struct kvm *kvm);
void __init sev_set_cpu_caps(void);
void __init sev_hardware_setup(void);
void sev_hardware_unsetup(void);
-int sev_cpu_init(struct svm_cpu_data *sd);
int sev_dev_get_attr(u32 group, u64 attr, u64 *val);
extern unsigned int max_sev_asid;
void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code);
@@ -817,7 +817,6 @@ static inline void sev_vm_destroy(struct kvm *kvm) {}
static inline void __init sev_set_cpu_caps(void) {}
static inline void __init sev_hardware_setup(void) {}
static inline void sev_hardware_unsetup(void) {}
-static inline int sev_cpu_init(struct svm_cpu_data *sd) { return 0; }
static inline int sev_dev_get_attr(u32 group, u64 attr, u64 *val) { return -ENXIO; }
#define max_sev_asid 0
static inline void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) {}
--
2.49.0.395.g12beb8f557-goog
next prev parent reply other threads:[~2025-03-26 19:37 UTC|newest]
Thread overview: 58+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-26 19:35 [RFC PATCH 00/24] KVM: SVM: Rework ASID management Yosry Ahmed
2025-03-26 19:35 ` [RFC PATCH 01/24] KVM: VMX: Generalize VPID allocation to be vendor-neutral Yosry Ahmed
2025-03-27 10:58 ` Nikunj A Dadhania
2025-03-27 17:13 ` Yosry Ahmed
2025-03-27 19:42 ` Sean Christopherson
2025-06-23 16:44 ` Sean Christopherson
2025-03-26 19:35 ` [RFC PATCH 02/24] KVM: SVM: Use cached local variable in init_vmcb() Yosry Ahmed
2025-04-03 19:56 ` Maxim Levitsky
2025-03-26 19:35 ` [RFC PATCH 03/24] KVM: SVM: Add helpers to set/clear ASID flush in VMCB Yosry Ahmed
2025-04-03 20:00 ` Maxim Levitsky
2025-06-23 16:46 ` Sean Christopherson
2025-03-26 19:35 ` [RFC PATCH 04/24] KVM: SVM: Flush everything if FLUSHBYASID is not available Yosry Ahmed
2025-04-03 20:00 ` Maxim Levitsky
2025-03-26 19:36 ` [RFC PATCH 05/24] KVM: SVM: Flush the ASID when running on a new CPU Yosry Ahmed
2025-04-03 20:00 ` Maxim Levitsky
2025-03-26 19:36 ` [RFC PATCH 06/24] KVM: SEV: Track ASID->vCPU instead of ASID->VMCB Yosry Ahmed
2025-04-03 20:04 ` Maxim Levitsky
2025-04-22 9:41 ` Yosry Ahmed
2025-06-20 23:13 ` Sean Christopherson
2025-06-23 19:50 ` Tom Lendacky
2025-06-23 20:37 ` Sean Christopherson
2025-03-26 19:36 ` [RFC PATCH 07/24] KVM: SEV: Track ASID->vCPU on vCPU load Yosry Ahmed
2025-04-03 20:04 ` Maxim Levitsky
2025-03-26 19:36 ` [RFC PATCH 08/24] KVM: SEV: Drop pre_sev_run() Yosry Ahmed
2025-04-03 20:04 ` Maxim Levitsky
2025-03-26 19:36 ` Yosry Ahmed [this message]
2025-04-03 20:05 ` [RFC PATCH 09/24] KVM: SEV: Generalize tracking ASID->vCPU with xarrays Maxim Levitsky
2025-04-22 9:50 ` Yosry Ahmed
2025-03-26 19:36 ` [RFC PATCH 10/24] KVM: SVM: Use a single ASID per VM Yosry Ahmed
2025-04-03 20:05 ` Maxim Levitsky
2025-04-22 9:51 ` Yosry Ahmed
2025-03-26 19:36 ` [RFC PATCH 11/24] KVM: nSVM: Use a separate ASID for nested guests Yosry Ahmed
2025-04-03 20:09 ` Maxim Levitsky
2025-04-22 10:08 ` Yosry Ahmed
2025-03-26 19:36 ` [RFC PATCH 12/24] KVM: x86: hyper-v: Pass is_guest_mode to kvm_hv_vcpu_purge_flush_tlb() Yosry Ahmed
2025-04-03 20:09 ` Maxim Levitsky
2025-06-23 19:22 ` Sean Christopherson
2025-03-26 19:36 ` [RFC PATCH 13/24] KVM: nSVM: Parameterize svm_flush_tlb_asid() by is_guest_mode Yosry Ahmed
2025-04-03 20:10 ` Maxim Levitsky
2025-04-22 10:04 ` Yosry Ahmed
2025-03-26 19:36 ` [RFC PATCH 14/24] KVM: nSVM: Split nested_svm_transition_tlb_flush() into entry/exit fns Yosry Ahmed
2025-03-26 19:36 ` [RFC PATCH 15/24] KVM: x86/mmu: rename __kvm_mmu_invalidate_addr() Yosry Ahmed
2025-04-03 20:10 ` Maxim Levitsky
2025-03-26 19:36 ` [RFC PATCH 16/24] KVM: x86/mmu: Allow skipping the gva flush in kvm_mmu_invalidate_addr() Yosry Ahmed
2025-04-03 20:10 ` Maxim Levitsky
2025-03-26 19:36 ` [RFC PATCH 17/24] KVM: nSVM: Flush both L1 and L2 ASIDs on KVM_REQ_TLB_FLUSH Yosry Ahmed
2025-04-03 20:10 ` Maxim Levitsky
2025-03-26 19:41 ` [RFC PATCH 18/24] KVM: nSVM: Handle nested TLB flush requests through TLB_CONTROL Yosry Ahmed
2025-03-26 19:43 ` [RFC PATCH 19/24] KVM: nSVM: Flush the TLB if L1 changes L2's ASID Yosry Ahmed
2025-03-26 19:44 ` [RFC PATCH 20/24] KVM: nSVM: Do not reset TLB_CONTROL in VMCB02 on nested entry Yosry Ahmed
2025-03-26 19:44 ` [RFC PATCH 21/24] KVM: nSVM: Service local TLB flushes before nested transitions Yosry Ahmed
2025-03-26 19:44 ` [RFC PATCH 22/24] KVM: nSVM: Handle INVLPGA interception correctly Yosry Ahmed
2025-04-03 20:10 ` Maxim Levitsky
2025-06-24 1:08 ` Sean Christopherson
2025-03-26 19:44 ` [RFC PATCH 23/24] KVM: nSVM: Allocate a new ASID for nested guests Yosry Ahmed
2025-04-03 20:11 ` Maxim Levitsky
2025-04-22 10:01 ` Yosry Ahmed
2025-03-26 19:44 ` [RFC PATCH 24/24] KVM: nSVM: Stop bombing the TLB on nested transitions Yosry Ahmed
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=20250326193619.3714986-10-yosry.ahmed@linux.dev \
--to=yosry.ahmed@linux.dev \
--cc=jmattson@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mlevitsk@redhat.com \
--cc=pbonzini@redhat.com \
--cc=riel@surriel.com \
--cc=seanjc@google.com \
--cc=thomas.lendacky@amd.com \
--cc=vkuznets@redhat.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;
as well as URLs for NNTP newsgroup(s).