source: mainline/generic/src/proc/scheduler.c@ 10e16a7

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 10e16a7 was 10e16a7, checked in by Ondrej Palkovsky <ondrap@…>, 19 years ago

Added scheduler queues output. The scheduler is buggy - on SMP
the cpus never get tu cpu_sleep, in slab2 test on 4 cpus everything
is on the first cpu.
The slab allocator passes tests in this configuration, but in slightly
different(more efficient) locking order it panics. TODO Find out why
does it panic.

  • Property mode set to 100644
File size: 14.8 KB
RevLine 
[f761f1eb]1/*
2 * Copyright (C) 2001-2004 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <proc/scheduler.h>
30#include <proc/thread.h>
31#include <proc/task.h>
[32ff43e6]32#include <mm/heap.h>
33#include <mm/frame.h>
34#include <mm/page.h>
[20d50a1]35#include <mm/as.h>
[32ff43e6]36#include <arch/asm.h>
37#include <arch/faddr.h>
38#include <arch/atomic.h>
39#include <synch/spinlock.h>
[f761f1eb]40#include <config.h>
41#include <context.h>
42#include <func.h>
43#include <arch.h>
44#include <list.h>
[02a99d2]45#include <panic.h>
[f761f1eb]46#include <typedefs.h>
[32ff43e6]47#include <cpu.h>
[9c0a9b3]48#include <print.h>
[623ba26c]49#include <debug.h>
[9c0a9b3]50
[59e07c91]51atomic_t nrdy;
[f761f1eb]52
[b60a22c]53/** Take actions before new thread runs
[70527f1]54 *
[b60a22c]55 * Perform actions that need to be
56 * taken before the newly selected
57 * tread is passed control.
[70527f1]58 *
59 */
[0ca6faa]60void before_thread_runs(void)
61{
[b49f4ae]62 before_thread_runs_arch();
[5f85c91]63#ifdef CONFIG_FPU_LAZY
[b49f4ae]64 if(THREAD==CPU->fpu_owner)
65 fpu_enable();
66 else
67 fpu_disable();
68#else
69 fpu_enable();
70 if (THREAD->fpu_context_exists)
71 fpu_context_restore(&(THREAD->saved_fpu_context));
72 else {
73 fpu_init();
74 THREAD->fpu_context_exists=1;
75 }
76#endif
[0ca6faa]77}
78
[5f85c91]79#ifdef CONFIG_FPU_LAZY
[b49f4ae]80void scheduler_fpu_lazy_request(void)
81{
82 fpu_enable();
83 if (CPU->fpu_owner != NULL) {
84 fpu_context_save(&CPU->fpu_owner->saved_fpu_context);
85 /* don't prevent migration */
86 CPU->fpu_owner->fpu_context_engaged=0;
87 }
88 if (THREAD->fpu_context_exists)
89 fpu_context_restore(&THREAD->saved_fpu_context);
90 else {
91 fpu_init();
92 THREAD->fpu_context_exists=1;
93 }
94 CPU->fpu_owner=THREAD;
95 THREAD->fpu_context_engaged = 1;
96}
97#endif
[0ca6faa]98
[70527f1]99/** Initialize scheduler
100 *
101 * Initialize kernel scheduler.
102 *
103 */
[f761f1eb]104void scheduler_init(void)
105{
106}
107
[70527f1]108
109/** Get thread to be scheduled
110 *
111 * Get the optimal thread to be scheduled
[d1a184f]112 * according to thread accounting and scheduler
[70527f1]113 * policy.
114 *
115 * @return Thread to be scheduled.
116 *
117 */
[e507afa]118static thread_t *find_best_thread(void)
[f761f1eb]119{
120 thread_t *t;
121 runq_t *r;
122 int i, n;
123
[623ba26c]124 ASSERT(CPU != NULL);
125
[f761f1eb]126loop:
[22f7769]127 interrupts_disable();
[f761f1eb]128
[43114c5]129 spinlock_lock(&CPU->lock);
130 n = CPU->nrdy;
131 spinlock_unlock(&CPU->lock);
[f761f1eb]132
[22f7769]133 interrupts_enable();
[f761f1eb]134
135 if (n == 0) {
[5f85c91]136 #ifdef CONFIG_SMP
[f761f1eb]137 /*
138 * If the load balancing thread is not running, wake it up and
139 * set CPU-private flag that the kcpulb has been started.
140 */
[43114c5]141 if (test_and_set(&CPU->kcpulbstarted) == 0) {
[76cec1e]142 waitq_wakeup(&CPU->kcpulb_wq, 0);
[f761f1eb]143 goto loop;
144 }
[5f85c91]145 #endif /* CONFIG_SMP */
[f761f1eb]146
147 /*
148 * For there was nothing to run, the CPU goes to sleep
149 * until a hardware interrupt or an IPI comes.
150 * This improves energy saving and hyperthreading.
151 * On the other hand, several hardware interrupts can be ignored.
152 */
153 cpu_sleep();
154 goto loop;
155 }
156
[22f7769]157 interrupts_disable();
[d896525]158
159 i = 0;
160retry:
161 for (; i<RQ_COUNT; i++) {
[43114c5]162 r = &CPU->rq[i];
[f761f1eb]163 spinlock_lock(&r->lock);
164 if (r->n == 0) {
165 /*
166 * If this queue is empty, try a lower-priority queue.
167 */
168 spinlock_unlock(&r->lock);
169 continue;
170 }
[3e1607f]171
[18e0a6c]172 /* avoid deadlock with relink_rq() */
[d896525]173 if (!spinlock_trylock(&CPU->lock)) {
174 /*
175 * Unlock r and try again.
176 */
177 spinlock_unlock(&r->lock);
178 goto retry;
179 }
[43114c5]180 CPU->nrdy--;
181 spinlock_unlock(&CPU->lock);
[f761f1eb]182
[59e07c91]183 atomic_dec(&nrdy);
[f761f1eb]184 r->n--;
185
186 /*
187 * Take the first thread from the queue.
188 */
189 t = list_get_instance(r->rq_head.next, thread_t, rq_link);
190 list_remove(&t->rq_link);
191
192 spinlock_unlock(&r->lock);
193
194 spinlock_lock(&t->lock);
[43114c5]195 t->cpu = CPU;
[f761f1eb]196
197 t->ticks = us2ticks((i+1)*10000);
[22f7769]198 t->priority = i; /* eventually correct rq index */
[f761f1eb]199
200 /*
201 * Clear the X_STOLEN flag so that t can be migrated when load balancing needs emerge.
202 */
203 t->flags &= ~X_STOLEN;
204 spinlock_unlock(&t->lock);
205
206 return t;
207 }
208 goto loop;
209
210}
211
[70527f1]212
213/** Prevent rq starvation
214 *
215 * Prevent low priority threads from starving in rq's.
216 *
217 * When the function decides to relink rq's, it reconnects
218 * respective pointers so that in result threads with 'pri'
219 * greater or equal 'start' are moved to a higher-priority queue.
220 *
221 * @param start Threshold priority.
222 *
[f761f1eb]223 */
[e16e036a]224static void relink_rq(int start)
[f761f1eb]225{
226 link_t head;
227 runq_t *r;
228 int i, n;
229
230 list_initialize(&head);
[43114c5]231 spinlock_lock(&CPU->lock);
232 if (CPU->needs_relink > NEEDS_RELINK_MAX) {
[f761f1eb]233 for (i = start; i<RQ_COUNT-1; i++) {
234 /* remember and empty rq[i + 1] */
[43114c5]235 r = &CPU->rq[i + 1];
[f761f1eb]236 spinlock_lock(&r->lock);
237 list_concat(&head, &r->rq_head);
238 n = r->n;
239 r->n = 0;
240 spinlock_unlock(&r->lock);
241
242 /* append rq[i + 1] to rq[i] */
[43114c5]243 r = &CPU->rq[i];
[f761f1eb]244 spinlock_lock(&r->lock);
245 list_concat(&r->rq_head, &head);
246 r->n += n;
247 spinlock_unlock(&r->lock);
248 }
[43114c5]249 CPU->needs_relink = 0;
[f761f1eb]250 }
[43114c5]251 spinlock_unlock(&CPU->lock);
[f761f1eb]252
253}
254
[70527f1]255
256/** Scheduler stack switch wrapper
257 *
258 * Second part of the scheduler() function
259 * using new stack. Handling the actual context
260 * switch to a new thread.
261 *
262 */
[e16e036a]263static void scheduler_separated_stack(void)
[f761f1eb]264{
265 int priority;
266
[623ba26c]267 ASSERT(CPU != NULL);
268
[43114c5]269 if (THREAD) {
270 switch (THREAD->state) {
[f761f1eb]271 case Running:
[76cec1e]272 THREAD->state = Ready;
273 spinlock_unlock(&THREAD->lock);
274 thread_ready(THREAD);
275 break;
[f761f1eb]276
277 case Exiting:
[76cec1e]278 frame_free((__address) THREAD->kstack);
279 if (THREAD->ustack) {
280 frame_free((__address) THREAD->ustack);
281 }
282
283 /*
284 * Detach from the containing task.
285 */
286 spinlock_lock(&TASK->lock);
287 list_remove(&THREAD->th_link);
288 spinlock_unlock(&TASK->lock);
289
290 spinlock_unlock(&THREAD->lock);
291
292 spinlock_lock(&threads_lock);
293 list_remove(&THREAD->threads_link);
294 spinlock_unlock(&threads_lock);
295
296 spinlock_lock(&CPU->lock);
[75e1db0]297 if(CPU->fpu_owner==THREAD)
298 CPU->fpu_owner=NULL;
[76cec1e]299 spinlock_unlock(&CPU->lock);
300
301 free(THREAD);
302
303 break;
304
[f761f1eb]305 case Sleeping:
[76cec1e]306 /*
307 * Prefer the thread after it's woken up.
308 */
[22f7769]309 THREAD->priority = -1;
[76cec1e]310
311 /*
312 * We need to release wq->lock which we locked in waitq_sleep().
313 * Address of wq->lock is kept in THREAD->sleep_queue.
314 */
315 spinlock_unlock(&THREAD->sleep_queue->lock);
316
317 /*
318 * Check for possible requests for out-of-context invocation.
319 */
320 if (THREAD->call_me) {
321 THREAD->call_me(THREAD->call_me_with);
322 THREAD->call_me = NULL;
323 THREAD->call_me_with = NULL;
324 }
325
326 spinlock_unlock(&THREAD->lock);
327
328 break;
[f761f1eb]329
330 default:
[76cec1e]331 /*
332 * Entering state is unexpected.
333 */
334 panic("tid%d: unexpected state %s\n", THREAD->tid, thread_states[THREAD->state]);
335 break;
[f761f1eb]336 }
[43114c5]337 THREAD = NULL;
[f761f1eb]338 }
[ba18512]339
[cd95d784]340
[43114c5]341 THREAD = find_best_thread();
[f761f1eb]342
[43114c5]343 spinlock_lock(&THREAD->lock);
[22f7769]344 priority = THREAD->priority;
[43114c5]345 spinlock_unlock(&THREAD->lock);
[7ce9284]346
[f761f1eb]347 relink_rq(priority);
348
[43114c5]349 spinlock_lock(&THREAD->lock);
[f761f1eb]350
351 /*
352 * If both the old and the new task are the same, lots of work is avoided.
353 */
[43114c5]354 if (TASK != THREAD->task) {
[20d50a1]355 as_t *as1 = NULL;
356 as_t *as2;
[f761f1eb]357
[43114c5]358 if (TASK) {
359 spinlock_lock(&TASK->lock);
[20d50a1]360 as1 = TASK->as;
[43114c5]361 spinlock_unlock(&TASK->lock);
[f761f1eb]362 }
363
[43114c5]364 spinlock_lock(&THREAD->task->lock);
[20d50a1]365 as2 = THREAD->task->as;
[43114c5]366 spinlock_unlock(&THREAD->task->lock);
[f761f1eb]367
368 /*
[20d50a1]369 * Note that it is possible for two tasks to share one address space.
[f761f1eb]370 */
[20d50a1]371 if (as1 != as2) {
[f761f1eb]372 /*
[20d50a1]373 * Both tasks and address spaces are different.
[f761f1eb]374 * Replace the old one with the new one.
375 */
[20d50a1]376 as_install(as2);
[f761f1eb]377 }
[43114c5]378 TASK = THREAD->task;
[f761f1eb]379 }
380
[43114c5]381 THREAD->state = Running;
[f761f1eb]382
383 #ifdef SCHEDULER_VERBOSE
[22f7769]384 printf("cpu%d: tid %d (priority=%d,ticks=%d,nrdy=%d)\n", CPU->id, THREAD->tid, THREAD->priority, THREAD->ticks, CPU->nrdy);
[f761f1eb]385 #endif
386
[3e1607f]387 /*
388 * Copy the knowledge of CPU, TASK, THREAD and preemption counter to thread's stack.
389 */
[bcdd9aa]390 the_copy(THE, (the_t *) THREAD->kstack);
391
[43114c5]392 context_restore(&THREAD->saved_context);
[f761f1eb]393 /* not reached */
394}
395
[70527f1]396
[e16e036a]397/** The scheduler
398 *
399 * The thread scheduling procedure.
[5fe5f1e]400 * Passes control directly to
401 * scheduler_separated_stack().
[e16e036a]402 *
403 */
404void scheduler(void)
405{
406 volatile ipl_t ipl;
407
408 ASSERT(CPU != NULL);
409
410 ipl = interrupts_disable();
411
[36e7ee98]412 if (atomic_get(&haltstate))
[e16e036a]413 halt();
414
415 if (THREAD) {
416 spinlock_lock(&THREAD->lock);
[5f85c91]417#ifndef CONFIG_FPU_LAZY
[e16e036a]418 fpu_context_save(&(THREAD->saved_fpu_context));
419#endif
420 if (!context_save(&THREAD->saved_context)) {
421 /*
422 * This is the place where threads leave scheduler();
423 */
424 before_thread_runs();
425 spinlock_unlock(&THREAD->lock);
426 interrupts_restore(THREAD->saved_context.ipl);
427 return;
428 }
429
430 /*
431 * Interrupt priority level of preempted thread is recorded here
432 * to facilitate scheduler() invocations from interrupts_disable()'d
433 * code (e.g. waitq_sleep_timeout()).
434 */
435 THREAD->saved_context.ipl = ipl;
436 }
437
438 /*
[05e2a7ad]439 * Through the 'THE' structure, we keep track of THREAD, TASK, CPU, VM
[e16e036a]440 * and preemption counter. At this point THE could be coming either
441 * from THREAD's or CPU's stack.
442 */
443 the_copy(THE, (the_t *) CPU->stack);
444
445 /*
446 * We may not keep the old stack.
447 * Reason: If we kept the old stack and got blocked, for instance, in
448 * find_best_thread(), the old thread could get rescheduled by another
449 * CPU and overwrite the part of its own stack that was also used by
450 * the scheduler on this CPU.
451 *
452 * Moreover, we have to bypass the compiler-generated POP sequence
453 * which is fooled by SP being set to the very top of the stack.
454 * Therefore the scheduler() function continues in
455 * scheduler_separated_stack().
456 */
457 context_save(&CPU->saved_context);
458 context_set(&CPU->saved_context, FADDR(scheduler_separated_stack), (__address) CPU->stack, CPU_STACK_SIZE);
459 context_restore(&CPU->saved_context);
460 /* not reached */
461}
462
463
464
465
466
[5f85c91]467#ifdef CONFIG_SMP
[70527f1]468/** Load balancing thread
469 *
470 * SMP load balancing thread, supervising thread supplies
471 * for the CPU it's wired to.
472 *
473 * @param arg Generic thread argument (unused).
474 *
[f761f1eb]475 */
476void kcpulb(void *arg)
477{
478 thread_t *t;
479 int count, i, j, k = 0;
[22f7769]480 ipl_t ipl;
[f761f1eb]481
482loop:
483 /*
484 * Sleep until there's some work to do.
485 */
[43114c5]486 waitq_sleep(&CPU->kcpulb_wq);
[f761f1eb]487
488not_satisfied:
489 /*
490 * Calculate the number of threads that will be migrated/stolen from
491 * other CPU's. Note that situation can have changed between two
492 * passes. Each time get the most up to date counts.
493 */
[22f7769]494 ipl = interrupts_disable();
[43114c5]495 spinlock_lock(&CPU->lock);
[80d2bdb]496 count = atomic_get(&nrdy) / config.cpu_active;
[43114c5]497 count -= CPU->nrdy;
498 spinlock_unlock(&CPU->lock);
[22f7769]499 interrupts_restore(ipl);
[f761f1eb]500
501 if (count <= 0)
502 goto satisfied;
503
504 /*
505 * Searching least priority queues on all CPU's first and most priority queues on all CPU's last.
506 */
507 for (j=RQ_COUNT-1; j >= 0; j--) {
508 for (i=0; i < config.cpu_active; i++) {
509 link_t *l;
510 runq_t *r;
511 cpu_t *cpu;
512
513 cpu = &cpus[(i + k) % config.cpu_active];
514
515 /*
516 * Not interested in ourselves.
517 * Doesn't require interrupt disabling for kcpulb is X_WIRED.
518 */
[43114c5]519 if (CPU == cpu)
[18e0a6c]520 continue;
[f761f1eb]521
[22f7769]522restart: ipl = interrupts_disable();
[18e0a6c]523 r = &cpu->rq[j];
[f761f1eb]524 spinlock_lock(&r->lock);
525 if (r->n == 0) {
526 spinlock_unlock(&r->lock);
[22f7769]527 interrupts_restore(ipl);
[f761f1eb]528 continue;
529 }
530
531 t = NULL;
532 l = r->rq_head.prev; /* search rq from the back */
533 while (l != &r->rq_head) {
534 t = list_get_instance(l, thread_t, rq_link);
535 /*
[76cec1e]536 * We don't want to steal CPU-wired threads neither threads already stolen.
[f761f1eb]537 * The latter prevents threads from migrating between CPU's without ever being run.
[76cec1e]538 * We don't want to steal threads whose FPU context is still in CPU.
[6a27d63]539 */
[f761f1eb]540 spinlock_lock(&t->lock);
[6a27d63]541 if ( (!(t->flags & (X_WIRED | X_STOLEN))) && (!(t->fpu_context_engaged)) ) {
[18e0a6c]542
[f761f1eb]543 /*
544 * Remove t from r.
545 */
546
547 spinlock_unlock(&t->lock);
548
549 /*
550 * Here we have to avoid deadlock with relink_rq(),
551 * because it locks cpu and r in a different order than we do.
552 */
553 if (!spinlock_trylock(&cpu->lock)) {
554 /* Release all locks and try again. */
555 spinlock_unlock(&r->lock);
[22f7769]556 interrupts_restore(ipl);
[f761f1eb]557 goto restart;
558 }
559 cpu->nrdy--;
560 spinlock_unlock(&cpu->lock);
561
[59e07c91]562 atomic_dec(&nrdy);
[f761f1eb]563
[76cec1e]564 r->n--;
[f761f1eb]565 list_remove(&t->rq_link);
566
567 break;
568 }
569 spinlock_unlock(&t->lock);
570 l = l->prev;
571 t = NULL;
572 }
573 spinlock_unlock(&r->lock);
574
575 if (t) {
576 /*
577 * Ready t on local CPU
578 */
579 spinlock_lock(&t->lock);
580 #ifdef KCPULB_VERBOSE
[43114c5]581 printf("kcpulb%d: TID %d -> cpu%d, nrdy=%d, avg=%d\n", CPU->id, t->tid, CPU->id, CPU->nrdy, nrdy / config.cpu_active);
[f761f1eb]582 #endif
583 t->flags |= X_STOLEN;
584 spinlock_unlock(&t->lock);
585
586 thread_ready(t);
587
[22f7769]588 interrupts_restore(ipl);
[f761f1eb]589
590 if (--count == 0)
591 goto satisfied;
592
593 /*
[76cec1e]594 * We are not satisfied yet, focus on another CPU next time.
[f761f1eb]595 */
596 k++;
597
598 continue;
599 }
[22f7769]600 interrupts_restore(ipl);
[f761f1eb]601 }
602 }
603
[43114c5]604 if (CPU->nrdy) {
[f761f1eb]605 /*
606 * Be a little bit light-weight and let migrated threads run.
607 */
608 scheduler();
609 }
610 else {
611 /*
612 * We failed to migrate a single thread.
613 * Something more sophisticated should be done.
614 */
615 scheduler();
616 }
617
618 goto not_satisfied;
[76cec1e]619
[f761f1eb]620satisfied:
621 /*
622 * Tell find_best_thread() to wake us up later again.
623 */
[80d2bdb]624 atomic_set(&CPU->kcpulbstarted,0);
[f761f1eb]625 goto loop;
626}
627
[5f85c91]628#endif /* CONFIG_SMP */
[10e16a7]629
630
631/** Print information about threads & scheduler queues */
632void sched_print_list(void)
633{
634 ipl_t ipl;
635 int cpu,i;
636 runq_t *r;
637 thread_t *t;
638 link_t *cur;
639
640 /* We are going to mess with scheduler structures,
641 * let's not be interrupted */
642 ipl = interrupts_disable();
643 printf("*********** Scheduler dump ***********\n");
644 for (cpu=0;cpu < config.cpu_count; cpu++) {
645 if (!cpus[cpu].active)
646 continue;
647 spinlock_lock(&cpus[cpu].lock);
648 printf("cpu%d: nrdy: %d needs_relink: %d\n",
649 cpus[cpu].id, cpus[cpu].nrdy, cpus[cpu].needs_relink);
650
651 for (i=0; i<RQ_COUNT; i++) {
652 r = &cpus[cpu].rq[i];
653 spinlock_lock(&r->lock);
654 if (!r->n) {
655 spinlock_unlock(&r->lock);
656 continue;
657 }
658 printf("Rq %d: ", i);
659 for (cur=r->rq_head.next; cur!=&r->rq_head; cur=cur->next) {
660 t = list_get_instance(cur, thread_t, rq_link);
661 printf("%d(%s) ", t->tid,
662 thread_states[t->state]);
663 }
664 printf("\n");
665 spinlock_unlock(&r->lock);
666 }
667 spinlock_unlock(&cpus[cpu].lock);
668 }
669
670 interrupts_restore(ipl);
671}
Note: See TracBrowser for help on using the repository browser.