Yield plays nice with preemption
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 14 Apr 2010 01:28:54 +0000 (18:28 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:42 +0000 (17:35 -0700)
Adds a being_nice parameter, which is how the process tells the kernel
it is giving up its resources in response to a preempt_pending.  It's
not tested heavily.

Documentation/processes.txt
kern/include/process.h
kern/src/process.c
kern/src/syscall.c
tests/mproctests.c
tests/proctests.c
user/include/parlib.h
user/parlib/syscall.c

index da62ec9..8f3bc22 100644 (file)
@@ -219,29 +219,40 @@ from _S to _M: at the entry point.
 
 2.4.4: Yielding
 --------------
-This will get revised soon, to account for different types of yields.
-
-Yielding gives up a core.  In _S mode, it will transition from RUNNING_S to
-RUNNABLE_S.  The context is saved in env_tf.  A yield will *not* transition
-from _M to _S.
-
-In _M mode, this yields the calling core.  The kernel will rip it out of your
-vcore list.  A process can yield its cores in any order.  The kernel will
-"fill in the holes of the vcoremap" when for any future newcores (e.g., proc A
-has 4 vcores, yields vcore2, and then asks for another vcore.  The new one
-will be vcore2).
-
-When you are in _M and yield your last core, it is an m_yield.  This
-completely suspends all cores, like a voluntary preemption.  When the process
-is run again, all cores will come up at the entry point (including vcore0 and
-the calling core).  This isn't implemented yet, and will wait on some work
-with preemption.
-
-We also need a type of yield (or a flag) that says the process is just giving
-up the core temporarily, but actually wants the core and does not want
-resource requests to be readjusted.  For example, in the event of a preemption
+sys_yield()/proc_yield() will give up the calling core, and may or may not
+adjust the desired number of cores, subject to its parameters.  Yield is
+performing two tasks, both of which result in giving up the core.  One is for
+not wanting the core anymore.  The other is in response to a preemption.  Yield
+may not be called remotely (ARSC).
+
+In _S mode, it will transition from RUNNING_S to RUNNABLE_S.  The context is
+saved in env_tf.
+
+In _M mode, this yields the calling core.  A yield will *not* transition from _M
+to _S.  The kernel will rip it out of your vcore list.  A process can yield its
+cores in any order.  The kernel will "fill in the holes of the vcoremap" for any
+future new cores requested (e.g., proc A has 4 vcores, yields vcore2, and then
+asks for another vcore.  The new one will be vcore2).  When any core starts in
+_M mode, even after a yield, it will come back at the hart_entry()/_start point.
+
+Yield will normally adjust your desired amount of vcores to the amount after the
+calling core is taken.  This is the way a process gives its cores back.
+
+Yield can also be used to say the process is just giving up the core in response
+to a pending preemption, but actually wants the core and does not want resource
+requests to be readjusted.  For example, in the event of a preemption
 notification, a process may yield (ought to!) so that the kernel does not need
-to waste effort with full preemption.
+to waste effort with full preemption.  This is done by passing in a bool
+(being_nice), which signals the kernel that it is in response to a preemption.
+The kernel will not readjust the amt_wanted, and if there is no preemption
+pending, the kernel will ignore the yield.
+
+There may be an m_yield(), which will yield all or some of the cores of an MPC,
+remotely.  This is discussed farther down a bit.  It's not clear what exactly
+it's purpose would be.
+
+We also haven't addressed other reasons to yield, or more specifically to wait,
+such as for an interrupt or an event of some sort.
 
 2.4.5: Others
 --------------
index c619fe7..2fcb1dd 100644 (file)
 #define PROC_RUNNABLE_M                        0x20
 #define PROC_RUNNING_M                 0x40
 
-#define procstate2str(state) ((state)==PROC_CREATED    ? "CREATED   " : \
+#define procstate2str(state) ((state)==PROC_CREATED    ? "CREATED"    : \
                               (state)==PROC_RUNNABLE_S ? "RUNNABLE_S" : \
-                              (state)==PROC_RUNNING_S  ? "RUNNING_S " : \
-                              (state)==PROC_WAITING    ? "WAITING   " : \
-                              (state)==PROC_DYING      ? "DYING     " : \
+                              (state)==PROC_RUNNING_S  ? "RUNNING_S : \
+                              (state)==PROC_WAITING    ? "WAITING"    : \
+                              (state)==PROC_DYING      ? "DYING"      : \
                               (state)==PROC_RUNNABLE_M ? "RUNNABLE_M" : \
-                              (state)==PROC_RUNNING_M  ? "RUNNING_M " : \
-                                                         "UNKNOWN   ")
+                              (state)==PROC_RUNNING_M  ? "RUNNING_M : \
+                                                         "UNKNOWN")
 
 #include <env.h>
 
@@ -81,7 +81,7 @@ bool proc_controls(struct proc *SAFE actor, struct proc *SAFE target);
 void proc_run(struct proc *SAFE p);
 void proc_restartcore(struct proc *SAFE p, trapframe_t *SAFE tf);
 void proc_destroy(struct proc *SAFE p);
-void proc_yield(struct proc *SAFE p);
+void proc_yield(struct proc *SAFE p, bool being_nice);
 void do_notify(struct proc *p, uint32_t vcoreid, unsigned int notif,
                struct notif_event *ne);
 void proc_notify(struct proc *p, unsigned int notif, struct notif_event *ne);
index cad3b75..906e720 100644 (file)
@@ -655,10 +655,34 @@ static uint32_t get_vcoreid(struct proc *SAFE p, uint32_t pcoreid)
  * - RES_CORES amt_wanted will be the amount running after taking away the
  *   yielder, unless there are none left, in which case it will be 1.
  *
- * This does not return (abandon_core()), so it will eat your reference.  */
-void proc_yield(struct proc *SAFE p)
+ * If the call is being nice, it means that it is in response to a preemption
+ * (which needs to be checked).  If there is no preemption pending, just return.
+ * No matter what, don't adjust the number of cores wanted.
+ *
+ * This usually does not return (abandon_core()), so it will eat your reference.
+ * */
+void proc_yield(struct proc *SAFE p, bool being_nice)
 {
-       spin_lock_irqsave(&p->proc_lock);
+       uint32_t vcoreid = get_vcoreid(p, core_id());
+       struct vcore *vc = &p->procinfo->vcoremap[vcoreid];
+
+       /* no reason to be nice, return */
+       if (being_nice && !vc->preempt_pending)
+               return;
+
+       spin_lock_irqsave(&p->proc_lock); /* horrible scalability.  =( */
+
+       /* fate is sealed, return and take the preempt message on the way out.
+        * we're making this check while holding the lock, since the preemptor
+        * should hold the lock when sending messages. */
+       if (vc->preempt_served) {
+               spin_unlock_irqsave(&p->proc_lock);
+               return;
+       }
+       /* no need to preempt later, since we are yielding (nice or otherwise) */
+       if (vc->preempt_pending)
+               vc->preempt_pending = 0;
+
        switch (p->state) {
                case (PROC_RUNNING_S):
                        p->env_tf= *current_tf;
@@ -671,13 +695,13 @@ void proc_yield(struct proc *SAFE p)
                        // give up core
                        __unmap_vcore(p, get_vcoreid(p, core_id()));
                        p->resources[RES_CORES].amt_granted = --(p->procinfo->num_vcores);
-                       p->resources[RES_CORES].amt_wanted = p->procinfo->num_vcores;
+                       if (!being_nice)
+                               p->resources[RES_CORES].amt_wanted = p->procinfo->num_vcores;
                        __seq_end_write(&p->procinfo->coremap_seqctr);
                        // add to idle list
                        put_idle_core(core_id());
                        // last vcore?  then we really want 1, and to yield the gang
                        if (p->procinfo->num_vcores == 0) {
-                               // might replace this with m_yield, if we have it directly
                                p->resources[RES_CORES].amt_wanted = 1;
                                __proc_set_state(p, PROC_RUNNABLE_M);
                                schedule_proc(p);
@@ -689,7 +713,7 @@ void proc_yield(struct proc *SAFE p)
                              __FUNCTION__);
        }
        spin_unlock_irqsave(&p->proc_lock);
-       proc_decref(p, 1);
+       proc_decref(p, 1); // need to eat the ref passed in.
        /* Clean up the core and idle.  For mgmt cores, they will ultimately call
         * manager, which will call schedule() and will repick the yielding proc. */
        abandon_core();
@@ -1164,6 +1188,9 @@ void __preempt(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
         * mapping would only happen if the vcore was free, which it isn't until
         * after we unmap. */
        vcoreid = get_vcoreid(p, coreid);
+       p->procinfo->vcoremap[vcoreid].preempt_served = FALSE;
+       /* either __preempt or proc_yield() ends the preempt phase. */
+       p->procinfo->vcoremap[vcoreid].preempt_pending = 0;
        vcpd = &p->procdata->vcore_preempt_data[vcoreid];
        printk("[kernel] received __preempt for proc %d's vcore %d on pcore %d\n",
               p->procinfo->pid, vcoreid, core_id());
index 7d1f396..bef44ee 100644 (file)
@@ -267,9 +267,9 @@ static error_t sys_proc_destroy(struct proc *p, pid_t pid, int exitcode)
        return ESUCCESS;
 }
 
-static int sys_proc_yield(struct proc *p)
+static int sys_proc_yield(struct proc *p, bool being_nice)
 {
-       proc_yield(p);
+       proc_yield(p, being_nice);
        return 0;
 }
 
@@ -294,7 +294,7 @@ static ssize_t sys_run_binary(env_t* e, void *DANGEROUS binary_buf, size_t len,
                        cache_color_alloc(llc_cache, env->cache_colors_map);
        }
        proc_decref(env, 1);
-       proc_yield(e);
+       proc_yield(e, 0);
        return 0;
 }
 
index 8e7baa7..b3e2ae3 100644 (file)
@@ -84,7 +84,7 @@ int main(int argc, char** argv)
                case TEST_YIELD_0_OUT_OF_ORDER:
                        udelay(5000000);
                        printf("Core %d yielding\n", vcoreid);
-                       sys_yield();
+                       sys_yield(0);
                        printf("Core 0 came back where it left off in RUNNING_M!!!\n");
                        break;
        }
