/* * 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 void context_switch(uintptr_t eip, page_dir_t pagedir, uintptr_t esi, uintptr_t edi, uintptr_t ebx, uintptr_t ebp, uintptr_t esp); ObjectList *readyQueue[PRIORITY_COUNT]; /* Switch to a task */ static void switch_to_task(Task *task) { /* Save current task state */ if (__builtin_expect(!!current, 1)) { lock(current); asm volatile("mov %%esi, %0" : "=r" (current->esi)); asm volatile("mov %%edi, %0" : "=r" (current->edi)); asm volatile("mov %%ebx, %0" : "=r" (current->ebx)); 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); } /* Switch to new context */ current = task; /* Given reference, so no get() */ context_switch(current->eip, current->pageDir, current->esi, current->edi, current->ebx, current->ebp, current->esp); end: /* This prevents GCC from optimising the jump to be after the return */ asm volatile("":::"memory"); } /* Find the next schedulable ready queue */ static ObjectList * highest_priority_queue(void) { enum Priority p; for (p = PRIORITY_COUNT - 1; p > 0; p--) { if (count(readyQueue[p])) return readyQueue[p]; } return NULL; } /* Schedule the next task */ void schedule(void) { if (current && current->inCriticalSection) return; Task *task = current; ObjectList *queue = highest_priority_queue(); /* Idle if necessary */ if (!queue) { if (current && current->state == RUNNING) return; current = NULL; asm volatile("sti"); while (!(queue = highest_priority_queue())) asm volatile("hlt"); asm volatile("cli"); current = task; } /* Schedule next task */ task = pop_from_start(queue); task->state = RUNNING; if (task == current) return; if (current && current->state == RUNNING) { current->state = READY; add(readyQueue[current->priority], current); } switch_to_task(task); } /* Initialise the scheduler */ void init_scheduler(void) { enum Priority p; for (p = 0; p < PRIORITY_COUNT; p++) readyQueue[p] = create_list(&taskType, LIST_NORMAL); }