[BACK]Return to kern_sched.c CVS log [TXT][DIR] Up to [local] / funnyos / kern

File: [local] / funnyos / kern / kern_sched.c (download)

Revision 1.3, Fri Nov 23 13:37:43 2007 UTC (16 years, 5 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);

}