proc.c

Go to the documentation of this file.
00001 
00088 #include "proc_p.h"
00089 #include "proc.h"
00090 
00091 #include "cfg/cfg_proc.h"
00092 #define LOG_LEVEL KERN_LOG_LEVEL
00093 #define LOG_FORMAT KERN_LOG_FORMAT
00094 #include <cfg/log.h>
00095 
00096 #include "cfg/cfg_monitor.h"
00097 #include <cfg/macros.h>    // ROUND_UP2
00098 #include <cfg/module.h>
00099 #include <cfg/depend.h>    // CONFIG_DEPEND()
00100 
00101 #include <cpu/irq.h>
00102 #include <cpu/types.h>
00103 #include <cpu/attr.h>
00104 #include <cpu/frame.h>
00105 
00106 #if CONFIG_KERN_HEAP
00107     #include <struct/heap.h>
00108 #endif
00109 
00110 #include <string.h>           /* memset() */
00111 
00112 #define PROC_SIZE_WORDS (ROUND_UP2(sizeof(Process), sizeof(cpu_stack_t)) / sizeof(cpu_stack_t))
00113 
00114 /*
00115  * The scheduer tracks ready processes by enqueuing them in the
00116  * ready list.
00117  *
00118  * \note Access to the list must occur while interrupts are disabled.
00119  */
00120 REGISTER List proc_ready_list;
00121 
00122 /*
00123  * Holds a pointer to the TCB of the currently running process.
00124  *
00125  * \note User applications should use proc_current() to retrieve this value.
00126  */
00127 REGISTER Process *current_process;
00128 
00130 static struct Process main_process;
00131 
00132 #if CONFIG_KERN_HEAP
00133 
00137 static HEAP_DEFINE_BUF(heap_buf, CONFIG_KERN_HEAP_SIZE);
00138 static Heap proc_heap;
00139 
00140 /*
00141  * Keep track of zombie processes (processes that are exiting and need to
00142  * release some resources).
00143  *
00144  * \note Access to the list must occur while kernel preemption is disabled.
00145  */
00146 static List zombie_list;
00147 
00148 #endif /* CONFIG_KERN_HEAP */
00149 
00150 /*
00151  * Check if the process context switch can be performed directly by the
00152  * architecture-dependent asm_switch_context() or if it must be delayed
00153  * because we're in the middle of an ISR.
00154  *
00155  * Return true if asm_switch_context() can be executed, false
00156  * otherwise.
00157  *
00158  * NOTE: if an architecture does not implement IRQ_RUNNING() this function
00159  * always returns true.
00160  */
00161 #define CONTEXT_SWITCH_FROM_ISR()   (!IRQ_RUNNING())
00162 
00163 /*
00164  * Save context of old process and switch to new process.
00165   */
00166 static void proc_context_switch(Process *next, Process *prev)
00167 {
00168     cpu_stack_t *dummy;
00169 
00170     if (UNLIKELY(next == prev))
00171         return;
00172     /*
00173      * If there is no old process, we save the old stack pointer into a
00174      * dummy variable that we ignore.  In fact, this happens only when the
00175      * old process has just exited.
00176      */
00177     asm_switch_context(&next->stack, prev ? &prev->stack : &dummy);
00178 }
00179 
00180 static void proc_initStruct(Process *proc)
00181 {
00182     /* Avoid warning for unused argument. */
00183     (void)proc;
00184 
00185 #if CONFIG_KERN_SIGNALS
00186     proc->sig_recv = 0;
00187     proc->sig_wait = 0;
00188 #endif
00189 
00190 #if CONFIG_KERN_HEAP
00191     proc->flags = 0;
00192 #endif
00193 
00194 #if CONFIG_KERN_PRI
00195     proc->link.pri = 0;
00196 #endif
00197 }
00198 
00199 MOD_DEFINE(proc);
00200 
00201 void proc_init(void)
00202 {
00203     LIST_INIT(&proc_ready_list);
00204 
00205 #if CONFIG_KERN_HEAP
00206     LIST_INIT(&zombie_list);
00207     heap_init(&proc_heap, heap_buf, sizeof(heap_buf));
00208 #endif
00209     /*
00210      * We "promote" the current context into a real process. The only thing we have
00211      * to do is create a PCB and make it current. We don't need to setup the stack
00212      * pointer because it will be written the first time we switch to another process.
00213      */
00214     proc_initStruct(&main_process);
00215     current_process = &main_process;
00216 
00217 #if CONFIG_KERN_MONITOR
00218     monitor_init();
00219     monitor_add(current_process, "main");
00220 #endif
00221     MOD_INIT(proc);
00222 }
00223 
00224 
00225 #if CONFIG_KERN_HEAP
00226 
00231 static void proc_freeZombies(void)
00232 {
00233     Process *proc;
00234 
00235     while (1)
00236     {
00237         PROC_ATOMIC(proc = (Process *)list_remHead(&zombie_list));
00238         if (proc == NULL)
00239             return;
00240 
00241         if (proc->flags & PF_FREESTACK)
00242         {
00243             PROC_ATOMIC(heap_freemem(&proc_heap, proc->stack_base,
00244                 proc->stack_size + PROC_SIZE_WORDS * sizeof(cpu_stack_t)));
00245         }
00246     }
00247 }
00248 
00252 static void proc_addZombie(Process *proc)
00253 {
00254     Node *node;
00255 #if CONFIG_KERN_PREEMPT
00256     ASSERT(!proc_preemptAllowed());
00257 #endif
00258 
00259 #if CONFIG_KERN_PRI
00260     node = &(proc)->link.link;
00261 #else
00262     node = &(proc)->link;
00263 #endif
00264     LIST_ASSERT_VALID(&zombie_list);
00265     ADDTAIL(&zombie_list, node);
00266 }
00267 
00268 #endif /* CONFIG_KERN_HEAP */
00269 
00284 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)
00285 {
00286     Process *proc;
00287     LOG_INFO("name=%s", name);
00288 #if CONFIG_KERN_HEAP
00289     bool free_stack = false;
00290 
00291     /*
00292      * Free up resources of a zombie process.
00293      *
00294      * We're implementing a kind of lazy garbage collector here for
00295      * efficiency reasons: we can avoid to introduce overhead into another
00296      * kernel task dedicated to free up resources (e.g., idle) and we're
00297      * not introducing any overhead into the scheduler after a context
00298      * switch (that would be *very* bad, because the scheduler runs with
00299      * IRQ disabled).
00300      *
00301      * In this way we are able to release the memory of the zombie tasks
00302      * without disabling IRQs and without introducing any significant
00303      * overhead in any other kernel task.
00304      */
00305     proc_freeZombies();
00306 
00307     /* Did the caller provide a stack for us? */
00308     if (!stack_base)
00309     {
00310         /* Did the caller specify the desired stack size? */
00311         if (!stack_size)
00312             stack_size = KERN_MINSTACKSIZE;
00313 
00314         /* Allocate stack dinamically */
00315         PROC_ATOMIC(stack_base =
00316             (cpu_stack_t *)heap_allocmem(&proc_heap, stack_size));
00317         if (stack_base == NULL)
00318             return NULL;
00319 
00320         free_stack = true;
00321     }
00322 
00323 #else // CONFIG_KERN_HEAP
00324 
00325     /* Stack must have been provided by the user */
00326     ASSERT_VALID_PTR(stack_base);
00327     ASSERT(stack_size);
00328 
00329 #endif // CONFIG_KERN_HEAP
00330 
00331 #if CONFIG_KERN_MONITOR
00332     /*
00333      * Fill-in the stack with a special marker to help debugging.
00334      * On 64bit platforms, CONFIG_KERN_STACKFILLCODE is larger
00335      * than an int, so the (int) cast is required to silence the
00336      * warning for truncating its size.
00337      */
00338     memset(stack_base, (int)CONFIG_KERN_STACKFILLCODE, stack_size);
00339 #endif
00340 
00341     /* Initialize the process control block */
00342     if (CPU_STACK_GROWS_UPWARD)
00343     {
00344         proc = (Process *)stack_base;
00345         proc->stack = stack_base + PROC_SIZE_WORDS;
00346         // On some architecture stack should be aligned, so we do it.
00347         proc->stack = (cpu_stack_t *)((uintptr_t)proc->stack + (sizeof(cpu_aligned_stack_t) - ((uintptr_t)proc->stack % sizeof(cpu_aligned_stack_t))));
00348         if (CPU_SP_ON_EMPTY_SLOT)
00349             proc->stack++;
00350     }
00351     else
00352     {
00353         proc = (Process *)(stack_base + stack_size / sizeof(cpu_stack_t) - PROC_SIZE_WORDS);
00354         // On some architecture stack should be aligned, so we do it.
00355         proc->stack = (cpu_stack_t *)((uintptr_t)proc - ((uintptr_t)proc % sizeof(cpu_aligned_stack_t)));
00356         if (CPU_SP_ON_EMPTY_SLOT)
00357             proc->stack--;
00358     }
00359     /* Ensure stack is aligned */
00360     ASSERT((uintptr_t)proc->stack % sizeof(cpu_aligned_stack_t) == 0);
00361 
00362     stack_size -= PROC_SIZE_WORDS * sizeof(cpu_stack_t);
00363     proc_initStruct(proc);
00364     proc->user_data = data;
00365 
00366 #if CONFIG_KERN_HEAP | CONFIG_KERN_MONITOR
00367     proc->stack_base = stack_base;
00368     proc->stack_size = stack_size;
00369     #if CONFIG_KERN_HEAP
00370     if (free_stack)
00371         proc->flags |= PF_FREESTACK;
00372     #endif
00373 #endif
00374     proc->user_entry = entry;
00375     CPU_CREATE_NEW_STACK(proc->stack);
00376 
00377 #if CONFIG_KERN_MONITOR
00378     monitor_add(proc, name);
00379 #endif
00380 
00381     /* Add to ready list */
00382     ATOMIC(SCHED_ENQUEUE(proc));
00383 
00384     return proc;
00385 }
00386 
00392 const char *proc_name(struct Process *proc)
00393 {
00394 #if CONFIG_KERN_MONITOR
00395     return proc ? proc->monitor.name : "<NULL>";
00396 #else
00397     (void)proc;
00398     return "---";
00399 #endif
00400 }
00401 
00403 const char *proc_currentName(void)
00404 {
00405     return proc_name(proc_current());
00406 }
00407 
00409 void proc_rename(struct Process *proc, const char *name)
00410 {
00411 #if CONFIG_KERN_MONITOR
00412     monitor_rename(proc, name);
00413 #else
00414     (void)proc; (void)name;
00415 #endif
00416 }
00417 
00418 
00419 #if CONFIG_KERN_PRI
00420 
00439 void proc_setPri(struct Process *proc, int pri)
00440 {
00441     if (proc->link.pri == pri)
00442         return;
00443 
00444     proc->link.pri = pri;
00445 
00446     if (proc != current_process)
00447         ATOMIC(sched_reenqueue(proc));
00448 }
00449 #endif // CONFIG_KERN_PRI
00450 
00451 INLINE void proc_run(void)
00452 {
00453     void (*entry)(void) = current_process->user_entry;
00454 
00455     LOG_INFO("New process starting at %p", entry);
00456     entry();
00457 }
00458 
00462 void proc_entry(void)
00463 {
00464     /*
00465      * Return from a context switch assumes interrupts are disabled, so
00466      * we need to explicitly re-enable them as soon as possible.
00467      */
00468     IRQ_ENABLE;
00469     /* Call the actual process's entry point */
00470     proc_run();
00471     proc_exit();
00472 }
00473 
00477 void proc_exit(void)
00478 {
00479     LOG_INFO("%p:%s", current_process, proc_currentName());
00480 
00481 #if CONFIG_KERN_MONITOR
00482     monitor_remove(current_process);
00483 #endif
00484 
00485     proc_forbid();
00486 #if CONFIG_KERN_HEAP
00487     /*
00488      * Set the task as zombie, its resources will be freed in proc_new() in
00489      * a lazy way, when another process will be created.
00490      */
00491     proc_addZombie(current_process);
00492 #endif
00493     current_process = NULL;
00494     proc_permit();
00495 
00496     proc_switch();
00497 
00498     /* never reached */
00499     ASSERT(0);
00500 }
00501 
00505 static void proc_schedule(void)
00506 {
00507     Process *old_process = current_process;
00508 
00509     IRQ_ASSERT_DISABLED();
00510 
00511     /* Poll on the ready queue for the first ready process */
00512     LIST_ASSERT_VALID(&proc_ready_list);
00513     while (!(current_process = (struct Process *)list_remHead(&proc_ready_list)))
00514     {
00515         /*
00516          * Make sure we physically reenable interrupts here, no matter what
00517          * the current task status is. This is important because if we
00518          * are idle-spinning, we must allow interrupts, otherwise no
00519          * process will ever wake up.
00520          *
00521          * During idle-spinning, an interrupt can occur and it may
00522          * modify \p proc_ready_list. To ensure that compiler reload this
00523          * variable every while cycle we call CPU_MEMORY_BARRIER.
00524          * The memory barrier ensure that all variables used in this context
00525          * are reloaded.
00526          * \todo If there was a way to write sig_wait() so that it does not
00527          * disable interrupts while waiting, there would not be any
00528          * reason to do this.
00529          */
00530         IRQ_ENABLE;
00531         CPU_IDLE;
00532         MEMORY_BARRIER;
00533         IRQ_DISABLE;
00534     }
00535     if (CONTEXT_SWITCH_FROM_ISR())
00536         proc_context_switch(current_process, old_process);
00537     /* This RET resumes the execution on the new process */
00538     LOG_INFO("resuming %p:%s\n", current_process, proc_currentName());
00539 }
00540 
00541 #if CONFIG_KERN_PREEMPT
00542 /* Global preemption nesting counter */
00543 cpu_atomic_t preempt_count;
00544 
00545 /*
00546  * The time sharing interval: when a process is scheduled on a CPU it gets an
00547  * amount of CONFIG_KERN_QUANTUM clock ticks. When these ticks expires and
00548  * preemption is enabled a new process is selected to run.
00549  */
00550 int _proc_quantum;
00551 
00555 bool proc_needPreempt(void)
00556 {
00557     if (UNLIKELY(current_process == NULL))
00558         return false;
00559     if (!proc_preemptAllowed())
00560         return false;
00561     if (LIST_EMPTY(&proc_ready_list))
00562         return false;
00563     return preempt_quantum() ? prio_next() > prio_curr() :
00564             prio_next() >= prio_curr();
00565 }
00566 
00570 void proc_preempt(void)
00571 {
00572     IRQ_ASSERT_DISABLED();
00573     ASSERT(current_process);
00574 
00575     /* Perform the kernel preemption */
00576     LOG_INFO("preempting %p:%s\n", current_process, proc_currentName());
00577     /* We are inside a IRQ context, so ATOMIC is not needed here */
00578     SCHED_ENQUEUE(current_process);
00579     preempt_reset_quantum();
00580     proc_schedule();
00581 }
00582 #endif /* CONFIG_KERN_PREEMPT */
00583 
00584 /* Immediately switch to a particular process */
00585 static void proc_switchTo(Process *proc)
00586 {
00587     Process *old_process = current_process;
00588 
00589     SCHED_ENQUEUE(current_process);
00590     preempt_reset_quantum();
00591     current_process = proc;
00592     proc_context_switch(current_process, old_process);
00593 }
00594 
00603 void proc_switch(void)
00604 {
00605     ASSERT(proc_preemptAllowed());
00606     ATOMIC(
00607         preempt_reset_quantum();
00608         proc_schedule();
00609     );
00610 }
00611 
00615 void proc_wakeup(Process *proc)
00616 {
00617     ASSERT(proc_preemptAllowed());
00618     ASSERT(current_process);
00619     IRQ_ASSERT_DISABLED();
00620 
00621     if (prio_proc(proc) >= prio_curr())
00622         proc_switchTo(proc);
00623     else
00624         SCHED_ENQUEUE_HEAD(proc);
00625 }
00626 
00630 void proc_yield(void)
00631 {
00632     Process *proc;
00633 
00634     /*
00635      * Voluntary preemption while preemption is disabled is considered
00636      * illegal, as not very useful in practice.
00637      *
00638      * ASSERT if it happens.
00639      */
00640     ASSERT(proc_preemptAllowed());
00641     IRQ_ASSERT_ENABLED();
00642 
00643     IRQ_DISABLE;
00644     proc = (struct Process *)list_remHead(&proc_ready_list);
00645     if (proc)
00646         proc_switchTo(proc);
00647     IRQ_ENABLE;
00648 }