/* * $Id: kern_sched.c,v 1.3 2007/11/23 13:37:43 nbrk Exp $ */ #include #include #include /* for HZ */ #include #include /* #define SCHED_DEBUG */ /* #define SCHED_REGDEBUG */ #ifdef SCHED_DEBUG #define DPRINTF(x...) do { printf(x); } while (0) #else #define DPRINTF(x...) { } #endif #ifdef SCHED_REGDEBUG #define DRPRINTF(x...) do { printf(x); } while (0) #else #define DRPRINTF(x...) { } #endif /* config.c gives us tasks that we'll schedule */ extern struct u_task config_tasklist[]; /* list of k_tasks */ struct k_task *ktasklist; /* current running task */ struct k_task *curktask; /* pcb of running task; we will save context there when enter irq_mode */ struct pcb *curpcb; /* interrupt frame */ extern struct pcb *iframep; /* total tasks; used to TID generation */ uint8_t ntasks; /* "idle" task exists forever and occupy cpu when there is no other task pretending. */ const struct u_task idle_utask = {"idle", 0, NULL}; void sched_init(void) { /* * Create struct k_task for each u_task that user described in config_tasklist. */ struct u_task *utaskp; struct k_task *ktaskp, *oldktaskp; ntasks = 0; /* * Create an "idle" task which always has TID 0. */ ktaskp = kmalloc(sizeof(struct k_task)); if (ktaskp == NULL) panic("can't allocate memory for idle task\n"); /* link u_task for idle task */ ktaskp->kt_utask = (struct u_task *)&idle_utask; /* TID (always 0) */ ktaskp->kt_tid = 0; /* idle task is always in state READY (or RUNNING, if on processor now) */ ktaskp->kt_state = TASK_READY; /* next task in list isn't up yet (and prev too) */ ktaskp->kt_next = NULL; /* make "idle" task first task in ktasklist, system k_task list */ ktasklist = ktaskp; /* bump ntasks to 1 */ ntasks = 1; /* preserve pointer to this k_task so we can link-in next task */ oldktaskp = ktaskp; /* * Idle task set up completed. * Configure all other system tasks, */ /* look through tasklist */ utaskp = config_tasklist; while(utaskp->ut_name != NULL) { /* try to allocate memory for ktask */ ktaskp = kmalloc(sizeof(struct k_task)); if (ktaskp == NULL) panic("can't allocate memory for task '%s'\n", utaskp->ut_name); /* * Successfully allocated new ktask. */ /* link u_task */ ktaskp->kt_utask = utaskp; /* set TID (Task Identificator) */ ktaskp->kt_tid = oldktaskp->kt_tid + 1; /* state */ ktaskp->kt_state = TASK_READY; /* set intitial entry point (initial pc) to task_enter */ ktaskp->kt_pcb.p_pc = (uint32_t)ktaskp->kt_utask->ut_enter; /* link current task in previous task's structure */ oldktaskp->kt_next = ktaskp; /* NULLify kt_next of current task */ ktaskp->kt_next = NULL; /* save current ktask for use with next ktask */ oldktaskp = ktaskp; /* bump ntasks */ ntasks++; /* shift to next element in config_tasklist[] */ utaskp++; } /* make this list circularly linked (e.g point list's last->next node into first) */ ktaskp->kt_next = ktasklist; DPRINTF("kern_sched: created %d tasks:", ntasks); /* print tasks' names */ ktaskp = ktasklist + 1; /* skip (first) "idle" task (display it as the end of circularly list) */ do { DPRINTF(" %s", ktaskp->kt_utask->ut_name); if (ktaskp->kt_tid == NULL) /* that was an "idle" task */ break; ktaskp = ktaskp->kt_next; } while(1); DPRINTF("; HZ=%d\n", HZ); /* * Run "idle" task in NOSCHED, so it will not schedule after first context siwtch; */ ktasklist->kt_state = TASK_NOSCHED; curktask = ktasklist; /* ktasklist always points to "idle" task */ /* point curpcb */ curpcb = &ktasklist->kt_pcb; } void sched_tick(void) { /* * Schedule next task that will occupy cpu (if any). * Remember that we are in IRQ_mode here. */ /* * All we need to do now is: * - see if context switch is required * - if so, save iframe into curpcb (save hardware context) * - select next task to run * - point curpcb to pcb of new task * - point curktask to k_task of new task * - copy curpcb into iframep (so we'll continue in another task when we exit irq_mode) * - if context switch is not required * - XXX we will not copy curpcb into iframep? (cause we end up in same task at next irq) * - anyway, account time, etc.. */ /* XXX use pre-emtive multitasking to simplify things for now! */ /* save hardware state of interrupted task */ *curpcb = *iframep; /* "suspend" interrupted task */ if(curktask->kt_state == TASK_RUNNING) /* XXX ugly hack to skip "idle" which is NOSCHED */ curktask->kt_state = TASK_READY; DPRINTF("sched_tick: task tid=%d (\"%s\") suspended\n", curktask->kt_tid, curktask->kt_utask->ut_name); DRPRINTF("r0=0x%x\n", iframep->p_r0); DRPRINTF("r1=0x%x\n", iframep->p_r1); DRPRINTF("r2=0x%x\n", iframep->p_r2); DRPRINTF("r3=0x%x\n", iframep->p_r3); DRPRINTF("r4=0x%x\n", iframep->p_r4); DRPRINTF("r5=0x%x\n", iframep->p_r5); DRPRINTF("r6=0x%x\n", iframep->p_r6); DRPRINTF("r7=0x%x\n", iframep->p_r7); DRPRINTF("r8=0x%x\n", iframep->p_r8); DRPRINTF("r9=0x%x\n", iframep->p_r9); DRPRINTF("r10=0x%x\n", iframep->p_r10); DRPRINTF("r11=0x%x\n", iframep->p_r11); DRPRINTF("r12=0x%x\n", iframep->p_r12); DRPRINTF("pc=0x%x\n", iframep->p_pc); /* round-robin select next task in circlelist */ curktask = curktask->kt_next; /* look for next ready task */ while(curktask->kt_state != TASK_READY) curktask = curktask->kt_next; /* run selected task */ curktask->kt_state = TASK_RUNNING; /* point curpcb into selected task's pcb */ curpcb = &curktask->kt_pcb; /* restore selected task's hardware context into an IRQ stack */ *iframep = *curpcb; DPRINTF("sched_tick: task tid=%d (\"%s\") resumed\n", curktask->kt_tid, curktask->kt_utask->ut_name); DRPRINTF("r0=0x%x\n", iframep->p_r0); DRPRINTF("r1=0x%x\n", iframep->p_r1); DRPRINTF("r2=0x%x\n", iframep->p_r2); DRPRINTF("r3=0x%x\n", iframep->p_r3); DRPRINTF("r4=0x%x\n", iframep->p_r4); DRPRINTF("r5=0x%x\n", iframep->p_r5); DRPRINTF("r6=0x%x\n", iframep->p_r6); DRPRINTF("r7=0x%x\n", iframep->p_r7); DRPRINTF("r8=0x%x\n", iframep->p_r8); DRPRINTF("r9=0x%x\n", iframep->p_r9); DRPRINTF("r10=0x%x\n", iframep->p_r10); DRPRINTF("r11=0x%x\n", iframep->p_r11); DRPRINTF("r12=0x%x\n", iframep->p_r12); DRPRINTF("pc=0x%x\n", iframep->p_pc); }