diff --git a/kernel/clock.c b/kernel/clock.c index 514c5b639..00191f611 100644 --- a/kernel/clock.c +++ b/kernel/clock.c @@ -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; diff --git a/kernel/debug.c b/kernel/debug.c index e710f6a21..bd510ec2f 100644 --- a/kernel/debug.c +++ b/kernel/debug.c @@ -129,6 +129,8 @@ rtsflagstr(int flags) FLAG(PAGEFAULT); FLAG(VMREQUEST); FLAG(VMREQTARGET); + FLAG(PREEMPTED); + FLAG(NO_QUANTUM); return str; } diff --git a/kernel/glo.h b/kernel/glo.h index eef660117..b300bbf54 100644 --- a/kernel/glo.h +++ b/kernel/glo.h @@ -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 */ diff --git a/kernel/proc.c b/kernel/proc.c index f58a7332d..169be2cce 100644 --- a/kernel/proc.c +++ b/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; rpp_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; diff --git a/kernel/proc.h b/kernel/proc.h index 8debbb88f..ceac7a64b 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -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) diff --git a/kernel/profile.c b/kernel/profile.c index 533626743..c4822cfd2 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -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). */