mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
KVM: SVM: Do all per-VM AVIC initialization during vCPU precreation phase
Move all per-VM AVIC initialization from VM creation to vCPU pre-creation, i.e. defer allocating the logical ID table and adding the VM to the GA Log list until vCPUs are created. This will allow removing the VM from the GA Log list before vCPUs are destroyed without needing yet another kvm_x86_ops hook (.vm_pre_destroy() is very intentionally called if and only if VM creation fully succeeds). As a bonus, this re-unites physical and logic table allocation, and avoids allocating a logical table in the unlikely scenario that userspace creates a VM without an in-kernel local APIC. Another bonus to hooking .vcpu_precreate() is that there is no need to unwind on failure, as the VM has already been created, i.e. KVM will run through all phases of VM destruction. In fact, unwinding is undesirable, as KVM tries to keep VM-wide behavior idempotent/sticky across creaton of multiple vCPUs. Reviewed-by: Naveen N Rao (AMD) <naveen@kernel.org> Link: https://patch.msgid.link/20260630210156.457151-3-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
This commit is contained in:
parent
8b47740b3a
commit
4fa9a3767d
|
|
@ -308,47 +308,32 @@ static int avic_alloc_physical_id_table(struct kvm *kvm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int avic_vcpu_precreate(struct kvm *kvm)
|
||||
static int avic_alloc_logical_id_table(struct kvm *kvm)
|
||||
{
|
||||
if (!irqchip_in_kernel(kvm) || WARN_ON_ONCE(!enable_apicv))
|
||||
return 0;
|
||||
|
||||
return avic_alloc_physical_id_table(kvm);
|
||||
}
|
||||
|
||||
void avic_vm_destroy(struct kvm *kvm)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct kvm_svm *kvm_svm = to_kvm_svm(kvm);
|
||||
|
||||
if (!enable_apicv)
|
||||
return;
|
||||
|
||||
free_page((unsigned long)kvm_svm->avic_logical_id_table);
|
||||
free_pages((unsigned long)kvm_svm->avic_physical_id_table,
|
||||
avic_get_physical_id_table_order(kvm));
|
||||
|
||||
spin_lock_irqsave(&svm_vm_data_hash_lock, flags);
|
||||
hash_del(&kvm_svm->hnode);
|
||||
spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags);
|
||||
}
|
||||
|
||||
int avic_vm_init(struct kvm *kvm)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err = -ENOMEM;
|
||||
struct kvm_svm *kvm_svm = to_kvm_svm(kvm);
|
||||
struct kvm_svm *k2;
|
||||
u32 vm_id;
|
||||
|
||||
if (!enable_apicv)
|
||||
if (kvm_svm->avic_logical_id_table)
|
||||
return 0;
|
||||
|
||||
kvm_svm->avic_logical_id_table = (void *)get_zeroed_page(GFP_KERNEL_ACCOUNT);
|
||||
if (!kvm_svm->avic_logical_id_table)
|
||||
goto free_avic;
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_irqsave(&svm_vm_data_hash_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void avic_add_vm_to_ga_log_list(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_svm *kvm_svm = to_kvm_svm(kvm);
|
||||
struct kvm_svm *k2;
|
||||
u32 vm_id;
|
||||
|
||||
lockdep_assert_held(&kvm->lock);
|
||||
|
||||
if (kvm_svm->avic_vm_id)
|
||||
return;
|
||||
|
||||
guard(spinlock_irqsave)(&svm_vm_data_hash_lock);
|
||||
again:
|
||||
vm_id = next_vm_id = (next_vm_id + 1) & AVIC_VM_ID_MASK;
|
||||
if (vm_id == 0) { /* id is 1-based, zero is not okay */
|
||||
|
|
@ -364,13 +349,48 @@ int avic_vm_init(struct kvm *kvm)
|
|||
}
|
||||
kvm_svm->avic_vm_id = vm_id;
|
||||
hash_add(svm_vm_data_hash, &kvm_svm->hnode, kvm_svm->avic_vm_id);
|
||||
spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags);
|
||||
}
|
||||
|
||||
int avic_vcpu_precreate(struct kvm *kvm)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!irqchip_in_kernel(kvm) || WARN_ON_ONCE(!enable_apicv))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Don't unwind on failure, all actions must be idempotent with respect
|
||||
* to creating multiple vCPUs, i.e. must persist until the VM is destroyed.
|
||||
*/
|
||||
r = avic_alloc_physical_id_table(kvm);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = avic_alloc_logical_id_table(kvm);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
avic_add_vm_to_ga_log_list(kvm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
free_avic:
|
||||
avic_vm_destroy(kvm);
|
||||
return err;
|
||||
void avic_vm_destroy(struct kvm *kvm)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct kvm_svm *kvm_svm = to_kvm_svm(kvm);
|
||||
|
||||
if (!enable_apicv)
|
||||
return;
|
||||
|
||||
free_page((unsigned long)kvm_svm->avic_logical_id_table);
|
||||
free_pages((unsigned long)kvm_svm->avic_physical_id_table,
|
||||
avic_get_physical_id_table_order(kvm));
|
||||
|
||||
if (kvm_svm->avic_vm_id) {
|
||||
spin_lock_irqsave(&svm_vm_data_hash_lock, flags);
|
||||
hash_del(&kvm_svm->hnode);
|
||||
spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static phys_addr_t avic_get_backing_page_address(struct vcpu_svm *svm)
|
||||
|
|
|
|||
|
|
@ -5297,12 +5297,6 @@ static int svm_vm_init(struct kvm *kvm)
|
|||
if (!pause_filter_count || !pause_filter_thresh)
|
||||
kvm_disable_exits(kvm, KVM_X86_DISABLE_EXITS_PAUSE);
|
||||
|
||||
if (enable_apicv) {
|
||||
int ret = avic_vm_init(kvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
svm_srso_vm_init();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -947,7 +947,6 @@ bool __init avic_hardware_setup(void);
|
|||
void avic_hardware_unsetup(void);
|
||||
int avic_vcpu_precreate(struct kvm *kvm);
|
||||
void avic_vm_destroy(struct kvm *kvm);
|
||||
int avic_vm_init(struct kvm *kvm);
|
||||
void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb);
|
||||
int avic_incomplete_ipi_interception(struct kvm_vcpu *vcpu);
|
||||
int avic_unaccelerated_access_interception(struct kvm_vcpu *vcpu);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user