KVM: Use a per-CPU variable to track which CPUs have enabled virtualization

Use a per-CPU variable instead of a shared bitmap to track which CPUs
have successfully enabled virtualization hardware.  Using a per-CPU bool
avoids the need for an additional allocation, and arguably yields easier
to read code.  Using a bitmap would be advantageous if KVM used it to
avoid generating IPIs to CPUs that failed to enable hardware, but that's
an extreme edge case and not worth optimizing, and the low level helpers
would still want to keep their individual checks as attempting to enable
virtualization hardware when it's already enabled can be problematic,
e.g. Intel's VMXON will fault.

Opportunistically change the order in hardware_enable_nolock() to set
the flag if and only if hardware enabling is successful, instead of
speculatively setting the flag and then clearing it on failure.

Add a comment explaining that the check in hardware_disable_nolock()
isn't simply paranoia.  Waaay back when, commit 1b6c016818 ("KVM: Keep
track of which cpus have virtualization enabled"), added the logic as a
guards against CPU hotplug racing with hardware enable/disable.  Now that
KVM has eliminated the race by taking cpu_hotplug_lock for read (via
cpus_read_lock()) when enabling or disabling hardware, at first glance it
appears that the check is now superfluous, i.e. it's tempting to remove
the per-CPU flag entirely...

Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20221130230934.1014142-47-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Sean Christopherson 2022-11-30 23:09:30 +00:00 committed by Paolo Bonzini
parent 667a83bf6a
commit 37d2588185

View File

@ -102,7 +102,7 @@ EXPORT_SYMBOL_GPL(halt_poll_ns_shrink);
DEFINE_MUTEX(kvm_lock);
LIST_HEAD(vm_list);
static cpumask_var_t cpus_hardware_enabled;
static DEFINE_PER_CPU(bool, hardware_enabled);
static int kvm_usage_count;
static atomic_t hardware_enable_failed;
@ -5096,21 +5096,17 @@ static struct miscdevice kvm_dev = {
static void hardware_enable_nolock(void *junk)
{
int cpu = smp_processor_id();
int r;
if (cpumask_test_cpu(cpu, cpus_hardware_enabled))
if (__this_cpu_read(hardware_enabled))
return;
cpumask_set_cpu(cpu, cpus_hardware_enabled);
r = kvm_arch_hardware_enable();
if (r) {
cpumask_clear_cpu(cpu, cpus_hardware_enabled);
if (kvm_arch_hardware_enable()) {
atomic_inc(&hardware_enable_failed);
pr_info("kvm: enabling virtualization on CPU%d failed\n", cpu);
pr_info("kvm: enabling virtualization on CPU%d failed\n",
raw_smp_processor_id());
return;
}
__this_cpu_write(hardware_enabled, true);
}
static int kvm_online_cpu(unsigned int cpu)
@ -5139,12 +5135,16 @@ static int kvm_online_cpu(unsigned int cpu)
static void hardware_disable_nolock(void *junk)
{
int cpu = smp_processor_id();
if (!cpumask_test_cpu(cpu, cpus_hardware_enabled))
/*
* Note, hardware_disable_all_nolock() tells all online CPUs to disable
* hardware, not just CPUs that successfully enabled hardware!
*/
if (!__this_cpu_read(hardware_enabled))
return;
cpumask_clear_cpu(cpu, cpus_hardware_enabled);
kvm_arch_hardware_disable();
__this_cpu_write(hardware_enabled, false);
}
static int kvm_offline_cpu(unsigned int cpu)
@ -5945,13 +5945,11 @@ int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module)
int r;
int cpu;
if (!zalloc_cpumask_var(&cpus_hardware_enabled, GFP_KERNEL))
return -ENOMEM;
r = cpuhp_setup_state_nocalls(CPUHP_AP_KVM_ONLINE, "kvm/cpu:online",
kvm_online_cpu, kvm_offline_cpu);
if (r)
goto out_free_2;
return r;
register_reboot_notifier(&kvm_reboot_notifier);
/* A kmem cache lets us meet the alignment requirements of fx_save. */
@ -6024,8 +6022,6 @@ out_free_4:
out_free_3:
unregister_reboot_notifier(&kvm_reboot_notifier);
cpuhp_remove_state_nocalls(CPUHP_AP_KVM_ONLINE);
out_free_2:
free_cpumask_var(cpus_hardware_enabled);
return r;
}
EXPORT_SYMBOL_GPL(kvm_init);
@ -6051,7 +6047,6 @@ void kvm_exit(void)
unregister_reboot_notifier(&kvm_reboot_notifier);
cpuhp_remove_state_nocalls(CPUHP_AP_KVM_ONLINE);
kvm_irqfd_exit();
free_cpumask_var(cpus_hardware_enabled);
}
EXPORT_SYMBOL_GPL(kvm_exit);