This patch moves the actual VMX enablement from the initialization
of the module to the creation of a VCPU.

With this approach, KVM can be easily autoloaded and does not block
other VMMs while being modprobe'd.
This improves the user experience a lot, since now other VMMs can
run, even though KVM is loaded, so users do not have to manually
load/unload KVM or any other VMM module.

Compared to the previously suggested approach "2", which would
introduce a complete framework that brings almost no benefit,
this approach enables coexistence of multiple VMMs without much
intervention. Thanks to Gerd for pointing that out.

I verified that this approach works with VirtualBox.

Signed-off-by: Alexander Graf <[EMAIL PROTECTED]>

---
 arch/x86/kvm/vmx.c |   82 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 75 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 64e2439..2c48590 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -116,6 +116,9 @@ static struct page *vmx_msr_bitmap;
 static DECLARE_BITMAP(vmx_vpid_bitmap, VMX_NR_VPIDS);
 static DEFINE_SPINLOCK(vmx_vpid_lock);
 
+static int vmx_usage_count = 0;
+static DEFINE_SPINLOCK(vmx_usage_lock);
+
 static struct vmcs_config {
        int size;
        int order;
@@ -1059,10 +1062,68 @@ static __init int vmx_disabled_by_bios(void)
        /* locked but not enabled */
 }
 
-static void hardware_enable(void *garbage)
+static void __vmx_off(void *garbage)
+{
+       asm volatile (__ex(ASM_VMX_VMXOFF) : : : "cc");
+       write_cr4(read_cr4() & ~X86_CR4_VMXE);
+}
+
+static void vmx_off(void)
+{
+       if (!vmx_usage_count)
+               return;
+
+       spin_lock(&vmx_usage_lock);
+
+       vmx_usage_count--;
+       if (vmx_usage_count == 0)
+               on_each_cpu(__vmx_off, NULL, 1);
+       
+       spin_unlock(&vmx_usage_lock);
+}
+
+static void __vmx_on(void *garbage)
 {
        int cpu = raw_smp_processor_id();
        u64 phys_addr = __pa(per_cpu(vmxarea, cpu));
+
+       write_cr4(read_cr4() | X86_CR4_VMXE); /* FIXME: not cpu hotplug safe */
+       asm volatile (ASM_VMX_VMXON_RAX
+                     : : "a"(&phys_addr), "m"(phys_addr)
+                     : "memory", "cc");
+}
+
+static void __vmx_check(void *r)
+{
+       if (read_cr4() & X86_CR4_VMXE)
+               *((int*)r) = -EBUSY;
+}
+
+static int vmx_on(void)
+{
+       int r = 0;
+       spin_lock(&vmx_usage_lock);
+       vmx_usage_count++;
+       if (vmx_usage_count == 1) {
+               on_each_cpu(__vmx_check, &r, 1);
+               if (r)
+                       goto out_1;
+
+               on_each_cpu(__vmx_on, NULL, 1);
+       }
+
+       goto out;
+
+out_1:
+       vmx_usage_count--;
+out:
+       spin_unlock(&vmx_usage_lock);
+       return r;
+}
+
+static void hardware_enable(void *garbage)
+{
+       int cpu = raw_smp_processor_id();
        u64 old;
 
        INIT_LIST_HEAD(&per_cpu(vcpus_on_cpu, cpu));
@@ -1075,10 +1136,9 @@ static void hardware_enable(void *garbage)
                wrmsrl(MSR_IA32_FEATURE_CONTROL, old |
                       FEATURE_CONTROL_LOCKED |
                       FEATURE_CONTROL_VMXON_ENABLED);
-       write_cr4(read_cr4() | X86_CR4_VMXE); /* FIXME: not cpu hotplug safe */
-       asm volatile (ASM_VMX_VMXON_RAX
-                     : : "a"(&phys_addr), "m"(phys_addr)
-                     : "memory", "cc");
+
+       if (vmx_usage_count)
+               __vmx_on(garbage);
 }
 
 static void vmclear_local_vcpus(void)
@@ -1094,8 +1154,9 @@ static void vmclear_local_vcpus(void)
 static void hardware_disable(void *garbage)
 {
        vmclear_local_vcpus();
-       asm volatile (__ex(ASM_VMX_VMXOFF) : : : "cc");
-       write_cr4(read_cr4() & ~X86_CR4_VMXE);
+
+       if (vmx_usage_count)
+               __vmx_off(garbage);
 }
 
 static __init int adjust_vmx_controls(u32 ctl_min, u32 ctl_opt,
@@ -3490,6 +3551,7 @@ static void vmx_free_vcpu(struct kvm_vcpu *vcpu)
        kfree(vmx->guest_msrs);
        kvm_vcpu_uninit(vcpu);
        kmem_cache_free(kvm_vcpu_cache, vmx);
+       vmx_off();
 }
 
 static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id)
@@ -3501,6 +3563,10 @@ static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, 
unsigned int id)
        if (!vmx)
                return ERR_PTR(-ENOMEM);
 
+       err = vmx_on();
+       if (err)
+               goto free_vmx;
+
        allocate_vpid(vmx);
 
        err = kvm_vcpu_init(&vmx->vcpu, kvm, id);
@@ -3550,6 +3616,8 @@ uninit_vcpu:
        kvm_vcpu_uninit(&vmx->vcpu);
 free_vcpu:
        kmem_cache_free(kvm_vcpu_cache, vmx);
+free_vmx:
+       vmx_off();
        return ERR_PTR(err);
 }
 
-- 
1.6.0.2

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to