minix/servers/sched/schedule.c
2010-05-26 07:16:39 +00:00

253 lines
7.4 KiB
C

/* This file contains the scheduling policy for SCHED
*
* The entry points are:
* do_noquantum: Called on behalf of process' that run out of quantum
* do_start_scheduling Request from PM to start scheduling a proc
* do_stop_scheduling Request from PM to stop scheduling a proc
* do_nice Request from PM to change the nice level on a proc
* init_scheduling Called from main.c to set up/prepare scheduling
*/
#include "sched.h"
#include "schedproc.h"
#include <minix/com.h>
#include <machine/archtypes.h>
#include "kernel/proc.h" /* for queue constants */
PRIVATE timer_t sched_timer;
PRIVATE unsigned balance_timeout;
#define BALANCE_TIMEOUT 5 /* how often to balance queues in seconds */
FORWARD _PROTOTYPE( int schedule_process, (struct schedproc * rmp) );
FORWARD _PROTOTYPE( void balance_queues, (struct timer *tp) );
#define DEFAULT_USER_TIME_SLICE 200
/*===========================================================================*
* do_noquantum *
*===========================================================================*/
PUBLIC int do_noquantum(message *m_ptr)
{
register struct schedproc *rmp;
int rv, proc_nr_n;
if (sched_isokendpt(m_ptr->m_source, &proc_nr_n) != OK) {
printf("SCHED: WARNING: got an invalid endpoint in OOQ msg %u.\n",
m_ptr->m_source);
return EBADSRCDST;
}
rmp = &schedproc[proc_nr_n];
if (rmp->priority < MIN_USER_Q) {
rmp->priority += 1; /* lower priority */
}
if ((rv = schedule_process(rmp)) != OK) {
return rv;
}
return OK;
}
/*===========================================================================*
* do_stop_scheduling *
*===========================================================================*/
PUBLIC int do_stop_scheduling(message *m_ptr)
{
register struct schedproc *rmp;
int rv, proc_nr_n;
/* Only accept stop messages from PM */
if (!is_from_pm(m_ptr))
return EPERM;
if (sched_isokendpt(m_ptr->SCHEDULING_ENDPOINT, &proc_nr_n) != OK) {
printf("SCHED: WARNING: got an invalid endpoint in OOQ msg %u.\n",
m_ptr->SCHEDULING_ENDPOINT);
return EBADSRCDST;
}
rmp = &schedproc[proc_nr_n];
rmp->flags = 0; /*&= ~IN_USE;*/
return OK;
}
/*===========================================================================*
* do_start_scheduling *
*===========================================================================*/
PUBLIC int do_start_scheduling(message *m_ptr)
{
register struct schedproc *rmp;
int rv, proc_nr_n, parent_nr_n;
/* Only accept start messages from PM */
if (!is_from_pm(m_ptr))
return EPERM;
/* Resolve endpoint to proc slot. */
if ((rv = sched_isemtyendpt(m_ptr->SCHEDULING_ENDPOINT, &proc_nr_n))
!= OK) {
return rv;
}
rmp = &schedproc[proc_nr_n];
/* Populate process slot */
rmp->endpoint = m_ptr->SCHEDULING_ENDPOINT;
rmp->parent = m_ptr->SCHEDULING_PARENT;
rmp->nice = m_ptr->SCHEDULING_NICE;
/* Find maximum priority from nice value */
rv = nice_to_priority(rmp->nice, &rmp->max_priority);
if (rv != OK)
return rv;
/* Inherit current priority and time slice from parent. Since there
* is currently only one scheduler scheduling the whole system, this
* value is local and we assert that the parent endpoint is valid */
if (rmp->endpoint == rmp->parent) {
/* We have a special case here for init, which is the first
process scheduled, and the parent of itself. */
rmp->priority = USER_Q;
rmp->time_slice = DEFAULT_USER_TIME_SLICE;
}
else {
if ((rv = sched_isokendpt(m_ptr->SCHEDULING_PARENT,
&parent_nr_n)) != OK)
return rv;
rmp->priority = schedproc[parent_nr_n].priority;
rmp->time_slice = schedproc[parent_nr_n].time_slice;
}
/* Take over scheduling the process. The kernel reply message populates
* the processes current priority and its time slice */
if ((rv = sys_schedctl(rmp->endpoint)) != OK) {
printf("Sched: Error overtaking scheduling for %d, kernel said %d\n",
rmp->endpoint, rv);
return rv;
}
rmp->flags = IN_USE;
/* Schedule the process, giving it some quantum */
if ((rv = schedule_process(rmp)) != OK) {
printf("Sched: Error while scheduling process, kernel replied %d\n",
rv);
return rv;
}
/* Mark ourselves as the new scheduler.
* By default, processes are scheduled by the parents scheduler. In case
* this scheduler would want to delegate scheduling to another
* scheduler, it could do so and then write the endpoint of that
* scheduler into SCHEDULING_SCHEDULER
*/
m_ptr->SCHEDULING_SCHEDULER = SCHED_PROC_NR;
return OK;
}
/*===========================================================================*
* do_nice *
*===========================================================================*/
PUBLIC int do_nice(message *m_ptr)
{
struct schedproc *rmp;
int rv;
int proc_nr_n;
int nice;
unsigned new_q, old_q, old_max_q;
int old_nice;
/* Only accept nice messages from PM */
if (!is_from_pm(m_ptr))
return EPERM;
if (sched_isokendpt(m_ptr->SCHEDULING_ENDPOINT, &proc_nr_n) != OK) {
printf("SCHED: WARNING: got an invalid endpoint in OOQ msg %u.\n",
m_ptr->SCHEDULING_ENDPOINT);
return EBADSRCDST;
}
rmp = &schedproc[proc_nr_n];
nice = m_ptr->SCHEDULING_NICE;
if ((rv = nice_to_priority(nice, &new_q)) != OK)
return rv;
/* Store old values, in case we need to roll back the changes */
old_q = rmp->priority;
old_max_q = rmp->max_priority;
old_nice = rmp->nice;
/* Update the proc entry and reschedule the process */
rmp->max_priority = rmp->priority = new_q;
rmp->nice = nice;
if ((rv = schedule_process(rmp)) != OK) {
/* Something went wrong when rescheduling the process, roll
* back the changes to proc struct */
rmp->priority = old_q;
rmp->max_priority = old_max_q;
rmp->nice = old_nice;
}
return rv;
}
/*===========================================================================*
* schedule_process *
*===========================================================================*/
PRIVATE int schedule_process(struct schedproc * rmp)
{
int rv;
if ((rv = sys_schedule(rmp->endpoint, rmp->priority,
rmp->time_slice)) != OK) {
printf("SCHED: An error occurred when trying to schedule %d: %d\n",
rmp->endpoint, rv);
}
return rv;
}
/*===========================================================================*
* start_scheduling *
*===========================================================================*/
PUBLIC void init_scheduling(void)
{
balance_timeout = BALANCE_TIMEOUT * sys_hz();
tmr_inittimer(&sched_timer);
sched_set_timer(&sched_timer, balance_timeout, balance_queues, 0);
}
/*===========================================================================*
* balance_queues *
*===========================================================================*/
/* This function in called every 100 ticks to rebalance the queues. The current
* scheduler bumps processes down one priority when ever they run out of
* quantum. This function will find all proccesses that have been bumped down,
* and pulls them back up. This default policy will soon be changed.
*/
PRIVATE void balance_queues(struct timer *tp)
{
struct schedproc *rmp;
int proc_nr;
int rv;
for (proc_nr=0, rmp=schedproc; proc_nr < NR_PROCS; proc_nr++, rmp++) {
if (rmp->flags & IN_USE) {
if (rmp->priority > rmp->max_priority) {
rmp->priority -= 1; /* increase priority */
schedule_process(rmp);
}
}
}
sched_set_timer(&sched_timer, balance_timeout, balance_queues, 0);
}