File: [local] / funnyos / kern / kern_sched.c (download)
Revision 1.3, Fri Nov 23 13:37:43 2007 UTC (16 years, 7 months ago) by nbrk
Branch: MAIN
CVS Tags: HEAD Changes since 1.2: +118 -5 lines
basic roundrobin multitasking support in FunnyOS!
_vector_irq saves Sys_mode context (which is treated as struct pcb) in stack and
passes sp to irq_trampoline() which then sets global iframep pointer to point to pcb of interrupted task.
"task" given by u_task (set by user at compile-time) and k_task which is dynamically allocated by the kernel.
scheduler uses list of k_task (circularly linked list) with one constant task "idle".
idle discarded in sched_init() with status TASK_NOSCHED and will not be scheduled after first sched_tick().
tasks created in task/ directory and should implement void ttt_enter(void) to start execution from.
tasks added by inserting new elements in config_tasklist[].
please note that current *ugly* design causes strange behavior if tasks printf something w/out delays..
also, we do not save task's CPSR yet..
|
/*
* $Id: kern_sched.c,v 1.3 2007/11/23 13:37:43 nbrk Exp $
*/
#include <sys/types.h>
#include <sys/kern_sched.h>
#include <sys/kern_time.h> /* for HZ */
#include <sys/mem.h>
#include <libkern/printf.h>
/* #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);
}