/* Copyright (C) 2004,2005 David Decotigny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _SOS_THREAD_H_ #define _SOS_THREAD_H_ /** * @file thread.h * * SOS Thread management API */ #include /* Forward declaration */ struct sos_thread; #include #include #include #include #include /** * The possible states of a valid thread */ typedef enum { SOS_THR_CREATED, /**< Thread created, not fully initialized */ SOS_THR_READY, /**< Thread fully initialized or waiting for CPU after having been blocked or preempted */ SOS_THR_RUNNING, /**< Thread currently running on CPU */ SOS_THR_BLOCKED, /**< Thread waiting for I/O (+ in at LEAST one kwaitq) and/or sleeping (+ in NO kwaitq) */ SOS_THR_ZOMBIE, /**< Thread terminated execution, waiting to be deleted by kernel */ } sos_thread_state_t; /** * TCB (Thread Control Block): structure describing a thread. Don't * access these fields directly: prefer using the accessor functions * below. */ struct sos_thread { #define SOS_THR_MAX_NAMELEN 32 char name[SOS_THR_MAX_NAMELEN]; sos_thread_state_t state; sos_sched_priority_t priority; /** * The hardware context of the thread. * * It will reflect the CPU state of the thread: * - From an interrupt handler: the state of the thread at the time * of the OUTERMOST irq. An IRQ is not allowed to make context * switches, so this context will remain valid from the begining of * the outermost IRQ handler to the end of it, no matter if there * are other IRQ handlers nesting in one another. You may safely * use it from IRQ handlers to query the state of the interrupted * thread, no matter if there has been other IRQ handlers * executing meanwhile. * - From normal kernel code, exceptions and syscall: the state of * the thread the last time there was a context switch from this * thread to another one. Thus this field WON'T reflect the * current's thread cpu_state in these cases. So, in these cases, * simply DO NOT USE IT outside thread.c ! Note: for syscall and * exception handlers, the VALID state of the interrupted thread is * passed as an argument to the handlers. */ struct sos_cpu_state *cpu_state; /* Kernel stack parameters */ sos_vaddr_t kernel_stack_base_addr; sos_size_t kernel_stack_size; /* Process this thread belongs to. Always NULL for a kernel thread */ struct sos_process *process; /** * Address space currently "squatted" by the thread, or used to be * active when the thread was interrupted/preempted. This is the MMU * configuration expected before the cpu_state of the thread is * restored on CPU. * - For kernel threads: should normally be NULL, meaning that the * thread will squat the current mm_context currently set in the * MMU. Might be NON NULL when a kernel thread squats a given * process to manipulate its address space. * - For user threads: should normally be NULL. More precisely: * - in user mode: the thread->process.mm_context is ALWAYS * set on MMU. squatted_mm_context is ALWAYS NULL in this * situation, meaning that the thread in user mode uses its * process-space as expected * - in kernel mode: NULL means that we keep on using the * mm_context currently set on MMU, which might be the * mm_context of another process. This is natural since a * thread in kernel mode normally only uses data in kernel * space. BTW, this limits the number of TLB flushes. However, * there are exceptions where this squatted_mm_context will * NOT be NULL. One is the copy_from/to_user API, which can * force the effective mm_context so that the MMU will be * (re)configured upon every context to the thread to match * the squatted_mm_context. Another exception is when a parent * thread creates the address space of a child process, in * which case the parent thread might temporarilly decide to * switch to the child's process space. * * This is the SOS implementation of the Linux "Lazy TLB" and * address-space loaning. */ struct sos_mm_context *squatted_mm_context; /* Data specific to each state */ union { struct { struct sos_sched_queue *rdy_queue; struct sos_thread *rdy_prev, *rdy_next; } ready; struct { struct sos_time user_time_spent_in_slice; } running; }; /* Anonymous union (gcc extenion) */ /* * Data used by the kwaitq subsystem: list of kwaitqueues the thread * is waiting for. * * @note: a RUNNING or READY thread might be in one or more * waitqueues ! The only property we have is that, among these * waitqueues (if any), _at least_ one has woken the thread. */ struct sos_kwaitq_entry *kwaitq_list; /** * Some statistics */ struct rusage { /* Updated by sched.c */ struct sos_time ru_utime; /* Time spent in user mode */ struct sos_time ru_stime; /* Time spent in kernel mode */ } rusage; /** * Chaining pointers for the list of threads in the parent process */ struct sos_thread *prev_in_process, *next_in_process; /** * Chaining pointers for global ("gbl") list of threads (debug) */ struct sos_thread *gbl_prev, *gbl_next; }; /** * Definition of the function executed by a kernel thread */ typedef void (*sos_kernel_thread_start_routine_t)(void *arg); /** * Initialize the subsystem responsible for thread management * * Initialize the primary kernel thread so that it can be handled the * same way as an ordinary thread created by sos_thread_create(). */ sos_ret_t sos_thread_subsystem_setup(sos_vaddr_t init_thread_stack_base_addr, sos_size_t init_thread_stack_size); /** * Create a new kernel thread */ struct sos_thread * sos_create_kernel_thread(const char *name, sos_kernel_thread_start_routine_t start_func, void *start_arg, sos_sched_priority_t priority); /** * Create a new user thread */ struct sos_thread * sos_create_user_thread(const char *name, struct sos_process *process, sos_uaddr_t user_initial_PC, sos_ui32_t user_start_arg1, sos_ui32_t user_start_arg2, sos_uaddr_t user_initial_SP, sos_sched_priority_t priority); /** * Terminate the execution of the current thread. For kernel threads, * it is called by default when the start routine returns. */ void sos_thread_exit() __attribute__((noreturn)); /** * Get the identifier of the thread currently running on CPU. Trivial * function. */ struct sos_thread *sos_thread_get_current(); /** * If thr == NULL, set the priority of the current thread. Trivial * function. * * @note NOT protected against interrupts */ sos_sched_priority_t sos_thread_get_priority(struct sos_thread *thr); /** * If thr == NULL, get the state of the current thread. Trivial * function. * * @note NOT protected against interrupts */ sos_thread_state_t sos_thread_get_state(struct sos_thread *thr); /** * If thr == NULL, set the priority of the current thread * * @note NO context-switch ever occurs in this function ! */ sos_ret_t sos_thread_set_priority(struct sos_thread *thr, sos_sched_priority_t priority); /** * Yield CPU to another ready thread. * * @note This is a BLOCKING FUNCTION */ sos_ret_t sos_thread_yield(); /** * Release the CPU for (at least) the given delay. * * @param delay The delay to wait for. If delay == NULL then wait * forever that any event occurs. * * @return SOS_OK when delay expired (and delay is reset to zero), * -SOS_EINTR otherwise (and delay contains the amount of time * remaining). * * @note This is a BLOCKING FUNCTION */ sos_ret_t sos_thread_sleep(/* in/out */struct sos_time *delay); /** * Mark the given thread as READY (if not already ready) even if it is * blocked in a kwaitq or in a sleep ! As a result, the interrupted * kwaitq/sleep function call of the thread will return with * -SOS_EINTR. * * @return -SOS_EINVAL if thread does not exist, or -SOS_EFATAL if * marked ZOMBIE. * * @note As a result, the semaphore/mutex/conditions/... functions * return values SHOULD ALWAYS be checked ! If they are != SOS_OK, * then the caller should consider that the resource is not aquired * because somebody woke the thread by some way. */ sos_ret_t sos_thread_force_unblock(struct sos_thread *thread); /** * Dump the backtrace of the current thread to console and/or bochs */ void sos_thread_dump_backtrace(sos_bool_t on_console, sos_bool_t on_bochs); /* ********************************************** * Restricted functions */ /** * Restricted function to change the current mm_context AND the * squatted_mm_context of the current thread in order to access the data * in this context * * @param mm_ctxt The mm_ctxt to restore. Might be NULL, meaning that: * - for a Kernel thread: the current MMU configuration is never * modified. The address space to use is limited to the kernel * space, user space might change due to preemptions to other * processes * - for a User thread in kernel mode: same as for kernel threads * - when a User thread will go back in user context: the MMU will * be reconfigured to match the mm_context of the thread's * process * * @note A non NULL parameter is allowed only if the * squatted_mm_context is not already set. A NULL parameter is allowed * only if the squatted_mm_context was already set. * * @note The use of this function is RESERVED to the syscall handler * and the copy_from/to_user functions */ sos_ret_t sos_thread_change_current_mm_context(struct sos_mm_context *mm_ctxt); /** * Restricted callback called when a syscall goes back in user mode, * to reconfigure the MMU to match that of the current thread's * process MMU context. * * @note The use of this function is RESERVED to the syscall wrapper */ void sos_thread_prepare_syscall_switch_back(struct sos_cpu_state *cpu_state); /** * Restricted callback called when an exception handler goes back to * the interrupted thread to reconfigure the MMU to match that of the * current thread's process MMU context. * * @note The use of this function is RESERVED to the exception wrappers */ void sos_thread_prepare_exception_switch_back(struct sos_cpu_state *cpu_state); /** * Restricted callback called when an IRQ is entered while the CPU was * NOT already servicing any other IRQ (ie the outermost IRQ handler * is entered). This callback simply updates the "cpu_state" field so * that IRQ handlers always know the state of the interrupted thread, * even if they are imbricated in other IRQ handlers. * * @note The use of this function is RESERVED to the irq wrappers */ void sos_thread_prepare_irq_servicing(struct sos_cpu_state *interrupted_state); /** * Restricted callback called when the outermost IRQ handler returns, * to select the thread to return to. This callbacks implements: * - preemption of user threads in user mode (time sharing / FIFO) * - non-preemption of user threads in kernel mode (interrupted thread * is restored on CPU "as is") * - non-preemption of kernel threads (same remark) * The MMU is reconfigured correctly to match the address space of the * selected thread. * * @return The CPU context of the thread to return to * * @note The use of this function is RESERVED to the irq wrappers */ struct sos_cpu_state * sos_thread_prepare_irq_switch_back(void); #endif /* _SOS_THREAD_H_ */