Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)diff --git a/task/schedule.c b/task/schedule.c new file mode 100644 index 0000000..1c00576 --- /dev/null +++ b/task/schedule.c @@ -0,0 +1,182 @@ +/* + * This file controls the Kernel's scheduler. It decides when a new task must + * be scheduled, and which tasks can actually be scheduled. + */ + +#include <stdint.h> +#include "../proc/proc.h" +#include "../mem/paging.h" +#include "task.h" + +uintptr_t read_eip(void); +void context_switch(uintptr_t eip, page_dir_t phys, + uintptr_t ebp, uintptr_t esp); +void handle_signals(void); + +TaskQueue readyQueue, tasks; +Task *currentTask[MAX_CPUS]; + +/* Switch to a task */ +static void +switch_to_task(Task *task) +{ + uintptr_t esp, ebp, eip; + asm volatile("mov %%esp, %0" : "=r" (esp)); + asm volatile("mov %%ebp, %0" : "=r" (ebp)); + eip = read_eip(); + if (eip == 0x10032004) /* Magic number */ + return; + + acquire(¤t->lock); + current->eip = eip; + current->esp = esp; + current->ebp = ebp; + release(¤t->lock); + current = task; + eip = current->eip; + esp = current->esp; + ebp = current->ebp; + + context_switch(eip, current->pageDir, ebp, esp); + /* UNREACHED */ + + /* + * This code actually returns to the read_eip() call above. This is due + * to how the call works. The context switch jumps to the address + * stored in the eip variable. This address is the return address of + * the read_eip() call. The context_switch() function sets the return + * value before jumping, so it appears as though read_eip() returned + * that value. The code uses the magic number 0x10032004 to tell when + * this is occurring and just returns early. + */ +} + +/* Find task by ID */ +Task * +find_task(pid_t tid) +{ + Task *task; + for (task = tasks.start; task && task->tid != tid; task = task->tnext); + return task; +} + +/* Add a task to a task queue */ +void +add_to_queue(TaskQueue *queue, Task *task) +{ + if (!queue->start) { + queue->start = task; + queue->end = task; + } else { + queue->end->next = task; + queue->end = task; + } + task->next = NULL; +} + +/* Remove a task from a task queue */ +void +remove_from_queue(TaskQueue *queue, Task *task) +{ + /* Start of list */ + if (queue->start == task) { + queue->start = task->next; + if (!queue->start) + queue->end = NULL; + return; + } + + /* Search */ + Task *prev; + for (prev = queue->start; prev->next; prev = prev->next) + if (prev->next == task) + break; + if (prev->next) { + prev->next = task->next; + if (queue->end == task) + queue->end = prev; + } +} + +/* Remove the first task from a task queue */ +Task * +pop_from_queue(TaskQueue *queue) +{ + Task *head = queue->start; + queue->start = head->next; + if (!queue->start) + queue->end = NULL; + return head; +} + +/* Block a task */ +void +block_task(int reason) +{ + acquire(¤t->lock); + current->state = reason; + release(¤t->lock); + schedule(); +} + +/* Unblock a task */ +void +unblock_task(Task *task) +{ + if (task->state == READY || task->state == RUNNING) + return; + task->state = READY; + if (!readyQueue.start || task->priority > current->priority) { + acquire(&readyQueue.lock); + task->next = readyQueue.start; + readyQueue.start = task; + if (!readyQueue.end) + readyQueue.end = task; + release(&readyQueue.lock); + } else { + add_to_queue(&readyQueue, task); + } +} + +/* Schedule next task */ +void +schedule(void) +{ + Task *task = current; + + /* Next schedulable task */ + if (readyQueue.start) { + acquire(&readyQueue.lock); + task = readyQueue.start; + readyQueue.start = task->next; + if (!readyQueue.start) + readyQueue.end = NULL; + task->state = RUNNING; + task->next = NULL; + if (current->state == RUNNING) { + current->state = READY; + add_to_queue(&readyQueue, current); + } + release(&readyQueue.lock); + switch_to_task(task); + /* Idle task */ + } else if (current->state != RUNNING) { + current = NULL; + asm volatile("sti"); + while (!readyQueue.start) + asm volatile("hlt"); + asm volatile("cli"); + current = task; + acquire(&readyQueue.lock); + task = readyQueue.start; + readyQueue.start = task->next; + if (!readyQueue.start) + readyQueue.end = NULL; + task->state = RUNNING; + task->next = NULL; + release(&readyQueue.lock); + switch_to_task(task); + } + + handle_signals(); +}