Helpers for temporarily changing address spaces
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 1 Aug 2011 20:56:07 +0000 (13:56 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:05 +0000 (17:36 -0700)
Often the kernel wants to work in a process's context, but isn't
naturally in it, such as sending events from remote cores in interrupt
context, or from a management core.  Use the switch_to() and
switch_back() helpers for this.  Pair them up, etc.

kern/include/process.h
kern/src/arsc.c
kern/src/event.c
kern/src/process.c
kern/src/testing.c

index bdcecab..e61a9da 100644 (file)
@@ -127,7 +127,11 @@ bool __proc_preempt_all(struct proc *p);
 void proc_preempt_core(struct proc *p, uint32_t pcoreid, uint64_t usec);
 void proc_preempt_all(struct proc *p, uint64_t usec);
 
+/* Current / cr3 / context management */
+struct proc *switch_to(struct proc *new_p);
+void switch_back(struct proc *new_p, struct proc *old_proc);
 void abandon_core(void);
+
 /* Hold the proc_lock, since it'll use the vcoremapping to send an unmapping
  * message for the region from start to end.  */
 void __proc_tlbshootdown(struct proc *p, uintptr_t start, uintptr_t end);
index 9c20d05..9f47c2f 100644 (file)
@@ -99,6 +99,8 @@ static intreg_t process_generic_syscalls(struct proc *p, size_t max)
                        // only switch cr3 for the very first request for this queue
                        // need to switch to the right context, so we can handle the user pointer
                        // that points to a data payload of the syscall
+                       /* TODO: use switch_to() and switch_back(), and take a closer look
+                        * at the logic of when we switch - seems like it might be buggy. */
                        lcr3(p->env_cr3);
                        pcpui->cur_proc = p;
                }
index 314c333..2565099 100644 (file)
@@ -53,8 +53,7 @@ static void post_ev_msg(struct event_mbox *mbox, struct event_msg *msg,
 void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
                 uint32_t vcoreid)
 {
-       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
-       struct proc *old_proc = pcpui->cur_proc;        /* uncounted ref */
+       struct proc *old_proc;
        struct event_mbox *ev_mbox = 0, *vcore_mbox;
        struct event_msg local_msg = {0};
        assert(p);
@@ -70,12 +69,7 @@ void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
        }
        /* ev_q is a user pointer, so we need to make sure we're in the right
         * address space */
-       if (old_proc != p) {
-               /* Technically, we're storing a ref here, but our current ref on p is
-                * sufficient (so long as we don't decref below) */
-               pcpui->cur_proc = p;
-               lcr3(p->env_cr3);
-       }
+       old_proc = switch_to(p);
        /* Get the mbox and vcoreid */
        /* If we're going with APPRO, we use the kernel's suggested vcore's ev_mbox.
         * vcoreid is already what the kernel suggests. */
@@ -134,15 +128,8 @@ void send_event(struct proc *p, struct event_queue *ev_q, struct event_msg *msg,
        }
        /* Fall through */
 out:
-       /* Return to the old address space.  We switched to p in the first place if
-        * it wasn't the same as the original current (old_proc). */
-       if (old_proc != p) {
-               pcpui->cur_proc = old_proc;
-               if (old_proc)
-                       lcr3(old_proc->env_cr3);
-               else
-                       lcr3(boot_cr3);
-       }
+       /* Return to the old address space. */
+       switch_back(p, old_proc);
 }
 
 /* Send an event for the kernel event ev_num.  These are the "one sided" kernel
@@ -162,21 +149,10 @@ void send_kernel_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
  * commonly used - just the monitor and sys_self_notify(). */
 void post_vcore_event(struct proc *p, struct event_msg *msg, uint32_t vcoreid)
 {
-       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
-       struct proc *old_proc = pcpui->cur_proc;        /* uncounted ref */
        /* Need to set p as current to post the event */
-       if (old_proc != p) {
-               pcpui->cur_proc = p;
-               lcr3(p->env_cr3);
-       }
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct proc *old_proc = switch_to(p);
        /* *ev_mbox is the user address of the vcpd mbox */
        post_ev_msg(get_proc_ev_mbox(vcoreid), msg, 0); /* no chance for a NOMSG */
-       /* Unload the address space, if applicable */
-       if (old_proc != p) {
-               pcpui->cur_proc = old_proc;
-               if (old_proc)
-                       lcr3(old_proc->env_cr3);
-               else
-                       lcr3(boot_cr3);
-       }
+       switch_back(p, old_proc);
 }
index 5fe22aa..a3a282a 100644 (file)
@@ -1312,6 +1312,38 @@ void abandon_core(void)
        }
 }
 
+/* Switches to the address space/context of new_p, doing nothing if we are
+ * already in new_p.  This won't add extra refcnts or anything, and needs to be
+ * paired with switch_back() at the end of whatever function you are in.  Don't
+ * migrate cores in the middle of a pair.  Specifically, the uncounted refs are
+ * one for the old_proc, which is passed back to the caller, and new_p is
+ * getting placed in cur_proc. */
+struct proc *switch_to(struct proc *new_p)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct proc *old_proc = pcpui->cur_proc;        /* uncounted ref */
+       /* If we aren't the proc already, then switch to it */
+       if (old_proc != new_p) {
+               pcpui->cur_proc = new_p;                                /* uncounted ref */
+               lcr3(new_p->env_cr3);
+       }
+       return old_proc;
+}
+
+/* This switches back to old_proc from new_p.  Pair it with switch_to(), and
+ * pass in its return value for old_proc. */
+void switch_back(struct proc *new_p, struct proc *old_proc)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       if (old_proc != new_p) {
+               pcpui->cur_proc = old_proc;
+               if (old_proc)
+                       lcr3(old_proc->env_cr3);
+               else
+                       lcr3(boot_cr3);
+       }
+}
+
 /* Will send a TLB shootdown message to every vcore in the main address space
  * (aka, all vcores for now).  The message will take the start and end virtual
  * addresses as well, in case we want to be more clever about how much we
index be55257..e6e86af 100644 (file)
@@ -976,9 +976,8 @@ void test_ucq(void)
        void send_msgs(struct alarm_waiter *waiter)
        {
                struct timer_chain *tchain;
-               struct proc *p = waiter->data;
+               struct proc *old_proc, *p = waiter->data;
                struct ucq *ucq = (struct ucq*)USTACKTOP;
-               physaddr_t old_cr3 = rcr3();
                struct event_msg msg;
 
                printk("Running the alarm handler!\n");
@@ -988,11 +987,11 @@ void test_ucq(void)
                        goto abort;
                }
                /* load their address space */
-               lcr3(p->env_cr3);
+               old_proc = switch_to(p);
                /* So it's mmaped, see if it is ready (note that this is dangerous) */
                if (!ucq->ucq_ready) {
                        printk("Not ready yet\n");
-                       lcr3(old_cr3);
+                       switch_back(p, old_proc);
                        goto abort;
                }
                /* So it's ready, time to finally do the tests... */
@@ -1012,7 +1011,7 @@ void test_ucq(void)
                 *  - concurrent producers / consumers...  ugh.
                 */
                /* done, switch back and free things */
-               lcr3(old_cr3);
+               switch_back(p, old_proc);
                proc_decref(p);
                kfree(waiter); /* since it was kmalloc()d */
                return;