VMM: Add helpers for loading guest pcores
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 1 Feb 2016 16:03:03 +0000 (11:03 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 2 Feb 2016 22:43:52 +0000 (17:43 -0500)
These are similar to the old vmx_{get,put}_cpu().  Those are somewhat of a
mess.

These helpers deal with concurrency (multiple cores trying to load the same
VM core at a time) and don't have any of the baggage from KVM/Dune.
Specifically, we don't need things like 'launched' or to handle cases where
the VMCS was already loaded.

I attempted, perhaps in vain, to keep most of this vendor-independent.  For
the most part, Intel or AMD will need to do the same things in
load_guest_pcore(), minus the VMCS particulars (maybe?).  Also, struct
vmx_vcpu will eventually be guest_pcore or something.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/vmm/intel/vmx.c
kern/arch/x86/vmm/intel/vmx.h
kern/arch/x86/vmm/vmm.c
kern/arch/x86/vmm/vmm.h
kern/include/smp.h
kern/src/process.c

index 9aa6265..b0a7c2c 100644 (file)
@@ -2273,3 +2273,14 @@ void vapic_status_dump_kernel(void *vapic)
 
        printk("-- END KERNEL APIC STATUS DUMP --\n");
 }
+
+void vmx_load_guest_pcore(struct vmx_vcpu *gpc)
+{
+       vmcs_load(gpc->vmcs);
+       __vmx_setup_cpu();
+}
+
+void vmx_unload_guest_pcore(struct vmx_vcpu *gpc)
+{
+       vmcs_clear(gpc->vmcs);
+}
index a95de07..3506f1e 100644 (file)
@@ -308,3 +308,6 @@ struct vmxec {
        uint32_t try_set_1;
        uint32_t try_set_0;
 };
+
+void vmx_load_guest_pcore(struct vmx_vcpu *gpc);
+void vmx_unload_guest_pcore(struct vmx_vcpu *gpc);
index 9c79757..0cd1b71 100644 (file)
@@ -60,6 +60,9 @@ static void vmmcp_posted_handler(struct hw_trapframe *hw_tf, void *data)
 
 void vmm_pcpu_init(void)
 {
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+
+       pcpui->guest_pcoreid = -1;
        if (!x86_supports_vmx)
                return;
        if (! intel_vmm_pcpu_init()) {
@@ -159,3 +162,51 @@ void __vmm_struct_cleanup(struct proc *p)
        ept_flush(p->env_pgdir.eptp);
        vmm->vmmcp = FALSE;
 }
+
+struct vmx_vcpu *lookup_guest_pcore(struct proc *p, int guest_pcoreid)
+{
+       /* nr_guest_pcores is written once at setup and never changed */
+       if (guest_pcoreid >= p->vmm.nr_guest_pcores)
+               return 0;
+       return p->vmm.guest_pcores[guest_pcoreid];
+}
+
+struct vmx_vcpu *load_guest_pcore(struct proc *p, int guest_pcoreid)
+{
+       struct vmx_vcpu *gpc;
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+
+       gpc = lookup_guest_pcore(p, guest_pcoreid);
+       if (!gpc)
+               return 0;
+       assert(pcpui->guest_pcoreid == -1);
+       spin_lock(&p->vmm.lock);
+       if (gpc->cpu != -1) {
+               spin_unlock(&p->vmm.lock);
+               return 0;
+       }
+       gpc->cpu = core_id();
+       spin_unlock(&p->vmm.lock);
+       /* We've got dibs on the gpc; we don't need to hold the lock any longer. */
+       pcpui->guest_pcoreid = guest_pcoreid;
+       ept_sync_context(vcpu_get_eptp(gpc));
+       vmx_load_guest_pcore(gpc);
+       return gpc;
+}
+
+void unload_guest_pcore(struct proc *p, int guest_pcoreid)
+{
+       struct vmx_vcpu *gpc;
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+
+       gpc = lookup_guest_pcore(p, guest_pcoreid);
+       assert(gpc);
+       spin_lock(&p->vmm.lock);
+       assert(gpc->cpu != -1);
+       ept_sync_context(vcpu_get_eptp(gpc));
+       vmx_unload_guest_pcore(gpc);
+       gpc->cpu = -1;
+       /* As soon as we unlock, this gpc can be started on another core */
+       spin_unlock(&p->vmm.lock);
+       pcpui->guest_pcoreid = -1;
+}
index 4dfeb9d..ec1a1bc 100644 (file)
@@ -17,6 +17,7 @@ static inline int cpu_has_svm(const char **msg)
 #define VMM_VMEXIT_NR_TYPES            65
 
 struct vmm {
+       spinlock_t lock;        /* protects guest_pcore assignment */
        qlock_t qlock;
        // always false.
        int amd;
@@ -59,3 +60,7 @@ struct vmx_vcpu *vmx_create_vcpu(struct proc *p, struct vmm_gpcore_init *gpci);
 void vmx_destroy_vcpu(struct vmx_vcpu *vcpu);
 uint64_t construct_eptp(physaddr_t root_hpa);
 void ept_flush(uint64_t eptp);
+
+struct vmx_vcpu *lookup_guest_pcore(struct proc *p, int guest_pcoreid);
+struct vmx_vcpu *load_guest_pcore(struct proc *p, int guest_pcoreid);
+void unload_guest_pcore(struct proc *p, int guest_pcoreid);
index 3d4fe95..7120058 100644 (file)
@@ -41,6 +41,7 @@ struct per_cpu_info {
        pseudodesc_t host_gdt;
        int vmx_enabled;
        void *local_vcpu;
+       int guest_pcoreid;
 #endif
        spinlock_t lock;
        /* Process management */
index 7929263..20e90a1 100644 (file)
@@ -410,6 +410,7 @@ error_t proc_alloc(struct proc **pp, struct proc *parent, int flags)
        TAILQ_INIT(&p->abortable_sleepers);
        spinlock_init_irqsave(&p->abort_list_lock);
        memset(&p->vmm, 0, sizeof(struct vmm));
+       spinlock_init(&p->vmm.lock);
        qlock_init(&p->vmm.qlock);
        printd("[%08x] new process %08x\n", current ? current->pid : 0, p->pid);
        *pp = p;