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
*/
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) {
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););
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;
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 *
*===========================================================================*/
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 */
/*===========================================================================*
* 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.
}
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;
}
/*===========================================================================*
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;
#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. */
#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)
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)
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)
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)
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)