source: mainline/kernel/generic/src/proc/scheduler.c@ 314f4b59

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 314f4b59 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 17.5 KB
RevLine 
[f761f1eb]1/*
[481d4751]2 * Copyright (c) 2010 Jakub Jermar
[f761f1eb]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
[cc73a8a1]29/** @addtogroup genericproc
[b45c443]30 * @{
31 */
32
[9179d0a]33/**
[b45c443]34 * @file
[da1bafb]35 * @brief Scheduler and load balancing.
[9179d0a]36 *
[cf26ba9]37 * This file contains the scheduler and kcpulb kernel thread which
[9179d0a]38 * performs load-balancing of per-CPU run queues.
39 */
40
[63e27ef]41#include <assert.h>
[f761f1eb]42#include <proc/scheduler.h>
43#include <proc/thread.h>
44#include <proc/task.h>
[32ff43e6]45#include <mm/frame.h>
46#include <mm/page.h>
[20d50a1]47#include <mm/as.h>
[b3f8fb7]48#include <time/timeout.h>
[fe19611]49#include <time/delay.h>
[32ff43e6]50#include <arch/asm.h>
51#include <arch/faddr.h>
[cce6acf]52#include <arch/cycle.h>
[23684b7]53#include <atomic.h>
[32ff43e6]54#include <synch/spinlock.h>
[8a64e81e]55#include <synch/workqueue.h>
[181a746]56#include <synch/rcu.h>
[f761f1eb]57#include <config.h>
58#include <context.h>
[b3f8fb7]59#include <fpu_context.h>
[b2e121a]60#include <halt.h>
[f761f1eb]61#include <arch.h>
[5c9a08b]62#include <adt/list.h>
[02a99d2]63#include <panic.h>
[32ff43e6]64#include <cpu.h>
[9c0a9b3]65#include <print.h>
[b2fa1204]66#include <log.h>
[df58e44]67#include <stacktrace.h>
[9c0a9b3]68
[7d6ec87]69static void scheduler_separated_stack(void);
70
[da1bafb]71atomic_t nrdy; /**< Number of ready threads in the system. */
[f761f1eb]72
[39cea6a]73/** Carry out actions before new task runs. */
[4e7d3dd]74static void before_task_runs(void)
[39cea6a]75{
76 before_task_runs_arch();
77}
78
[97f1691]79/** Take actions before new thread runs.
[70527f1]80 *
[b60a22c]81 * Perform actions that need to be
82 * taken before the newly selected
[df58e44]83 * thread is passed control.
[70527f1]84 *
[a3eeceb6]85 * THREAD->lock is locked on entry
86 *
[70527f1]87 */
[4e7d3dd]88static void before_thread_runs(void)
[0ca6faa]89{
[b49f4ae]90 before_thread_runs_arch();
[181a746]91 rcu_before_thread_runs();
[a35b458]92
[f76fed4]93#ifdef CONFIG_FPU_LAZY
[df58e44]94 if (THREAD == CPU->fpu_owner)
[b49f4ae]95 fpu_enable();
96 else
[da1bafb]97 fpu_disable();
[e1326cf]98#elif defined CONFIG_FPU
[b49f4ae]99 fpu_enable();
100 if (THREAD->fpu_context_exists)
[f76fed4]101 fpu_context_restore(THREAD->saved_fpu_context);
[b49f4ae]102 else {
[f76fed4]103 fpu_init();
[6eef3c4]104 THREAD->fpu_context_exists = true;
[b49f4ae]105 }
[f76fed4]106#endif
[a35b458]107
[5b7a107]108#ifdef CONFIG_UDEBUG
[df58e44]109 if (THREAD->btrace) {
110 istate_t *istate = THREAD->udebug.uspace_state;
111 if (istate != NULL) {
112 printf("Thread %" PRIu64 " stack trace:\n", THREAD->tid);
113 stack_trace_istate(istate);
114 }
[a35b458]115
[df58e44]116 THREAD->btrace = false;
117 }
[5b7a107]118#endif
[0ca6faa]119}
120
[7d6ec87]121/** Take actions after THREAD had run.
[97f1691]122 *
123 * Perform actions that need to be
124 * taken after the running thread
[7d6ec87]125 * had been preempted by the scheduler.
[97f1691]126 *
127 * THREAD->lock is locked on entry
128 *
129 */
[4e7d3dd]130static void after_thread_ran(void)
[97f1691]131{
[8a64e81e]132 workq_after_thread_ran();
[181a746]133 rcu_after_thread_ran();
[97f1691]134 after_thread_ran_arch();
135}
136
[5f85c91]137#ifdef CONFIG_FPU_LAZY
[b49f4ae]138void scheduler_fpu_lazy_request(void)
139{
[09c18f7]140restart:
[b49f4ae]141 fpu_enable();
[da1bafb]142 irq_spinlock_lock(&CPU->lock, false);
[a35b458]143
[a3eeceb6]144 /* Save old context */
[da1bafb]145 if (CPU->fpu_owner != NULL) {
146 irq_spinlock_lock(&CPU->fpu_owner->lock, false);
[f76fed4]147 fpu_context_save(CPU->fpu_owner->saved_fpu_context);
[a35b458]148
[da1bafb]149 /* Don't prevent migration */
[6eef3c4]150 CPU->fpu_owner->fpu_context_engaged = false;
[da1bafb]151 irq_spinlock_unlock(&CPU->fpu_owner->lock, false);
[09c18f7]152 CPU->fpu_owner = NULL;
[b49f4ae]153 }
[a35b458]154
[da1bafb]155 irq_spinlock_lock(&THREAD->lock, false);
[7d6ec87]156 if (THREAD->fpu_context_exists) {
[f76fed4]157 fpu_context_restore(THREAD->saved_fpu_context);
[7d6ec87]158 } else {
[f76fed4]159 /* Allocate FPU context */
160 if (!THREAD->saved_fpu_context) {
161 /* Might sleep */
[da1bafb]162 irq_spinlock_unlock(&THREAD->lock, false);
163 irq_spinlock_unlock(&CPU->lock, false);
[4e33b6b]164 THREAD->saved_fpu_context =
[82d515e9]165 (fpu_context_t *) slab_alloc(fpu_context_cache, 0);
[a35b458]166
[09c18f7]167 /* We may have switched CPUs during slab_alloc */
[da1bafb]168 goto restart;
[f76fed4]169 }
170 fpu_init();
[6eef3c4]171 THREAD->fpu_context_exists = true;
[b49f4ae]172 }
[a35b458]173
[6eabb6e6]174 CPU->fpu_owner = THREAD;
[6eef3c4]175 THREAD->fpu_context_engaged = true;
[da1bafb]176 irq_spinlock_unlock(&THREAD->lock, false);
[a35b458]177
[da1bafb]178 irq_spinlock_unlock(&CPU->lock, false);
[b49f4ae]179}
[da1bafb]180#endif /* CONFIG_FPU_LAZY */
[0ca6faa]181
[70527f1]182/** Initialize scheduler
183 *
184 * Initialize kernel scheduler.
185 *
186 */
[f761f1eb]187void scheduler_init(void)
188{
189}
190
[70527f1]191/** Get thread to be scheduled
192 *
193 * Get the optimal thread to be scheduled
[d1a184f]194 * according to thread accounting and scheduler
[70527f1]195 * policy.
196 *
197 * @return Thread to be scheduled.
198 *
199 */
[e507afa]200static thread_t *find_best_thread(void)
[f761f1eb]201{
[63e27ef]202 assert(CPU != NULL);
[a35b458]203
[f761f1eb]204loop:
[a35b458]205
[248fc1a]206 if (atomic_get(&CPU->nrdy) == 0) {
[f761f1eb]207 /*
208 * For there was nothing to run, the CPU goes to sleep
209 * until a hardware interrupt or an IPI comes.
210 * This improves energy saving and hyperthreading.
211 */
[da1bafb]212 irq_spinlock_lock(&CPU->lock, false);
213 CPU->idle = true;
214 irq_spinlock_unlock(&CPU->lock, false);
215 interrupts_enable();
[a35b458]216
[da1bafb]217 /*
[328e0d3]218 * An interrupt might occur right now and wake up a thread.
219 * In such case, the CPU will continue to go to sleep
220 * even though there is a runnable thread.
221 */
[da1bafb]222 cpu_sleep();
223 interrupts_disable();
224 goto loop;
[f761f1eb]225 }
[181a746]226
[63e27ef]227 assert(!CPU->idle);
[a35b458]228
[da1bafb]229 unsigned int i;
[ea63704]230 for (i = 0; i < RQ_COUNT; i++) {
[da1bafb]231 irq_spinlock_lock(&(CPU->rq[i].lock), false);
232 if (CPU->rq[i].n == 0) {
[f761f1eb]233 /*
234 * If this queue is empty, try a lower-priority queue.
235 */
[da1bafb]236 irq_spinlock_unlock(&(CPU->rq[i].lock), false);
[f761f1eb]237 continue;
238 }
[a35b458]239
[248fc1a]240 atomic_dec(&CPU->nrdy);
[59e07c91]241 atomic_dec(&nrdy);
[da1bafb]242 CPU->rq[i].n--;
[a35b458]243
[f761f1eb]244 /*
245 * Take the first thread from the queue.
246 */
[55b77d9]247 thread_t *thread = list_get_instance(
248 list_first(&CPU->rq[i].rq), thread_t, rq_link);
[da1bafb]249 list_remove(&thread->rq_link);
[a35b458]250
[da1bafb]251 irq_spinlock_pass(&(CPU->rq[i].lock), &thread->lock);
[a35b458]252
[da1bafb]253 thread->cpu = CPU;
254 thread->ticks = us2ticks((i + 1) * 10000);
255 thread->priority = i; /* Correct rq index */
[a35b458]256
[f761f1eb]257 /*
[6eef3c4]258 * Clear the stolen flag so that it can be migrated
[32fffef0]259 * when load balancing needs emerge.
[f761f1eb]260 */
[6eef3c4]261 thread->stolen = false;
[da1bafb]262 irq_spinlock_unlock(&thread->lock, false);
[a35b458]263
[da1bafb]264 return thread;
[f761f1eb]265 }
[a35b458]266
[f761f1eb]267 goto loop;
268}
269
[70527f1]270/** Prevent rq starvation
271 *
272 * Prevent low priority threads from starving in rq's.
273 *
274 * When the function decides to relink rq's, it reconnects
275 * respective pointers so that in result threads with 'pri'
[abbc16e]276 * greater or equal start are moved to a higher-priority queue.
[70527f1]277 *
278 * @param start Threshold priority.
279 *
[f761f1eb]280 */
[e16e036a]281static void relink_rq(int start)
[f761f1eb]282{
[55b77d9]283 list_t list;
[a35b458]284
[55b77d9]285 list_initialize(&list);
[da1bafb]286 irq_spinlock_lock(&CPU->lock, false);
[a35b458]287
[43114c5]288 if (CPU->needs_relink > NEEDS_RELINK_MAX) {
[da1bafb]289 int i;
[4e33b6b]290 for (i = start; i < RQ_COUNT - 1; i++) {
[da1bafb]291 /* Remember and empty rq[i + 1] */
[a35b458]292
[da1bafb]293 irq_spinlock_lock(&CPU->rq[i + 1].lock, false);
[55b77d9]294 list_concat(&list, &CPU->rq[i + 1].rq);
[da1bafb]295 size_t n = CPU->rq[i + 1].n;
296 CPU->rq[i + 1].n = 0;
297 irq_spinlock_unlock(&CPU->rq[i + 1].lock, false);
[a35b458]298
[da1bafb]299 /* Append rq[i + 1] to rq[i] */
[a35b458]300
[da1bafb]301 irq_spinlock_lock(&CPU->rq[i].lock, false);
[55b77d9]302 list_concat(&CPU->rq[i].rq, &list);
[da1bafb]303 CPU->rq[i].n += n;
304 irq_spinlock_unlock(&CPU->rq[i].lock, false);
[f761f1eb]305 }
[a35b458]306
[43114c5]307 CPU->needs_relink = 0;
[f761f1eb]308 }
[a35b458]309
[da1bafb]310 irq_spinlock_unlock(&CPU->lock, false);
[f761f1eb]311}
312
[7d6ec87]313/** The scheduler
314 *
315 * The thread scheduling procedure.
316 * Passes control directly to
317 * scheduler_separated_stack().
318 *
319 */
320void scheduler(void)
321{
322 volatile ipl_t ipl;
[a35b458]323
[63e27ef]324 assert(CPU != NULL);
[a35b458]325
[7d6ec87]326 ipl = interrupts_disable();
[a35b458]327
[7d6ec87]328 if (atomic_get(&haltstate))
329 halt();
[a35b458]330
[7d6ec87]331 if (THREAD) {
[da1bafb]332 irq_spinlock_lock(&THREAD->lock, false);
[a35b458]333
[1ba37fa]334 /* Update thread kernel accounting */
[a2a00e8]335 THREAD->kcycles += get_cycle() - THREAD->last_cycle;
[a35b458]336
[e1326cf]337#if (defined CONFIG_FPU) && (!defined CONFIG_FPU_LAZY)
[f76fed4]338 fpu_context_save(THREAD->saved_fpu_context);
339#endif
[7d6ec87]340 if (!context_save(&THREAD->saved_context)) {
341 /*
342 * This is the place where threads leave scheduler();
343 */
[a35b458]344
[cce6acf]345 /* Save current CPU cycle */
346 THREAD->last_cycle = get_cycle();
[a35b458]347
[da1bafb]348 irq_spinlock_unlock(&THREAD->lock, false);
[7d6ec87]349 interrupts_restore(THREAD->saved_context.ipl);
[a35b458]350
[7d6ec87]351 return;
352 }
[a35b458]353
[7d6ec87]354 /*
[4e33b6b]355 * Interrupt priority level of preempted thread is recorded
356 * here to facilitate scheduler() invocations from
[da1bafb]357 * interrupts_disable()'d code (e.g. waitq_sleep_timeout()).
358 *
[7d6ec87]359 */
360 THREAD->saved_context.ipl = ipl;
361 }
[a35b458]362
[7d6ec87]363 /*
[b4dc35a]364 * Through the 'THE' structure, we keep track of THREAD, TASK, CPU, AS
[7d6ec87]365 * and preemption counter. At this point THE could be coming either
366 * from THREAD's or CPU's stack.
[da1bafb]367 *
[7d6ec87]368 */
369 the_copy(THE, (the_t *) CPU->stack);
[a35b458]370
[7d6ec87]371 /*
372 * We may not keep the old stack.
373 * Reason: If we kept the old stack and got blocked, for instance, in
374 * find_best_thread(), the old thread could get rescheduled by another
375 * CPU and overwrite the part of its own stack that was also used by
376 * the scheduler on this CPU.
377 *
378 * Moreover, we have to bypass the compiler-generated POP sequence
379 * which is fooled by SP being set to the very top of the stack.
380 * Therefore the scheduler() function continues in
381 * scheduler_separated_stack().
[da1bafb]382 *
[7d6ec87]383 */
384 context_save(&CPU->saved_context);
[32fffef0]385 context_set(&CPU->saved_context, FADDR(scheduler_separated_stack),
[26aafe8]386 (uintptr_t) CPU->stack, STACK_SIZE);
[7d6ec87]387 context_restore(&CPU->saved_context);
[a35b458]388
[da1bafb]389 /* Not reached */
[7d6ec87]390}
[70527f1]391
392/** Scheduler stack switch wrapper
393 *
394 * Second part of the scheduler() function
395 * using new stack. Handling the actual context
396 * switch to a new thread.
397 *
398 */
[7d6ec87]399void scheduler_separated_stack(void)
[f761f1eb]400{
[31d8e10]401 DEADLOCK_PROBE_INIT(p_joinwq);
[481d4751]402 task_t *old_task = TASK;
403 as_t *old_as = AS;
[a35b458]404
[63e27ef]405 assert((!THREAD) || (irq_spinlock_locked(&THREAD->lock)));
406 assert(CPU != NULL);
407 assert(interrupts_disabled());
[a35b458]408
[481d4751]409 /*
410 * Hold the current task and the address space to prevent their
411 * possible destruction should thread_destroy() be called on this or any
412 * other processor while the scheduler is still using them.
413 */
414 if (old_task)
415 task_hold(old_task);
[a35b458]416
[481d4751]417 if (old_as)
418 as_hold(old_as);
[a35b458]419
[43114c5]420 if (THREAD) {
[da1bafb]421 /* Must be run after the switch to scheduler stack */
[97f1691]422 after_thread_ran();
[a35b458]423
[43114c5]424 switch (THREAD->state) {
[06e1e95]425 case Running:
[da1bafb]426 irq_spinlock_unlock(&THREAD->lock, false);
[76cec1e]427 thread_ready(THREAD);
428 break;
[a35b458]429
[06e1e95]430 case Exiting:
[181a746]431 rcu_thread_exiting();
[fe19611]432repeat:
[def5207]433 if (THREAD->detached) {
[da1bafb]434 thread_destroy(THREAD, false);
[fe19611]435 } else {
436 /*
[4e33b6b]437 * The thread structure is kept allocated until
438 * somebody calls thread_detach() on it.
[fe19611]439 */
[da1bafb]440 if (!irq_spinlock_trylock(&THREAD->join_wq.lock)) {
[fe19611]441 /*
442 * Avoid deadlock.
443 */
[da1bafb]444 irq_spinlock_unlock(&THREAD->lock, false);
[ea7890e7]445 delay(HZ);
[da1bafb]446 irq_spinlock_lock(&THREAD->lock, false);
[31d8e10]447 DEADLOCK_PROBE(p_joinwq,
448 DEADLOCK_THRESHOLD);
[fe19611]449 goto repeat;
450 }
[5c8ba05]451 _waitq_wakeup_unsafe(&THREAD->join_wq,
452 WAKEUP_FIRST);
[da1bafb]453 irq_spinlock_unlock(&THREAD->join_wq.lock, false);
[a35b458]454
[48d14222]455 THREAD->state = Lingering;
[da1bafb]456 irq_spinlock_unlock(&THREAD->lock, false);
[fe19611]457 }
[76cec1e]458 break;
[a35b458]459
[06e1e95]460 case Sleeping:
[76cec1e]461 /*
462 * Prefer the thread after it's woken up.
463 */
[22f7769]464 THREAD->priority = -1;
[a35b458]465
[76cec1e]466 /*
[4e33b6b]467 * We need to release wq->lock which we locked in
468 * waitq_sleep(). Address of wq->lock is kept in
469 * THREAD->sleep_queue.
[76cec1e]470 */
[da1bafb]471 irq_spinlock_unlock(&THREAD->sleep_queue->lock, false);
[a35b458]472
[da1bafb]473 irq_spinlock_unlock(&THREAD->lock, false);
[76cec1e]474 break;
[a35b458]475
[06e1e95]476 default:
[76cec1e]477 /*
478 * Entering state is unexpected.
479 */
[f651e80]480 panic("tid%" PRIu64 ": unexpected state %s.",
[1e9d0e3]481 THREAD->tid, thread_states[THREAD->state]);
[76cec1e]482 break;
[f761f1eb]483 }
[a35b458]484
[43114c5]485 THREAD = NULL;
[f761f1eb]486 }
[a35b458]487
[43114c5]488 THREAD = find_best_thread();
[a35b458]489
[da1bafb]490 irq_spinlock_lock(&THREAD->lock, false);
491 int priority = THREAD->priority;
492 irq_spinlock_unlock(&THREAD->lock, false);
[a35b458]493
[da1bafb]494 relink_rq(priority);
[a35b458]495
[f761f1eb]496 /*
[4e7d3dd]497 * If both the old and the new task are the same,
498 * lots of work is avoided.
[f761f1eb]499 */
[43114c5]500 if (TASK != THREAD->task) {
[481d4751]501 as_t *new_as = THREAD->task->as;
[a35b458]502
[f761f1eb]503 /*
[4e7d3dd]504 * Note that it is possible for two tasks
505 * to share one address space.
[f761f1eb]506 */
[481d4751]507 if (old_as != new_as) {
[f761f1eb]508 /*
[20d50a1]509 * Both tasks and address spaces are different.
[f761f1eb]510 * Replace the old one with the new one.
511 */
[481d4751]512 as_switch(old_as, new_as);
[f761f1eb]513 }
[a35b458]514
[f76fed4]515 TASK = THREAD->task;
[39cea6a]516 before_task_runs();
[f761f1eb]517 }
[a35b458]518
[481d4751]519 if (old_task)
520 task_release(old_task);
[a35b458]521
[481d4751]522 if (old_as)
523 as_release(old_as);
[a35b458]524
[da1bafb]525 irq_spinlock_lock(&THREAD->lock, false);
[43114c5]526 THREAD->state = Running;
[a35b458]527
[f76fed4]528#ifdef SCHEDULER_VERBOSE
[b2fa1204]529 log(LF_OTHER, LVL_DEBUG,
530 "cpu%u: tid %" PRIu64 " (priority=%d, ticks=%" PRIu64
531 ", nrdy=%" PRIua ")", CPU->id, THREAD->tid, THREAD->priority,
[1e9d0e3]532 THREAD->ticks, atomic_get(&CPU->nrdy));
[da1bafb]533#endif
[a35b458]534
[97f1691]535 /*
536 * Some architectures provide late kernel PA2KA(identity)
537 * mapping in a page fault handler. However, the page fault
538 * handler uses the kernel stack of the running thread and
539 * therefore cannot be used to map it. The kernel stack, if
540 * necessary, is to be mapped in before_thread_runs(). This
541 * function must be executed before the switch to the new stack.
542 */
543 before_thread_runs();
[a35b458]544
[3e1607f]545 /*
[4e33b6b]546 * Copy the knowledge of CPU, TASK, THREAD and preemption counter to
547 * thread's stack.
[3e1607f]548 */
[bcdd9aa]549 the_copy(THE, (the_t *) THREAD->kstack);
[a35b458]550
[43114c5]551 context_restore(&THREAD->saved_context);
[a35b458]552
[da1bafb]553 /* Not reached */
[f761f1eb]554}
555
[5f85c91]556#ifdef CONFIG_SMP
[70527f1]557/** Load balancing thread
558 *
559 * SMP load balancing thread, supervising thread supplies
560 * for the CPU it's wired to.
561 *
562 * @param arg Generic thread argument (unused).
563 *
[f761f1eb]564 */
565void kcpulb(void *arg)
566{
[228666c]567 atomic_count_t average;
[da1bafb]568 atomic_count_t rdy;
[a35b458]569
[2cb5e64]570 /*
571 * Detach kcpulb as nobody will call thread_join_timeout() on it.
572 */
573 thread_detach(THREAD);
[a35b458]574
[f761f1eb]575loop:
576 /*
[3260ada]577 * Work in 1s intervals.
[f761f1eb]578 */
[3260ada]579 thread_sleep(1);
[a35b458]580
[f761f1eb]581not_satisfied:
582 /*
583 * Calculate the number of threads that will be migrated/stolen from
584 * other CPU's. Note that situation can have changed between two
585 * passes. Each time get the most up to date counts.
[da1bafb]586 *
[f761f1eb]587 */
[444ec64]588 average = atomic_get(&nrdy) / config.cpu_active + 1;
[da1bafb]589 rdy = atomic_get(&CPU->nrdy);
[a35b458]590
[da1bafb]591 if (average <= rdy)
[f761f1eb]592 goto satisfied;
[a35b458]593
[da1bafb]594 atomic_count_t count = average - rdy;
[a35b458]595
[f761f1eb]596 /*
[4e33b6b]597 * Searching least priority queues on all CPU's first and most priority
598 * queues on all CPU's last.
[f761f1eb]599 */
[da1bafb]600 size_t acpu;
601 size_t acpu_bias = 0;
602 int rq;
[a35b458]603
[da1bafb]604 for (rq = RQ_COUNT - 1; rq >= 0; rq--) {
605 for (acpu = 0; acpu < config.cpu_active; acpu++) {
606 cpu_t *cpu = &cpus[(acpu + acpu_bias) % config.cpu_active];
[a35b458]607
[f761f1eb]608 /*
609 * Not interested in ourselves.
[4e33b6b]610 * Doesn't require interrupt disabling for kcpulb has
611 * THREAD_FLAG_WIRED.
[da1bafb]612 *
[f761f1eb]613 */
[43114c5]614 if (CPU == cpu)
[248fc1a]615 continue;
[a35b458]616
[248fc1a]617 if (atomic_get(&cpu->nrdy) <= average)
618 continue;
[a35b458]619
[da1bafb]620 irq_spinlock_lock(&(cpu->rq[rq].lock), true);
621 if (cpu->rq[rq].n == 0) {
622 irq_spinlock_unlock(&(cpu->rq[rq].lock), true);
[f761f1eb]623 continue;
624 }
[a35b458]625
[da1bafb]626 thread_t *thread = NULL;
[a35b458]627
[da1bafb]628 /* Search rq from the back */
[55b77d9]629 link_t *link = cpu->rq[rq].rq.head.prev;
[a35b458]630
[55b77d9]631 while (link != &(cpu->rq[rq].rq.head)) {
[43ac0cc]632 thread = (thread_t *) list_get_instance(link,
633 thread_t, rq_link);
[a35b458]634
[f761f1eb]635 /*
[43ac0cc]636 * Do not steal CPU-wired threads, threads
637 * already stolen, threads for which migration
638 * was temporarily disabled or threads whose
639 * FPU context is still in the CPU.
[6a27d63]640 */
[da1bafb]641 irq_spinlock_lock(&thread->lock, false);
[a35b458]642
[6eef3c4]643 if ((!thread->wired) && (!thread->stolen) &&
644 (!thread->nomigrate) &&
645 (!thread->fpu_context_engaged)) {
[f761f1eb]646 /*
[da1bafb]647 * Remove thread from ready queue.
[f761f1eb]648 */
[43ac0cc]649 irq_spinlock_unlock(&thread->lock,
650 false);
[a35b458]651
[248fc1a]652 atomic_dec(&cpu->nrdy);
[59e07c91]653 atomic_dec(&nrdy);
[a35b458]654
[da1bafb]655 cpu->rq[rq].n--;
656 list_remove(&thread->rq_link);
[a35b458]657
[f761f1eb]658 break;
659 }
[a35b458]660
[da1bafb]661 irq_spinlock_unlock(&thread->lock, false);
[a35b458]662
[da1bafb]663 link = link->prev;
664 thread = NULL;
[f761f1eb]665 }
[a35b458]666
[da1bafb]667 if (thread) {
[f761f1eb]668 /*
[da1bafb]669 * Ready thread on local CPU
[f761f1eb]670 */
[a35b458]671
[43ac0cc]672 irq_spinlock_pass(&(cpu->rq[rq].lock),
673 &thread->lock);
[a35b458]674
[f76fed4]675#ifdef KCPULB_VERBOSE
[b2fa1204]676 log(LF_OTHER, LVL_DEBUG,
677 "kcpulb%u: TID %" PRIu64 " -> cpu%u, "
678 "nrdy=%ld, avg=%ld", CPU->id, t->tid,
[1e9d0e3]679 CPU->id, atomic_get(&CPU->nrdy),
[6f4495f5]680 atomic_get(&nrdy) / config.cpu_active);
[f76fed4]681#endif
[a35b458]682
[6eef3c4]683 thread->stolen = true;
[da1bafb]684 thread->state = Entering;
[a35b458]685
[da1bafb]686 irq_spinlock_unlock(&thread->lock, true);
687 thread_ready(thread);
[a35b458]688
[f761f1eb]689 if (--count == 0)
690 goto satisfied;
[a35b458]691
[f761f1eb]692 /*
[4e33b6b]693 * We are not satisfied yet, focus on another
694 * CPU next time.
[da1bafb]695 *
[f761f1eb]696 */
[da1bafb]697 acpu_bias++;
[a35b458]698
[f761f1eb]699 continue;
[da1bafb]700 } else
701 irq_spinlock_unlock(&(cpu->rq[rq].lock), true);
[a35b458]702
[f761f1eb]703 }
704 }
[a35b458]705
[248fc1a]706 if (atomic_get(&CPU->nrdy)) {
[f761f1eb]707 /*
708 * Be a little bit light-weight and let migrated threads run.
[da1bafb]709 *
[f761f1eb]710 */
711 scheduler();
[3260ada]712 } else {
[f761f1eb]713 /*
714 * We failed to migrate a single thread.
[3260ada]715 * Give up this turn.
[da1bafb]716 *
[f761f1eb]717 */
[3260ada]718 goto loop;
[f761f1eb]719 }
[a35b458]720
[f761f1eb]721 goto not_satisfied;
[a35b458]722
[f761f1eb]723satisfied:
724 goto loop;
725}
[5f85c91]726#endif /* CONFIG_SMP */
[10e16a7]727
[da1bafb]728/** Print information about threads & scheduler queues
729 *
730 */
[10e16a7]731void sched_print_list(void)
732{
[da1bafb]733 size_t cpu;
[4184e76]734 for (cpu = 0; cpu < config.cpu_count; cpu++) {
[10e16a7]735 if (!cpus[cpu].active)
736 continue;
[a35b458]737
[da1bafb]738 irq_spinlock_lock(&cpus[cpu].lock, true);
[a35b458]739
[7e752b2]740 printf("cpu%u: address=%p, nrdy=%" PRIua ", needs_relink=%zu\n",
[6f4495f5]741 cpus[cpu].id, &cpus[cpu], atomic_get(&cpus[cpu].nrdy),
742 cpus[cpu].needs_relink);
[a35b458]743
[da1bafb]744 unsigned int i;
[4e33b6b]745 for (i = 0; i < RQ_COUNT; i++) {
[da1bafb]746 irq_spinlock_lock(&(cpus[cpu].rq[i].lock), false);
747 if (cpus[cpu].rq[i].n == 0) {
748 irq_spinlock_unlock(&(cpus[cpu].rq[i].lock), false);
[10e16a7]749 continue;
750 }
[a35b458]751
[5b86d10]752 printf("\trq[%u]: ", i);
[feeac0d]753 list_foreach(cpus[cpu].rq[i].rq, rq_link, thread_t,
754 thread) {
[da1bafb]755 printf("%" PRIu64 "(%s) ", thread->tid,
756 thread_states[thread->state]);
[10e16a7]757 }
758 printf("\n");
[a35b458]759
[da1bafb]760 irq_spinlock_unlock(&(cpus[cpu].rq[i].lock), false);
[10e16a7]761 }
[a35b458]762
[da1bafb]763 irq_spinlock_unlock(&cpus[cpu].lock, true);
[10e16a7]764 }
765}
[b45c443]766
[cc73a8a1]767/** @}
[b45c443]768 */
Note: See TracBrowser for help on using the repository browser.