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:
parent
86cc12b9a3
commit
daf7940c69
6 changed files with 125 additions and 45 deletions
|
@ -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;
|
||||
|
|
|
@ -129,6 +129,8 @@ rtsflagstr(int flags)
|
|||
FLAG(PAGEFAULT);
|
||||
FLAG(VMREQUEST);
|
||||
FLAG(VMREQTARGET);
|
||||
FLAG(PREEMPTED);
|
||||
FLAG(NO_QUANTUM);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
126
kernel/proc.c
126
kernel/proc.c
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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). */
|
||||
|
|
Loading…
Reference in a new issue