Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 1b6c016818a5 ("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>

authored by

Sean Christopherson and committed by
Paolo Bonzini
37d25881 667a83bf

+18 -23
+18 -23
virt/kvm/kvm_main.c
··· 102 102 DEFINE_MUTEX(kvm_lock); 103 103 LIST_HEAD(vm_list); 104 104 105 - static cpumask_var_t cpus_hardware_enabled; 105 + static DEFINE_PER_CPU(bool, hardware_enabled); 106 106 static int kvm_usage_count; 107 107 static atomic_t hardware_enable_failed; 108 108 ··· 5096 5096 5097 5097 static void hardware_enable_nolock(void *junk) 5098 5098 { 5099 - int cpu = smp_processor_id(); 5100 - int r; 5101 - 5102 - if (cpumask_test_cpu(cpu, cpus_hardware_enabled)) 5099 + if (__this_cpu_read(hardware_enabled)) 5103 5100 return; 5104 5101 5105 - cpumask_set_cpu(cpu, cpus_hardware_enabled); 5106 - 5107 - r = kvm_arch_hardware_enable(); 5108 - 5109 - if (r) { 5110 - cpumask_clear_cpu(cpu, cpus_hardware_enabled); 5102 + if (kvm_arch_hardware_enable()) { 5111 5103 atomic_inc(&hardware_enable_failed); 5112 - pr_info("kvm: enabling virtualization on CPU%d failed\n", cpu); 5104 + pr_info("kvm: enabling virtualization on CPU%d failed\n", 5105 + raw_smp_processor_id()); 5106 + return; 5113 5107 } 5108 + 5109 + __this_cpu_write(hardware_enabled, true); 5114 5110 } 5115 5111 5116 5112 static int kvm_online_cpu(unsigned int cpu) ··· 5135 5139 5136 5140 static void hardware_disable_nolock(void *junk) 5137 5141 { 5138 - int cpu = smp_processor_id(); 5139 - 5140 - if (!cpumask_test_cpu(cpu, cpus_hardware_enabled)) 5142 + /* 5143 + * Note, hardware_disable_all_nolock() tells all online CPUs to disable 5144 + * hardware, not just CPUs that successfully enabled hardware! 5145 + */ 5146 + if (!__this_cpu_read(hardware_enabled)) 5141 5147 return; 5142 - cpumask_clear_cpu(cpu, cpus_hardware_enabled); 5148 + 5143 5149 kvm_arch_hardware_disable(); 5150 + 5151 + __this_cpu_write(hardware_enabled, false); 5144 5152 } 5145 5153 5146 5154 static int kvm_offline_cpu(unsigned int cpu) ··· 5945 5945 int r; 5946 5946 int cpu; 5947 5947 5948 - if (!zalloc_cpumask_var(&cpus_hardware_enabled, GFP_KERNEL)) 5949 - return -ENOMEM; 5950 - 5951 5948 r = cpuhp_setup_state_nocalls(CPUHP_AP_KVM_ONLINE, "kvm/cpu:online", 5952 5949 kvm_online_cpu, kvm_offline_cpu); 5953 5950 if (r) 5954 - goto out_free_2; 5951 + return r; 5952 + 5955 5953 register_reboot_notifier(&kvm_reboot_notifier); 5956 5954 5957 5955 /* A kmem cache lets us meet the alignment requirements of fx_save. */ ··· 6022 6024 out_free_3: 6023 6025 unregister_reboot_notifier(&kvm_reboot_notifier); 6024 6026 cpuhp_remove_state_nocalls(CPUHP_AP_KVM_ONLINE); 6025 - out_free_2: 6026 - free_cpumask_var(cpus_hardware_enabled); 6027 6027 return r; 6028 6028 } 6029 6029 EXPORT_SYMBOL_GPL(kvm_init); ··· 6047 6051 unregister_reboot_notifier(&kvm_reboot_notifier); 6048 6052 cpuhp_remove_state_nocalls(CPUHP_AP_KVM_ONLINE); 6049 6053 kvm_irqfd_exit(); 6050 - free_cpumask_var(cpus_hardware_enabled); 6051 6054 } 6052 6055 EXPORT_SYMBOL_GPL(kvm_exit); 6053 6056