Kthreads launched from KMSGs are tracked as ktasks
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 3 Oct 2013 23:44:57 +0000 (16:44 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 16 Jan 2014 02:18:05 +0000 (18:18 -0800)
The kernel tasks don't have a particular address space, and won't store a
reference to whatever process happens to be running.  Previously, things like
loopbackread() would save a reference to whatever process happened to be
running there, which would keep that process from fully DYING.

Any routine kernel message will have is_ktask set.  If they block, the kthread
code will know to handle these differently.  This means that syscall context
shouldn't somehow try to execute as an RKM.  As a reminder, when you first
launch an RKM, it is in early_rkm_ctx.  So put another way, no syscall work or
other work on behalf of a process that can block should execute in
early_rkm_ctx().  Anyway, this all is subject to change, if I need to.
Comments inline will track the latest.

kern/include/kthread.h
kern/src/kthread.c
kern/src/process.c
kern/src/smp.c
kern/src/trap.c

index 0a505bd..395cd54 100644 (file)
@@ -33,6 +33,7 @@ struct kthread {
        void                                            *errbuf;        /* TODO: avoiding include loops */
        TAILQ_ENTRY(kthread)            link;
        /* ID, other shit, etc */
+       bool                                            is_ktask;       /* default is FALSE */
 };
 
 /* Semaphore for kthreads to sleep on.  0 or less means you need to sleep */
index 11ed469..c6b098e 100644 (file)
@@ -243,6 +243,13 @@ void sem_down(struct semaphore *sem)
                new_kthread = pcpui->spare;
                new_stacktop = new_kthread->stacktop;
                pcpui->spare = 0;
+               /* Based on how we set is_ktask (in PRKM), we'll usually have a spare
+                * with is_ktask set, even though the default setting is off.  The
+                * reason is that the launching of blocked kthreads also uses PRKM, and
+                * that KMSG (__launch_kthread) doesn't return.  Thus the soon-to-be
+                * spare kthread, that is launching another, has is_ktask set. */
+               new_kthread->is_ktask = FALSE;
+               new_kthread->proc = 0;
        } else {
                new_kthread = __kthread_zalloc();
                new_stacktop = get_kstack();
@@ -264,13 +271,20 @@ void sem_down(struct semaphore *sem)
        assert(*kth_stack_poison == 0xdeadbeef);
        *kth_stack_poison = 0;
 #endif /* CONFIG_KTHREAD_POISON */
-       /* The kthread needs to stay in the process context (if there is one), but
+       /* Kthreads that are ktasks are not related to any process, and do not need
+        * to work in a process's address space.  They can operate in any address
+        * space that has the kernel mapped (like boot_pgdir, or any pgdir).
+        *
+        * Other kthreads need to stay in the process context (if there is one), but
         * we want the core (which could be a vcore) to stay in the context too.  In
         * the future, we could check owning_proc. If it isn't set, we could leave
         * the process context and transfer the refcnt to kthread->proc. */
-       kthread->proc = current;
-       if (kthread->proc)
+       if (!kthread->is_ktask) {
+               kthread->proc = current;
                proc_incref(kthread->proc, 1);
+       } else {
+               kthread->proc = 0;
+       } 
        /* Save the context, toggle blocking for the reactivation */
        save_kernel_ctx(&kthread->context);
        if (!blocking)
index 0527f3e..bd43d75 100644 (file)
@@ -626,7 +626,11 @@ void __proc_run_m(struct proc *p)
  * in current. */
 void __proc_startcore(struct proc *p, struct user_context *ctx)
 {
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
        assert(!irq_is_enabled());
+       /* Should never have ktask still set.  If we do, future syscalls could try
+        * to block later and lose track of our address space. */
+       assert(!pcpui->cur_kthread->is_ktask);
        __set_proc_current(p);
        /* Clear the current_ctx, since it is no longer used */
        current_ctx = 0;        /* TODO: might not need this... */
index 8797bd1..e425e57 100644 (file)
@@ -58,6 +58,7 @@ static void __attribute__((noinline, noreturn)) __smp_idle(void)
 {
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
        clear_rkmsg(pcpui);
+       pcpui->cur_kthread->is_ktask = FALSE;
        enable_irq();   /* one-shot change to get any IRQs before we halt later */
        while (1) {
                disable_irq();
index c0eef53..d25b356 100644 (file)
@@ -126,8 +126,21 @@ void process_routine_kmsg(void)
                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 */
+               /* The kmsg could block.  If it does, we want the kthread code to know
+                * it's not running on behalf of a process, and we're actually spawning
+                * a kernel task.  While we do have a syscall that does work in an RKM
+                * (change_to), it's not really the rest of the syscall context. */
+               pcpui->cur_kthread->is_ktask = TRUE;
                pcpui_trace_kmsg(pcpui, (uintptr_t)msg_cp.pc);
                msg_cp.pc(msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
+               /* And if we make it back, be sure to unset this.  If we never return,
+                * but the kthread exits via some other way (smp_idle()), then
+                * smp_idle() will deal with the flag.  The default state is "off".  For
+                * an example of an RKM that does this, check out the
+                * monitor->mon_bin_run.  Finally, if the kthread gets swapped out of
+                * pcpui, such as in __launch_kthread(), the next time the kthread is
+                * reused, is_ktask will be reset. */
+               pcpui->cur_kthread->is_ktask = FALSE;
                /* 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,