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 
00197 # if CONFIG_KERN_PRI_INHERIT
00198     proc->orig_pri = proc->inh_link.pri = proc->link.pri;
00199     proc->inh_blocked_by = NULL;
00200     LIST_INIT(&proc->inh_list);
00201 # endif
00202 #endif
00203 }
00204 
00205 MOD_DEFINE(proc);
00206 
00207 void proc_init(void)
00208 {
00209     LIST_INIT(&proc_ready_list);
00210 
00211 #if CONFIG_KERN_HEAP
00212     LIST_INIT(&zombie_list);
00213     heap_init(&proc_heap, heap_buf, sizeof(heap_buf));
00214 #endif
00215     /*
00216      * We "promote" the current context into a real process. The only thing we have
00217      * to do is create a PCB and make it current. We don't need to setup the stack
00218      * pointer because it will be written the first time we switch to another process.
00219      */
00220     proc_initStruct(&main_process);
00221     current_process = &main_process;
00222 
00223 #if CONFIG_KERN_MONITOR
00224     monitor_init();
00225     monitor_add(current_process, "main");
00226 #endif
00227     MOD_INIT(proc);
00228 }
00229 
00230 
00231 #if CONFIG_KERN_HEAP
00232 
00237 static void proc_freeZombies(void)
00238 {
00239     Process *proc;
00240 
00241     while (1)
00242     {
00243         PROC_ATOMIC(proc = (Process *)list_remHead(&zombie_list));
00244         if (proc == NULL)
00245             return;
00246 
00247         if (proc->flags & PF_FREESTACK)
00248         {
00249             PROC_ATOMIC(heap_freemem(&proc_heap, proc->stack_base,
00250                 proc->stack_size + PROC_SIZE_WORDS * sizeof(cpu_stack_t)));
00251         }
00252     }
00253 }
00254 
00258 static void proc_addZombie(Process *proc)
00259 {
00260     Node *node;
00261 #if CONFIG_KERN_PREEMPT
00262     ASSERT(!proc_preemptAllowed());
00263 #endif
00264 
00265 #if CONFIG_KERN_PRI
00266     node = &(proc)->link.link;
00267 #else
00268     node = &(proc)->link;
00269 #endif
00270     LIST_ASSERT_VALID(&zombie_list);
00271     ADDTAIL(&zombie_list, node);
00272 }
00273 
00274 #endif /* CONFIG_KERN_HEAP */
00275 
00290 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)
00291 {
00292     Process *proc;
00293     LOG_INFO("name=%s", name);
00294 #if CONFIG_KERN_HEAP
00295     bool free_stack = false;
00296 
00297     /*
00298      * Free up resources of a zombie process.
00299      *
00300      * We're implementing a kind of lazy garbage collector here for
00301      * efficiency reasons: we can avoid to introduce overhead into another
00302      * kernel task dedicated to free up resources (e.g., idle) and we're
00303      * not introducing any overhead into the scheduler after a context
00304      * switch (that would be *very* bad, because the scheduler runs with
00305      * IRQ disabled).
00306      *
00307      * In this way we are able to release the memory of the zombie tasks
00308      * without disabling IRQs and without introducing any significant
00309      * overhead in any other kernel task.
00310      */
00311     proc_freeZombies();
00312 
00313     /* Did the caller provide a stack for us? */
00314     if (!stack_base)
00315     {
00316         /* Did the caller specify the desired stack size? */
00317         if (!stack_size)
00318             stack_size = KERN_MINSTACKSIZE;
00319 
00320         /* Allocate stack dinamically */
00321         PROC_ATOMIC(stack_base =
00322             (cpu_stack_t *)heap_allocmem(&proc_heap, stack_size));
00323         if (stack_base == NULL)
00324             return NULL;
00325 
00326         free_stack = true;
00327     }
00328 
00329 #else // CONFIG_KERN_HEAP
00330 
00331     /* Stack must have been provided by the user */
00332     ASSERT2(IS_VALID_PTR(stack_base), "Invalid stack pointer. Did you forget to \
00333         enable CONFIG_KERN_HEAP?");
00334     ASSERT2(stack_size, "Stack size cannot be 0.");
00335 
00336 #endif // CONFIG_KERN_HEAP
00337 
00338 #if CONFIG_KERN_MONITOR
00339     /*
00340      * Fill-in the stack with a special marker to help debugging.
00341      * On 64bit platforms, CONFIG_KERN_STACKFILLCODE is larger
00342      * than an int, so the (int) cast is required to silence the
00343      * warning for truncating its size.
00344      */
00345     memset(stack_base, (int)CONFIG_KERN_STACKFILLCODE, stack_size);
00346 #endif
00347 
00348     /* Initialize the process control block */
00349     if (CPU_STACK_GROWS_UPWARD)
00350     {
00351         proc = (Process *)stack_base;
00352         proc->stack = stack_base + PROC_SIZE_WORDS;
00353         // On some architecture stack should be aligned, so we do it.
00354         proc->stack = (cpu_stack_t *)((uintptr_t)proc->stack + (sizeof(cpu_aligned_stack_t) - ((uintptr_t)proc->stack % sizeof(cpu_aligned_stack_t))));
00355         if (CPU_SP_ON_EMPTY_SLOT)
00356             proc->stack++;
00357     }
00358     else
00359     {
00360         proc = (Process *)(stack_base + stack_size / sizeof(cpu_stack_t) - PROC_SIZE_WORDS);
00361         // On some architecture stack should be aligned, so we do it.
00362         proc->stack = (cpu_stack_t *)((uintptr_t)proc - ((uintptr_t)proc % sizeof(cpu_aligned_stack_t)));
00363         if (CPU_SP_ON_EMPTY_SLOT)
00364             proc->stack--;
00365     }
00366     /* Ensure stack is aligned */
00367     ASSERT((uintptr_t)proc->stack % sizeof(cpu_aligned_stack_t) == 0);
00368 
00369     stack_size -= PROC_SIZE_WORDS * sizeof(cpu_stack_t);
00370     proc_initStruct(proc);
00371     proc->user_data = data;
00372 
00373 #if CONFIG_KERN_HEAP | CONFIG_KERN_MONITOR
00374     proc->stack_base = stack_base;
00375     proc->stack_size = stack_size;
00376     #if CONFIG_KERN_HEAP
00377     if (free_stack)
00378         proc->flags |= PF_FREESTACK;
00379     #endif
00380 #endif
00381     proc->user_entry = entry;
00382     CPU_CREATE_NEW_STACK(proc->stack);
00383 
00384 #if CONFIG_KERN_MONITOR
00385     monitor_add(proc, name);
00386 #endif
00387 
00388     /* Add to ready list */
00389     ATOMIC(SCHED_ENQUEUE(proc));
00390 
00391     return proc;
00392 }
00393 
00399 const char *proc_name(struct Process *proc)
00400 {
00401 #if CONFIG_KERN_MONITOR
00402     return proc ? proc->monitor.name : "<NULL>";
00403 #else
00404     (void)proc;
00405     return "---";
00406 #endif
00407 }
00408 
00410 const char *proc_currentName(void)
00411 {
00412     return proc_name(proc_current());
00413 }
00414 
00416 void proc_rename(struct Process *proc, const char *name)
00417 {
00418 #if CONFIG_KERN_MONITOR
00419     monitor_rename(proc, name);
00420 #else
00421     (void)proc; (void)name;
00422 #endif
00423 }
00424 
00425 
00426 #if CONFIG_KERN_PRI
00427 
00446 void proc_setPri(struct Process *proc, int pri)
00447 {
00448 #if CONFIG_KERN_PRI_INHERIT
00449     int new_pri;
00450 
00451     /*
00452      * Whatever it will happen below, this is the new
00453      * original priority of the process, i.e., the priority
00454      * it has without taking inheritance under account.
00455      */
00456     proc->orig_pri = pri;
00457 
00458     /* If not changing anything we can just leave */
00459     if ((new_pri = __prio_proc(proc)) == proc->link.pri)
00460         return;
00461 
00462     /*
00463      * Actual process priority is the highest among its
00464      * own priority and the one of the top-priority
00465      * process that it is blocking (returned by
00466      * __prio_proc()).
00467      */
00468     proc->link.pri = new_pri;
00469 #else
00470     if (proc->link.pri == pri)
00471         return;
00472 
00473     proc->link.pri = pri;
00474 #endif // CONFIG_KERN_PRI_INHERIT
00475 
00476     if (proc != current_process)
00477         ATOMIC(sched_reenqueue(proc));
00478 }
00479 #endif // CONFIG_KERN_PRI
00480 
00481 INLINE void proc_run(void)
00482 {
00483     void (*entry)(void) = current_process->user_entry;
00484 
00485     LOG_INFO("New process starting at %p", entry);
00486     entry();
00487 }
00488 
00492 void proc_entry(void)
00493 {
00494     /*
00495      * Return from a context switch assumes interrupts are disabled, so
00496      * we need to explicitly re-enable them as soon as possible.
00497      */
00498     IRQ_ENABLE;
00499     /* Call the actual process's entry point */
00500     proc_run();
00501     proc_exit();
00502 }
00503 
00507 void proc_exit(void)
00508 {
00509     LOG_INFO("%p:%s", current_process, proc_currentName());
00510 
00511 #if CONFIG_KERN_MONITOR
00512     monitor_remove(current_process);
00513 #endif
00514 
00515     proc_forbid();
00516 #if CONFIG_KERN_HEAP
00517     /*
00518      * Set the task as zombie, its resources will be freed in proc_new() in
00519      * a lazy way, when another process will be created.
00520      */
00521     proc_addZombie(current_process);
00522 #endif
00523     current_process = NULL;
00524     proc_permit();
00525 
00526     proc_switch();
00527 
00528     /* never reached */
00529     ASSERT(0);
00530 }
00531 
00535 static void proc_schedule(void)
00536 {
00537     Process *old_process = current_process;
00538 
00539     IRQ_ASSERT_DISABLED();
00540 
00541     /* Poll on the ready queue for the first ready process */
00542     LIST_ASSERT_VALID(&proc_ready_list);
00543     while (!(current_process = (struct Process *)list_remHead(&proc_ready_list)))
00544     {
00545         /*
00546          * Make sure we physically reenable interrupts here, no matter what
00547          * the current task status is. This is important because if we
00548          * are idle-spinning, we must allow interrupts, otherwise no
00549          * process will ever wake up.
00550          *
00551          * During idle-spinning, an interrupt can occur and it may
00552          * modify \p proc_ready_list. To ensure that compiler reload this
00553          * variable every while cycle we call CPU_MEMORY_BARRIER.
00554          * The memory barrier ensure that all variables used in this context
00555          * are reloaded.
00556          * \todo If there was a way to write sig_wait() so that it does not
00557          * disable interrupts while waiting, there would not be any
00558          * reason to do this.
00559          */
00560         IRQ_ENABLE;
00561         CPU_IDLE;
00562         MEMORY_BARRIER;
00563         IRQ_DISABLE;
00564     }
00565     if (CONTEXT_SWITCH_FROM_ISR())
00566         proc_context_switch(current_process, old_process);
00567     /* This RET resumes the execution on the new process */
00568     LOG_INFO("resuming %p:%s\n", current_process, proc_currentName());
00569 }
00570 
00571 #if CONFIG_KERN_PREEMPT
00572 /* Global preemption nesting counter */
00573 cpu_atomic_t preempt_count;
00574 
00575 /*
00576  * The time sharing interval: when a process is scheduled on a CPU it gets an
00577  * amount of CONFIG_KERN_QUANTUM clock ticks. When these ticks expires and
00578  * preemption is enabled a new process is selected to run.
00579  */
00580 int _proc_quantum;
00581 
00585 bool proc_needPreempt(void)
00586 {
00587     if (UNLIKELY(current_process == NULL))
00588         return false;
00589     if (!proc_preemptAllowed())
00590         return false;
00591     if (LIST_EMPTY(&proc_ready_list))
00592         return false;
00593     return preempt_quantum() ? prio_next() > prio_curr() :
00594             prio_next() >= prio_curr();
00595 }
00596 
00600 void proc_preempt(void)
00601 {
00602     IRQ_ASSERT_DISABLED();
00603     ASSERT(current_process);
00604 
00605     /* Perform the kernel preemption */
00606     LOG_INFO("preempting %p:%s\n", current_process, proc_currentName());
00607     /* We are inside a IRQ context, so ATOMIC is not needed here */
00608     SCHED_ENQUEUE(current_process);
00609     preempt_reset_quantum();
00610     proc_schedule();
00611 }
00612 #endif /* CONFIG_KERN_PREEMPT */
00613 
00614 /* Immediately switch to a particular process */
00615 static void proc_switchTo(Process *proc)
00616 {
00617     Process *old_process = current_process;
00618 
00619     SCHED_ENQUEUE(current_process);
00620     preempt_reset_quantum();
00621     current_process = proc;
00622     proc_context_switch(current_process, old_process);
00623 }
00624 
00633 void proc_switch(void)
00634 {
00635     ASSERT(proc_preemptAllowed());
00636     ATOMIC(
00637         preempt_reset_quantum();
00638         proc_schedule();
00639     );
00640 }
00641 
00645 void proc_wakeup(Process *proc)
00646 {
00647     ASSERT(proc_preemptAllowed());
00648     ASSERT(current_process);
00649     IRQ_ASSERT_DISABLED();
00650 
00651     if (prio_proc(proc) >= prio_curr())
00652         proc_switchTo(proc);
00653     else
00654         SCHED_ENQUEUE_HEAD(proc);
00655 }
00656 
00660 void proc_yield(void)
00661 {
00662     Process *proc;
00663 
00664     /*
00665      * Voluntary preemption while preemption is disabled is considered
00666      * illegal, as not very useful in practice.
00667      *
00668      * ASSERT if it happens.
00669      */
00670     ASSERT(proc_preemptAllowed());
00671     IRQ_ASSERT_ENABLED();
00672 
00673     IRQ_DISABLE;
00674     proc = (struct Process *)list_remHead(&proc_ready_list);
00675     if (proc)
00676         proc_switchTo(proc);
00677     IRQ_ENABLE;
00678 }