nuclear@51: #include nuclear@55: #include nuclear@51: #include "sched.h" nuclear@51: #include "proc.h" nuclear@51: #include "intr.h" nuclear@51: #include "asmops.h" nuclear@51: #include "config.h" nuclear@51: nuclear@55: #define EMPTY(q) ((q)->head == 0) nuclear@51: nuclear@51: struct proc_list { nuclear@51: struct process *head, *tail; nuclear@51: }; nuclear@51: nuclear@55: static void idle_proc(void); nuclear@55: static void ins_back(struct proc_list *list, struct process *proc); nuclear@55: static void ins_front(struct proc_list *list, struct process *proc); nuclear@55: static void remove(struct proc_list *list, struct process *proc); nuclear@55: static int hash_addr(void *addr); nuclear@51: nuclear@51: static struct proc_list runq; nuclear@51: static struct proc_list zombieq; nuclear@51: nuclear@55: #define HTBL_SIZE 101 nuclear@55: static struct proc_list wait_htable[HTBL_SIZE]; nuclear@55: nuclear@55: nuclear@51: void schedule(void) nuclear@51: { nuclear@52: disable_intr(); nuclear@52: nuclear@55: if(EMPTY(&runq)) { nuclear@56: if(!get_current_proc()) { nuclear@56: /* we're already in the idle process, don't reenter it nuclear@56: * or you'll fill up the stack very quickly. nuclear@56: */ nuclear@56: return; nuclear@56: } nuclear@56: nuclear@55: idle_proc(); nuclear@56: return; nuclear@51: } nuclear@51: nuclear@51: /* if the current process exhausted its timeslice, nuclear@51: * move it to the back of the queue. nuclear@51: */ nuclear@51: if(runq.head->ticks_left <= 0) { nuclear@51: if(runq.head->next) { nuclear@51: struct process *proc = runq.head; nuclear@51: remove(&runq, proc); nuclear@51: ins_back(&runq, proc); nuclear@51: } nuclear@51: nuclear@51: /* start a new timeslice */ nuclear@51: runq.head->ticks_left = TIMESLICE_TICKS; nuclear@51: } nuclear@51: nuclear@55: /* always enter context_switch with interrupts disabled */ nuclear@51: context_switch(runq.head->id); nuclear@51: } nuclear@51: nuclear@55: void add_proc(int pid) nuclear@51: { nuclear@51: int istate; nuclear@52: struct process *proc; nuclear@51: nuclear@51: istate = get_intr_state(); nuclear@51: disable_intr(); nuclear@51: nuclear@52: proc = get_process(pid); nuclear@52: nuclear@51: ins_back(&runq, proc); nuclear@53: proc->state = STATE_RUNNABLE; nuclear@51: nuclear@51: set_intr_state(istate); nuclear@51: } nuclear@51: nuclear@55: /* block the process until we get a wakeup call for address ev */ nuclear@55: void wait(void *wait_addr) nuclear@55: { nuclear@55: struct process *p; nuclear@55: int hash_idx; nuclear@51: nuclear@55: disable_intr(); nuclear@55: nuclear@55: p = get_current_proc(); nuclear@55: assert(p); nuclear@55: nuclear@55: /* remove it from the runqueue ... */ nuclear@55: remove(&runq, p); nuclear@55: nuclear@55: /* and place it in the wait hash table based on sleep_addr */ nuclear@55: hash_idx = hash_addr(wait_addr); nuclear@55: ins_back(wait_htable + hash_idx, p); nuclear@55: nuclear@55: p->state = STATE_BLOCKED; nuclear@55: p->wait_addr = wait_addr; nuclear@56: nuclear@56: /* call the scheduler to give time to another process */ nuclear@56: schedule(); nuclear@55: } nuclear@55: nuclear@55: /* wake up all the processes sleeping on this address */ nuclear@55: void wakeup(void *wait_addr) nuclear@51: { nuclear@55: int hash_idx; nuclear@55: struct process *iter; nuclear@55: struct proc_list *list; nuclear@55: nuclear@55: hash_idx = hash_addr(wait_addr); nuclear@55: list = wait_htable + hash_idx; nuclear@55: nuclear@55: iter = list->head; nuclear@55: while(iter) { nuclear@55: if(iter->wait_addr == wait_addr) { nuclear@55: /* found one, remove it, and make it runnable */ nuclear@55: struct process *p = iter; nuclear@55: iter = iter->next; nuclear@55: nuclear@55: remove(list, p); nuclear@55: p->state = STATE_RUNNABLE; nuclear@55: ins_back(&runq, p); nuclear@55: } else { nuclear@55: iter = iter->next; nuclear@55: } nuclear@55: } nuclear@55: } nuclear@55: nuclear@55: static void idle_proc(void) nuclear@55: { nuclear@55: /* make sure we send any pending EOIs if needed. nuclear@55: * end_of_irq will actually check if it's needed first. nuclear@55: */ nuclear@55: struct intr_frame *ifrm = get_intr_frame(); nuclear@55: end_of_irq(INTR_TO_IRQ(ifrm->inum)); nuclear@55: nuclear@56: set_current_pid(0); nuclear@56: nuclear@55: /* make sure interrupts are enabled before halting */ nuclear@56: while(EMPTY(&runq)) { nuclear@56: enable_intr(); nuclear@56: halt_cpu(); nuclear@56: disable_intr(); nuclear@56: } nuclear@55: } nuclear@55: nuclear@55: nuclear@55: /* list operations */ nuclear@55: static void ins_back(struct proc_list *list, struct process *proc) nuclear@55: { nuclear@55: if(EMPTY(list)) { nuclear@55: list->head = proc; nuclear@51: } else { nuclear@55: list->tail->next = proc; nuclear@51: } nuclear@51: nuclear@51: proc->next = 0; nuclear@55: proc->prev = list->tail; nuclear@55: list->tail = proc; nuclear@51: } nuclear@51: nuclear@55: static void ins_front(struct proc_list *list, struct process *proc) nuclear@51: { nuclear@55: if(EMPTY(list)) { nuclear@55: list->tail = proc; nuclear@51: } else { nuclear@55: list->head->prev = proc; nuclear@51: } nuclear@51: nuclear@55: proc->next = list->head; nuclear@51: proc->prev = 0; nuclear@55: list->head = proc; nuclear@51: } nuclear@51: nuclear@55: static void remove(struct proc_list *list, struct process *proc) nuclear@51: { nuclear@51: if(proc->prev) { nuclear@51: proc->prev->next = proc->next; nuclear@51: } nuclear@51: if(proc->next) { nuclear@51: proc->next->prev = proc->prev; nuclear@51: } nuclear@55: if(list->head == proc) { nuclear@55: list->head = proc->next; nuclear@51: } nuclear@55: if(list->tail == proc) { nuclear@55: list->tail = proc->prev; nuclear@51: } nuclear@51: } nuclear@55: nuclear@55: static int hash_addr(void *addr) nuclear@55: { nuclear@55: return (uint32_t)addr % HTBL_SIZE; nuclear@55: }