Asserts/checks for early RKMSG context
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 14 Nov 2012 23:53:04 +0000 (15:53 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 21 Nov 2012 23:41:17 +0000 (15:41 -0800)
The kernel tracks if it is in 'early routine kernel message context,'
which is an RKMSG before it blocks.  Once it blocks/kthreads, the core
leaves that context (in smp_idle), never to return for that kthread.
When the kthread starts up again, it'll be in default context.

Other than allowing us to assert messages that must be routine are in
fact routine, we can detect when a kmsg blocked, thus removing the
restriction on kmsg handler writers to put smp_idle at the bottom of
their functions.

There might be a better way to do this (permanent kthreads, track
whether it blocked in there?), but the basics are the same: when we come
back in to PRKM, we need to detect if we blocked or not, and if so, we
smp_idle.  And if not, we clear whatever flag we were using.  And that
same flag gets cleared in smp_idle as well.

kern/src/kthread.c
kern/src/smp.c
kern/src/testing.c
kern/src/trap.c

index 1d3b51f..00eb723 100644 (file)
@@ -85,6 +85,8 @@ static void __launch_kthread(struct trapframe *tf, uint32_t srcid, long a0,
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
        struct proc *cur_proc = pcpui->cur_proc;
        
+       /* Make sure we are a routine kmsg */
+       assert(in_early_rkmsg_ctx(pcpui));
        if (pcpui->owning_proc && pcpui->owning_proc != kthread->proc) {
                /* Some process should be running here that is not the same as the
                 * kthread.  This means the _M is getting interrupted or otherwise
@@ -112,7 +114,9 @@ static void __launch_kthread(struct trapframe *tf, uint32_t srcid, long a0,
        }
        /* o/w, just run the kthread.  any trapframes that are supposed to run or
         * were interrupted will run whenever the kthread smp_idles() or otherwise
-        * finishes. */
+        * finishes.  We also need to clear the RKMSG context since we will not
+        * return from restart_kth. */
+       clear_rkmsg(pcpui);
        restart_kthread(kthread);
        assert(0);
 }
index 0667a8d..fd16a42 100644 (file)
@@ -49,6 +49,8 @@ static void try_run_proc(void)
  * tell it to do something else, or perhaps to halt in another manner. */
 static void __attribute__((noinline, noreturn)) __smp_idle(void)
 {
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       clear_rkmsg(pcpui);
        /* TODO: idle, abandon_core(), and proc_restartcore() need cleaned up */
        enable_irq();   /* get any IRQs before we halt later */
        try_run_proc();
index 05c60a0..01a6849 100644 (file)
@@ -1470,7 +1470,6 @@ void __test_cv_signal(struct trapframe *tf, uint32_t srcid, long a0,
        else
                cv_broadcast(cv);
        atomic_dec(&counter);
-       smp_idle();
 }
 void __test_cv_waiter(struct trapframe *tf, uint32_t srcid, long a0,
                       long a1, long a2)
@@ -1479,7 +1478,6 @@ void __test_cv_waiter(struct trapframe *tf, uint32_t srcid, long a0,
        /* check state, etc */
        cv_wait_and_unlock(cv);
        atomic_dec(&counter);
-       smp_idle();
 }
 void __test_cv_waiter_t3(struct trapframe *tf, uint32_t srcid, long a0,
                          long a1, long a2)
@@ -1496,7 +1494,6 @@ void __test_cv_waiter_t3(struct trapframe *tf, uint32_t srcid, long a0,
        cmb();
        assert(state);
        atomic_dec(&counter);
-       smp_idle();     /* kmsgs that might block cannot return! */
 }
 
 void test_cv(void)
index 983311a..628a1a7 100644 (file)
@@ -124,11 +124,23 @@ void process_routine_kmsg(void)
                msg_cp = *kmsg;
                kmem_cache_free(kernel_msg_cache, (void*)kmsg);
                assert(msg_cp.dstid == pcoreid);        /* caught a brutal bug with this */
+               set_rkmsg(pcpui);                                       /* we're now in early RKM ctx */
                /* Note we pass pcpui->cur_tf to all kmsgs.  I'm leaning towards
                 * dropping the TFs completely, but might find a debugging use for them
                 * later. */
                msg_cp.pc(pcpui->cur_tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1,
                          msg_cp.arg2);
+               /* If we aren't still in early RKM, it is because the KMSG blocked
+                * (thus leaving early RKM, finishing in default context) and then
+                * returned.  This is a 'detached' RKM.  Must idle in this scenario,
+                * since we might have migrated or otherwise weren't meant to PRKM
+                * (can't return twice).  Also note that this may involve a core
+                * migration, so we need to reread pcpui.*/
+               cmb();
+               pcpui = &per_cpu_info[core_id()];
+               if (!in_early_rkmsg_ctx(pcpui))
+                       smp_idle();
+               clear_rkmsg(pcpui);
                /* Some RKMs might turn on interrupts (perhaps in the future) and then
                 * return. */
                disable_irq();