]> Zhao Yanbai Git Server - minix.git/commitdiff
pick_proc() called only just before returning to userspace
authorTomas Hruby <tom@minix3.org>
Mon, 9 Nov 2009 17:48:31 +0000 (17:48 +0000)
committerTomas Hruby <tom@minix3.org>
Mon, 9 Nov 2009 17:48:31 +0000 (17:48 +0000)
- new proc_is_runnable() macro to test whether process is runnable. All tests
  whether p_rts_flags == 0 converted to use this macro

- pick_proc() calls removed from enqueue() and dequeue()

- removed the test for recursive calls from pick_proc() as it certainly cannot
  be called recursively now

- PREEMPTED flag to mark processes that were preempted by enqueueuing a higher
  priority process in enqueue()

- enqueue_head() to enqueue PREEMPTED processes again at the head of their
  current priority queue

- NO_QUANTUM flag to block and dequeue processes preempted by timer tick with
  exceeded quantum. They need to be enqueued again in schedcheck()

- next_ptr global variable removed

kernel/clock.c
kernel/debug.c
kernel/glo.h
kernel/proc.c
kernel/proc.h
kernel/profile.c

index 514c5b6391e7bc17df86298c600934b02ddd4ecc..00191f611d84e5ba9bb92d5d8cbd662e4a74d376 100644 (file)
@@ -278,11 +278,8 @@ PUBLIC int ap_timer_int_handler(void)
        /* check if the process is still runnable after checking the vtimer */
        if (p->p_rts_flags == 0 && p->p_ticks_left <= 0 &&
                        priv(p)->s_flags & PREEMPTIBLE) {
-               proc_ptr = NULL; /* no process is scheduled for dequeue and
-                                   enqueue */
-               dequeue(p);     /* take it off the queues */
-               enqueue(p);     /* and reinsert it again */
-               proc_ptr = p; /* restore some consitent state */
+               /* this dequeues the process */
+               RTS_SET(p, NO_QUANTUM);
        }
 
        return 1;
index e710f6a21a893b2d8461b0bb2fc030f705b90c92..bd510ec2f6f32549fd1496d74dcd23befbbd4af9 100644 (file)
@@ -129,6 +129,8 @@ rtsflagstr(int flags)
        FLAG(PAGEFAULT);
        FLAG(VMREQUEST);
        FLAG(VMREQTARGET);
+       FLAG(PREEMPTED);
+       FLAG(NO_QUANTUM);
 
        return str;
 }
index eef6601172cfe263c28f09fe65da3443958a6cd6..b300bbf541cb550700da1a3d25a7228604705962 100644 (file)
@@ -31,7 +31,6 @@ EXTERN struct loadinfo kloadinfo;     /* status of load average */
 
 /* Process scheduling information and the kernel reentry count. */
 EXTERN struct proc *proc_ptr;  /* pointer to currently running process */
-EXTERN struct proc *next_ptr;  /* next process to run after restart() */
 EXTERN struct proc *bill_ptr;  /* process to bill for clock ticks */
 EXTERN struct proc *vmrestart;  /* first process on vmrestart queue */
 EXTERN struct proc *vmrequest;  /* first process on vmrequest queue */
index f58a7332d050b52dbba62625ee77056447fa0c63..169be2cceed41f23a63f27949e7c9f54638bf6c6 100644 (file)
@@ -62,7 +62,8 @@ FORWARD _PROTOTYPE( int try_async, (struct proc *caller_ptr));
 FORWARD _PROTOTYPE( int try_one, (struct proc *src_ptr, struct proc *dst_ptr,
                int *postponed));
 FORWARD _PROTOTYPE( void sched, (struct proc *rp, int *queue, int *front));
-FORWARD _PROTOTYPE( void pick_proc, (void));
+FORWARD _PROTOTYPE( struct proc * pick_proc, (void));
+FORWARD _PROTOTYPE( void enqueue_head, (struct proc *rp));
 
 #define PICK_ANY       1
 #define PICK_HIGHERONLY        2
@@ -132,28 +133,47 @@ PUBLIC struct proc * schedcheck(void)
         */
        NOREC_ENTER(schedch);
        vmassert(intr_disabled());
