Skip to content

Commit ce5d244

Browse files
ouptonMarc Zyngier
authored andcommitted
KVM: arm64: Destroy mpidr_data for 'late' vCPU creation
A particularly annoying userspace could create a vCPU after KVM has computed mpidr_data for the VM, either by racing against VGIC initialization or having a userspace irqchip. In any case, this means mpidr_data no longer fully describes the VM, and attempts to find the new vCPU with kvm_mpidr_to_vcpu() will fail. The fix is to discard mpidr_data altogether, as it is only a performance optimization and not required for correctness. In all likelihood KVM will recompute the mappings when KVM_RUN is called on the new vCPU. Note that reads of mpidr_data are not guarded by a lock; promote to RCU to cope with the possibility of mpidr_data being invalidated at runtime. Fixes: 54a8006 ("KVM: arm64: Fast-track kvm_mpidr_to_vcpu() when mpidr_data is available") Signed-off-by: Oliver Upton <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Marc Zyngier <[email protected]>
1 parent 5053c3f commit ce5d244

File tree

1 file changed

+41
-9
lines changed

1 file changed

+41
-9
lines changed

arch/arm64/kvm/arm.c

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,23 @@ void kvm_arch_create_vm_debugfs(struct kvm *kvm)
195195
kvm_sys_regs_create_debugfs(kvm);
196196
}
197197

198+
static void kvm_destroy_mpidr_data(struct kvm *kvm)
199+
{
200+
struct kvm_mpidr_data *data;
201+
202+
mutex_lock(&kvm->arch.config_lock);
203+
204+
data = rcu_dereference_protected(kvm->arch.mpidr_data,
205+
lockdep_is_held(&kvm->arch.config_lock));
206+
if (data) {
207+
rcu_assign_pointer(kvm->arch.mpidr_data, NULL);
208+
synchronize_rcu();
209+
kfree(data);
210+
}
211+
212+
mutex_unlock(&kvm->arch.config_lock);
213+
}
214+
198215
/**
199216
* kvm_arch_destroy_vm - destroy the VM data structure
200217
* @kvm: pointer to the KVM struct
@@ -209,7 +226,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
209226
if (is_protected_kvm_enabled())
210227
pkvm_destroy_hyp_vm(kvm);
211228

212-
kfree(kvm->arch.mpidr_data);
229+
kvm_destroy_mpidr_data(kvm);
230+
213231
kfree(kvm->arch.sysreg_masks);
214232
kvm_destroy_vcpus(kvm);
215233

@@ -395,6 +413,13 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
395413

396414
vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu;
397415

416+
/*
417+
* This vCPU may have been created after mpidr_data was initialized.
418+
* Throw out the pre-computed mappings if that is the case which forces
419+
* KVM to fall back to iteratively searching the vCPUs.
420+
*/
421+
kvm_destroy_mpidr_data(vcpu->kvm);
422+
398423
err = kvm_vgic_vcpu_init(vcpu);
399424
if (err)
400425
return err;
@@ -594,7 +619,8 @@ static void kvm_init_mpidr_data(struct kvm *kvm)
594619

595620
mutex_lock(&kvm->arch.config_lock);
596621

597-
if (kvm->arch.mpidr_data || atomic_read(&kvm->online_vcpus) == 1)
622+
if (rcu_access_pointer(kvm->arch.mpidr_data) ||
623+
atomic_read(&kvm->online_vcpus) == 1)
598624
goto out;
599625

600626
kvm_for_each_vcpu(c, vcpu, kvm) {
@@ -631,7 +657,7 @@ static void kvm_init_mpidr_data(struct kvm *kvm)
631657
data->cmpidr_to_idx[index] = c;
632658
}
633659

634-
kvm->arch.mpidr_data = data;
660+
rcu_assign_pointer(kvm->arch.mpidr_data, data);
635661
out:
636662
mutex_unlock(&kvm->arch.config_lock);
637663
}
@@ -2470,21 +2496,27 @@ static int __init init_hyp_mode(void)
24702496

24712497
struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr)
24722498
{
2473-
struct kvm_vcpu *vcpu;
2499+
struct kvm_vcpu *vcpu = NULL;
2500+
struct kvm_mpidr_data *data;
24742501
unsigned long i;
24752502

24762503
mpidr &= MPIDR_HWID_BITMASK;
24772504

2478-
if (kvm->arch.mpidr_data) {
2479-
u16 idx = kvm_mpidr_index(kvm->arch.mpidr_data, mpidr);
2505+
rcu_read_lock();
2506+
data = rcu_dereference(kvm->arch.mpidr_data);
24802507

2481-
vcpu = kvm_get_vcpu(kvm,
2482-
kvm->arch.mpidr_data->cmpidr_to_idx[idx]);
2508+
if (data) {
2509+
u16 idx = kvm_mpidr_index(data, mpidr);
2510+
2511+
vcpu = kvm_get_vcpu(kvm, data->cmpidr_to_idx[idx]);
24832512
if (mpidr != kvm_vcpu_get_mpidr_aff(vcpu))
24842513
vcpu = NULL;
2514+
}
24852515

2516+
rcu_read_unlock();
2517+
2518+
if (vcpu)
24862519
return vcpu;
2487-
}
24882520

24892521
kvm_for_each_vcpu(i, vcpu, kvm) {
24902522
if (mpidr == kvm_vcpu_get_mpidr_aff(vcpu))

0 commit comments

Comments
 (0)