Orion
Barry Importing existing Orion kernel d41a53c (3 years, 3 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();
+}