minix/servers/sched/schedule.c
Tomas Hruby 1f89845bb2 SMP - can boot even if some cpus fail to boot
- EBADCPU is returned is scheduler tries to run a process on a CPU
  that either does not exist or isn't booted

- this change was originally meant to deal with stupid cpuid
  instruction which provides totally useless information about
  hyper-threading and MPS which does not deal with ht at all. ACPI
  provides correct information. If ht is turned off it looks like some
  CPUs failed to boot.  Nevertheless this patch may be handy for
  testing/benchmarking in the future.
2010-09-15 14:11:21 +00:00

369 lines
10 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 to start scheduling a proc
* do_stop_scheduling Request to stop scheduling a proc
* do_nice Request 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 <assert.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,
unsigned flags));
FORWARD _PROTOTYPE( void balance_queues, (struct timer *tp) );
#define SCHEDULE_CHANGE_PRIO 0x1
#define SCHEDULE_CHANGE_QUANTUM 0x2
#define SCHEDULE_CHANGE_CPU 0x4
#define SCHEDULE_CHANGE_ALL ( \
SCHEDULE_CHANGE_PRIO | \
SCHEDULE_CHANGE_QUANTUM | \
SCHEDULE_CHANGE_CPU \
)
#define schedule_process_local(p) \
schedule_process(p, SCHEDULE_CHANGE_PRIO | SCHEDULE_CHANGE_QUANTUM)
#define schedule_process_migrate(p) \
schedule_process(p, SCHEDULE_CHANGE_CPU)
#define CPU_DEAD -1
#define cpu_is_available(c) (cpu_proc[c] >= 0)
#define DEFAULT_USER_TIME_SLICE 200
/* processes created by RS are sysytem processes */
#define is_system_proc(p) ((p)->parent == RS_PROC_NR)
PRIVATE unsigned cpu_proc[CONFIG_MAX_CPUS];
PRIVATE void pick_cpu(struct schedproc * proc)
{
#ifdef CONFIG_SMP
unsigned cpu, c;
unsigned cpu_load = (unsigned) -1;
if (machine.processors_count == 1) {
proc->cpu = machine.bsp_id;
return;
}
/* schedule sysytem processes only on the boot cpu */
if (is_system_proc(proc)) {
proc->cpu = machine.bsp_id;
return;
}
/* if no other cpu available, try BSP */
cpu = machine.bsp_id;
for (c = 0; c < machine.processors_count; c++) {
/* skip dead cpus */
if (!cpu_is_available(c))
continue;
if (c != machine.bsp_id && cpu_load > cpu_proc[c]) {
cpu_load = cpu_proc[c];
cpu = c;
}
}
proc->cpu = cpu;
cpu_proc[cpu]++;
#else
proc->cpu = 0;
#endif
}
/*===========================================================================*
* 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 EBADEPT;
}
rmp = &schedproc[proc_nr_n];
if (rmp->priority < MIN_USER_Q) {
rmp->priority += 1; /* lower priority */
}
if ((rv = schedule_process_local(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;
/* check who can send you requests */
if (!accept_message(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 "
"%ld\n", m_ptr->SCHEDULING_ENDPOINT);
return EBADEPT;
}
rmp = &schedproc[proc_nr_n];
#ifdef CONFIG_SMP
cpu_proc[rmp->cpu]--;
#endif
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, nice;
/* we can handle two kinds of messages here */
assert(m_ptr->m_type == SCHEDULING_START ||
m_ptr->m_type == SCHEDULING_INHERIT);
/* check who can send you requests */
if (!accept_message(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->max_priority = (unsigned) m_ptr->SCHEDULING_MAXPRIO;
if (rmp->max_priority >= NR_SCHED_QUEUES) {
return EINVAL;
}
/* 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;
/*
* Since kernel never changes the cpu of a process, all are
* started on the BSP and the userspace scheduling hasn't
* changed that yet either, we can be sure that BSP is the
* processor where the processes run now.
*/
#ifdef CONFIG_SMP
rmp->cpu = machine.bsp_id;
/* FIXME set the cpu mask */
#endif
}
switch (m_ptr->m_type) {
case SCHEDULING_START:
/* We have a special case here for system processes, for which
* quanum and priority are set explicitly rather than inherited
* from the parent */
rmp->priority = rmp->max_priority;
rmp->time_slice = (unsigned) m_ptr->SCHEDULING_QUANTUM;
break;
case SCHEDULING_INHERIT:
/* 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 ((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;
break;
default:
/* not reachable */
assert(0);
}
/* Take over scheduling the process. The kernel reply message populates
* the processes current priority and its time slice */
if ((rv = sys_schedctl(0, rmp->endpoint, 0, 0, 0)) != OK) {
printf("Sched: Error taking over scheduling for %d, kernel said %d\n",
rmp->endpoint, rv);
return rv;
}
rmp->flags = IN_USE;
/* Schedule the process, giving it some quantum */
pick_cpu(rmp);
while ((rv = schedule_process(rmp, SCHEDULE_CHANGE_ALL)) == EBADCPU) {
/* don't try this CPU ever again */
cpu_proc[rmp->cpu] = CPU_DEAD;
pick_cpu(rmp);
}
if (rv != 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;
unsigned new_q, old_q, old_max_q;
/* check who can send you requests */
if (!accept_message(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 "
"%ld\n", m_ptr->SCHEDULING_ENDPOINT);
return EBADEPT;
}
rmp = &schedproc[proc_nr_n];
new_q = (unsigned) m_ptr->SCHEDULING_MAXPRIO;
if (new_q >= NR_SCHED_QUEUES) {
return EINVAL;
}
/* Store old values, in case we need to roll back the changes */
old_q = rmp->priority;
old_max_q = rmp->max_priority;
/* Update the proc entry and reschedule the process */
rmp->max_priority = rmp->priority = new_q;
if ((rv = schedule_process_local(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;
}
return rv;
}
/*===========================================================================*
* schedule_process *
*===========================================================================*/
PRIVATE int schedule_process(struct schedproc * rmp, unsigned flags)
{
int err;
int new_prio, new_quantum, new_cpu;
pick_cpu(rmp);
if (flags & SCHEDULE_CHANGE_PRIO)
new_prio = rmp->priority;
else
new_prio = -1;
if (flags & SCHEDULE_CHANGE_QUANTUM)
new_quantum = rmp->time_slice;
else
new_quantum = -1;
if (flags & SCHEDULE_CHANGE_CPU)
new_cpu = rmp->cpu;
else
new_cpu = -1;
if ((err = sys_schedule(rmp->endpoint, new_prio,
new_quantum, new_cpu)) != OK) {
printf("PM: An error occurred when trying to schedule %d: %d\n",
rmp->endpoint, err);
}
return err;
}
/*===========================================================================*
* start_scheduling *
*===========================================================================*/
PUBLIC void init_scheduling(void)
{
balance_timeout = BALANCE_TIMEOUT * sys_hz();
init_timer(&sched_timer);
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_local(rmp);
}
}
}
set_timer(&sched_timer, balance_timeout, balance_queues, 0);
}