4 * Copyright 2001,2004 Develer S.r.l. (http://www.develer.com/)
5 * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
6 * This file is part of DevLib - See devlib/README for information.
9 * \brief Simple realtime multitasking scheduler.
10 * Context switching is only done cooperatively.
14 * \author Bernardo Innocenti <bernie@develer.com>
15 * \author Stefano Fedrigo <aleph@develer.com>
20 * Revision 1.8 2004/08/14 19:37:57 rasky
21 * Merge da SC: macros.h, pool.h, BIT_CHANGE, nome dei processi, etc.
23 * Revision 1.7 2004/08/02 20:20:29 aleph
24 * Merge from project_ks
26 * Revision 1.6 2004/07/30 14:24:16 rasky
27 * Task switching con salvataggio perfetto stato di interrupt (SR)
28 * Kernel monitor per dump informazioni su stack dei processi
30 * Revision 1.5 2004/07/14 14:18:09 rasky
31 * Merge da SC: Rimosso timer dentro il task, che รจ uno spreco di memoria per troppi task
33 * Revision 1.4 2004/07/13 19:21:28 aleph
34 * Avoid warning for unused arg when compiled without some CONFIG_KERN_xx options
36 * Revision 1.3 2004/06/06 18:37:57 bernie
37 * Rename event macros to look like regular functions.
39 * Revision 1.2 2004/06/03 11:27:09 bernie
40 * Add dual-license information.
42 * Revision 1.1 2004/05/23 17:27:00 bernie
43 * Import kern/ subdirectory.
52 #include <drv/kdebug.h>
54 #include <string.h> /* memset() */
56 /*! CPU dependent context switching routines
57 * \note This function *MUST* preserve also the status of the interrupts.
59 extern void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp);
60 extern int asm_switch_version(void);
63 * The scheduer tracks ready and waiting processes
64 * by enqueuing them in these lists. A pointer to the currently
65 * running process is stored in the CurrentProcess pointer.
67 * NOTE: these variables are protected by DI/EI locking
69 REGISTER Process *CurrentProcess;
70 REGISTER List ProcReadyList;
73 #if CONFIG_KERN_PREEMPTIVE
75 * The time sharing scheduler forces a task switch when
76 * the current process has consumed its quantum.
82 /* In Win32 we must emulate stack on the real process stack */
83 #if (ARCH & ARCH_EMUL)
84 extern List StackFreeList;
87 /* The main process (the one that executes main()) */
88 struct Process MainProcess;
91 #if CONFIG_KERN_MONITOR
94 static void monitor_init(void)
96 INITLIST(&MonitorProcs);
99 static void monitor_add(Process* proc, const char* name, cpustack_t* stack_base, size_t stack_size)
101 proc->monitor.name = name;
102 proc->monitor.stack_base = stack_base;
103 proc->monitor.stack_size = stack_size;
105 ADDTAIL(&MonitorProcs, &proc->monitor.link);
108 static void monitor_remove(Process* proc)
110 REMOVE(&proc->monitor.link);
113 #define MONITOR_NODE_TO_PROCESS(node) \
114 (struct Process*)((char*)(node) - offsetof(struct Process, monitor.link))
116 size_t monitor_check_stack(cpustack_t* stack_base, size_t stack_size)
124 end = stack_base + stack_size / sizeof(cpustack_t) - 1;
126 if (CPU_STACK_GROWS_UPWARD)
136 if (*cur != CONFIG_KERN_STACKFILLCODE)
139 if (CPU_STACK_GROWS_UPWARD)
145 sp_free = ABS(cur - beg) * sizeof(cpustack_t);
149 void monitor_debug_stacks(void)
154 if (ISLISTEMPTY(&MonitorProcs))
156 kprintf("No stacks registered in the monitor\n");
160 kprintf("%-24s %-6s%-8s%-8s%-8s\n", "Process name", "TCB", "SPbase", "SPsize", "SPfree");
165 for (p = MONITOR_NODE_TO_PROCESS(MonitorProcs.head);
166 p->monitor.link.succ;
167 p = MONITOR_NODE_TO_PROCESS(p->monitor.link.succ))
169 size_t free = monitor_check_stack(p->monitor.stack_base, p->monitor.stack_size);
170 kprintf("%-24s %04x %04x %4x %4x\n", p->monitor.name, (uint16_t)p, (uint16_t)p->monitor.stack_base, (uint16_t)p->monitor.stack_size, (uint16_t)free);
177 static void proc_init_struct(Process* proc)
179 /* Avoid warning for unused argument */
182 #if CONFIG_KERN_SIGNALS
194 INITLIST(&ProcReadyList);
196 #if CONFIG_KERN_MONITOR
200 /* We "promote" the current context into a real process. The only thing we have
201 * to do is create a PCB and make it current. We don't need to setup the stack
202 * pointer because it will be written the first time we switch to another process.
204 proc_init_struct(&MainProcess);
205 CurrentProcess = &MainProcess;
207 /* Make sure the assembly routine is up-to-date with us */
208 ASSERT(asm_switch_version() == 1);
213 * Create a new process, starting at the provided entry point.
215 * \return Process structure of new created process
216 * if successful, NULL otherwise.
218 struct Process *proc_new_with_name(UNUSED(const char*, name), void (*entry)(void), IPTR data, size_t stacksize, cpustack_t *stack_base)
223 size_t proc_size_words = ROUND2(sizeof(Process), sizeof(cpustack_t)) / sizeof(cpustack_t);
225 bool free_stack = false;
228 #if (ARCH & ARCH_EMUL)
229 /* Ignore stack provided by caller
230 * and use the large enough default instead
232 stack_base = (cpustack_t *)StackFreeList.head;
233 REMOVE((Node *)stack_base);
234 stacksize = DEF_STACKSIZE;
235 #elif CONFIG_KERN_HEAP
236 /* Did the caller provide a stack for us? */
239 /* Did the caller specify the desired stack size? */
241 stacksize = CONFIG_KERN_DEFSTACKSIZE + sizeof(Process);
243 /* Allocate stack dinamically */
244 if (!(stack_base = heap_alloc(stacksize)))
250 /* Stack must have been provided by the user */
255 #if CONFIG_KERN_MONITOR
256 /* Fill-in the stack with a special marker to help debugging */
257 memset(stack_base, CONFIG_KERN_STACKFILLCODE, stacksize / sizeof(cpustack_t));
260 /* Initialize the process control block */
261 if (CPU_STACK_GROWS_UPWARD)
263 proc = (Process*)stack_base;
264 proc->stack = stack_base + proc_size_words;
265 if (CPU_SP_ON_EMPTY_SLOT)
270 proc = (Process*)(stack_base + stacksize / sizeof(cpustack_t) - proc_size_words);
271 proc->stack = (cpustack_t*)proc;
272 if (CPU_SP_ON_EMPTY_SLOT)
276 proc_init_struct(proc);
277 proc->user_data = data;
280 proc->stack_base = stack_base;
281 proc->stack_size = stack_size;
283 proc->flags |= PF_FREESTACK;
286 /* Initialize process stack frame */
287 CPU_PUSH_CALL_CONTEXT(proc->stack, proc_exit);
288 CPU_PUSH_CALL_CONTEXT(proc->stack, entry);
290 /* Push a clean set of CPU registers for asm_switch_context() */
291 for (i = 0; i < CPU_SAVED_REGS_CNT; i++)
292 CPU_PUSH_WORD(proc->stack, CPU_REG_INIT_VALUE(i));
294 /* Add to ready list */
295 DISABLE_IRQSAVE(flags);
297 ENABLE_IRQRESTORE(flags);
299 #if CONFIG_KERN_MONITOR
300 monitor_add(proc, name, stack_base, stacksize);
308 * System scheduler: pass CPU control to the next process in
311 * Saving and restoring the context on the stack is done
312 * by a CPU-dependent support routine which must usually be
313 * written in assembly.
315 void proc_schedule(void)
317 /* This function must not have any "auto" variables, otherwise
318 * the compiler might put them on the stack of the process
319 * being switched out.
321 static Process *old_process;
322 static cpuflags_t flags;
324 /* Remember old process to save its context later */
325 old_process = CurrentProcess;
327 /* Poll on the ready queue for the first ready process */
328 DISABLE_IRQSAVE(flags);
329 while (!(CurrentProcess = (struct Process*)REMHEAD(&ProcReadyList)))
332 * Make sure we physically reenable interrupts here, no matter what
333 * the current task status is. This is important because if we
334 * are idle-spinning, we must allow interrupts, otherwise no
335 * process will ever wake up.
337 * \todo If there was a way to code sig_wait so that it does not
338 * disable interrupts while waiting, there would not be any
345 ENABLE_IRQRESTORE(flags);
347 /* Optimization: don't switch contexts when the active
348 * process has not changed.
350 if (CurrentProcess != old_process)
352 static cpustack_t* dummy;
354 #if CONFIG_KERN_PREEMPTIVE
355 /* Reset quantum for this process */
356 Quantum = CONFIG_KERN_QUANTUM;
359 /* Save context of old process and switch to new process. If there is no
360 * old process, we save the old stack pointer into a dummy variable that
361 * we ignore. In fact, this happens only when the old process has just
363 * TODO: Instead of physically clearing the process at exit time, a zombie
364 * list should be created.
366 asm_switch_context(&CurrentProcess->stack, old_process ? &old_process->stack : &dummy);
369 /* This RET resumes the execution on the new process */
374 * Terminate the current process
379 /* The following code is BROKEN.
380 * We are freeing our own stack before entering proc_schedule()
381 * BAJO: A correct fix would be to rearrange the scheduler with
382 * an additional parameter which frees the old stack/process
383 * after a context switch.
385 if (CurrentProcess->flags & PF_FREESTACK)
386 heap_free(CurrentProcess->stack_base, CurrentProcess->stack_size);
387 heap_free(CurrentProcess);
390 #if (ARCH & ARCH_EMUL)
392 /* Reinsert process stack in free list */
393 ADDHEAD(&StackFreeList, (Node *)(CurrentProcess->stack
394 - (DEF_STACKSIZE / sizeof(cpustack_t))));
396 /* NOTE: At this point the first two words of what used
397 * to be our stack contain a list node. From now on, we
398 * rely on the compiler not reading/writing the stack.
400 #endif /* ARCH_EMUL */
402 #if CONFIG_KERN_MONITOR
403 monitor_remove(CurrentProcess);
406 CurrentProcess = NULL;
413 * Co-operative context switch
415 void proc_switch(void)
417 /* Just like proc_schedule, this function must not have auto variables. */
418 static cpuflags_t flags;
420 DISABLE_IRQSAVE(flags);
421 SCHED_ENQUEUE(CurrentProcess);
422 ENABLE_IRQRESTORE(flags);
429 * Get the pointer to the current process
431 struct Process *proc_current(void)
433 return CurrentProcess;
437 * Get the pointer to the user data of the current process
439 IPTR proc_current_user_data(void)
441 return CurrentProcess->user_data;
444 #if 0 /* Simple testcase for the scheduler */
447 * Proc scheduling test subthread 1
449 static void NORETURN proc_test_thread1(void)
460 * Proc scheduling test subthread 2
462 static void NORETURN proc_test_thread2(void)
472 static cpustack_t proc_test_stack1[CONFIG_KERN_DEFSTACKSIZE/sizeof(cpustack_t)];
473 static cpustack_t proc_test_stack2[CONFIG_KERN_DEFSTACKSIZE/sizeof(cpustack_t)];
476 * Proc scheduling test
478 void NORETURN proc_test(void)
480 proc_new(proc_test_thread1, NULL, sizeof(proc_test_stack1), proc_test_stack1);
481 proc_new(proc_test_thread2, NULL, sizeof(proc_test_stack2), proc_test_stack2);
482 kputs("Created tasks\n");
485 kdump(proc_test_stack1+sizeof(proc_test_stack1)-64, 64);
487 kdump(proc_test_stack2+sizeof(proc_test_stack1)-64, 64);
491 kputs(">main task\n");