pick_proc() called only just before returning to userspace

- 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
This commit is contained in:
Tomas Hruby 2009-11-09 17:48:31 +00:00
parent 86cc12b9a3
commit daf7940c69
6 changed files with 125 additions and 45 deletions

View 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;

View file

@ -129,6 +129,8 @@ rtsflagstr(int flags)
FLAG(PAGEFAULT);
FLAG(VMREQUEST);
FLAG(VMREQTARGET);
FLAG(PREEMPTED);
FLAG(NO_QUANTUM);
return str;
}

View 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 */

View 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;

View 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)

View 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). */