-       if(next_ptr) {
-               proc_ptr = next_ptr;
-               next_ptr = NULL;
+
+       /*
+        * if the current process is still runnable check the misc flags and let
+        * it run unless it becomes not runnable in the meantime
+        */
+       if (proc_is_runnable(proc_ptr))
+               goto check_misc_flags;
+
+       /*
+        * if a process becomes not runnable while handling the misc flags, we
+        * need to pick a new one here and start from scratch. Also if the
+        * current process wasn' runnable, we pick a new one here
+        */
+not_runnable_pick_new:
+       if (proc_is_preempted(proc_ptr)) {
+               proc_ptr->p_rts_flags &= ~PREEMPTED;
+               if (proc_is_runnable(proc_ptr))
+                       enqueue_head(proc_ptr);
        }
+       /* this enqueues the process again */
+       if (proc_no_quantum(proc_ptr))
+               RTS_UNSET(proc_ptr, NO_QUANTUM);
+       proc_ptr = pick_proc();
+
+check_misc_flags:
+
        vmassert(proc_ptr);
-       vmassert(!proc_ptr->p_rts_flags);
+       vmassert(proc_is_runnable(proc_ptr));
        while (proc_ptr->p_misc_flags &
                (MF_DELIVERMSG | MF_SC_DEFER | MF_SC_TRACE | MF_SC_ACTIVE)) {
 
-               vmassert(!next_ptr);
-               vmassert(!proc_ptr->p_rts_flags);
+               vmassert(proc_is_runnable(proc_ptr));
                if (proc_ptr->p_misc_flags & MF_DELIVERMSG) {
                        TRACE(VF_SCHEDULING, printf("delivering to %s / %d\n",
                                proc_ptr->p_name, proc_ptr->p_endpoint););
                        if(delivermsg(proc_ptr) == VMSUSPEND) {
-                               vmassert(next_ptr);
                                TRACE(VF_SCHEDULING,
                                        printf("suspending %s / %d\n",
                                        proc_ptr->p_name,
                                        proc_ptr->p_endpoint););
-                               vmassert(proc_ptr->p_rts_flags);
-                               vmassert(next_ptr != proc_ptr);
+                               vmassert(!proc_is_runnable(proc_ptr));
                        }
                }
                else if (proc_ptr->p_misc_flags & MF_SC_DEFER) {
@@ -201,13 +221,12 @@ PUBLIC struct proc * schedcheck(void)
                        break;
                }
 
-               /* If proc_ptr is now descheduled,
-                * continue with another process.
+               /*
+                * the selected process might not be runnable anymore. We have
+                * to checkit and schedule another one
                 */
-               if (next_ptr) {
-                       proc_ptr = next_ptr;
-                       next_ptr = NULL;
-               }
+               if (!proc_is_runnable(proc_ptr))
+                       goto not_runnable_pick_new;
        }
        TRACE(VF_SCHEDULING, printf("starting %s / %d\n",
                proc_ptr->p_name, proc_ptr->p_endpoint););
@@ -1189,13 +1208,15 @@ register struct proc *rp;       /* this process is now runnable */
   CHECK_RUNQUEUES;
 #endif
 
-  /* Now select the next process to run, if there isn't a current
-   * process yet or current process isn't ready any more, or
-   * it's PREEMPTIBLE.
+  /*
+   * enqueueing a process with a higher priority than the current one, it gets
+   * preempted. The current process must be preemptible. Testing the priority
+   * also makes sure that a process does not preempt itself
    */
-  if(!proc_ptr || (proc_ptr->p_priority > rp->p_priority) ||
+  vmassert(proc_ptr);
+  if ((proc_ptr->p_priority > rp->p_priority) &&
                  (priv(proc_ptr)->s_flags & PREEMPTIBLE))
-     pick_proc();
+     RTS_SET(proc_ptr, PREEMPTED); /* calls dequeue() */
 
 #if DEBUG_SCHED_CHECK
   CHECK_RUNQUEUES;
@@ -1204,6 +1225,50 @@ register struct proc *rp;        /* this process is now runnable */
   NOREC_RETURN(enqueuefunc, );
 }
 
+/*===========================================================================*
+ *                             enqueue_head                                 *
+ *===========================================================================*/
+/*
+ * put a process at the front of its run queue. It comes handy when a process is
+ * preempted and removed from run queue to not to have a currently not-runnable
+ * process on a run queue. We have to put this process back at the fron to be
+ * fair
+ */
+PRIVATE void enqueue_head(struct proc *rp)
+{
+  int q;                                       /* scheduling queue to use */
+
+#if DEBUG_SCHED_CHECK
+  if(!intr_disabled()) { minix_panic("enqueue with interrupts enabled", NO_NUM); }
+  if (rp->p_ready) minix_panic("enqueue already ready process", NO_NUM);
+#endif
+
+  /*
+   * the process was runnable without its quantum expired when dequeued. A
+   * process with no time left should vahe been handled else and differently
+   */
+  vmassert(rp->p_ticks_left);
+
+  vmassert(q >= 0);
+  vmassert(q < IDLE_Q || rp->p_endpoint == IDLE);
+
+  q = rp->p_priority;
+
+  /* Now add the process to the queue. */
+  if (rdy_head[q] == NIL_PROC) {               /* add to empty queue */
+      rdy_head[q] = rdy_tail[q] = rp;          /* create a new queue */
+      rp->p_nextready = NIL_PROC;              /* mark new end */
+  }
+  else                                         /* add to head of queue */
+      rp->p_nextready = rdy_head[q];           /* chain head of queue */
+      rdy_head[q] = rp;                                /* set new queue head */
+
+#if DEBUG_SCHED_CHECK
+  rp->p_ready = 1;
+  CHECK_RUNQUEUES;
+#endif
+}
+
 /*===========================================================================*
  *                             dequeue                                      * 
  *===========================================================================*/
@@ -1249,8 +1314,6 @@ register struct proc *rp; /* this process is no longer runnable */
                rp->p_ready = 0;
                  CHECK_RUNQUEUES;
 #endif
-          if (rp == proc_ptr || rp == next_ptr)        /* active process removed */
-              pick_proc();             /* pick new process to run */
           break;
       }
       prev_xp = *xpp;                          /* save previous in chain */
