Uthread can handle clear_notif_pending
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 23 Nov 2011 01:31:00 +0000 (17:31 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 15 Dec 2011 22:48:41 +0000 (14:48 -0800)
clear_notif_pending() might not return, and it might return and clear
your current_uthread.  The latter can happen if you service a preemption
of another vcore that was stuck in vc context and fail to change_to that
vcore (which happens if that vcore starts up for any reason).

So if we lose our current_uthread, we just restart the vcore.  We're
also careful to make sure we don't lose a *uthread if clear_notif
doesn't return.

In the process, I also made a little helper, since there's a lot of
common code between run_cur_uth and run_uth.  __run_cur_uth_raw() is
still a bit weird and different: enough so that I'll leave it alone.

user/parlib/uthread.c
user/parlib/vcore.c

index 4165e76..e3d5b21 100644 (file)
@@ -274,46 +274,55 @@ void ros_syscall_blockon(struct syscall *sysc)
        uthread_yield(TRUE);
 }
 
-/* Runs whatever thread is vcore's current_uthread */
-void run_current_uthread(void)
+/* Helper for run_current and run_uthread.  Make sure the uthread you want to
+ * run is the current_uthread before calling this.  It will pop the TF of
+ * whatever you send in (run_cur and run_uth use different TFs).
+ *
+ * This will adjust the thread's state, do one last check on notif_pending, and
+ * pop the tf.  Note that the notif check is an optimization.  pop_ros_tf() will
+ * definitely handle it, but it will take a syscall to do so later. */
+static void __run_cur_uthread(struct user_trapframe *utf)
 {
        uint32_t vcoreid = vcore_id();
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
-       assert(current_uthread);
-       assert(current_uthread->state == UT_RUNNING);
-       printd("[U] Vcore %d is restarting uthread %08p\n", vcoreid,
-              current_uthread);
        clear_notif_pending(vcoreid);
+       /* clear_notif might have handled a preemption event, and we might not have
+        * a current_uthread anymore.  Need to recheck */
+       cmb();
+       if (!current_uthread) {
+               /* Start over, as if we just had a notif from the kernel.
+                * Note that  we're resetting the stack here.  Don't do anything other
+                * than call vcore_entry() */
+               set_stack_pointer((void*)vcpd->transition_stack);
+               uthread_vcore_entry();
+               assert(0);
+       }
+       /* Go ahead and start the uthread */
        /* utf no longer represents the current state of the uthread */
        current_uthread->flags &= ~UTHREAD_SAVED;
        set_tls_desc(current_uthread->tls_desc, vcoreid);
        /* Pop the user trap frame */
-       pop_ros_tf(&vcpd->notif_tf, vcoreid);
-       assert(0);
+       pop_ros_tf(utf, vcoreid);
 }
 
-/* Runs the uthread, but doesn't care about notif pending.  Only call this when
- * there was a DONT_MIGRATE uthread, or a similar situation where the uthread
- * will check messages soon (like calling enable_notifs()). */
-static void __run_current_uthread_raw(void)
+/* Runs whatever thread is vcore's current_uthread */
+void run_current_uthread(void)
 {
        uint32_t vcoreid = vcore_id();
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
-       /* We need to manually say we have a notif pending, so we eventually return
-        * to vcore context.  (note the kernel turned it off for us) */
-       vcpd->notif_pending = TRUE;
-       /* utf no longer represents the current state of the uthread */
-       current_uthread->flags &= ~UTHREAD_SAVED;
-       set_tls_desc(current_uthread->tls_desc, vcoreid);
-       /* Pop the user trap frame */
-       pop_ros_tf_raw(&vcpd->notif_tf, vcoreid);
+       assert(current_uthread);
+       assert(current_uthread->state == UT_RUNNING);
+       printd("[U] Vcore %d is restarting uthread %08p\n", vcoreid,
+              current_uthread);
+       /* Run, using the TF in the VCPD.  FP state should already be loaded */
+       __run_cur_uthread(&vcpd->notif_tf);
        assert(0);
 }
 
 /* Launches the uthread on the vcore.  Don't call this on current_uthread. */
 void run_uthread(struct uthread *uthread)
 {
-       uint32_t vcoreid;
+       uint32_t vcoreid = vcore_id();
        assert(uthread != current_uthread);
        if (uthread->state != UT_RUNNABLE) {
                /* had vcore3 throw this, when the UT blocked on vcore1 and didn't come
@@ -325,15 +334,27 @@ void run_uthread(struct uthread *uthread)
        uthread->state = UT_RUNNING;
        /* Save a ptr to the uthread we'll run in the transition context's TLS */
        current_uthread = uthread;
-       vcoreid = vcore_id();
-       clear_notif_pending(vcoreid);
-       /* utf no longer represents the current state of the uthread */
-       uthread->flags &= ~UTHREAD_SAVED;
-       set_tls_desc(uthread->tls_desc, vcoreid);
        /* Load silly state (Floating point) too.  For real */
        /* TODO: (HSS) */
+       __run_cur_uthread(&uthread->utf);
+       assert(0);
+}
+
+/* Runs the uthread, but doesn't care about notif pending.  Only call this when
+ * there was a DONT_MIGRATE uthread, or a similar situation where the uthread
+ * will check messages soon (like calling enable_notifs()). */
+static void __run_current_uthread_raw(void)
+{
+       uint32_t vcoreid = vcore_id();
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       /* We need to manually say we have a notif pending, so we eventually return
+        * to vcore context.  (note the kernel turned it off for us) */
+       vcpd->notif_pending = TRUE;
+       /* utf no longer represents the current state of the uthread */
+       current_uthread->flags &= ~UTHREAD_SAVED;
+       set_tls_desc(current_uthread->tls_desc, vcoreid);
        /* Pop the user trap frame */
-       pop_ros_tf(&uthread->utf, vcoreid);
+       pop_ros_tf_raw(&vcpd->notif_tf, vcoreid);
        assert(0);
 }
 
index 405f6e1..3fb8354 100644 (file)
@@ -274,7 +274,11 @@ void vcore_yield(bool preempt_pending)
  * events, and we will have send pending to 0. 
  *
  * Note that this won't catch every race/case of an incoming event.  Future
- * events will get caught in pop_ros_tf() or proc_yield() */
+ * events will get caught in pop_ros_tf() or proc_yield().
+ *
+ * Also note that this handles events, which may change your current uthread or
+ * might not return!  Be careful calling this.  Check run_uthread for an example
+ * of how to use this. */
 bool clear_notif_pending(uint32_t vcoreid)
 {
        bool handled_event = FALSE;