@@ -120,7 +120,7 @@ void hart_entry(void)
                                break;
                        case TEST_YIELD_OUT_OF_ORDER:
                                printf("Core %d yielding\n", vcoreid);
-                               sys_yield();
+                               sys_yield(0);
                                break;
                        case TEST_YIELD_0_OUT_OF_ORDER:
                                udelay(7500000);
@@ -138,7 +138,7 @@ static void global_tests(uint32_t vcoreid)
        switch (test) {
                case TEST_YIELD_ALL:
                        printf("Core %d yielding\n", vcoreid);
-                       sys_yield();
+                       sys_yield(0);
                        // should be RUNNABLE_M now, amt_wanted == 1
                        while(1);
                case TEST_SWITCH_TO_RUNNABLE_S:
@@ -155,7 +155,7 @@ static void global_tests(uint32_t vcoreid)
                case TEST_CRAZY_YIELDS:
                        udelay(300000*vcoreid);
                        hart_request(5);
-                       sys_yield();
+                       sys_yield(0);
                        printf("should  never see me, unless you slip into *_S\n");
                        break;
                case TEST_CONCURRENT_SYSCALLS:
index cc4c6e2..f7ad8d2 100644 (file)
@@ -21,11 +21,11 @@ int main(int argc, char** argv)
                }
        }
        printf("Process %x, Started and yielding.\n", pid);
