proc.c

Go to the documentation of this file.
00001 
00039 #include "proc_p.h"
00040 #include "proc.h"
00041 
00042 #include "cfg/cfg_proc.h"
00043 #define LOG_LEVEL KERN_LOG_LEVEL
00044 #define LOG_FORMAT KERN_LOG_FORMAT
00045 #include <cfg/log.h>
00046 
00047 #include "cfg/cfg_monitor.h"
00048 #include <cfg/macros.h>    // ROUND_UP2
00049 #include <cfg/module.h>
00050 #include <cfg/depend.h>    // CONFIG_DEPEND()
00051 
00052 #include <cpu/irq.h>
00053 #include <cpu/types.h>
00054 #include <cpu/attr.h>
00055 #include <cpu/frame.h>
00056 
00057 #if CONFIG_KERN_HEAP
00058     #include <struct/heap.h>
00059 #endif
00060 
00061 #include <string.h>           /* memset() */
00062 
00063 #define PROC_SIZE_WORDS (ROUND_UP2(sizeof(Process), sizeof(cpu_stack_t)) / sizeof(cpu_stack_t))
00064 
00065 /*
00066  * The scheduer tracks ready processes by enqueuing them in the
00067  * ready list.
00068  *
00069  * \note Access to the list must occur while interrupts are disabled.
00070  */
00071 REGISTER List proc_ready_list;
00072 
00073 /*
00074  * Holds a pointer to the TCB of the currently running process.
00075  *
00076  * \note User applications should use proc_current() to retrieve this value.
00077  */
00078 REGISTER Process *current_process;
00079 
00081 static struct Process main_process;
00082 
00083 #if CONFIG_KERN_HEAP
00084 
00088 static HEAP_DEFINE_BUF(heap_buf, CONFIG_KERN_HEAP_SIZE);
00089 static Heap proc_heap;
00090 
00091 /*
00092  * Keep track of zombie processes (processes that are exiting and need to
00093  * release some resources).
00094  *
00095  * \note Access to the list must occur while kernel preemption is disabled.
00096  */
00097 static List zombie_list;
00098 
00099 #endif /* CONFIG_KERN_HEAP */
00100 
00101 static void proc_initStruct(Process *proc)
00102 {
00103     /* Avoid warning for unused argument. */
00104     (void)proc;
00105 
00106 #if CONFIG_KERN_SIGNALS
00107     proc->sig_recv = 0;
00108     proc->sig_wait = 0;
00109 #endif
00110 
00111 #if CONFIG_KERN_HEAP
00112     proc->flags = 0;
00113 #endif
00114 
00115 #if CONFIG_KERN_PRI
00116     proc->link.pri = 0;
00117 #endif
00118 }
00119 
00120 MOD_DEFINE(proc);
00121 
00122 void proc_init(void)
00123 {
00124     LIST_INIT(&proc_ready_list);
00125 
00126 #if CONFIG_KERN_HEAP
00127     LIST_INIT(&zombie_list);
00128     heap_init(&proc_heap, heap_buf, sizeof(heap_buf));
00129 #endif
00130     /*
00131      * We "promote" the current context into a real process. The only thing we have
00132      * to do is create a PCB and make it current. We don't need to setup the stack
00133      * pointer because it will be written the first time we switch to another process.
00134      */
00135     proc_initStruct(&main_process);
00136     current_process = &main_process;
00137 
00138 #if CONFIG_KERN_MONITOR
00139     monitor_init();
00140     monitor_add(current_process, "main");
00141 #endif
00142     MOD_INIT(proc);
00143 }
00144 
00145 
00146 #if CONFIG_KERN_HEAP
00147 
00152 static void proc_freeZombies(void)
00153 {
00154     Process *proc;
00155 
00156     while (1)
00157     {
00158         PROC_ATOMIC(proc = (Process *)list_remHead(&zombie_list));
00159         if (proc == NULL)
00160             return;
00161 
00162         if (proc->flags & PF_FREESTACK)
00163         {
00164             PROC_ATOMIC(heap_freemem(&proc_heap, proc->stack_base,
00165                 proc->stack_size + PROC_SIZE_WORDS * sizeof(cpu_stack_t)));
00166         }
00167     }
00168 }
00169 
00173 static void proc_addZombie(Process *proc)
00174 {
00175     Node *node;
00176 #if CONFIG_KERN_PREEMPT
00177     ASSERT(!proc_preemptAllowed());
00178 #endif
00179 
00180 #if CONFIG_KERN_PRI
00181     node = &(proc)->link.link;
00182 #else
00183     node = &(proc)->link;
00184 #endif
00185     LIST_ASSERT_VALID(&zombie_list);
00186     ADDTAIL(&zombie_list, node);
00187 }
00188 
00189 #endif /* CONFIG_KERN_HEAP */
00190 
00205 struct Process *proc_new_with_name(UNUSED_ARG(const char *, name), void (*entry)(void), iptr_t data, size_t stack_size, cpu_stack_t *stack_base)
00206 {
00207     Process *proc;
00208     LOG_INFO("name=%s", name);
00209 #if CONFIG_KERN_HEAP
00210     bool free_stack = false;
00211 
00212     /*
00213      * Free up resources of a zombie process.
00214      *
00215      * We're implementing a kind of lazy garbage collector here for
00216      * efficiency reasons: we can avoid to introduce overhead into another
00217      * kernel task dedicated to free up resources (e.g., idle) and we're
00218      * not introducing any overhead into the scheduler after a context
00219      * switch (that would be *very* bad, because the scheduler runs with
00220      * IRQ disabled).
00221      *
00222      * In this way we are able to release the memory of the zombie tasks
00223      * without disabling IRQs and without introducing any significant
00224      * overhead in any other kernel task.
00225      */
00226     proc_freeZombies();
00227 
00228     /* Did the caller provide a stack for us? */
00229     if (!stack_base)
00230     {
00231         /* Did the caller specify the desired stack size? */
00232         if (!stack_size)
00233             stack_size = KERN_MINSTACKSIZE;
00234 
00235         /* Allocate stack dinamically */
00236         PROC_ATOMIC(stack_base =
00237             (cpu_stack_t *)heap_allocmem(&proc_heap, stack_size));
00238         if (stack_base == NULL)
00239             return NULL;
00240 
00241         free_stack = true;
00242     }
00243 
00244 #else // CONFIG_KERN_HEAP
00245 
00246     /* Stack must have been provided by the user */
00247     ASSERT_VALID_PTR(stack_base);
00248     ASSERT(stack_size);
00249 
00250 #endif // CONFIG_KERN_HEAP
00251 
00252 #if CONFIG_KERN_MONITOR
00253     /*
00254      * Fill-in the stack with a special marker to help debugging.
00255      * On 64bit platforms, CONFIG_KERN_STACKFILLCODE is larger
00256      * than an int, so the (int) cast is required to silence the
00257      * warning for truncating its size.
00258      */
00259     memset(stack_base, (int)CONFIG_KERN_STACKFILLCODE, stack_size);
00260 #endif
00261 
00262     /* Initialize the process control block */
00263     if (CPU_STACK_GROWS_UPWARD)
00264     {
00265         proc = (Process *)stack_base;
00266         proc->stack = stack_base + PROC_SIZE_WORDS;
00267         // On some architecture stack should be aligned, so we do it.
00268         proc->stack = (cpu_stack_t *)((uintptr_t)proc->stack + (sizeof(cpu_aligned_stack_t) - ((uintptr_t)proc->stack % sizeof(cpu_aligned_stack_t))));
00269         if (CPU_SP_ON_EMPTY_SLOT)
00270             proc->stack++;
00271     }
00272     else
00273     {
00274         proc = (Process *)(stack_base + stack_size / sizeof(cpu_stack_t) - PROC_SIZE_WORDS);
00275         // On some architecture stack should be aligned, so we do it.
00276         proc->stack = (cpu_stack_t *)((uintptr_t)proc - ((uintptr_t)proc % sizeof(cpu_aligned_stack_t)));
00277         if (CPU_SP_ON_EMPTY_SLOT)
00278             proc->stack--;
00279     }
00280     /* Ensure stack is aligned */
00281     ASSERT((uintptr_t)proc->stack % sizeof(cpu_aligned_stack_t) == 0);
00282 
00283     stack_size -= PROC_SIZE_WORDS * sizeof(cpu_stack_t);
00284     proc_initStruct(proc);
00285     proc->user_data = data;
00286 
00287 #if CONFIG_KERN_HEAP | CONFIG_KERN_MONITOR
00288     proc->stack_base = stack_base;
00289     proc->stack_size = stack_size;
00290     #if CONFIG_KERN_HEAP
00291     if (free_stack)
00292         proc->flags |= PF_FREESTACK;
00293     #endif
00294 #endif
00295     proc->user_entry = entry;
00296     CPU_CREATE_NEW_STACK(proc->stack);
00297 
00298 #if CONFIG_KERN_MONITOR
00299     monitor_add(proc, name);
00300 #endif
00301 
00302     /* Add to ready list */
00303     ATOMIC(SCHED_ENQUEUE(proc));
00304 
00305     return proc;
00306 }
00307 
00313 const char *proc_name(struct Process *proc)
00314 {
00315 #if CONFIG_KERN_MONITOR
00316     return proc ? proc->monitor.name : "<NULL>";
00317 #else
00318     (void)proc;
00319     return "---";
00320 #endif
00321 }
00322 
00324 const char *proc_currentName(void)
00325 {
00326     return proc_name(proc_current());
00327 }
00328 
00330 void proc_rename(struct Process *proc, const char *name)
00331 {
00332 #if CONFIG_KERN_MONITOR
00333     monitor_rename(proc, name);
00334 #else
00335     (void)proc; (void)name;
00336 #endif
00337 }
00338 
00339 
00340 #if CONFIG_KERN_PRI
00341 
00360 void proc_setPri(struct Process *proc, int pri)
00361 {
00362     if (proc->link.pri == pri)
00363         return;
00364 
00365     proc->link.pri = pri;
00366 
00367     if (proc != current_process)
00368         ATOMIC(sched_reenqueue(proc));
00369 }
00370 #endif // CONFIG_KERN_PRI
00371 
00372 INLINE void proc_run(void)
00373 {
00374     void (*entry)(void) = current_process->user_entry;
00375 
00376     LOG_INFO("New process starting at %p", entry);
00377     entry();
00378 }
00379 
00383 void proc_entry(void)
00384 {
00385     /*
00386      * Return from a context switch assumes interrupts are disabled, so
00387      * we need to explicitly re-enable them as soon as possible.
00388      */
00389     IRQ_ENABLE;
00390     /* Call the actual process's entry point */
00391     proc_run();
00392     proc_exit();
00393 }
00394 
00398 void proc_exit(void)
00399 {
00400     LOG_INFO("%p:%s", current_process, proc_currentName());
00401 
00402 #if CONFIG_KERN_MONITOR
00403     monitor_remove(current_process);
00404 #endif
00405 
00406     proc_forbid();
00407 #if CONFIG_KERN_HEAP
00408     /*
00409      * Set the task as zombie, its resources will be freed in proc_new() in
00410      * a lazy way, when another process will be created.
00411      */
00412     proc_addZombie(current_process);
00413 #endif
00414     current_process = NULL;
00415     proc_permit();
00416 
00417     proc_switch();
00418 
00419     /* never reached */
00420     ASSERT(0);
00421 }
00422 
00426 void proc_schedule(void)
00427 {
00428     Process *old_process = current_process;
00429 
00430     IRQ_ASSERT_DISABLED();
00431 
00432     /* Poll on the ready queue for the first ready process */
00433     LIST_ASSERT_VALID(&proc_ready_list);
00434     while (!(current_process = (struct Process *)list_remHead(&proc_ready_list)))
00435     {
00436         /*
00437          * Make sure we physically reenable interrupts here, no matter what
00438          * the current task status is. This is important because if we
00439          * are idle-spinning, we must allow interrupts, otherwise no
00440          * process will ever wake up.
00441          *
00442          * During idle-spinning, an interrupt can occur and it may
00443          * modify \p proc_ready_list. To ensure that compiler reload this
00444          * variable every while cycle we call CPU_MEMORY_BARRIER.
00445          * The memory barrier ensure that all variables used in this context
00446          * are reloaded.
00447          * \todo If there was a way to write sig_wait() so that it does not
00448          * disable interrupts while waiting, there would not be any
00449          * reason to do this.
00450          */
00451         IRQ_ENABLE;
00452         CPU_IDLE;
00453         MEMORY_BARRIER;
00454         IRQ_DISABLE;
00455     }
00456     proc_switchTo(current_process, old_process);
00457     /* This RET resumes the execution on the new process */
00458     LOG_INFO("resuming %p:%s\n", current_process, proc_currentName());
00459 }