1433d6423SLionel Sambuc /* This file contains the scheduling policy for SCHED 2433d6423SLionel Sambuc * 3433d6423SLionel Sambuc * The entry points are: 4433d6423SLionel Sambuc * do_noquantum: Called on behalf of process' that run out of quantum 5433d6423SLionel Sambuc * do_start_scheduling Request to start scheduling a proc 6433d6423SLionel Sambuc * do_stop_scheduling Request to stop scheduling a proc 7433d6423SLionel Sambuc * do_nice Request to change the nice level on a proc 8433d6423SLionel Sambuc * init_scheduling Called from main.c to set up/prepare scheduling 9433d6423SLionel Sambuc */ 10433d6423SLionel Sambuc #include "sched.h" 11433d6423SLionel Sambuc #include "schedproc.h" 12433d6423SLionel Sambuc #include <assert.h> 13433d6423SLionel Sambuc #include <minix/com.h> 14433d6423SLionel Sambuc #include <machine/archtypes.h> 15433d6423SLionel Sambuc 16433d6423SLionel Sambuc static unsigned balance_timeout; 17433d6423SLionel Sambuc 18433d6423SLionel Sambuc #define BALANCE_TIMEOUT 5 /* how often to balance queues in seconds */ 19433d6423SLionel Sambuc 20433d6423SLionel Sambuc static int schedule_process(struct schedproc * rmp, unsigned flags); 21433d6423SLionel Sambuc 22433d6423SLionel Sambuc #define SCHEDULE_CHANGE_PRIO 0x1 23433d6423SLionel Sambuc #define SCHEDULE_CHANGE_QUANTUM 0x2 24433d6423SLionel Sambuc #define SCHEDULE_CHANGE_CPU 0x4 25433d6423SLionel Sambuc 26433d6423SLionel Sambuc #define SCHEDULE_CHANGE_ALL ( \ 27433d6423SLionel Sambuc SCHEDULE_CHANGE_PRIO | \ 28433d6423SLionel Sambuc SCHEDULE_CHANGE_QUANTUM | \ 29433d6423SLionel Sambuc SCHEDULE_CHANGE_CPU \ 30433d6423SLionel Sambuc ) 31433d6423SLionel Sambuc 32433d6423SLionel Sambuc #define schedule_process_local(p) \ 33433d6423SLionel Sambuc schedule_process(p, SCHEDULE_CHANGE_PRIO | SCHEDULE_CHANGE_QUANTUM) 34433d6423SLionel Sambuc #define schedule_process_migrate(p) \ 35433d6423SLionel Sambuc schedule_process(p, SCHEDULE_CHANGE_CPU) 36433d6423SLionel Sambuc 37433d6423SLionel Sambuc #define CPU_DEAD -1 38433d6423SLionel Sambuc 39433d6423SLionel Sambuc #define cpu_is_available(c) (cpu_proc[c] >= 0) 40433d6423SLionel Sambuc 41433d6423SLionel Sambuc #define DEFAULT_USER_TIME_SLICE 200 42433d6423SLionel Sambuc 43433d6423SLionel Sambuc /* processes created by RS are sysytem processes */ 44433d6423SLionel Sambuc #define is_system_proc(p) ((p)->parent == RS_PROC_NR) 45433d6423SLionel Sambuc 46433d6423SLionel Sambuc static unsigned cpu_proc[CONFIG_MAX_CPUS]; 47433d6423SLionel Sambuc 48433d6423SLionel Sambuc static void pick_cpu(struct schedproc * proc) 49433d6423SLionel Sambuc { 50433d6423SLionel Sambuc #ifdef CONFIG_SMP 51433d6423SLionel Sambuc unsigned cpu, c; 52433d6423SLionel Sambuc unsigned cpu_load = (unsigned) -1; 53433d6423SLionel Sambuc 54433d6423SLionel Sambuc if (machine.processors_count == 1) { 55433d6423SLionel Sambuc proc->cpu = machine.bsp_id; 56433d6423SLionel Sambuc return; 57433d6423SLionel Sambuc } 58433d6423SLionel Sambuc 59433d6423SLionel Sambuc /* schedule sysytem processes only on the boot cpu */ 60433d6423SLionel Sambuc if (is_system_proc(proc)) { 61433d6423SLionel Sambuc proc->cpu = machine.bsp_id; 62433d6423SLionel Sambuc return; 63433d6423SLionel Sambuc } 64433d6423SLionel Sambuc 65433d6423SLionel Sambuc /* if no other cpu available, try BSP */ 66433d6423SLionel Sambuc cpu = machine.bsp_id; 67433d6423SLionel Sambuc for (c = 0; c < machine.processors_count; c++) { 68433d6423SLionel Sambuc /* skip dead cpus */ 69433d6423SLionel Sambuc if (!cpu_is_available(c)) 70433d6423SLionel Sambuc continue; 71433d6423SLionel Sambuc if (c != machine.bsp_id && cpu_load > cpu_proc[c]) { 72433d6423SLionel Sambuc cpu_load = cpu_proc[c]; 73433d6423SLionel Sambuc cpu = c; 74433d6423SLionel Sambuc } 75433d6423SLionel Sambuc } 76433d6423SLionel Sambuc proc->cpu = cpu; 77433d6423SLionel Sambuc cpu_proc[cpu]++; 78433d6423SLionel Sambuc #else 79433d6423SLionel Sambuc proc->cpu = 0; 80433d6423SLionel Sambuc #endif 81433d6423SLionel Sambuc } 82433d6423SLionel Sambuc 83433d6423SLionel Sambuc /*===========================================================================* 84433d6423SLionel Sambuc * do_noquantum * 85433d6423SLionel Sambuc *===========================================================================*/ 86433d6423SLionel Sambuc 87433d6423SLionel Sambuc int do_noquantum(message *m_ptr) 88433d6423SLionel Sambuc { 89433d6423SLionel Sambuc register struct schedproc *rmp; 90433d6423SLionel Sambuc int rv, proc_nr_n; 91433d6423SLionel Sambuc 92433d6423SLionel Sambuc if (sched_isokendpt(m_ptr->m_source, &proc_nr_n) != OK) { 93433d6423SLionel Sambuc printf("SCHED: WARNING: got an invalid endpoint in OOQ msg %u.\n", 94433d6423SLionel Sambuc m_ptr->m_source); 95433d6423SLionel Sambuc return EBADEPT; 96433d6423SLionel Sambuc } 97433d6423SLionel Sambuc 98433d6423SLionel Sambuc rmp = &schedproc[proc_nr_n]; 99433d6423SLionel Sambuc if (rmp->priority < MIN_USER_Q) { 100433d6423SLionel Sambuc rmp->priority += 1; /* lower priority */ 101433d6423SLionel Sambuc } 102433d6423SLionel Sambuc 103433d6423SLionel Sambuc if ((rv = schedule_process_local(rmp)) != OK) { 104433d6423SLionel Sambuc return rv; 105433d6423SLionel Sambuc } 106433d6423SLionel Sambuc return OK; 107433d6423SLionel Sambuc } 108433d6423SLionel Sambuc 109433d6423SLionel Sambuc /*===========================================================================* 110433d6423SLionel Sambuc * do_stop_scheduling * 111433d6423SLionel Sambuc *===========================================================================*/ 112433d6423SLionel Sambuc int do_stop_scheduling(message *m_ptr) 113433d6423SLionel Sambuc { 114433d6423SLionel Sambuc register struct schedproc *rmp; 115433d6423SLionel Sambuc int proc_nr_n; 116433d6423SLionel Sambuc 117433d6423SLionel Sambuc /* check who can send you requests */ 118433d6423SLionel Sambuc if (!accept_message(m_ptr)) 119433d6423SLionel Sambuc return EPERM; 120433d6423SLionel Sambuc 121433d6423SLionel Sambuc if (sched_isokendpt(m_ptr->m_lsys_sched_scheduling_stop.endpoint, 122433d6423SLionel Sambuc &proc_nr_n) != OK) { 123433d6423SLionel Sambuc printf("SCHED: WARNING: got an invalid endpoint in OOQ msg " 124433d6423SLionel Sambuc "%d\n", m_ptr->m_lsys_sched_scheduling_stop.endpoint); 125433d6423SLionel Sambuc return EBADEPT; 126433d6423SLionel Sambuc } 127433d6423SLionel Sambuc 128433d6423SLionel Sambuc rmp = &schedproc[proc_nr_n]; 129433d6423SLionel Sambuc #ifdef CONFIG_SMP 130433d6423SLionel Sambuc cpu_proc[rmp->cpu]--; 131433d6423SLionel Sambuc #endif 132433d6423SLionel Sambuc rmp->flags = 0; /*&= ~IN_USE;*/ 133433d6423SLionel Sambuc 134433d6423SLionel Sambuc return OK; 135433d6423SLionel Sambuc } 136433d6423SLionel Sambuc 137433d6423SLionel Sambuc /*===========================================================================* 138433d6423SLionel Sambuc * do_start_scheduling * 139433d6423SLionel Sambuc *===========================================================================*/ 140433d6423SLionel Sambuc int do_start_scheduling(message *m_ptr) 141433d6423SLionel Sambuc { 142433d6423SLionel Sambuc register struct schedproc *rmp; 143433d6423SLionel Sambuc int rv, proc_nr_n, parent_nr_n; 144433d6423SLionel Sambuc 145433d6423SLionel Sambuc /* we can handle two kinds of messages here */ 146433d6423SLionel Sambuc assert(m_ptr->m_type == SCHEDULING_START || 147433d6423SLionel Sambuc m_ptr->m_type == SCHEDULING_INHERIT); 148433d6423SLionel Sambuc 149433d6423SLionel Sambuc /* check who can send you requests */ 150433d6423SLionel Sambuc if (!accept_message(m_ptr)) 151433d6423SLionel Sambuc return EPERM; 152433d6423SLionel Sambuc 153433d6423SLionel Sambuc /* Resolve endpoint to proc slot. */ 154433d6423SLionel Sambuc if ((rv = sched_isemtyendpt(m_ptr->m_lsys_sched_scheduling_start.endpoint, 155433d6423SLionel Sambuc &proc_nr_n)) != OK) { 156433d6423SLionel Sambuc return rv; 157433d6423SLionel Sambuc } 158433d6423SLionel Sambuc rmp = &schedproc[proc_nr_n]; 159433d6423SLionel Sambuc 160433d6423SLionel Sambuc /* Populate process slot */ 161433d6423SLionel Sambuc rmp->endpoint = m_ptr->m_lsys_sched_scheduling_start.endpoint; 162433d6423SLionel Sambuc rmp->parent = m_ptr->m_lsys_sched_scheduling_start.parent; 163433d6423SLionel Sambuc rmp->max_priority = m_ptr->m_lsys_sched_scheduling_start.maxprio; 164433d6423SLionel Sambuc if (rmp->max_priority >= NR_SCHED_QUEUES) { 165433d6423SLionel Sambuc return EINVAL; 166433d6423SLionel Sambuc } 167433d6423SLionel Sambuc 168433d6423SLionel Sambuc /* Inherit current priority and time slice from parent. Since there 169433d6423SLionel Sambuc * is currently only one scheduler scheduling the whole system, this 170433d6423SLionel Sambuc * value is local and we assert that the parent endpoint is valid */ 171433d6423SLionel Sambuc if (rmp->endpoint == rmp->parent) { 172433d6423SLionel Sambuc /* We have a special case here for init, which is the first 173433d6423SLionel Sambuc process scheduled, and the parent of itself. */ 174433d6423SLionel Sambuc rmp->priority = USER_Q; 175433d6423SLionel Sambuc rmp->time_slice = DEFAULT_USER_TIME_SLICE; 176433d6423SLionel Sambuc 177433d6423SLionel Sambuc /* 178433d6423SLionel Sambuc * Since kernel never changes the cpu of a process, all are 179433d6423SLionel Sambuc * started on the BSP and the userspace scheduling hasn't 180433d6423SLionel Sambuc * changed that yet either, we can be sure that BSP is the 181433d6423SLionel Sambuc * processor where the processes run now. 182433d6423SLionel Sambuc */ 183433d6423SLionel Sambuc #ifdef CONFIG_SMP 184433d6423SLionel Sambuc rmp->cpu = machine.bsp_id; 185433d6423SLionel Sambuc /* FIXME set the cpu mask */ 186433d6423SLionel Sambuc #endif 187433d6423SLionel Sambuc } 188433d6423SLionel Sambuc 189433d6423SLionel Sambuc switch (m_ptr->m_type) { 190433d6423SLionel Sambuc 191433d6423SLionel Sambuc case SCHEDULING_START: 192433d6423SLionel Sambuc /* We have a special case here for system processes, for which 193433d6423SLionel Sambuc * quanum and priority are set explicitly rather than inherited 194433d6423SLionel Sambuc * from the parent */ 195433d6423SLionel Sambuc rmp->priority = rmp->max_priority; 196433d6423SLionel Sambuc rmp->time_slice = m_ptr->m_lsys_sched_scheduling_start.quantum; 197433d6423SLionel Sambuc break; 198433d6423SLionel Sambuc 199433d6423SLionel Sambuc case SCHEDULING_INHERIT: 200433d6423SLionel Sambuc /* Inherit current priority and time slice from parent. Since there 201433d6423SLionel Sambuc * is currently only one scheduler scheduling the whole system, this 202433d6423SLionel Sambuc * value is local and we assert that the parent endpoint is valid */ 203433d6423SLionel Sambuc if ((rv = sched_isokendpt(m_ptr->m_lsys_sched_scheduling_start.parent, 204433d6423SLionel Sambuc &parent_nr_n)) != OK) 205433d6423SLionel Sambuc return rv; 206433d6423SLionel Sambuc 207433d6423SLionel Sambuc rmp->priority = schedproc[parent_nr_n].priority; 208433d6423SLionel Sambuc rmp->time_slice = schedproc[parent_nr_n].time_slice; 209433d6423SLionel Sambuc break; 210433d6423SLionel Sambuc 211433d6423SLionel Sambuc default: 212433d6423SLionel Sambuc /* not reachable */ 213433d6423SLionel Sambuc assert(0); 214433d6423SLionel Sambuc } 215433d6423SLionel Sambuc 216433d6423SLionel Sambuc /* Take over scheduling the process. The kernel reply message populates 217433d6423SLionel Sambuc * the processes current priority and its time slice */ 218433d6423SLionel Sambuc if ((rv = sys_schedctl(0, rmp->endpoint, 0, 0, 0)) != OK) { 219433d6423SLionel Sambuc printf("Sched: Error taking over scheduling for %d, kernel said %d\n", 220433d6423SLionel Sambuc rmp->endpoint, rv); 221433d6423SLionel Sambuc return rv; 222433d6423SLionel Sambuc } 223433d6423SLionel Sambuc rmp->flags = IN_USE; 224433d6423SLionel Sambuc 225433d6423SLionel Sambuc /* Schedule the process, giving it some quantum */ 226433d6423SLionel Sambuc pick_cpu(rmp); 227433d6423SLionel Sambuc while ((rv = schedule_process(rmp, SCHEDULE_CHANGE_ALL)) == EBADCPU) { 228433d6423SLionel Sambuc /* don't try this CPU ever again */ 229433d6423SLionel Sambuc cpu_proc[rmp->cpu] = CPU_DEAD; 230433d6423SLionel Sambuc pick_cpu(rmp); 231433d6423SLionel Sambuc } 232433d6423SLionel Sambuc 233433d6423SLionel Sambuc if (rv != OK) { 234433d6423SLionel Sambuc printf("Sched: Error while scheduling process, kernel replied %d\n", 235433d6423SLionel Sambuc rv); 236433d6423SLionel Sambuc return rv; 237433d6423SLionel Sambuc } 238433d6423SLionel Sambuc 239433d6423SLionel Sambuc /* Mark ourselves as the new scheduler. 240433d6423SLionel Sambuc * By default, processes are scheduled by the parents scheduler. In case 241433d6423SLionel Sambuc * this scheduler would want to delegate scheduling to another 242433d6423SLionel Sambuc * scheduler, it could do so and then write the endpoint of that 243433d6423SLionel Sambuc * scheduler into the "scheduler" field. 244433d6423SLionel Sambuc */ 245433d6423SLionel Sambuc 246433d6423SLionel Sambuc m_ptr->m_sched_lsys_scheduling_start.scheduler = SCHED_PROC_NR; 247433d6423SLionel Sambuc 248433d6423SLionel Sambuc return OK; 249433d6423SLionel Sambuc } 250433d6423SLionel Sambuc 251433d6423SLionel Sambuc /*===========================================================================* 252433d6423SLionel Sambuc * do_nice * 253433d6423SLionel Sambuc *===========================================================================*/ 254433d6423SLionel Sambuc int do_nice(message *m_ptr) 255433d6423SLionel Sambuc { 256433d6423SLionel Sambuc struct schedproc *rmp; 257433d6423SLionel Sambuc int rv; 258433d6423SLionel Sambuc int proc_nr_n; 259433d6423SLionel Sambuc unsigned new_q, old_q, old_max_q; 260433d6423SLionel Sambuc 261433d6423SLionel Sambuc /* check who can send you requests */ 262433d6423SLionel Sambuc if (!accept_message(m_ptr)) 263433d6423SLionel Sambuc return EPERM; 264433d6423SLionel Sambuc 265433d6423SLionel Sambuc if (sched_isokendpt(m_ptr->m_pm_sched_scheduling_set_nice.endpoint, &proc_nr_n) != OK) { 266433d6423SLionel Sambuc printf("SCHED: WARNING: got an invalid endpoint in OoQ msg " 267433d6423SLionel Sambuc "%d\n", m_ptr->m_pm_sched_scheduling_set_nice.endpoint); 268433d6423SLionel Sambuc return EBADEPT; 269433d6423SLionel Sambuc } 270433d6423SLionel Sambuc 271433d6423SLionel Sambuc rmp = &schedproc[proc_nr_n]; 272433d6423SLionel Sambuc new_q = m_ptr->m_pm_sched_scheduling_set_nice.maxprio; 273433d6423SLionel Sambuc if (new_q >= NR_SCHED_QUEUES) { 274433d6423SLionel Sambuc return EINVAL; 275433d6423SLionel Sambuc } 276433d6423SLionel Sambuc 277433d6423SLionel Sambuc /* Store old values, in case we need to roll back the changes */ 278433d6423SLionel Sambuc old_q = rmp->priority; 279433d6423SLionel Sambuc old_max_q = rmp->max_priority; 280433d6423SLionel Sambuc 281433d6423SLionel Sambuc /* Update the proc entry and reschedule the process */ 282433d6423SLionel Sambuc rmp->max_priority = rmp->priority = new_q; 283433d6423SLionel Sambuc 284433d6423SLionel Sambuc if ((rv = schedule_process_local(rmp)) != OK) { 285433d6423SLionel Sambuc /* Something went wrong when rescheduling the process, roll 286433d6423SLionel Sambuc * back the changes to proc struct */ 287433d6423SLionel Sambuc rmp->priority = old_q; 288433d6423SLionel Sambuc rmp->max_priority = old_max_q; 289433d6423SLionel Sambuc } 290433d6423SLionel Sambuc 291433d6423SLionel Sambuc return rv; 292433d6423SLionel Sambuc } 293433d6423SLionel Sambuc 294433d6423SLionel Sambuc /*===========================================================================* 295433d6423SLionel Sambuc * schedule_process * 296433d6423SLionel Sambuc *===========================================================================*/ 297433d6423SLionel Sambuc static int schedule_process(struct schedproc * rmp, unsigned flags) 298433d6423SLionel Sambuc { 299433d6423SLionel Sambuc int err; 300433d6423SLionel Sambuc int new_prio, new_quantum, new_cpu; 301433d6423SLionel Sambuc 302433d6423SLionel Sambuc pick_cpu(rmp); 303433d6423SLionel Sambuc 304433d6423SLionel Sambuc if (flags & SCHEDULE_CHANGE_PRIO) 305433d6423SLionel Sambuc new_prio = rmp->priority; 306433d6423SLionel Sambuc else 307433d6423SLionel Sambuc new_prio = -1; 308433d6423SLionel Sambuc 309433d6423SLionel Sambuc if (flags & SCHEDULE_CHANGE_QUANTUM) 310433d6423SLionel Sambuc new_quantum = rmp->time_slice; 311433d6423SLionel Sambuc else 312433d6423SLionel Sambuc new_quantum = -1; 313433d6423SLionel Sambuc 314433d6423SLionel Sambuc if (flags & SCHEDULE_CHANGE_CPU) 315433d6423SLionel Sambuc new_cpu = rmp->cpu; 316433d6423SLionel Sambuc else 317433d6423SLionel Sambuc new_cpu = -1; 318433d6423SLionel Sambuc 319433d6423SLionel Sambuc if ((err = sys_schedule(rmp->endpoint, new_prio, 320433d6423SLionel Sambuc new_quantum, new_cpu)) != OK) { 321433d6423SLionel Sambuc printf("PM: An error occurred when trying to schedule %d: %d\n", 322433d6423SLionel Sambuc rmp->endpoint, err); 323433d6423SLionel Sambuc } 324433d6423SLionel Sambuc 325433d6423SLionel Sambuc return err; 326433d6423SLionel Sambuc } 327433d6423SLionel Sambuc 328433d6423SLionel Sambuc 329433d6423SLionel Sambuc /*===========================================================================* 330*6c31058dSDavid van Moolenbroek * init_scheduling * 331433d6423SLionel Sambuc *===========================================================================*/ 332433d6423SLionel Sambuc void init_scheduling(void) 333433d6423SLionel Sambuc { 334*6c31058dSDavid van Moolenbroek int r; 335*6c31058dSDavid van Moolenbroek 336433d6423SLionel Sambuc balance_timeout = BALANCE_TIMEOUT * sys_hz(); 337*6c31058dSDavid van Moolenbroek 338*6c31058dSDavid van Moolenbroek if ((r = sys_setalarm(balance_timeout, 0)) != OK) 339*6c31058dSDavid van Moolenbroek panic("sys_setalarm failed: %d", r); 340433d6423SLionel Sambuc } 341433d6423SLionel Sambuc 342433d6423SLionel Sambuc /*===========================================================================* 343433d6423SLionel Sambuc * balance_queues * 344433d6423SLionel Sambuc *===========================================================================*/ 345433d6423SLionel Sambuc 346*6c31058dSDavid van Moolenbroek /* This function in called every N ticks to rebalance the queues. The current 347433d6423SLionel Sambuc * scheduler bumps processes down one priority when ever they run out of 348433d6423SLionel Sambuc * quantum. This function will find all proccesses that have been bumped down, 349433d6423SLionel Sambuc * and pulls them back up. This default policy will soon be changed. 350433d6423SLionel Sambuc */ 351*6c31058dSDavid van Moolenbroek void balance_queues(void) 352433d6423SLionel Sambuc { 353433d6423SLionel Sambuc struct schedproc *rmp; 354*6c31058dSDavid van Moolenbroek int r, proc_nr; 355433d6423SLionel Sambuc 356433d6423SLionel Sambuc for (proc_nr=0, rmp=schedproc; proc_nr < NR_PROCS; proc_nr++, rmp++) { 357433d6423SLionel Sambuc if (rmp->flags & IN_USE) { 358433d6423SLionel Sambuc if (rmp->priority > rmp->max_priority) { 359433d6423SLionel Sambuc rmp->priority -= 1; /* increase priority */ 360433d6423SLionel Sambuc schedule_process_local(rmp); 361433d6423SLionel Sambuc } 362433d6423SLionel Sambuc } 363433d6423SLionel Sambuc } 364433d6423SLionel Sambuc 365*6c31058dSDavid van Moolenbroek if ((r = sys_setalarm(balance_timeout, 0)) != OK) 366*6c31058dSDavid van Moolenbroek panic("sys_setalarm failed: %d", r); 367433d6423SLionel Sambuc } 368