KVM: Keep track of which cpus have virtualization enabled

By keeping track of which cpus have virtualization enabled, we
prevent double-enable or double-disable during hotplug, which is a
very fatal oops.

Signed-off-by: Avi Kivity <avi@qumranet.com>
This commit is contained in:
Avi Kivity 2007-05-24 13:03:52 +03:00
parent a52b1752c0
commit 1b6c016818

View File

@ -50,8 +50,12 @@ MODULE_LICENSE("GPL");
static DEFINE_SPINLOCK(kvm_lock); static DEFINE_SPINLOCK(kvm_lock);
static LIST_HEAD(vm_list); static LIST_HEAD(vm_list);
static cpumask_t cpus_hardware_enabled;
struct kvm_arch_ops *kvm_arch_ops; struct kvm_arch_ops *kvm_arch_ops;
static void hardware_disable(void *ignored);
#define STAT_OFFSET(x) offsetof(struct kvm_vcpu, stat.x) #define STAT_OFFSET(x) offsetof(struct kvm_vcpu, stat.x)
static struct kvm_stats_debugfs_item { static struct kvm_stats_debugfs_item {
@ -2930,7 +2934,7 @@ static int kvm_reboot(struct notifier_block *notifier, unsigned long val,
* in vmx root mode. * in vmx root mode.
*/ */
printk(KERN_INFO "kvm: exiting hardware virtualization\n"); printk(KERN_INFO "kvm: exiting hardware virtualization\n");
on_each_cpu(kvm_arch_ops->hardware_disable, NULL, 0, 1); on_each_cpu(hardware_disable, NULL, 0, 1);
} }
return NOTIFY_OK; return NOTIFY_OK;
} }
@ -2973,6 +2977,27 @@ static void decache_vcpus_on_cpu(int cpu)
spin_unlock(&kvm_lock); spin_unlock(&kvm_lock);
} }
static void hardware_enable(void *junk)
{
int cpu = raw_smp_processor_id();
if (cpu_isset(cpu, cpus_hardware_enabled))
return;
cpu_set(cpu, cpus_hardware_enabled);
kvm_arch_ops->hardware_enable(NULL);
}
static void hardware_disable(void *junk)
{
int cpu = raw_smp_processor_id();
if (!cpu_isset(cpu, cpus_hardware_enabled))
return;
cpu_clear(cpu, cpus_hardware_enabled);
decache_vcpus_on_cpu(cpu);
kvm_arch_ops->hardware_disable(NULL);
}
static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val, static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val,
void *v) void *v)
{ {
@ -2985,16 +3010,13 @@ static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val,
case CPU_UP_CANCELED_FROZEN: case CPU_UP_CANCELED_FROZEN:
printk(KERN_INFO "kvm: disabling virtualization on CPU%d\n", printk(KERN_INFO "kvm: disabling virtualization on CPU%d\n",
cpu); cpu);
decache_vcpus_on_cpu(cpu); smp_call_function_single(cpu, hardware_disable, NULL, 0, 1);
smp_call_function_single(cpu, kvm_arch_ops->hardware_disable,
NULL, 0, 1);
break; break;
case CPU_ONLINE: case CPU_ONLINE:
case CPU_ONLINE_FROZEN: case CPU_ONLINE_FROZEN:
printk(KERN_INFO "kvm: enabling virtualization on CPU%d\n", printk(KERN_INFO "kvm: enabling virtualization on CPU%d\n",
cpu); cpu);
smp_call_function_single(cpu, kvm_arch_ops->hardware_enable, smp_call_function_single(cpu, hardware_enable, NULL, 0, 1);
NULL, 0, 1);
break; break;
} }
return NOTIFY_OK; return NOTIFY_OK;
@ -3088,14 +3110,13 @@ static void kvm_exit_debug(void)
static int kvm_suspend(struct sys_device *dev, pm_message_t state) static int kvm_suspend(struct sys_device *dev, pm_message_t state)
{ {
decache_vcpus_on_cpu(raw_smp_processor_id()); on_each_cpu(hardware_disable, NULL, 0, 0);
on_each_cpu(kvm_arch_ops->hardware_disable, NULL, 0, 1);
return 0; return 0;
} }
static int kvm_resume(struct sys_device *dev) static int kvm_resume(struct sys_device *dev)
{ {
on_each_cpu(kvm_arch_ops->hardware_enable, NULL, 0, 1); on_each_cpu(hardware_disable, NULL, 0, 0);
return 0; return 0;
} }
@ -3136,7 +3157,7 @@ int kvm_init_arch(struct kvm_arch_ops *ops, struct module *module)
if (r < 0) if (r < 0)
goto out; goto out;
on_each_cpu(kvm_arch_ops->hardware_enable, NULL, 0, 1); on_each_cpu(hardware_enable, NULL, 0, 1);
r = register_cpu_notifier(&kvm_cpu_notifier); r = register_cpu_notifier(&kvm_cpu_notifier);
if (r) if (r)
goto out_free_1; goto out_free_1;
@ -3168,7 +3189,7 @@ out_free_2:
unregister_reboot_notifier(&kvm_reboot_notifier); unregister_reboot_notifier(&kvm_reboot_notifier);
unregister_cpu_notifier(&kvm_cpu_notifier); unregister_cpu_notifier(&kvm_cpu_notifier);
out_free_1: out_free_1:
on_each_cpu(kvm_arch_ops->hardware_disable, NULL, 0, 1); on_each_cpu(hardware_disable, NULL, 0, 1);
kvm_arch_ops->hardware_unsetup(); kvm_arch_ops->hardware_unsetup();
out: out:
kvm_arch_ops = NULL; kvm_arch_ops = NULL;
@ -3182,7 +3203,7 @@ void kvm_exit_arch(void)
sysdev_class_unregister(&kvm_sysdev_class); sysdev_class_unregister(&kvm_sysdev_class);
unregister_reboot_notifier(&kvm_reboot_notifier); unregister_reboot_notifier(&kvm_reboot_notifier);
unregister_cpu_notifier(&kvm_cpu_notifier); unregister_cpu_notifier(&kvm_cpu_notifier);
on_each_cpu(kvm_arch_ops->hardware_disable, NULL, 0, 1); on_each_cpu(hardware_disable, NULL, 0, 1);
kvm_arch_ops->hardware_unsetup(); kvm_arch_ops->hardware_unsetup();
kvm_arch_ops = NULL; kvm_arch_ops = NULL;
} }