/* * This file contains the scheduler. It implements a basic task switching * routine, as well as the schedule() function. The scheduler can be called * from anywhere, and will switch to the next task decided by the scheduler * rules. If it cannot find a task to schedule, it just idles until one becomes * available. This avoids the need for an idle task. */ #include #include #define PRIORITY_COUNT 6 TaskQueue *readyQueue[PRIORITY_COUNT]; /* Switch to a task */ static void switch_to_task(Task *task) { lock(current); asm volatile("mov %%esp, %0" : "=r" (current->esp)); asm volatile("mov %%ebp, %0" : "=r" (current->ebp)); current->eip = (uintptr_t) &&end; unlock(current); put(current); current = task; /* Use the passed reference */ asm volatile ( "cli;" "movl %0, %%ecx;" "movl %1, %%esp;" "movl %2, %%ebp;" "movl %3, %%cr3;" "sti;" "jmp *%%ecx" :: "g" (current->eip), "g" (current->esp), "g" (current->ebp), "g" (current->pageDir) ); end: } /* Find the next schedulable ready queue */ static TaskQueue * highest_priority_queue(void) { enum Priority p; for (p = PRIORITY_COUNT - 1; p > 0; p--) { if (readyQueue[p]->start) return readyQueue[p]; } return NULL; } /* Schedule the next task */ void schedule(void) { Task *task = current; TaskQueue *queue = highest_priority_queue(); /* Next schedulable task */ if (queue) { task = pop_from_queue(queue); task->state = RUNNING; if (current->state == RUNNING) { current->state = READY; add_to_queue(readyQueue[current->priority], current); } switch_to_task(task); /* Idle */ } else if (current->state != RUNNING) { current = NULL; asm volatile("sti"); while (!(queue = highest_priority_queue())) asm volatile("hlt"); asm volatile("cli"); current = task; task = pop_from_queue(queue); task->state = RUNNING; switch_to_task(task); } } /* Initialise the scheduler */ void init_scheduler(void) { enum Priority p; for (p = 0; p < PRIORITY_COUNT; p++) readyQueue[p] = new(&taskQueueType); }