diff --git a/src/kernel/core/scheduler.c b/src/kernel/core/scheduler.c index e0fd63a83f..d27078384d 100644 --- a/src/kernel/core/scheduler.c +++ b/src/kernel/core/scheduler.c @@ -21,119 +21,20 @@ #include #include +#define TRACE_SCHEDULER 0 +#if TRACE_SCHEDULER +# define TRACE(x) dprintf x +#else +# define TRACE(x) ; +#endif + + +// prototypes +static int dump_run_queue(int argc, char **argv); static int _rand(void); // The run queue. Holds the threads ready to run ordered by priority. -static struct thread_queue run_q = { NULL, NULL }; -static int dump_run_q(int argc, char **argv); - - -static int -dump_run_q(int argc, char **argv) -{ - struct thread *t; - - t = run_q.head; - if (!t) - dprintf("Run queue is empty!\n"); - else { - while (t) { - dprintf("Thread id: %ld - priority: %d\n", t->id, t->priority); - t = t->q_next; - } - } - - return 0; -} - - -/** Enqueues the thread to the run queue. - * Note: THREAD_LOCK must be held when entering this function - */ - -void -thread_enqueue_run_q(struct thread *t) -{ - struct thread *curr, *prev; - - // these shouldn't exist - if(t->priority > B_MAX_PRIORITY) - t->priority = B_MAX_PRIORITY; - if(t->priority < B_MIN_PRIORITY) - t->priority = B_MIN_PRIORITY; - - if (run_q.head == NULL) { - t->q_next = NULL; - run_q.head = t; - } else { - for (curr = run_q.head, prev = NULL; curr && (curr->priority >= t->priority); curr = curr->q_next) { - if (prev) - prev = prev->q_next; - else - prev = run_q.head; - } - t->q_next = curr; - if (prev) - prev->q_next = t; - else - run_q.head = t; - } -} - - -status_t -thread_set_priority(thread_id id, int32 priority) -{ - struct thread *t, *curr, *prev; - int retval; - - // make sure the passed in priority is within bounds - if (priority > B_MAX_PRIORITY) - priority = B_MAX_PRIORITY; - if (priority < B_MIN_PRIORITY) - priority = B_MIN_PRIORITY; - - t = thread_get_current_thread(); - if (t->id == id) { - // it's ourself, so we know we aren't in the run queue, and we can manipulate - // our structure directly - retval = t->priority; - // note that this might not return the correct value if we are preempted - // here, and another thread changes our priority before the next line is - // executed - t->priority = priority; - } else { - int state = disable_interrupts(); - GRAB_THREAD_LOCK(); - - t = thread_get_thread_struct_locked(id); - if (t) { - retval = t->priority; - if ((t->state == B_THREAD_READY) && (t->priority != priority)) { - // this thread is in the ready queue right now, so it needs to be reinserted - for (curr = run_q.head, prev = NULL; curr && (curr->id != t->id); curr = curr->q_next) { - if (prev) - prev = prev->q_next; - else - prev = run_q.head; - } - if (prev) - prev->q_next = curr->q_next; - else - run_q.head = curr->q_next; - t->priority = priority; - thread_enqueue_run_q(t); - } else - t->priority = priority; - } else - retval = B_BAD_THREAD_ID; - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - } - - return retval; -} +static struct thread_queue gRunQueue = {NULL, NULL}; static int @@ -149,8 +50,192 @@ _rand(void) } -// this starts the scheduler. Must be run under the context of -// the initial idle thread. +static int +dump_run_queue(int argc, char **argv) +{ + struct thread *thread; + + thread = gRunQueue.head; + if (!thread) + dprintf("Run queue is empty!\n"); + else { + while (thread) { + dprintf("Thread id: %ld - priority: %d\n", thread->id, thread->priority); + 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) +{ + struct thread *curr, *prev; + + // these shouldn't exist + if (thread->priority > B_MAX_PRIORITY) + thread->priority = B_MAX_PRIORITY; + if (thread->priority < B_MIN_PRIORITY) + thread->priority = B_MIN_PRIORITY; + + for (curr = gRunQueue.head, prev = NULL; curr && (curr->priority >= thread->priority); curr = curr->queue_next) { + if (prev) + prev = prev->queue_next; + else + prev = gRunQueue.head; + } + thread->queue_next = curr; + if (prev) + prev->queue_next = thread; + else + gRunQueue.head = thread; +} + + +/** 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; + + // find thread in run queue + for (item = gRunQueue.head, prev = NULL; item && item != thread; item = item->queue_next) { + if (prev) + prev = prev->queue_next; + else + prev = gRunQueue.head; + } + + ASSERT(item == thread); + + if (prev) + prev->queue_next = item->queue_next; + else + gRunQueue.head = item->queue_next; +} + + +static void +context_switch(struct thread *fromThread, struct thread *toThread) +{ + bigtime_t now; + + // track kernel time + now = system_time(); + fromThread->kernel_time += now - fromThread->last_time; + toThread->last_time = now; + + toThread->cpu = fromThread->cpu; + fromThread->cpu = NULL; + + arch_thread_set_current_thread(toThread); + arch_thread_context_switch(fromThread, toThread); +} + + +static int32 +reschedule_event(timer *unused) +{ + // 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->info.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 = 0x%x\n", smp_get_current_cpu(), thread_get_current_thread())); + + switch (oldThread->next_state) { + case B_THREAD_RUNNING: + case B_THREAD_READY: + TRACE(("enqueueing thread 0x%x into run q. pri = %d\n", oldThread, oldThread->priority)); + scheduler_enqueue_in_run_queue(oldThread); + break; + case B_THREAD_SUSPENDED: + TRACE(("reschedule(): suspending thread 0x%lx\n", oldThread->id)); + break; + case THREAD_STATE_FREE_ON_RESCHED: + // This will hopefully be eliminated once the slab + // allocator is done + thread_enqueue(oldThread, &dead_q); + break; + default: + TRACE(("not enqueueing thread 0x%x into run q. next_state = %d\n", old_thread, old_thread->next_state)); + break; + } + oldThread->state = oldThread->next_state; + + // select next thread from the run queue + nextThread = gRunQueue.head; + prevThread = NULL; + while (nextThread && (nextThread->priority > B_IDLE_PRIORITY)) { + // 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 + if (_rand() > 0x3000) + break; + + prevThread = nextThread; + nextThread = nextThread->queue_next; + } + + 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 + gRunQueue.head = nextThread->queue_next; + + nextThread->state = B_THREAD_RUNNING; + nextThread->next_state = B_THREAD_READY; + + if (nextThread != oldThread || oldThread->cpu->info.preempted) { + bigtime_t quantum = 3000; // ToDo: calculate quantum! + timer *quantum_timer= &oldThread->cpu->info.quantum_timer; + + if (!oldThread->cpu->info.preempted) + _local_timer_cancel_event(oldThread->cpu->info.cpu_num, quantum_timer); + + oldThread->cpu->info.preempted = 0; + add_timer(quantum_timer, &reschedule_event, quantum, B_ONE_SHOT_RELATIVE_TIMER); + + if (nextThread != oldThread) + context_switch(oldThread, nextThread); + } +} + + +/** This starts the scheduler. Must be run under the context of + * the initial idle thread. + */ + void start_scheduler(void) { @@ -169,113 +254,12 @@ start_scheduler(void) state = disable_interrupts(); GRAB_THREAD_LOCK(); - resched(); + scheduler_reschedule(); RELEASE_THREAD_LOCK(); restore_interrupts(state); - - add_debugger_command("run_q", &dump_run_q, "list threads in run queue"); + + add_debugger_command("run_queue", &dump_run_queue, "list threads in run queue"); } -static void -context_switch(struct thread *t_from, struct thread *t_to) -{ - bigtime_t now; - - // track kernel time - now = system_time(); - t_from->kernel_time += now - t_from->last_time; - t_to->last_time = now; - - t_to->cpu = t_from->cpu; - arch_thread_set_current_thread(t_to); - t_from->cpu = NULL; - arch_thread_context_switch(t_from, t_to); -} - - -static int32 -reschedule_event(timer *unused) -{ - // 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->info.preempted= 1; - return B_INVOKE_SCHEDULER; -} - - -// runs the scheduler. -// NOTE: expects thread_spinlock to be held -void -resched(void) -{ - struct thread *next_thread, *prev_thread; - struct thread *old_thread = thread_get_current_thread(); - bigtime_t quantum; - timer *quantum_timer; - -// dprintf("resched(): cpu %d, cur_thread = 0x%x\n", smp_get_current_cpu(), thread_get_current_thread()); - - switch(old_thread->next_state) { - case B_THREAD_RUNNING: - case B_THREAD_READY: -// dprintf("enqueueing thread 0x%x into run q. pri = %d\n", old_thread, old_thread->priority); - thread_enqueue_run_q(old_thread); - break; - case B_THREAD_SUSPENDED: - dprintf("resched(): suspending thread 0x%lx\n", old_thread->id); - break; - case THREAD_STATE_FREE_ON_RESCHED: - // This will hopefully be eliminated once the slab - // allocator is done - thread_enqueue(old_thread, &dead_q); - break; - default: -// dprintf("not enqueueing thread 0x%x into run q. next_state = %d\n", old_thread, old_thread->next_state); - ; - } - old_thread->state = old_thread->next_state; - - // select next thread from the run queue - next_thread = run_q.head; - prev_thread = NULL; - while ((next_thread) && (next_thread->priority > B_IDLE_PRIORITY)) { - // always extract real time threads - if (next_thread->priority >= B_FIRST_REAL_TIME_PRIORITY) - break; - // never skip last non-idle normal thread - if (next_thread->q_next && (next_thread->q_next->priority == B_IDLE_PRIORITY)) - break; - // skip normal threads sometimes - if (_rand() > 0x3000) - break; - prev_thread = next_thread; - next_thread = next_thread->q_next; - } - - if (!next_thread) - panic("resched(): run queue is empty!\n"); - - // extract selected thread from the run queue - if (prev_thread) - prev_thread->q_next = next_thread->q_next; - else - run_q.head = next_thread->q_next; - - next_thread->state = B_THREAD_RUNNING; - next_thread->next_state = B_THREAD_READY; - - if ((next_thread != old_thread) || (old_thread->cpu->info.preempted)) { - // XXX calculate quantum - quantum = 3000; - quantum_timer = &old_thread->cpu->info.quantum_timer; - if (!old_thread->cpu->info.preempted) - _local_timer_cancel_event(old_thread->cpu->info.cpu_num, quantum_timer); - old_thread->cpu->info.preempted = 0; - add_timer(quantum_timer, &reschedule_event, quantum, B_ONE_SHOT_RELATIVE_TIMER); - if (next_thread != old_thread) - context_switch(old_thread, next_thread); - } -} -