@@ -1299,17 +1362,15 @@ int *front;                                     /* return: front or back */
 /*===========================================================================*
  *                             pick_proc                                    * 
  *===========================================================================*/
-PRIVATE void pick_proc()
+PRIVATE struct proc * pick_proc(void)
 {
-/* Decide who to run now.  A new process is selected by setting 'next_ptr'.
+/* Decide who to run now.  A new process is selected an returned.
  * When a billable process is selected, record it in 'bill_ptr', so that the 
  * clock task can tell who to bill for system time.
  */
   register struct proc *rp;                    /* process to run */
   int q;                               /* iterate over queues */
 
-  NOREC_ENTER(pick);
-
   /* Check each of the scheduling queues for ready processes. The number of
    * queues is defined in proc.h, and priorities are set in the task table.
    * The lowest queue contains IDLE, which is always ready.
@@ -1322,14 +1383,13 @@ PRIVATE void pick_proc()
        }
        TRACE(VF_PICKPROC, printf("found %s / %d on queue %d\n", 
                rp->p_name, rp->p_endpoint, q););
-       next_ptr = rp;                  /* run process 'rp' next */
-       vmassert(proc_ptr != next_ptr);
-       vmassert(!next_ptr->p_rts_flags);
+       vmassert(!proc_is_runnable(rp));
        if (priv(rp)->s_flags & BILLABLE)               
                bill_ptr = rp;          /* bill for system time */
-       NOREC_RETURN(pick, );
+       return rp;
   }
   minix_panic("no runnable processes", NO_NUM);
