From 53892c92a02939d7e657f4fbb9563bc692716318 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 21 Oct 2008 12:37:13 +0000 Subject: [PATCH] * Replaced scheduler_remove_from_run_queue() by scheduler_set_thread_priority(). Setting the thread priority was the only situation in which it was used. * Renamed scheduler.cpp to scheduler_simple.cpp. * The scheduler functions are no longer called directly. Instead there's an operation vector now, which is initialized at kernel init time. This allows for picking the most suitable scheduler for the machine (e.g. a non-SMP scheduler on a non-SMP machine). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28262 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/kscheduler.h | 24 +- src/system/kernel/Jamfile | 1 + src/system/kernel/main.cpp | 4 +- src/system/kernel/scheduler/scheduler.cpp | 373 +--------------- .../kernel/scheduler/scheduler_simple.cpp | 413 ++++++++++++++++++ .../kernel/scheduler/scheduler_simple.h | 12 + .../kernel/scheduler/scheduling_analysis.cpp | 2 +- src/system/kernel/thread.cpp | 9 +- 8 files changed, 452 insertions(+), 386 deletions(-) create mode 100644 src/system/kernel/scheduler/scheduler_simple.cpp create mode 100644 src/system/kernel/scheduler/scheduler_simple.h diff --git a/headers/private/kernel/kscheduler.h b/headers/private/kernel/kscheduler.h index 1728442075..b4682be4cb 100644 --- a/headers/private/kernel/kscheduler.h +++ b/headers/private/kernel/kscheduler.h @@ -1,4 +1,5 @@ /* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ @@ -13,16 +14,29 @@ struct scheduling_analysis; struct thread; +struct scheduler_ops { + void (*enqueue_in_run_queue)(struct thread* thread); + void (*reschedule)(void); + void (*set_thread_priority)(struct thread* thread, int32 priority); + + void (*start)(void); +}; + +extern struct scheduler_ops* gScheduler; + +#define scheduler_enqueue_in_run_queue(thread) \ + gScheduler->enqueue_in_run_queue(thread) +#define scheduler_set_thread_priority(thread, priority) \ + gScheduler->set_thread_priority(thread, priority) +#define scheduler_reschedule() gScheduler->reschedule() +#define scheduler_start() gScheduler->start() + + #ifdef __cplusplus extern "C" { #endif -void scheduler_enqueue_in_run_queue(struct thread *thread); -void scheduler_remove_from_run_queue(struct thread *thread); -void scheduler_reschedule(void); - void scheduler_init(void); -void scheduler_start(void); status_t _user_analyze_scheduling(bigtime_t from, bigtime_t until, void* buffer, size_t size, struct scheduling_analysis* analysis); diff --git a/src/system/kernel/Jamfile b/src/system/kernel/Jamfile index b9f373d323..754db5d890 100644 --- a/src/system/kernel/Jamfile +++ b/src/system/kernel/Jamfile @@ -51,6 +51,7 @@ KernelMergeObject kernel_core.o : # scheduler scheduler.cpp + scheduler_simple.cpp scheduler_tracing.cpp scheduling_analysis.cpp diff --git a/src/system/kernel/main.cpp b/src/system/kernel/main.cpp index 078bdcf59f..48404b909b 100644 --- a/src/system/kernel/main.cpp +++ b/src/system/kernel/main.cpp @@ -163,6 +163,8 @@ _start(kernel_args *bootKernelArgs, int currentCPU) smp_init_post_generic_syscalls(); TRACE("init cbuf\n"); cbuf_init(); + TRACE("init scheduler\n"); + scheduler_init(); TRACE("init threads\n"); thread_init(&sKernelArgs); TRACE("init ports\n"); @@ -178,8 +180,6 @@ _start(kernel_args *bootKernelArgs, int currentCPU) TRACE("init VM threads\n"); vm_init_post_thread(&sKernelArgs); low_resource_manager_init_post_thread(); - TRACE("init scheduler\n"); - scheduler_init(); TRACE("init notification services\n"); notifications_init(); TRACE("init VFS\n"); diff --git a/src/system/kernel/scheduler/scheduler.cpp b/src/system/kernel/scheduler/scheduler.cpp index 67ef33c8c5..959fc5593f 100644 --- a/src/system/kernel/scheduler/scheduler.cpp +++ b/src/system/kernel/scheduler/scheduler.cpp @@ -1,371 +1,20 @@ /* * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. - * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. - * Copyright 2002, Angelo Mottola, a.mottola@libero.it. * Distributed under the terms of the MIT License. - * - * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. - * Distributed under the terms of the NewOS License. */ -/*! The thread scheduler */ - - -#include - -#include -#include -#include #include -#include -#include -#include -#include -#include -#include "scheduler_tracing.h" +#include "scheduler_simple.h" -//#define TRACE_SCHEDULER -#ifdef TRACE_SCHEDULER -# define TRACE(x) dprintf x -#else -# define TRACE(x) ; -#endif - - -// The run queue. Holds the threads ready to run ordered by priority. -static struct thread *sRunQueue = NULL; -static cpu_mask_t sIdleCPUs = 0; - - -static int -_rand(void) -{ - static int next = 0; - - if (next == 0) - next = system_time(); - - next = next * 1103515245 + 12345; - return (next >> 16) & 0x7FFF; -} - - -static int -dump_run_queue(int argc, char **argv) -{ - struct thread *thread; - - thread = sRunQueue; - if (!thread) - kprintf("Run queue is empty!\n"); - else { - kprintf("thread id priority name\n"); - while (thread) { - kprintf("%p %-7ld %-8ld %s\n", thread, thread->id, - thread->priority, thread->name); - thread = thread->queue_next; - } - } - - return 0; -} - - -/*! Enqueues the thread into the run queue. - Note: thread lock must be held when entering this function -*/ -void -scheduler_enqueue_in_run_queue(struct thread *thread) -{ - if (thread->state == B_THREAD_RUNNING) { - // The thread is currently running (on another CPU) and we cannot - // insert it into the run queue. Set the next state to ready so the - // thread is inserted into the run queue on the next reschedule. - thread->next_state = B_THREAD_READY; - return; - } - - thread->state = thread->next_state = B_THREAD_READY; - - struct thread *curr, *prev; - for (curr = sRunQueue, prev = NULL; curr - && curr->priority >= thread->next_priority; - curr = curr->queue_next) { - if (prev) - prev = prev->queue_next; - else - prev = sRunQueue; - } - - T(EnqueueThread(thread, prev, curr)); - - thread->queue_next = curr; - if (prev) - prev->queue_next = thread; - else - sRunQueue = thread; - - thread->next_priority = thread->priority; - - if (thread->priority != B_IDLE_PRIORITY) { - int32 currentCPU = smp_get_current_cpu(); - if (sIdleCPUs != 0) { - if (thread->pinned_to_cpu > 0) { - // thread is pinned to a CPU -- notify it, if it is idle - int32 targetCPU = thread->previous_cpu->cpu_num; - if ((sIdleCPUs & (1 << targetCPU)) != 0) { - sIdleCPUs &= ~(1 << targetCPU); - smp_send_ici(targetCPU, SMP_MSG_RESCHEDULE_IF_IDLE, 0, 0, - 0, NULL, SMP_MSG_FLAG_ASYNC); - } - } else { - // Thread is not pinned to any CPU -- take it ourselves, if we - // are idle, otherwise notify the next idle CPU. In either case - // we clear the idle bit of the chosen CPU, so that the - // scheduler_enqueue_in_run_queue() won't try to bother the - // same CPU again, if invoked before it handled the interrupt. - cpu_mask_t idleCPUs = CLEAR_BIT(sIdleCPUs, currentCPU); - if ((sIdleCPUs & (1 << currentCPU)) != 0) { - sIdleCPUs = idleCPUs; - } else { - int32 targetCPU = 0; - for (; targetCPU < B_MAX_CPU_COUNT; targetCPU++) { - cpu_mask_t mask = 1 << targetCPU; - if ((idleCPUs & mask) != 0) { - sIdleCPUs &= ~mask; - break; - } - } - - smp_send_ici(targetCPU, SMP_MSG_RESCHEDULE_IF_IDLE, 0, 0, - 0, NULL, SMP_MSG_FLAG_ASYNC); - } - } - } - } -} - - -/*! Removes a thread from the run queue. - Note: thread lock must be held when entering this function -*/ -void -scheduler_remove_from_run_queue(struct thread *thread) -{ - struct thread *item, *prev; - - T(RemoveThread(thread)); - - // find thread in run queue - for (item = sRunQueue, prev = NULL; item && item != thread; - item = item->queue_next) { - if (prev) - prev = prev->queue_next; - else - prev = sRunQueue; - } - - ASSERT(item == thread); - - if (prev) - prev->queue_next = item->queue_next; - else - sRunQueue = item->queue_next; -} - - -static void -context_switch(struct thread *fromThread, struct thread *toThread) -{ - if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) - user_debug_thread_unscheduled(fromThread); - - toThread->previous_cpu = toThread->cpu = fromThread->cpu; - fromThread->cpu = NULL; - - arch_thread_set_current_thread(toThread); - arch_thread_context_switch(fromThread, toThread); - - // Looks weird, but is correct. fromThread had been unscheduled earlier, - // but is back now. The notification for a thread scheduled the first time - // happens in thread.cpp:thread_kthread_entry(). - if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) - user_debug_thread_scheduled(fromThread); -} - - -static int32 -reschedule_event(timer *unused) -{ - if (thread_get_current_thread()->keep_scheduled > 0) - return B_HANDLED_INTERRUPT; - - // this function is called as a result of the timer event set by the - // scheduler returning this causes a reschedule on the timer event - thread_get_current_thread()->cpu->preempted = 1; - return B_INVOKE_SCHEDULER; -} - - -/*! Runs the scheduler. - Note: expects thread spinlock to be held -*/ -void -scheduler_reschedule(void) -{ - struct thread *oldThread = thread_get_current_thread(); - struct thread *nextThread, *prevThread; - - TRACE(("reschedule(): cpu %d, cur_thread = %ld\n", smp_get_current_cpu(), thread_get_current_thread()->id)); - - oldThread->cpu->invoke_scheduler = false; - - oldThread->state = oldThread->next_state; - switch (oldThread->next_state) { - case B_THREAD_RUNNING: - case B_THREAD_READY: - TRACE(("enqueueing thread %ld into run q. pri = %ld\n", oldThread->id, oldThread->priority)); - scheduler_enqueue_in_run_queue(oldThread); - break; - case B_THREAD_SUSPENDED: - TRACE(("reschedule(): suspending thread %ld\n", oldThread->id)); - break; - case THREAD_STATE_FREE_ON_RESCHED: - break; - default: - TRACE(("not enqueueing thread %ld into run q. next_state = %ld\n", oldThread->id, oldThread->next_state)); - break; - } - - nextThread = sRunQueue; - prevThread = NULL; - - if (oldThread->cpu->disabled) { - // CPU is disabled - just select an idle thread - while (nextThread && nextThread->priority > B_IDLE_PRIORITY) { - prevThread = nextThread; - nextThread = nextThread->queue_next; - } - } else { - while (nextThread) { - // select next thread from the run queue - while (nextThread && nextThread->priority > B_IDLE_PRIORITY) { -#if 0 - if (oldThread == nextThread && nextThread->was_yielded) { - // ignore threads that called thread_yield() once - nextThread->was_yielded = false; - prevThread = nextThread; - nextThread = nextThread->queue_next; - } -#endif - - // skip thread, if it doesn't want to run on this CPU - if (nextThread->pinned_to_cpu > 0 - && nextThread->previous_cpu != oldThread->cpu) { - prevThread = nextThread; - nextThread = nextThread->queue_next; - continue; - } - - // always extract real time threads - if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY) - break; - - // never skip last non-idle normal thread - if (nextThread->queue_next && nextThread->queue_next->priority == B_IDLE_PRIORITY) - break; - - // skip normal threads sometimes (roughly 20%) - if (_rand() > 0x1a00) - break; - - // skip until next lower priority - int32 priority = nextThread->priority; - do { - prevThread = nextThread; - nextThread = nextThread->queue_next; - } while (nextThread->queue_next != NULL - && priority == nextThread->queue_next->priority - && nextThread->queue_next->priority > B_IDLE_PRIORITY); - } - - if (nextThread->cpu - && nextThread->cpu->cpu_num != oldThread->cpu->cpu_num) { - panic("thread in run queue that's still running on another CPU!\n"); - // ToDo: remove this check completely when we're sure that this - // cannot happen anymore. - prevThread = nextThread; - nextThread = nextThread->queue_next; - continue; - } - - break; - } - } - - if (!nextThread) - panic("reschedule(): run queue is empty!\n"); - - // extract selected thread from the run queue - if (prevThread) - prevThread->queue_next = nextThread->queue_next; - else - sRunQueue = nextThread->queue_next; - - T(ScheduleThread(nextThread, oldThread)); - - nextThread->state = B_THREAD_RUNNING; - nextThread->next_state = B_THREAD_READY; - oldThread->was_yielded = false; - - // track kernel time (user time is tracked in thread_at_kernel_entry()) - bigtime_t now = system_time(); - oldThread->kernel_time += now - oldThread->last_time; - nextThread->last_time = now; - - // track CPU activity - if (!thread_is_idle_thread(oldThread)) { - oldThread->cpu->active_time += - (oldThread->kernel_time - oldThread->cpu->last_kernel_time) - + (oldThread->user_time - oldThread->cpu->last_user_time); - } - - if (!thread_is_idle_thread(nextThread)) { - oldThread->cpu->last_kernel_time = nextThread->kernel_time; - oldThread->cpu->last_user_time = nextThread->user_time; - } - - if (nextThread != oldThread || oldThread->cpu->preempted) { - bigtime_t quantum = 3000; // ToDo: calculate quantum! - timer *quantumTimer = &oldThread->cpu->quantum_timer; - - if (!oldThread->cpu->preempted) - cancel_timer(quantumTimer); - - oldThread->cpu->preempted = 0; - add_timer(quantumTimer, &reschedule_event, quantum, - B_ONE_SHOT_RELATIVE_TIMER | B_TIMER_ACQUIRE_THREAD_LOCK); - - // update the idle bit for this CPU in the CPU mask - int32 cpuNum = smp_get_current_cpu(); - if (nextThread->priority == B_IDLE_PRIORITY) - sIdleCPUs = SET_BIT(sIdleCPUs, cpuNum); - else - sIdleCPUs = CLEAR_BIT(sIdleCPUs, cpuNum); - - if (nextThread != oldThread) - context_switch(oldThread, nextThread); - } -} +struct scheduler_ops* gScheduler; void scheduler_init(void) { - add_debugger_command_etc("run_queue", &dump_run_queue, - "List threads in run queue", "\nLists threads in run queue", 0); + scheduler_simple_init(); #if SCHEDULER_TRACING add_debugger_command_etc("scheduler", &cmd_scheduler, @@ -375,19 +24,3 @@ scheduler_init(void) " - ID of the thread.\n", 0); #endif } - - -/*! This starts the scheduler. Must be run under the context of - the initial idle thread. -*/ -void -scheduler_start(void) -{ - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); - - scheduler_reschedule(); - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); -} diff --git a/src/system/kernel/scheduler/scheduler_simple.cpp b/src/system/kernel/scheduler/scheduler_simple.cpp new file mode 100644 index 0000000000..5905b762d6 --- /dev/null +++ b/src/system/kernel/scheduler/scheduler_simple.cpp @@ -0,0 +1,413 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2002, Angelo Mottola, a.mottola@libero.it. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ + +/*! The thread scheduler */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scheduler_tracing.h" + + +//#define TRACE_SCHEDULER +#ifdef TRACE_SCHEDULER +# define TRACE(x) dprintf x +#else +# define TRACE(x) ; +#endif + + +// The run queue. Holds the threads ready to run ordered by priority. +static struct thread *sRunQueue = NULL; +static cpu_mask_t sIdleCPUs = 0; + + +static int +_rand(void) +{ + static int next = 0; + + if (next == 0) + next = system_time(); + + next = next * 1103515245 + 12345; + return (next >> 16) & 0x7FFF; +} + + +static int +dump_run_queue(int argc, char **argv) +{ + struct thread *thread; + + thread = sRunQueue; + if (!thread) + kprintf("Run queue is empty!\n"); + else { + kprintf("thread id priority name\n"); + while (thread) { + kprintf("%p %-7ld %-8ld %s\n", thread, thread->id, + thread->priority, thread->name); + thread = thread->queue_next; + } + } + + return 0; +} + + +/*! Enqueues the thread into the run queue. + Note: thread lock must be held when entering this function +*/ +static void +simple_enqueue_in_run_queue(struct thread *thread) +{ + if (thread->state == B_THREAD_RUNNING) { + // The thread is currently running (on another CPU) and we cannot + // insert it into the run queue. Set the next state to ready so the + // thread is inserted into the run queue on the next reschedule. + thread->next_state = B_THREAD_READY; + return; + } + + thread->state = thread->next_state = B_THREAD_READY; + + struct thread *curr, *prev; + for (curr = sRunQueue, prev = NULL; curr + && curr->priority >= thread->next_priority; + curr = curr->queue_next) { + if (prev) + prev = prev->queue_next; + else + prev = sRunQueue; + } + + T(EnqueueThread(thread, prev, curr)); + + thread->queue_next = curr; + if (prev) + prev->queue_next = thread; + else + sRunQueue = thread; + + thread->next_priority = thread->priority; + + if (thread->priority != B_IDLE_PRIORITY) { + int32 currentCPU = smp_get_current_cpu(); + if (sIdleCPUs != 0) { + if (thread->pinned_to_cpu > 0) { + // thread is pinned to a CPU -- notify it, if it is idle + int32 targetCPU = thread->previous_cpu->cpu_num; + if ((sIdleCPUs & (1 << targetCPU)) != 0) { + sIdleCPUs &= ~(1 << targetCPU); + smp_send_ici(targetCPU, SMP_MSG_RESCHEDULE_IF_IDLE, 0, 0, + 0, NULL, SMP_MSG_FLAG_ASYNC); + } + } else { + // Thread is not pinned to any CPU -- take it ourselves, if we + // are idle, otherwise notify the next idle CPU. In either case + // we clear the idle bit of the chosen CPU, so that the + // simple_enqueue_in_run_queue() won't try to bother the + // same CPU again, if invoked before it handled the interrupt. + cpu_mask_t idleCPUs = CLEAR_BIT(sIdleCPUs, currentCPU); + if ((sIdleCPUs & (1 << currentCPU)) != 0) { + sIdleCPUs = idleCPUs; + } else { + int32 targetCPU = 0; + for (; targetCPU < B_MAX_CPU_COUNT; targetCPU++) { + cpu_mask_t mask = 1 << targetCPU; + if ((idleCPUs & mask) != 0) { + sIdleCPUs &= ~mask; + break; + } + } + + smp_send_ici(targetCPU, SMP_MSG_RESCHEDULE_IF_IDLE, 0, 0, + 0, NULL, SMP_MSG_FLAG_ASYNC); + } + } + } + } +} + + +/*! Sets the priority of a thread. + Note: thread lock must be held when entering this function +*/ +static void +simple_set_thread_priority(struct thread *thread, int32 priority) +{ + if (priority == thread->priority) + return; + + if (thread->state != B_THREAD_READY) { + thread->priority = priority; + return; + } + + // The thread is in the run queue. We need to remove it and re-insert it at + // a new position. + + T(RemoveThread(thread)); + + // find thread in run queue + struct thread *item, *prev; + for (item = sRunQueue, prev = NULL; item && item != thread; + item = item->queue_next) { + if (prev) + prev = prev->queue_next; + else + prev = sRunQueue; + } + + ASSERT(item == thread); + + // remove the thread + if (prev) + prev->queue_next = item->queue_next; + else + sRunQueue = item->queue_next; + + // set priority and re-insert + thread->priority = priority; + simple_enqueue_in_run_queue(thread); +} + + +static void +context_switch(struct thread *fromThread, struct thread *toThread) +{ + if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) + user_debug_thread_unscheduled(fromThread); + + toThread->previous_cpu = toThread->cpu = fromThread->cpu; + fromThread->cpu = NULL; + + arch_thread_set_current_thread(toThread); + arch_thread_context_switch(fromThread, toThread); + + // Looks weird, but is correct. fromThread had been unscheduled earlier, + // but is back now. The notification for a thread scheduled the first time + // happens in thread.cpp:thread_kthread_entry(). + if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0) + user_debug_thread_scheduled(fromThread); +} + + +static int32 +reschedule_event(timer *unused) +{ + if (thread_get_current_thread()->keep_scheduled > 0) + return B_HANDLED_INTERRUPT; + + // this function is called as a result of the timer event set by the + // scheduler returning this causes a reschedule on the timer event + thread_get_current_thread()->cpu->preempted = 1; + return B_INVOKE_SCHEDULER; +} + + +/*! Runs the scheduler. + Note: expects thread spinlock to be held +*/ +static void +simple_reschedule(void) +{ + struct thread *oldThread = thread_get_current_thread(); + struct thread *nextThread, *prevThread; + + TRACE(("reschedule(): cpu %d, cur_thread = %ld\n", smp_get_current_cpu(), thread_get_current_thread()->id)); + + oldThread->cpu->invoke_scheduler = false; + + oldThread->state = oldThread->next_state; + switch (oldThread->next_state) { + case B_THREAD_RUNNING: + case B_THREAD_READY: + TRACE(("enqueueing thread %ld into run q. pri = %ld\n", oldThread->id, oldThread->priority)); + simple_enqueue_in_run_queue(oldThread); + break; + case B_THREAD_SUSPENDED: + TRACE(("reschedule(): suspending thread %ld\n", oldThread->id)); + break; + case THREAD_STATE_FREE_ON_RESCHED: + break; + default: + TRACE(("not enqueueing thread %ld into run q. next_state = %ld\n", oldThread->id, oldThread->next_state)); + break; + } + + nextThread = sRunQueue; + prevThread = NULL; + + if (oldThread->cpu->disabled) { + // CPU is disabled - just select an idle thread + while (nextThread && nextThread->priority > B_IDLE_PRIORITY) { + prevThread = nextThread; + nextThread = nextThread->queue_next; + } + } else { + while (nextThread) { + // select next thread from the run queue + while (nextThread && nextThread->priority > B_IDLE_PRIORITY) { +#if 0 + if (oldThread == nextThread && nextThread->was_yielded) { + // ignore threads that called thread_yield() once + nextThread->was_yielded = false; + prevThread = nextThread; + nextThread = nextThread->queue_next; + } +#endif + + // skip thread, if it doesn't want to run on this CPU + if (nextThread->pinned_to_cpu > 0 + && nextThread->previous_cpu != oldThread->cpu) { + prevThread = nextThread; + nextThread = nextThread->queue_next; + continue; + } + + // always extract real time threads + if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY) + break; + + // never skip last non-idle normal thread + if (nextThread->queue_next && nextThread->queue_next->priority == B_IDLE_PRIORITY) + break; + + // skip normal threads sometimes (roughly 20%) + if (_rand() > 0x1a00) + break; + + // skip until next lower priority + int32 priority = nextThread->priority; + do { + prevThread = nextThread; + nextThread = nextThread->queue_next; + } while (nextThread->queue_next != NULL + && priority == nextThread->queue_next->priority + && nextThread->queue_next->priority > B_IDLE_PRIORITY); + } + + if (nextThread->cpu + && nextThread->cpu->cpu_num != oldThread->cpu->cpu_num) { + panic("thread in run queue that's still running on another CPU!\n"); + // ToDo: remove this check completely when we're sure that this + // cannot happen anymore. + prevThread = nextThread; + nextThread = nextThread->queue_next; + continue; + } + + break; + } + } + + if (!nextThread) + panic("reschedule(): run queue is empty!\n"); + + // extract selected thread from the run queue + if (prevThread) + prevThread->queue_next = nextThread->queue_next; + else + sRunQueue = nextThread->queue_next; + + T(ScheduleThread(nextThread, oldThread)); + + nextThread->state = B_THREAD_RUNNING; + nextThread->next_state = B_THREAD_READY; + oldThread->was_yielded = false; + + // track kernel time (user time is tracked in thread_at_kernel_entry()) + bigtime_t now = system_time(); + oldThread->kernel_time += now - oldThread->last_time; + nextThread->last_time = now; + + // track CPU activity + if (!thread_is_idle_thread(oldThread)) { + oldThread->cpu->active_time += + (oldThread->kernel_time - oldThread->cpu->last_kernel_time) + + (oldThread->user_time - oldThread->cpu->last_user_time); + } + + if (!thread_is_idle_thread(nextThread)) { + oldThread->cpu->last_kernel_time = nextThread->kernel_time; + oldThread->cpu->last_user_time = nextThread->user_time; + } + + if (nextThread != oldThread || oldThread->cpu->preempted) { + bigtime_t quantum = 3000; // ToDo: calculate quantum! + timer *quantumTimer = &oldThread->cpu->quantum_timer; + + if (!oldThread->cpu->preempted) + cancel_timer(quantumTimer); + + oldThread->cpu->preempted = 0; + add_timer(quantumTimer, &reschedule_event, quantum, + B_ONE_SHOT_RELATIVE_TIMER | B_TIMER_ACQUIRE_THREAD_LOCK); + + // update the idle bit for this CPU in the CPU mask + int32 cpuNum = smp_get_current_cpu(); + if (nextThread->priority == B_IDLE_PRIORITY) + sIdleCPUs = SET_BIT(sIdleCPUs, cpuNum); + else + sIdleCPUs = CLEAR_BIT(sIdleCPUs, cpuNum); + + if (nextThread != oldThread) + context_switch(oldThread, nextThread); + } +} + + +/*! This starts the scheduler. Must be run under the context of + the initial idle thread. +*/ +static void +simple_start(void) +{ + cpu_status state = disable_interrupts(); + GRAB_THREAD_LOCK(); + + simple_reschedule(); + + RELEASE_THREAD_LOCK(); + restore_interrupts(state); +} + + +static scheduler_ops kSimpleOps = { + simple_enqueue_in_run_queue, + simple_reschedule, + simple_set_thread_priority, + simple_start +}; + + +// #pragma mark - + + +void +scheduler_simple_init() +{ + gScheduler = &kSimpleOps; + + add_debugger_command_etc("run_queue", &dump_run_queue, + "List threads in run queue", "\nLists threads in run queue", 0); +} diff --git a/src/system/kernel/scheduler/scheduler_simple.h b/src/system/kernel/scheduler/scheduler_simple.h new file mode 100644 index 0000000000..bac69ac78c --- /dev/null +++ b/src/system/kernel/scheduler/scheduler_simple.h @@ -0,0 +1,12 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef KERNEL_SCHEDULER_SIMPLE_H +#define KERNEL_SCHEDULER_SIMPLE_H + + +void scheduler_simple_init(); + + +#endif // KERNEL_SCHEDULER_SIMPLE_H diff --git a/src/system/kernel/scheduler/scheduling_analysis.cpp b/src/system/kernel/scheduler/scheduling_analysis.cpp index 9f6868edf4..7c5e86ee54 100644 --- a/src/system/kernel/scheduler/scheduling_analysis.cpp +++ b/src/system/kernel/scheduler/scheduling_analysis.cpp @@ -228,7 +228,7 @@ public: fRemainingBytes = (addr_t)fHashTable - (addr_t)fBuffer; image_info info; - if (elf_get_image_info_for_address((addr_t)&scheduler_start, &info) + if (elf_get_image_info_for_address((addr_t)&scheduler_init, &info) == B_OK) { fKernelStart = (addr_t)info.text; fKernelEnd = (addr_t)info.data + info.data_size; diff --git a/src/system/kernel/thread.cpp b/src/system/kernel/thread.cpp index 959c037772..11e33541c2 100644 --- a/src/system/kernel/thread.cpp +++ b/src/system/kernel/thread.cpp @@ -2523,14 +2523,7 @@ set_thread_priority(thread_id id, int32 priority) thread = thread_get_thread_struct_locked(id); if (thread) { oldPriority = thread->priority; - thread->next_priority = priority; - if (thread->state == B_THREAD_READY && thread->priority != priority) { - // if the thread is in the run queue, we reinsert it at a new position - scheduler_remove_from_run_queue(thread); - thread->priority = priority; - scheduler_enqueue_in_run_queue(thread); - } else - thread->priority = priority; + scheduler_set_thread_priority(thread, priority); } else oldPriority = B_BAD_THREAD_ID;