vmm: Add vthread_join()
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 13 Sep 2017 17:31:41 +0000 (13:31 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 15 Sep 2017 13:31:24 +0000 (09:31 -0400)
The main idea is surprisingly easy - the controller needs to call
uth_2ls_thread_exit(), which is what normal threads call when they exit.
This is normally hidden by the 2LS.

A vthread can exit by making a vmcall, then the controller exits on its
behalf.  The joiner actually joins on the controller (which is a uthread),
instead of the guest_thread.

The slightly tricky part was getting the uthreads to be reinitialized
properly, and noting that the join exit callback is called from vcore
context.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
user/vmm/include/vmm/vthread.h
user/vmm/sched.c
user/vmm/vthread.c

index fed3f91..7b44d56 100644 (file)
@@ -38,5 +38,8 @@ void vthread_init_ctx(struct vthread *vth, uintptr_t entry_pt, uintptr_t arg,
 void vthread_run(struct vthread *vthread);
 struct vthread *vthread_create(struct virtual_machine *vm, void *entry,
                                void *arg);
+void vthread_join(struct vthread *vth, void **retval_loc);
+/* Callback, here for sched.c */
+void __vthread_exited(struct vthread *vth);
 
 __END_DECLS
index 13244b7..5b56a99 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <vmm/sched.h>
 #include <vmm/vmm.h>
+#include <vmm/vthread.h>
 #include <sys/mman.h>
 #include <stdlib.h>
 #include <assert.h>
@@ -463,24 +464,43 @@ static void task_thread_dtor(void *obj, void *priv)
 {
        struct task_thread *tth = (struct task_thread*)obj;
 
+       uthread_cleanup((struct uthread*)tth);
        __free_stack(tth->stacktop, tth->stacksize);
 }
 
-static void vmm_thread_exited(struct uthread *uth)
+static void task_thread_exit(struct task_thread *tth)
 {
-       struct vmm_thread *vth = (struct vmm_thread*)uth;
-       struct task_thread *tth = (struct task_thread*)uth;
-
-       /* Catch bugs.  Right now, only tasks threads can exit. */
-       assert(vth->type == VMM_THREAD_TASK);
+       struct uthread *uth = (struct uthread*)tth;
 
-       acct_thread_blocked((struct vmm_thread*)tth);
-       uthread_cleanup(uth);
        if (uth->flags & UTHREAD_IS_THREAD0)
                return;
        kmem_cache_free(task_thread_cache, tth);
 }
 
+static void ctlr_thread_exit(struct ctlr_thread *cth)
+{
+       __vthread_exited((struct vthread*)cth->buddy);
+}
+
+static void vmm_thread_exited(struct uthread *uth)
+{
+       struct vmm_thread *vth = (struct vmm_thread*)uth;
+
+       assert(vth->type != VMM_THREAD_GUEST);
+
+       acct_thread_blocked(vth);
+       switch (vth->type) {
+       case VMM_THREAD_TASK:
+               task_thread_exit((struct task_thread*)uth);
+               break;
+       case VMM_THREAD_CTLR:
+               ctlr_thread_exit((struct ctlr_thread*)uth);
+               break;
+       case VMM_THREAD_GUEST:
+               panic("Guest threads shouldn't be able to exit");
+       }
+}
+
 static void destroy_guest_thread(struct guest_thread *gth)
 {
        struct ctlr_thread *cth = gth->buddy;
index c5e24e6..f773eda 100644 (file)
 #include <parlib/uthread.h>
 #include <sys/mman.h>
 #include <sys/syscall.h>
+#include <sys/queue.h>
 #include <vmm/vmm.h>
 #include <vmm/vthread.h>
 
+static struct vmm_thread_tq parked_vths = TAILQ_HEAD_INITIALIZER(parked_vths);
+static struct spin_pdr_lock park_lock = SPINPDR_INITIALIZER;
+
 static void *pages(size_t count)
 {
        void *v;
@@ -86,15 +90,55 @@ void __add_gth_to_vm(struct virtual_machine *vm, struct guest_thread *gth)
        vm->nr_gpcs++;
 }
 
+/* If we fully destroy these uthreads, we'll need to call uthread_cleanup() */
+void __vthread_exited(struct vthread *vth)
+{
+       struct virtual_machine *vm = vth_to_vm(vth);
+
+       spin_pdr_lock(&park_lock);
+       TAILQ_INSERT_HEAD(&parked_vths, (struct vmm_thread*)vth, tq_next);
+       spin_pdr_unlock(&park_lock);
+}
+
+/* The tricky part is that we need to reinit the threads */
+static struct vthread *get_parked_vth(struct virtual_machine *vm)
+{
+       struct vmm_thread *vmth;
+       struct guest_thread *gth;
+       struct ctlr_thread *cth;
+       /* These are from create_guest_thread() */
+       struct uth_thread_attr gth_attr = {.want_tls = FALSE};
+       struct uth_thread_attr cth_attr = {.want_tls = TRUE};
+
+       spin_pdr_lock(&park_lock);
+       vmth = TAILQ_FIRST(&parked_vths);
+       if (!vmth) {
+               spin_pdr_unlock(&park_lock);
+               return NULL;
+       }
+       TAILQ_REMOVE(&parked_vths, vmth, tq_next);
+       spin_pdr_unlock(&park_lock);
+
+       gth = (struct guest_thread*)vmth;
+       cth = gth->buddy;
+       uthread_init((struct uthread*)gth, &gth_attr);
+       uthread_init((struct uthread*)cth, &cth_attr);
+       return (struct vthread*)gth;
+}
+
 struct vthread *vthread_alloc(struct virtual_machine *vm,
                               struct vmm_gpcore_init *gpci)
 {
        static parlib_once_t once = PARLIB_ONCE_INIT;
        struct guest_thread *gth;
+       struct vthread *vth;
        int ret;
 
        parlib_run_once(&once, vmsetup, vm);
 
+       vth = get_parked_vth(vm);
+       if (vth)
+               return vth;
        uth_mutex_lock(&vm->mtx);
        ret = syscall(SYS_vmm_add_gpcs, 1, gpci);
        assert(ret == 1);
@@ -176,3 +220,10 @@ struct vthread *vthread_create(struct virtual_machine *vm, void *entry,
        vthread_run(vth);
        return vth;
 }
+
+void vthread_join(struct vthread *vth, void **retval_loc)
+{
+       struct ctlr_thread *cth = ((struct guest_thread*)vth)->buddy;
+
+       uthread_join((struct uthread*)cth, retval_loc);
+}