-       sys_yield();
+       sys_yield(0);
        printf("Process %x, Return from yield1, starting yield2.\n", pid);
-       sys_yield();
+       sys_yield(0);
        printf("Process %x, Return from yield2, starting yield3.\n", pid);
-       sys_yield();
+       sys_yield(0);
        printf("Process %x, Return from yield3, exiting.\n", pid);
        return 0;
 }
index e693dc5..f1f50b4 100644 (file)
@@ -35,7 +35,7 @@ void *      sys_brk(void* addr);
 /* Process Management */
 int         sys_getpid(void);
 int         sys_proc_destroy(int pid, int exitcode);
-void        sys_yield(void);
+void        sys_yield(bool being_nice);
 int         sys_proc_create(char* path);
 int         sys_proc_run(int pid);
 ssize_t     sys_shared_page_alloc(void *COUNT(PGSIZE) *addr, pid_t p2, 
index ecc7bed..8a23d11 100644 (file)
@@ -94,14 +94,14 @@ ssize_t sys_resource_req(int type, size_t amt_max, size_t amt_min, uint32_t flag
        return ros_syscall(SYS_resource_req, type, amt_max, amt_min, flags, 0);
 }
 
-void sys_reboot()
+void sys_reboot(void)
 {
        ros_syscall(SYS_reboot,0,0,0,0,0);
 }
 
-void sys_yield()
+void sys_yield(bool being_nice)
 {
-       ros_syscall(SYS_yield,0,0,0,0,0);
+       ros_syscall(SYS_yield, being_nice, 0, 0, 0, 0);
 }
 
 int sys_proc_create(char* path)