sos-code-article7/sos/thread.h

390 lines
12 KiB
C

/* 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 <sos/errno.h>
/* Forward declaration */
struct sos_thread;
#include <hwcore/cpu_context.h>
#include <sos/sched.h>
#include <sos/kwaitq.h>
#include <sos/time.h>
#include <sos/process.h>
/**
* 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_ */