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

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

Reduce locking further with lazy FPU

It turns out we only need a lock to synchronize between the trap
handler and thread destructor. The atomic operations introduced
are just plain reads and writes, written in an ugly fashion to
appease C11 undefined behavior gods.

In principle we could get rid of that if we made cpu_t::fpu_owner
a strong reference, but that would mean a thread structure could
be held in limbo indefinitely if a new thread is not being
scheduled or doesn't use FPU.

  • Property mode set to 100644
File size: 16.2 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
[174156fd]29/** @addtogroup kernel_generic_proc
[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>
[4621d23]42#include <atomic.h>
[f761f1eb]43#include <proc/scheduler.h>
44#include <proc/thread.h>
45#include <proc/task.h>
[32ff43e6]46#include <mm/frame.h>
47#include <mm/page.h>
[20d50a1]48#include <mm/as.h>
[b3f8fb7]49#include <time/timeout.h>
[fe19611]50#include <time/delay.h>
[32ff43e6]51#include <arch/asm.h>
52#include <arch/faddr.h>
[cce6acf]53#include <arch/cycle.h>
[23684b7]54#include <atomic.h>
[32ff43e6]55#include <synch/spinlock.h>
[f761f1eb]56#include <config.h>
57#include <context.h>
[b3f8fb7]58#include <fpu_context.h>
[b2e121a]59#include <halt.h>
[f761f1eb]60#include <arch.h>
[5c9a08b]61#include <adt/list.h>
[02a99d2]62#include <panic.h>
[32ff43e6]63#include <cpu.h>
[bab75df6]64#include <stdio.h>
[b2fa1204]65#include <log.h>
[df58e44]66#include <stacktrace.h>
[9c0a9b3]67
[7d6ec87]68static void scheduler_separated_stack(void);
69
[31e15be]70atomic_size_t nrdy; /**< Number of ready threads in the system. */
[f761f1eb]71
[97f1691]72/** Take actions before new thread runs.
[70527f1]73 *
[b60a22c]74 * Perform actions that need to be
75 * taken before the newly selected
[df58e44]76 * thread is passed control.
[70527f1]77 *
[a3eeceb6]78 * THREAD->lock is locked on entry
79 *
[70527f1]80 */
[4e7d3dd]81static void before_thread_runs(void)
[0ca6faa]82{
[b49f4ae]83 before_thread_runs_arch();
[a35b458]84
[f76fed4]85#ifdef CONFIG_FPU_LAZY
[f3dbe27]86 /*
87 * The only concurrent modification possible for fpu_owner here is
88 * another thread changing it from itself to NULL in its destructor.
89 */
90 thread_t *owner = atomic_load_explicit(&CPU->fpu_owner,
91 memory_order_relaxed);
[06f81c4]92
[f3dbe27]93 if (THREAD == owner)
[b49f4ae]94 fpu_enable();
95 else
[da1bafb]96 fpu_disable();
[e1326cf]97#elif defined CONFIG_FPU
[b49f4ae]98 fpu_enable();
99 if (THREAD->fpu_context_exists)
[0366d09d]100 fpu_context_restore(&THREAD->fpu_context);
[b49f4ae]101 else {
[f76fed4]102 fpu_init();
[6eef3c4]103 THREAD->fpu_context_exists = true;
[b49f4ae]104 }
[f76fed4]105#endif
[a35b458]106
[5b7a107]107#ifdef CONFIG_UDEBUG
[df58e44]108 if (THREAD->btrace) {
109 istate_t *istate = THREAD->udebug.uspace_state;
110 if (istate != NULL) {
111 printf("Thread %" PRIu64 " stack trace:\n", THREAD->tid);
112 stack_trace_istate(istate);
113 }
[a35b458]114
[df58e44]115 THREAD->btrace = false;
116 }
[5b7a107]117#endif
[0ca6faa]118}
119
[7d6ec87]120/** Take actions after THREAD had run.
[97f1691]121 *
122 * Perform actions that need to be
123 * taken after the running thread
[7d6ec87]124 * had been preempted by the scheduler.
[97f1691]125 *
126 * THREAD->lock is locked on entry
127 *
128 */
[4e7d3dd]129static void after_thread_ran(void)
[97f1691]130{
131 after_thread_ran_arch();
132}
133
[5f85c91]134#ifdef CONFIG_FPU_LAZY
[b49f4ae]135void scheduler_fpu_lazy_request(void)
136{
137 fpu_enable();
[f3dbe27]138
139 /* We need this lock to ensure synchronization with thread destructor. */
[169815e]140 irq_spinlock_lock(&CPU->fpu_lock, false);
[a35b458]141
[a3eeceb6]142 /* Save old context */
[f3dbe27]143 thread_t *owner = atomic_load_explicit(&CPU->fpu_owner, memory_order_relaxed);
144 if (owner != NULL) {
145 fpu_context_save(&owner->fpu_context);
146 atomic_store_explicit(&CPU->fpu_owner, NULL, memory_order_relaxed);
[b49f4ae]147 }
[a35b458]148
[f3dbe27]149 irq_spinlock_unlock(&CPU->fpu_lock, false);
150
[7d6ec87]151 if (THREAD->fpu_context_exists) {
[0366d09d]152 fpu_context_restore(&THREAD->fpu_context);
[7d6ec87]153 } else {
[f76fed4]154 fpu_init();
[6eef3c4]155 THREAD->fpu_context_exists = true;
[b49f4ae]156 }
[a35b458]157
[f3dbe27]158 atomic_store_explicit(&CPU->fpu_owner, THREAD, memory_order_relaxed);
[b49f4ae]159}
[da1bafb]160#endif /* CONFIG_FPU_LAZY */
[0ca6faa]161
[70527f1]162/** Initialize scheduler
163 *
164 * Initialize kernel scheduler.
165 *
166 */
[f761f1eb]167void scheduler_init(void)
168{
169}
170
[70527f1]171/** Get thread to be scheduled
172 *
173 * Get the optimal thread to be scheduled
[d1a184f]174 * according to thread accounting and scheduler
[70527f1]175 * policy.
176 *
177 * @return Thread to be scheduled.
178 *
179 */
[e507afa]180static thread_t *find_best_thread(void)
[f761f1eb]181{
[63e27ef]182 assert(CPU != NULL);
[a35b458]183
[f761f1eb]184loop:
[036e97c]185 if (atomic_load(&CPU->nrdy) == 0) {
[f761f1eb]186 /*
187 * For there was nothing to run, the CPU goes to sleep
188 * until a hardware interrupt or an IPI comes.
189 * This improves energy saving and hyperthreading.
190 */
[da1bafb]191 CPU->idle = true;
[a35b458]192
[da1bafb]193 /*
[bea6233]194 * Go to sleep with interrupts enabled.
195 * Ideally, this should be atomic, but this is not guaranteed on
196 * all platforms yet, so it is possible we will go sleep when
197 * a thread has just become available.
[328e0d3]198 */
[bea6233]199 cpu_interruptible_sleep();
200
201 /* Interrupts are disabled again. */
[da1bafb]202 goto loop;
[f761f1eb]203 }
[181a746]204
[63e27ef]205 assert(!CPU->idle);
[a35b458]206
[da1bafb]207 unsigned int i;
[ea63704]208 for (i = 0; i < RQ_COUNT; i++) {
[da1bafb]209 irq_spinlock_lock(&(CPU->rq[i].lock), false);
210 if (CPU->rq[i].n == 0) {
[f761f1eb]211 /*
212 * If this queue is empty, try a lower-priority queue.
213 */
[da1bafb]214 irq_spinlock_unlock(&(CPU->rq[i].lock), false);
[f761f1eb]215 continue;
216 }
[a35b458]217
[248fc1a]218 atomic_dec(&CPU->nrdy);
[59e07c91]219 atomic_dec(&nrdy);
[da1bafb]220 CPU->rq[i].n--;
[a35b458]221
[f761f1eb]222 /*
223 * Take the first thread from the queue.
224 */
[55b77d9]225 thread_t *thread = list_get_instance(
226 list_first(&CPU->rq[i].rq), thread_t, rq_link);
[da1bafb]227 list_remove(&thread->rq_link);
[a35b458]228
[da1bafb]229 irq_spinlock_pass(&(CPU->rq[i].lock), &thread->lock);
[a35b458]230
[da1bafb]231 thread->cpu = CPU;
232 thread->priority = i; /* Correct rq index */
[a35b458]233
[aae2869]234 /* Time allocation in microseconds. */
235 uint64_t time_to_run = (i + 1) * 10000;
236
237 /* This is safe because interrupts are disabled. */
238 CPU->preempt_deadline = CPU->current_clock_tick + us2ticks(time_to_run);
239
[f761f1eb]240 /*
[6eef3c4]241 * Clear the stolen flag so that it can be migrated
[32fffef0]242 * when load balancing needs emerge.
[f761f1eb]243 */
[6eef3c4]244 thread->stolen = false;
[da1bafb]245 irq_spinlock_unlock(&thread->lock, false);
[a35b458]246
[da1bafb]247 return thread;
[f761f1eb]248 }
[a35b458]249
[f761f1eb]250 goto loop;
251}
252
[c680333]253static void switch_task(task_t *task)
254{
255 /* If the task stays the same, a lot of work is avoided. */
256 if (TASK == task)
257 return;
258
259 as_t *old_as = AS;
260 as_t *new_as = task->as;
261
262 /* It is possible for two tasks to share one address space. */
263 if (old_as != new_as)
264 as_switch(old_as, new_as);
265
266 if (TASK)
267 task_release(TASK);
268
269 TASK = task;
270
271 task_hold(TASK);
272
273 before_task_runs_arch();
274}
275
[70527f1]276/** Prevent rq starvation
277 *
278 * Prevent low priority threads from starving in rq's.
279 *
280 * When the function decides to relink rq's, it reconnects
281 * respective pointers so that in result threads with 'pri'
[abbc16e]282 * greater or equal start are moved to a higher-priority queue.
[70527f1]283 *
284 * @param start Threshold priority.
285 *
[f761f1eb]286 */
[e16e036a]287static void relink_rq(int start)
[f761f1eb]288{
[011c79a]289 if (CPU->current_clock_tick < CPU->relink_deadline)
290 return;
291
292 CPU->relink_deadline = CPU->current_clock_tick + NEEDS_RELINK_MAX;
[a35b458]293
[3118355]294 /* Temporary cache for lists we are moving. */
[011c79a]295 list_t list;
[55b77d9]296 list_initialize(&list);
[a35b458]297
[3118355]298 size_t n = 0;
299
300 /* Move every list (except the one with highest priority) one level up. */
301 for (int i = RQ_COUNT - 1; i > start; i--) {
302 irq_spinlock_lock(&CPU->rq[i].lock, false);
[a35b458]303
[3118355]304 /* Swap lists. */
305 list_swap(&CPU->rq[i].rq, &list);
[a35b458]306
[3118355]307 /* Swap number of items. */
308 size_t tmpn = CPU->rq[i].n;
309 CPU->rq[i].n = n;
310 n = tmpn;
[a35b458]311
[011c79a]312 irq_spinlock_unlock(&CPU->rq[i].lock, false);
[f761f1eb]313 }
[a35b458]314
[3118355]315 /* Append the contents of rq[start + 1] to rq[start]. */
316 if (n != 0) {
317 irq_spinlock_lock(&CPU->rq[start].lock, false);
318 list_concat(&CPU->rq[start].rq, &list);
319 CPU->rq[start].n += n;
320 irq_spinlock_unlock(&CPU->rq[start].lock, false);
321 }
[f761f1eb]322}
323
[111b9b9]324void scheduler(void)
325{
326 ipl_t ipl = interrupts_disable();
327
328 if (atomic_load(&haltstate))
329 halt();
330
331 if (THREAD) {
332 irq_spinlock_lock(&THREAD->lock, false);
333 }
334
335 scheduler_locked(ipl);
336}
337
[7d6ec87]338/** The scheduler
339 *
340 * The thread scheduling procedure.
341 * Passes control directly to
342 * scheduler_separated_stack().
343 *
344 */
[111b9b9]345void scheduler_locked(ipl_t ipl)
[7d6ec87]346{
[63e27ef]347 assert(CPU != NULL);
[a35b458]348
[7d6ec87]349 if (THREAD) {
[1ba37fa]350 /* Update thread kernel accounting */
[a2a00e8]351 THREAD->kcycles += get_cycle() - THREAD->last_cycle;
[a35b458]352
[e1326cf]353#if (defined CONFIG_FPU) && (!defined CONFIG_FPU_LAZY)
[0366d09d]354 fpu_context_save(&THREAD->fpu_context);
[f76fed4]355#endif
[7d6ec87]356 if (!context_save(&THREAD->saved_context)) {
357 /*
358 * This is the place where threads leave scheduler();
359 */
[a35b458]360
[cce6acf]361 /* Save current CPU cycle */
362 THREAD->last_cycle = get_cycle();
[a35b458]363
[da1bafb]364 irq_spinlock_unlock(&THREAD->lock, false);
[c030818]365 interrupts_restore(THREAD->saved_ipl);
[a35b458]366
[7d6ec87]367 return;
368 }
[a35b458]369
[7d6ec87]370 /*
[4e33b6b]371 * Interrupt priority level of preempted thread is recorded
372 * here to facilitate scheduler() invocations from
[da1bafb]373 * interrupts_disable()'d code (e.g. waitq_sleep_timeout()).
374 *
[7d6ec87]375 */
[c030818]376 THREAD->saved_ipl = ipl;
[7d6ec87]377 }
[a35b458]378
[7d6ec87]379 /*
[a6e55886]380 * Through the 'CURRENT' structure, we keep track of THREAD, TASK, CPU, AS
381 * and preemption counter. At this point CURRENT could be coming either
[7d6ec87]382 * from THREAD's or CPU's stack.
[da1bafb]383 *
[7d6ec87]384 */
[a6e55886]385 current_copy(CURRENT, (current_t *) CPU->stack);
[a35b458]386
[7d6ec87]387 /*
388 * We may not keep the old stack.
389 * Reason: If we kept the old stack and got blocked, for instance, in
390 * find_best_thread(), the old thread could get rescheduled by another
391 * CPU and overwrite the part of its own stack that was also used by
392 * the scheduler on this CPU.
393 *
394 * Moreover, we have to bypass the compiler-generated POP sequence
395 * which is fooled by SP being set to the very top of the stack.
396 * Therefore the scheduler() function continues in
397 * scheduler_separated_stack().
[da1bafb]398 *
[7d6ec87]399 */
[daadfa6]400 context_t ctx;
401 context_save(&ctx);
402 context_set(&ctx, FADDR(scheduler_separated_stack),
[26aafe8]403 (uintptr_t) CPU->stack, STACK_SIZE);
[daadfa6]404 context_restore(&ctx);
[a35b458]405
[da1bafb]406 /* Not reached */
[7d6ec87]407}
[70527f1]408
409/** Scheduler stack switch wrapper
410 *
411 * Second part of the scheduler() function
412 * using new stack. Handling the actual context
413 * switch to a new thread.
414 *
415 */
[7d6ec87]416void scheduler_separated_stack(void)
[f761f1eb]417{
[63e27ef]418 assert((!THREAD) || (irq_spinlock_locked(&THREAD->lock)));
419 assert(CPU != NULL);
420 assert(interrupts_disabled());
[a35b458]421
[43114c5]422 if (THREAD) {
[da1bafb]423 /* Must be run after the switch to scheduler stack */
[97f1691]424 after_thread_ran();
[a35b458]425
[43114c5]426 switch (THREAD->state) {
[06e1e95]427 case Running:
[da1bafb]428 irq_spinlock_unlock(&THREAD->lock, false);
[76cec1e]429 thread_ready(THREAD);
430 break;
[a35b458]431
[06e1e95]432 case Exiting:
[1871118]433 irq_spinlock_unlock(&THREAD->lock, false);
[111b9b9]434 waitq_close(&THREAD->join_wq);
[1871118]435
436 /*
437 * Release the reference CPU has for the thread.
438 * If there are no other references (e.g. threads calling join),
439 * the thread structure is deallocated.
440 */
441 thread_put(THREAD);
[76cec1e]442 break;
[a35b458]443
[06e1e95]444 case Sleeping:
[76cec1e]445 /*
446 * Prefer the thread after it's woken up.
447 */
[22f7769]448 THREAD->priority = -1;
[da1bafb]449 irq_spinlock_unlock(&THREAD->lock, false);
[76cec1e]450 break;
[a35b458]451
[06e1e95]452 default:
[76cec1e]453 /*
454 * Entering state is unexpected.
455 */
[f651e80]456 panic("tid%" PRIu64 ": unexpected state %s.",
[1e9d0e3]457 THREAD->tid, thread_states[THREAD->state]);
[76cec1e]458 break;
[f761f1eb]459 }
[a35b458]460
[43114c5]461 THREAD = NULL;
[f761f1eb]462 }
[a35b458]463
[43114c5]464 THREAD = find_best_thread();
[a35b458]465
[da1bafb]466 irq_spinlock_lock(&THREAD->lock, false);
467 int priority = THREAD->priority;
468 irq_spinlock_unlock(&THREAD->lock, false);
[a35b458]469
[da1bafb]470 relink_rq(priority);
[a35b458]471
[c680333]472 switch_task(THREAD->task);
[a35b458]473
[da1bafb]474 irq_spinlock_lock(&THREAD->lock, false);
[43114c5]475 THREAD->state = Running;
[a35b458]476
[f76fed4]477#ifdef SCHEDULER_VERBOSE
[b2fa1204]478 log(LF_OTHER, LVL_DEBUG,
479 "cpu%u: tid %" PRIu64 " (priority=%d, ticks=%" PRIu64
[077842c]480 ", nrdy=%zu)", CPU->id, THREAD->tid, THREAD->priority,
[036e97c]481 THREAD->ticks, atomic_load(&CPU->nrdy));
[da1bafb]482#endif
[a35b458]483
[97f1691]484 /*
485 * Some architectures provide late kernel PA2KA(identity)
486 * mapping in a page fault handler. However, the page fault
487 * handler uses the kernel stack of the running thread and
488 * therefore cannot be used to map it. The kernel stack, if
489 * necessary, is to be mapped in before_thread_runs(). This
490 * function must be executed before the switch to the new stack.
491 */
492 before_thread_runs();
[a35b458]493
[3e1607f]494 /*
[4e33b6b]495 * Copy the knowledge of CPU, TASK, THREAD and preemption counter to
496 * thread's stack.
[3e1607f]497 */
[a6e55886]498 current_copy(CURRENT, (current_t *) THREAD->kstack);
[a35b458]499
[43114c5]500 context_restore(&THREAD->saved_context);
[a35b458]501
[da1bafb]502 /* Not reached */
[f761f1eb]503}
504
[5f85c91]505#ifdef CONFIG_SMP
[fbaf6ac]506
507static thread_t *steal_thread_from(cpu_t *old_cpu, int i)
508{
509 runq_t *old_rq = &old_cpu->rq[i];
510 runq_t *new_rq = &CPU->rq[i];
511
[06f81c4]512 ipl_t ipl = interrupts_disable();
513
514 irq_spinlock_lock(&old_rq->lock, false);
[fbaf6ac]515
[f3dbe27]516 /*
517 * If fpu_owner is any thread in the list, its store is seen here thanks to
518 * the runqueue lock.
519 */
520 thread_t *fpu_owner = atomic_load_explicit(&old_cpu->fpu_owner,
521 memory_order_relaxed);
522
[fbaf6ac]523 /* Search rq from the back */
524 list_foreach_rev(old_rq->rq, rq_link, thread_t, thread) {
525
526 irq_spinlock_lock(&thread->lock, false);
527
528 /*
529 * Do not steal CPU-wired threads, threads
530 * already stolen, threads for which migration
531 * was temporarily disabled or threads whose
532 * FPU context is still in the CPU.
533 */
[06f81c4]534 if (thread->stolen || thread->nomigrate ||
[f3dbe27]535 thread == fpu_owner) {
[fbaf6ac]536 irq_spinlock_unlock(&thread->lock, false);
537 continue;
538 }
539
540 thread->stolen = true;
541 thread->cpu = CPU;
542
543 irq_spinlock_unlock(&thread->lock, false);
544
545 /*
546 * Ready thread on local CPU
547 */
548
549#ifdef KCPULB_VERBOSE
550 log(LF_OTHER, LVL_DEBUG,
551 "kcpulb%u: TID %" PRIu64 " -> cpu%u, "
552 "nrdy=%ld, avg=%ld", CPU->id, thread->tid,
553 CPU->id, atomic_load(&CPU->nrdy),
554 atomic_load(&nrdy) / config.cpu_active);
555#endif
556
557 /* Remove thread from ready queue. */
558 old_rq->n--;
559 list_remove(&thread->rq_link);
[06f81c4]560 irq_spinlock_unlock(&old_rq->lock, false);
[fbaf6ac]561
562 /* Append thread to local queue. */
[06f81c4]563 irq_spinlock_lock(&new_rq->lock, false);
[fbaf6ac]564 list_append(&thread->rq_link, &new_rq->rq);
565 new_rq->n++;
[06f81c4]566 irq_spinlock_unlock(&new_rq->lock, false);
[fbaf6ac]567
568 atomic_dec(&old_cpu->nrdy);
569 atomic_inc(&CPU->nrdy);
[06f81c4]570 interrupts_restore(ipl);
[fbaf6ac]571 return thread;
572 }
573
[06f81c4]574 irq_spinlock_unlock(&old_rq->lock, false);
575 interrupts_restore(ipl);
[fbaf6ac]576 return NULL;
577}
578
[70527f1]579/** Load balancing thread
580 *
581 * SMP load balancing thread, supervising thread supplies
582 * for the CPU it's wired to.
583 *
584 * @param arg Generic thread argument (unused).
585 *
[f761f1eb]586 */
587void kcpulb(void *arg)
588{
[3cfe2b8]589 size_t average;
590 size_t rdy;
[a35b458]591
[f761f1eb]592loop:
593 /*
[3260ada]594 * Work in 1s intervals.
[f761f1eb]595 */
[3260ada]596 thread_sleep(1);
[a35b458]597
[f761f1eb]598not_satisfied:
599 /*
600 * Calculate the number of threads that will be migrated/stolen from
601 * other CPU's. Note that situation can have changed between two
602 * passes. Each time get the most up to date counts.
[da1bafb]603 *
[f761f1eb]604 */
[036e97c]605 average = atomic_load(&nrdy) / config.cpu_active + 1;
606 rdy = atomic_load(&CPU->nrdy);
[a35b458]607
[da1bafb]608 if (average <= rdy)
[f761f1eb]609 goto satisfied;
[a35b458]610
[3cfe2b8]611 size_t count = average - rdy;
[a35b458]612
[f761f1eb]613 /*
[4e33b6b]614 * Searching least priority queues on all CPU's first and most priority
615 * queues on all CPU's last.
[f761f1eb]616 */
[da1bafb]617 size_t acpu;
618 int rq;
[a35b458]619
[da1bafb]620 for (rq = RQ_COUNT - 1; rq >= 0; rq--) {
621 for (acpu = 0; acpu < config.cpu_active; acpu++) {
[fbaf6ac]622 cpu_t *cpu = &cpus[acpu];
[a35b458]623
[f761f1eb]624 /*
625 * Not interested in ourselves.
[4e33b6b]626 * Doesn't require interrupt disabling for kcpulb has
627 * THREAD_FLAG_WIRED.
[da1bafb]628 *
[f761f1eb]629 */
[43114c5]630 if (CPU == cpu)
[248fc1a]631 continue;
[a35b458]632
[036e97c]633 if (atomic_load(&cpu->nrdy) <= average)
[248fc1a]634 continue;
[a35b458]635
[fbaf6ac]636 if (steal_thread_from(cpu, rq) && --count == 0)
637 goto satisfied;
[f761f1eb]638 }
639 }
[a35b458]640
[036e97c]641 if (atomic_load(&CPU->nrdy)) {
[f761f1eb]642 /*
643 * Be a little bit light-weight and let migrated threads run.
[da1bafb]644 *
[f761f1eb]645 */
646 scheduler();
[3260ada]647 } else {
[f761f1eb]648 /*
649 * We failed to migrate a single thread.
[3260ada]650 * Give up this turn.
[da1bafb]651 *
[f761f1eb]652 */
[3260ada]653 goto loop;
[f761f1eb]654 }
[a35b458]655
[f761f1eb]656 goto not_satisfied;
[a35b458]657
[f761f1eb]658satisfied:
659 goto loop;
660}
[5f85c91]661#endif /* CONFIG_SMP */
[10e16a7]662
[da1bafb]663/** Print information about threads & scheduler queues
664 *
665 */
[10e16a7]666void sched_print_list(void)
667{
[da1bafb]668 size_t cpu;
[4184e76]669 for (cpu = 0; cpu < config.cpu_count; cpu++) {
[10e16a7]670 if (!cpus[cpu].active)
671 continue;
[a35b458]672
[011c79a]673 /* Technically a data race, but we don't really care in this case. */
674 int needs_relink = cpus[cpu].relink_deadline - cpus[cpu].current_clock_tick;
675
676 printf("cpu%u: address=%p, nrdy=%zu, needs_relink=%d\n",
[036e97c]677 cpus[cpu].id, &cpus[cpu], atomic_load(&cpus[cpu].nrdy),
[011c79a]678 needs_relink);
[a35b458]679
[da1bafb]680 unsigned int i;
[4e33b6b]681 for (i = 0; i < RQ_COUNT; i++) {
[da1bafb]682 irq_spinlock_lock(&(cpus[cpu].rq[i].lock), false);
683 if (cpus[cpu].rq[i].n == 0) {
684 irq_spinlock_unlock(&(cpus[cpu].rq[i].lock), false);
[10e16a7]685 continue;
686 }
[a35b458]687
[5b86d10]688 printf("\trq[%u]: ", i);
[feeac0d]689 list_foreach(cpus[cpu].rq[i].rq, rq_link, thread_t,
690 thread) {
[da1bafb]691 printf("%" PRIu64 "(%s) ", thread->tid,
692 thread_states[thread->state]);
[10e16a7]693 }
694 printf("\n");
[a35b458]695
[da1bafb]696 irq_spinlock_unlock(&(cpus[cpu].rq[i].lock), false);
[10e16a7]697 }
698 }
699}
[b45c443]700
[cc73a8a1]701/** @}
[b45c443]702 */
Note: See TracBrowser for help on using the repository browser.