kern

annotate src/sched.c @ 55:88a6c4e192f9

Fixed most important task switching bugs. Now it seems that I can switch in and out of user space reliably.
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 15 Aug 2011 04:03:39 +0300
parents 23abbeea4d5f
children 0be4615594df
rev   line source
nuclear@51 1 #include <stdio.h>
nuclear@55 2 #include <assert.h>
nuclear@51 3 #include "sched.h"
nuclear@51 4 #include "proc.h"
nuclear@51 5 #include "intr.h"
nuclear@51 6 #include "asmops.h"
nuclear@51 7 #include "config.h"
nuclear@51 8
nuclear@55 9 #define EMPTY(q) ((q)->head == 0)
nuclear@51 10
nuclear@51 11 struct proc_list {
nuclear@51 12 struct process *head, *tail;
nuclear@51 13 };
nuclear@51 14
nuclear@55 15 static void idle_proc(void);
nuclear@55 16 static void ins_back(struct proc_list *list, struct process *proc);
nuclear@55 17 static void ins_front(struct proc_list *list, struct process *proc);
nuclear@55 18 static void remove(struct proc_list *list, struct process *proc);
nuclear@55 19 static int hash_addr(void *addr);
nuclear@51 20
nuclear@51 21 static struct proc_list runq;
nuclear@51 22 static struct proc_list zombieq;
nuclear@51 23
nuclear@55 24 #define HTBL_SIZE 101
nuclear@55 25 static struct proc_list wait_htable[HTBL_SIZE];
nuclear@55 26
nuclear@55 27
nuclear@51 28 void schedule(void)
nuclear@51 29 {
nuclear@52 30 disable_intr();
nuclear@52 31
nuclear@55 32 if(EMPTY(&runq)) {
nuclear@55 33 idle_proc();
nuclear@51 34 /* this won't return, it'll just wake up in an interrupt later */
nuclear@51 35 }
nuclear@51 36
nuclear@51 37 /* if the current process exhausted its timeslice,
nuclear@51 38 * move it to the back of the queue.
nuclear@51 39 */
nuclear@51 40 if(runq.head->ticks_left <= 0) {
nuclear@51 41 if(runq.head->next) {
nuclear@51 42 struct process *proc = runq.head;
nuclear@51 43 remove(&runq, proc);
nuclear@51 44 ins_back(&runq, proc);
nuclear@51 45 }
nuclear@51 46
nuclear@51 47 /* start a new timeslice */
nuclear@51 48 runq.head->ticks_left = TIMESLICE_TICKS;
nuclear@51 49 }
nuclear@51 50
nuclear@55 51 /* always enter context_switch with interrupts disabled */
nuclear@51 52 context_switch(runq.head->id);
nuclear@51 53 }
nuclear@51 54
nuclear@55 55 void add_proc(int pid)
nuclear@51 56 {
nuclear@51 57 int istate;
nuclear@52 58 struct process *proc;
nuclear@51 59
nuclear@51 60 istate = get_intr_state();
nuclear@51 61 disable_intr();
nuclear@51 62
nuclear@52 63 proc = get_process(pid);
nuclear@52 64
nuclear@51 65 ins_back(&runq, proc);
nuclear@53 66 proc->state = STATE_RUNNABLE;
nuclear@51 67
nuclear@51 68 set_intr_state(istate);
nuclear@51 69 }
nuclear@51 70
nuclear@55 71 /* block the process until we get a wakeup call for address ev */
nuclear@55 72 void wait(void *wait_addr)
nuclear@55 73 {
nuclear@55 74 struct process *p;
nuclear@55 75 int hash_idx;
nuclear@51 76
nuclear@55 77 disable_intr();
nuclear@55 78
nuclear@55 79 p = get_current_proc();
nuclear@55 80 assert(p);
nuclear@55 81
nuclear@55 82 /* remove it from the runqueue ... */
nuclear@55 83 remove(&runq, p);
nuclear@55 84
nuclear@55 85 /* and place it in the wait hash table based on sleep_addr */
nuclear@55 86 hash_idx = hash_addr(wait_addr);
nuclear@55 87 ins_back(wait_htable + hash_idx, p);
nuclear@55 88
nuclear@55 89 p->state = STATE_BLOCKED;
nuclear@55 90 p->wait_addr = wait_addr;
nuclear@55 91 }
nuclear@55 92
nuclear@55 93 /* wake up all the processes sleeping on this address */
nuclear@55 94 void wakeup(void *wait_addr)
nuclear@51 95 {
nuclear@55 96 int hash_idx;
nuclear@55 97 struct process *iter;
nuclear@55 98 struct proc_list *list;
nuclear@55 99
nuclear@55 100 hash_idx = hash_addr(wait_addr);
nuclear@55 101 list = wait_htable + hash_idx;
nuclear@55 102
nuclear@55 103 iter = list->head;
nuclear@55 104 while(iter) {
nuclear@55 105 if(iter->wait_addr == wait_addr) {
nuclear@55 106 /* found one, remove it, and make it runnable */
nuclear@55 107 struct process *p = iter;
nuclear@55 108 iter = iter->next;
nuclear@55 109
nuclear@55 110 remove(list, p);
nuclear@55 111 p->state = STATE_RUNNABLE;
nuclear@55 112 ins_back(&runq, p);
nuclear@55 113 } else {
nuclear@55 114 iter = iter->next;
nuclear@55 115 }
nuclear@55 116 }
nuclear@55 117 }
nuclear@55 118
nuclear@55 119 static void idle_proc(void)
nuclear@55 120 {
nuclear@55 121 /* make sure we send any pending EOIs if needed.
nuclear@55 122 * end_of_irq will actually check if it's needed first.
nuclear@55 123 */
nuclear@55 124 struct intr_frame *ifrm = get_intr_frame();
nuclear@55 125 end_of_irq(INTR_TO_IRQ(ifrm->inum));
nuclear@55 126
nuclear@55 127 /* make sure interrupts are enabled before halting */
nuclear@55 128 enable_intr();
nuclear@55 129 halt_cpu();
nuclear@55 130 }
nuclear@55 131
nuclear@55 132
nuclear@55 133 /* list operations */
nuclear@55 134 static void ins_back(struct proc_list *list, struct process *proc)
nuclear@55 135 {
nuclear@55 136 if(EMPTY(list)) {
nuclear@55 137 list->head = proc;
nuclear@51 138 } else {
nuclear@55 139 list->tail->next = proc;
nuclear@51 140 }
nuclear@51 141
nuclear@51 142 proc->next = 0;
nuclear@55 143 proc->prev = list->tail;
nuclear@55 144 list->tail = proc;
nuclear@51 145 }
nuclear@51 146
nuclear@55 147 static void ins_front(struct proc_list *list, struct process *proc)
nuclear@51 148 {
nuclear@55 149 if(EMPTY(list)) {
nuclear@55 150 list->tail = proc;
nuclear@51 151 } else {
nuclear@55 152 list->head->prev = proc;
nuclear@51 153 }
nuclear@51 154
nuclear@55 155 proc->next = list->head;
nuclear@51 156 proc->prev = 0;
nuclear@55 157 list->head = proc;
nuclear@51 158 }
nuclear@51 159
nuclear@55 160 static void remove(struct proc_list *list, struct process *proc)
nuclear@51 161 {
nuclear@51 162 if(proc->prev) {
nuclear@51 163 proc->prev->next = proc->next;
nuclear@51 164 }
nuclear@51 165 if(proc->next) {
nuclear@51 166 proc->next->prev = proc->prev;
nuclear@51 167 }
nuclear@55 168 if(list->head == proc) {
nuclear@55 169 list->head = proc->next;
nuclear@51 170 }
nuclear@55 171 if(list->tail == proc) {
nuclear@55 172 list->tail = proc->prev;
nuclear@51 173 }
nuclear@51 174 }
nuclear@55 175
nuclear@55 176 static int hash_addr(void *addr)
nuclear@55 177 {
nuclear@55 178 return (uint32_t)addr % HTBL_SIZE;
nuclear@55 179 }