proc.c

Go to the documentation of this file.
00001 
00044 #include "proc_p.h"
00045 #include "proc.h"
00046 //#include "hw.h"
00047 #include <mware/event.h>
00048 #include <cpu/irq.h>
00049 #include <cpu/types.h>
00050 #include <cpu/attr.h>
00051 #include <cfg/debug.h>
00052 #include <cfg/module.h>
00053 #include <cfg/arch_config.h>  /* ARCH_EMUL */
00054 #include <cfg/macros.h>  /* ABS() */
00055 
00056 #include <string.h> /* memset() */
00057 
00063 EXTERN_C void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp);
00064 EXTERN_C int asm_switch_version(void);
00065 
00066 /*
00067  * The scheduer tracks ready and waiting processes
00068  * by enqueuing them in these lists. A pointer to the currently
00069  * running process is stored in the CurrentProcess pointer.
00070  *
00071  * NOTE: these variables are protected by DI/EI locking
00072  */
00073 REGISTER Process *CurrentProcess;
00074 REGISTER List     ProcReadyList;
00075 
00076 
00077 #if CONFIG_KERN_PREEMPTIVE
00078 /*
00079  * The time sharing scheduler forces a task switch when
00080  * the current process has consumed its quantum.
00081  */
00082 uint16_t Quantum;
00083 #endif
00084 
00085 
00086 /* In Win32 we must emulate stack on the real process stack */
00087 #if (ARCH & ARCH_EMUL)
00088 extern List StackFreeList;
00089 #endif
00090 
00092 struct Process MainProcess;
00093 
00094 
00095 static void proc_init_struct(Process *proc)
00096 {
00097     /* Avoid warning for unused argument. */
00098     (void)proc;
00099 
00100 #if CONFIG_KERN_SIGNALS
00101     proc->sig_recv = 0;
00102 #endif
00103 
00104 #if CONFIG_KERN_PREEMPTIVE
00105     proc->forbid_cnt = 0;
00106 #endif
00107 
00108 #if CONFIG_KERN_HEAP
00109     proc->flags = 0;
00110 #endif
00111 }
00112 
00113 MOD_DEFINE(proc);
00114 
00115 void proc_init(void)
00116 {
00117     LIST_INIT(&ProcReadyList);
00118 
00119 #if CONFIG_KERN_MONITOR
00120     monitor_init();
00121 #endif
00122 
00123     /* We "promote" the current context into a real process. The only thing we have
00124      * to do is create a PCB and make it current. We don't need to setup the stack
00125      * pointer because it will be written the first time we switch to another process.
00126      */
00127     proc_init_struct(&MainProcess);
00128     CurrentProcess = &MainProcess;
00129 
00130     /* Make sure the assembly routine is up-to-date with us */
00131     ASSERT(asm_switch_version() == 1);
00132     MOD_INIT(proc);
00133 }
00134 
00135 
00142 struct Process *proc_new_with_name(UNUSED(const char *, name), void (*entry)(void), iptr_t data, size_t stacksize, cpustack_t *stack_base)
00143 {
00144     Process *proc;
00145     size_t i;
00146     size_t proc_size_words = ROUND2(sizeof(Process), sizeof(cpustack_t)) / sizeof(cpustack_t);
00147 #if CONFIG_KERN_HEAP
00148     bool free_stack = false;
00149 #endif
00150 
00151 #if (ARCH & ARCH_EMUL)
00152     /* Ignore stack provided by caller and use the large enough default instead. */
00153     stack_base = (cpustack_t *)LIST_HEAD(&StackFreeList);
00154     REMOVE(LIST_HEAD(&StackFreeList));
00155     stacksize = CONFIG_PROC_DEFSTACKSIZE;
00156 #elif CONFIG_KERN_HEAP
00157     /* Did the caller provide a stack for us? */
00158     if (!stack_base)
00159     {
00160         /* Did the caller specify the desired stack size? */
00161         if (!stacksize)
00162             stacksize = CONFIG_PROC_DEFSTACKSIZE + sizeof(Process);
00163 
00164         /* Allocate stack dinamically */
00165         if (!(stack_base = heap_alloc(stacksize)))
00166             return NULL;
00167 
00168         free_stack = true;
00169     }
00170 #else
00171     /* Stack must have been provided by the user */
00172     ASSERT(stack_base);
00173     ASSERT(stacksize);
00174 #endif
00175 
00176 #if CONFIG_KERN_MONITOR
00177     /* Fill-in the stack with a special marker to help debugging */
00178     memset(stack_base, CONFIG_KERN_STACKFILLCODE, stacksize / sizeof(cpustack_t));
00179 #endif
00180 
00181     /* Initialize the process control block */
00182     if (CPU_STACK_GROWS_UPWARD)
00183     {
00184         proc = (Process*)stack_base;
00185         proc->stack = stack_base + proc_size_words;
00186         if (CPU_SP_ON_EMPTY_SLOT)
00187             proc->stack++;
00188     }
00189     else
00190     {
00191         proc = (Process*)(stack_base + stacksize / sizeof(cpustack_t) - proc_size_words);
00192         proc->stack = (cpustack_t*)proc;
00193         if (CPU_SP_ON_EMPTY_SLOT)
00194             proc->stack--;
00195     }
00196 
00197     proc_init_struct(proc);
00198     proc->user_data = data;
00199 
00200 #if CONFIG_KERN_HEAP
00201     proc->stack_base = stack_base;
00202     proc->stack_size = stack_size;
00203     if (free_stack)
00204         proc->flags |= PF_FREESTACK;
00205 #endif
00206 
00207     /* Initialize process stack frame */
00208     CPU_PUSH_CALL_CONTEXT(proc->stack, proc_exit);
00209     CPU_PUSH_CALL_CONTEXT(proc->stack, entry);
00210 
00211     /* Push a clean set of CPU registers for asm_switch_context() */
00212     for (i = 0; i < CPU_SAVED_REGS_CNT; i++)
00213         CPU_PUSH_WORD(proc->stack, CPU_REG_INIT_VALUE(i));
00214 
00215     /* Add to ready list */
00216     ATOMIC(SCHED_ENQUEUE(proc));
00217 
00218 #if CONFIG_KERN_MONITOR
00219     monitor_add(proc, name, stack_base, stacksize);
00220 #endif
00221 
00222     return proc;
00223 }
00224 
00226 void proc_rename(struct Process *proc, const char *name)
00227 {
00228 #if CONFIG_KERN_MONITOR
00229     monitor_rename(proc, name);
00230 #else
00231     (void)proc; (void)name;
00232 #endif
00233 }
00234 
00235 
00244 void proc_schedule(void)
00245 {
00246     struct Process *old_process;
00247     cpuflags_t flags;
00248 
00249     /* Remember old process to save its context later */
00250     old_process = CurrentProcess;
00251 
00252 #ifdef IRQ_RUNNING
00253     /* Scheduling in interrupts is a nono. */
00254     ASSERT(!IRQ_RUNNING());
00255 #endif
00256 
00257     /* Poll on the ready queue for the first ready process */
00258     IRQ_SAVE_DISABLE(flags);
00259     while (!(CurrentProcess = (struct Process *)list_remHead(&ProcReadyList)))
00260     {
00261         /*
00262          * Make sure we physically reenable interrupts here, no matter what
00263          * the current task status is. This is important because if we
00264          * are idle-spinning, we must allow interrupts, otherwise no
00265          * process will ever wake up.
00266          *
00267          * During idle-spinning, an interrupt can occur and it may
00268          * modify \p ProcReadyList. To ensure that compiler reload this
00269          * variable every while cycle we call CPU_MEMORY_BARRIER.
00270          * The memory barrier ensure that all variables used in this context
00271          * are reloaded.
00272          * \todo If there was a way to write sig_wait() so that it does not
00273          * disable interrupts while waiting, there would not be any
00274          * reason to do this.
00275          */
00276         IRQ_ENABLE;
00277         CPU_IDLE;
00278         MEMORY_BARRIER;
00279         IRQ_DISABLE;
00280     }
00281     IRQ_RESTORE(flags);
00282 
00283     /*
00284      * Optimization: don't switch contexts when the active
00285      * process has not changed.
00286      */
00287     if (CurrentProcess != old_process)
00288     {
00289         cpustack_t *dummy;
00290 
00291 #if CONFIG_KERN_PREEMPTIVE
00292         /* Reset quantum for this process */
00293         Quantum = CONFIG_KERN_QUANTUM;
00294 #endif
00295 
00296         /* Save context of old process and switch to new process. If there is no
00297          * old process, we save the old stack pointer into a dummy variable that
00298          * we ignore. In fact, this happens only when the old process has just
00299          * exited.
00300          * TODO: Instead of physically clearing the process at exit time, a zombie
00301          * list should be created.
00302          */
00303         asm_switch_context(&CurrentProcess->stack, old_process ? &old_process->stack : &dummy);
00304     }
00305 
00306     /* This RET resumes the execution on the new process */
00307 }
00308 
00309 
00313 void proc_exit(void)
00314 {
00315 #if CONFIG_KERN_MONITOR
00316     monitor_remove(CurrentProcess);
00317 #endif
00318 
00319 #if CONFIG_KERN_HEAP
00320     /*
00321      * The following code is BROKEN.
00322      * We are freeing our own stack before entering proc_schedule()
00323      * BAJO: A correct fix would be to rearrange the scheduler with
00324      *  an additional parameter which frees the old stack/process
00325      *  after a context switch.
00326      */
00327     if (CurrentProcess->flags & PF_FREESTACK)
00328         heap_free(CurrentProcess->stack_base, CurrentProcess->stack_size);
00329     heap_free(CurrentProcess);
00330 #endif
00331 
00332 #if (ARCH & ARCH_EMUL)
00333 #warning This is wrong
00334     /* Reinsert process stack in free list */
00335     ADDHEAD(&StackFreeList, (Node *)(CurrentProcess->stack
00336         - (CONFIG_PROC_DEFSTACKSIZE / sizeof(cpustack_t))));
00337 
00338     /*
00339      * NOTE: At this point the first two words of what used
00340      * to be our stack contain a list node. From now on, we
00341      * rely on the compiler not reading/writing the stack.
00342      */
00343 #endif /* ARCH_EMUL */
00344 
00345     CurrentProcess = NULL;
00346     proc_schedule();
00347     /* not reached */
00348 }
00349 
00350 
00354 void proc_switch(void)
00355 {
00356     cpuflags_t flags;
00357 
00358     IRQ_SAVE_DISABLE(flags);
00359     SCHED_ENQUEUE(CurrentProcess);
00360     IRQ_RESTORE(flags);
00361 
00362     proc_schedule();
00363 }
00364 
00365 
00369 struct Process *proc_current(void)
00370 {
00371     return CurrentProcess;
00372 }
00373 
00377 iptr_t proc_current_user_data(void)
00378 {
00379     return CurrentProcess->user_data;
00380 }
00381 
00382 
00383 #if CONFIG_KERN_PREEMPTIVE
00384 
00399 void proc_forbid(void)
00400 {
00401     /* No need to protect against interrupts here. */
00402     ++CurrentProcess->forbid_cnt;
00403 }
00404 
00410 void proc_permit(void)
00411 {
00412     /* No need to protect against interrupts here. */
00413     --CurrentProcess->forbid_cnt;
00414 }
00415 
00416 #endif /* CONFIG_KERN_PREEMPTIVE */