+  return NULL;
 }
 
 /*===========================================================================*
@@ -1354,10 +1414,10 @@ timer_t *tp;                                    /* watchdog timer pointer */
   for (rp=BEG_PROC_ADDR; rp<END_PROC_ADDR; rp++) {
       if (! isemptyp(rp)) {                            /* check slot use */
          if (rp->p_priority > rp->p_max_priority) {    /* update priority? */
-             if (rp->p_rts_flags == 0) dequeue(rp);    /* take off queue */
+             if (proc_is_runnable(rp)) dequeue(rp);    /* take off queue */
              ticks_added += rp->p_quantum_size;        /* do accounting */
              rp->p_priority -= 1;                      /* raise priority */
-             if (rp->p_rts_flags == 0) enqueue(rp);    /* put on queue */
+             if (proc_is_runnable(rp)) enqueue(rp);    /* put on queue */
          }
          else {
              ticks_added += rp->p_quantum_size - rp->p_ticks_left;
index 8debbb88fe8a8a90babcbda0a531357cf231fc64..ceac7a64bc9c2cf2f0c790713ed81b3dd5082bfe 100644 (file)
@@ -119,6 +119,24 @@ struct proc {
 #define VMREQUEST       0x800  /* originator of vm memory request */
 #define VMREQTARGET    0x1000  /* target of vm memory request */
 #define SYS_LOCK       0x2000  /* temporary process lock flag for systask */
+#define PREEMPTED      0x4000  /* this process was preempted by a higher
+                                  priority process and we should pick a new one
+                                  to run. Processes with this flag should be
+                                  returned to the front of their current
+                                  priority queue if they are still runnable
+                                  before we pick a new one
+                                */
+#define NO_QUANTUM     0x8000  /* process ran out of its quantum and we should
+                                  pick a new one. Process was dequeued and
+                                  should be enqueued at the end of some run
+                                  queue again */
+
+/* A process is runnable iff p_rts_flags == 0. */
+#define rts_f_is_runnable(flg) ((flg) == 0)
+#define proc_is_runnable(p)    (rts_f_is_runnable((p)->p_rts_flags))
+
+#define proc_is_preempted(p)   ((p)->p_rts_flags & PREEMPTED)
+#define proc_no_quantum(p)     ((p)->p_rts_flags & NO_QUANTUM)
 
 /* These runtime flags can be tested and manipulated by these macros. */
 
@@ -129,7 +147,7 @@ struct proc {
 #define RTS_SET(rp, f)                                                 \
        do {                                                            \
                vmassert(intr_disabled());                              \
-               if(!(rp)->p_rts_flags) { dequeue(rp); }                 \
+               if(proc_is_runnable(rp)) { dequeue(rp); }               \
                (rp)->p_rts_flags |=  (f);                              \
                vmassert(intr_disabled());                              \
        } while(0)
@@ -141,7 +159,9 @@ struct proc {
                vmassert(intr_disabled());                              \
                rts = (rp)->p_rts_flags;                                \
                (rp)->p_rts_flags &= ~(f);                              \
-               if(rts && !(rp)->p_rts_flags) { enqueue(rp); }          \
+               if(!rts_f_is_runnable(rts) && proc_is_runnable(rp)) {   \
+                       enqueue(rp);                                    \
+               }                                                       \
                vmassert(intr_disabled());                              \
        } while(0)
 
@@ -150,7 +170,7 @@ struct proc {
        do {                                                            \
                int u = 0;                                              \
                if(!intr_disabled()) { u = 1; lock; }                   \
-               if(!(rp)->p_rts_flags) { dequeue(rp); }                 \
+               if(proc_is_runnable(rp)) { dequeue(rp); }               \
                (rp)->p_rts_flags |=  (f);                              \
                if(u) { unlock; }                                       \
        } while(0)
@@ -163,7 +183,9 @@ struct proc {
                if(!intr_disabled()) { u = 1; lock; }                   \
                rts = (rp)->p_rts_flags;                                \
                (rp)->p_rts_flags &= ~(f);                              \
-               if(rts && !(rp)->p_rts_flags) { enqueue(rp); }          \
+               if(!rts_f_is_runnable(rts) && proc_is_runnable(rp)) {   \
+                       enqueue(rp);                                    \
+               }                                                       \
                if(u) { unlock; }                                       \
        } while(0)
 
@@ -172,7 +194,7 @@ struct proc {
        do {                                                            \
                int u = 0;                                              \
                if(!intr_disabled()) { u = 1; lock; }                   \
-               if(!(rp)->p_rts_flags && (f)) { dequeue(rp); }          \
+               if(proc_is_runnable(rp) && (f)) { dequeue(rp); }                \
                (rp)->p_rts_flags = (f);                                \
                if(u) { unlock; }                                       \
        } while(0)
index 5336267437a54abe7d68f06b60c5d13884926549..c4822cfd21eb81b0a0b53125e84ea5ce32fe587c 100644 (file)
@@ -90,7 +90,7 @@ irq_hook_t *hook;
        sprof_info.idle_samples++;
   } else
   /* Runnable system process? */
-  if (priv(proc_ptr)->s_flags & SYS_PROC && !proc_ptr->p_rts_flags) {
+  if (priv(proc_ptr)->s_flags & SYS_PROC && proc_is_runnable(proc_ptr)) {
        /* Note: k_reenter is always 0 here. */
 
        /* Store sample (process name and program counter). */