summaryrefslogtreecommitdiffstats
path: root/virt
diff options
context:
space:
mode:
authorIsaku Yamahata <isaku.yamahata@intel.com>2022-11-30 23:09:31 +0000
committerPaolo Bonzini <pbonzini@redhat.com>2022-12-29 15:48:35 -0500
commite6fb7d6eb4217badb018169ec2871da8e92f6555 (patch)
treef3175950a68fc03b44b98c72810a8783821949d1 /virt
parent37d258818562bbb5baf487489848640abceafbd9 (diff)
downloadlinux-stable-e6fb7d6eb4217badb018169ec2871da8e92f6555.tar.gz
linux-stable-e6fb7d6eb4217badb018169ec2871da8e92f6555.tar.bz2
linux-stable-e6fb7d6eb4217badb018169ec2871da8e92f6555.zip
KVM: Make hardware_enable_failed a local variable in the "enable all" path
Rework detecting hardware enabling errors to use a local variable in the "enable all" path to track whether or not enabling was successful across all CPUs. Using a global variable complicates paths that enable hardware only on the current CPU, e.g. kvm_resume() and kvm_online_cpu(). Opportunistically add a WARN if hardware enabling fails during kvm_resume(), KVM is all kinds of hosed if CPU0 fails to enable hardware. The WARN is largely futile in the current code, as KVM BUG()s on spurious faults on VMX instructions, e.g. attempting to run a vCPU on CPU if hardware enabling fails will explode. ------------[ cut here ]------------ kernel BUG at arch/x86/kvm/x86.c:508! invalid opcode: 0000 [#1] SMP CPU: 3 PID: 1009 Comm: CPU 4/KVM Not tainted 6.1.0-rc1+ #11 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:kvm_spurious_fault+0xa/0x10 Call Trace: vmx_vcpu_load_vmcs+0x192/0x230 [kvm_intel] vmx_vcpu_load+0x16/0x60 [kvm_intel] kvm_arch_vcpu_load+0x32/0x1f0 vcpu_load+0x2f/0x40 kvm_arch_vcpu_ioctl_run+0x19/0x9d0 kvm_vcpu_ioctl+0x271/0x660 __x64_sys_ioctl+0x80/0xb0 do_syscall_64+0x2b/0x50 entry_SYSCALL_64_after_hwframe+0x46/0xb0 But, the WARN may provide a breadcrumb to understand what went awry, and someday KVM may fix one or both of those bugs, e.g. by finding a way to eat spurious faults no matter the context (easier said than done due to side effects of certain operations, e.g. Intel's VMCLEAR). Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com> [sean: rebase, WARN on failure in kvm_resume()] Signed-off-by: Sean Christopherson <seanjc@google.com> Message-Id: <20221130230934.1014142-48-seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'virt')
-rw-r--r--virt/kvm/kvm_main.c35
1 files changed, 16 insertions, 19 deletions
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index bcfc0bc15153..2d26a898ac6c 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -104,7 +104,6 @@ LIST_HEAD(vm_list);
static DEFINE_PER_CPU(bool, hardware_enabled);
static int kvm_usage_count;
-static atomic_t hardware_enable_failed;
static struct kmem_cache *kvm_vcpu_cache;
@@ -5094,19 +5093,25 @@ static struct miscdevice kvm_dev = {
&kvm_chardev_ops,
};
-static void hardware_enable_nolock(void *junk)
+static int __hardware_enable_nolock(void)
{
if (__this_cpu_read(hardware_enabled))
- return;
+ return 0;
if (kvm_arch_hardware_enable()) {
- atomic_inc(&hardware_enable_failed);
pr_info("kvm: enabling virtualization on CPU%d failed\n",
raw_smp_processor_id());
- return;
+ return -EIO;
}
__this_cpu_write(hardware_enabled, true);
+ return 0;
+}
+
+static void hardware_enable_nolock(void *failed)
+{
+ if (__hardware_enable_nolock())
+ atomic_inc(failed);
}
static int kvm_online_cpu(unsigned int cpu)
@@ -5119,16 +5124,8 @@ static int kvm_online_cpu(unsigned int cpu)
* errors when scheduled to this CPU.
*/
mutex_lock(&kvm_lock);
- if (kvm_usage_count) {
- WARN_ON_ONCE(atomic_read(&hardware_enable_failed));
-
- hardware_enable_nolock(NULL);
-
- if (atomic_read(&hardware_enable_failed)) {
- atomic_set(&hardware_enable_failed, 0);
- ret = -EIO;
- }
- }
+ if (kvm_usage_count)
+ ret = __hardware_enable_nolock();
mutex_unlock(&kvm_lock);
return ret;
}
@@ -5176,6 +5173,7 @@ static void hardware_disable_all(void)
static int hardware_enable_all(void)
{
+ atomic_t failed = ATOMIC_INIT(0);
int r = 0;
/*
@@ -5191,10 +5189,9 @@ static int hardware_enable_all(void)
kvm_usage_count++;
if (kvm_usage_count == 1) {
- atomic_set(&hardware_enable_failed, 0);
- on_each_cpu(hardware_enable_nolock, NULL, 1);
+ on_each_cpu(hardware_enable_nolock, &failed, 1);
- if (atomic_read(&hardware_enable_failed)) {
+ if (atomic_read(&failed)) {
hardware_disable_all_nolock();
r = -EBUSY;
}
@@ -5828,7 +5825,7 @@ static void kvm_resume(void)
lockdep_assert_irqs_disabled();
if (kvm_usage_count)
- hardware_enable_nolock(NULL);
+ WARN_ON_ONCE(__hardware_enable_nolock());
}
static struct syscore_ops kvm_syscore_ops = {