diff options
Diffstat (limited to 'src/include')
-rw-r--r-- | src/include/errno.h | 1 | ||||
-rw-r--r-- | src/include/kernel/syscalls.H | 2 | ||||
-rw-r--r-- | src/include/kernel/task.H | 39 | ||||
-rw-r--r-- | src/include/kernel/taskmgr.H | 133 | ||||
-rw-r--r-- | src/include/sys/syscall.h | 23 | ||||
-rw-r--r-- | src/include/sys/task.h | 82 | ||||
-rw-r--r-- | src/include/sys/vfs.h | 7 | ||||
-rw-r--r-- | src/include/util/locked/list.H | 53 |
8 files changed, 328 insertions, 12 deletions
diff --git a/src/include/errno.h b/src/include/errno.h index d4c296179..79eb30bbf 100644 --- a/src/include/errno.h +++ b/src/include/errno.h @@ -31,6 +31,7 @@ #define EFAULT 14 // Bad address #define EINVAL 22 // Invalid argument #define ENFILE 23 // Too many open files in system +#define EDEADLK 35 // Operation would cause deadlock. #define EWOULDBLOCK EAGAIN // operation would block diff --git a/src/include/kernel/syscalls.H b/src/include/kernel/syscalls.H index 70dd436be..553fbf158 100644 --- a/src/include/kernel/syscalls.H +++ b/src/include/kernel/syscalls.H @@ -49,6 +49,8 @@ namespace Systemcalls TASK_END = 2, /** task_affinity_migrate_to_master() */ TASK_MIGRATE_TO_MASTER = 3, + /** task_wait() / task_wait_tid() */ + TASK_WAIT, /** msgq_create() */ MSGQ_CREATE, diff --git a/src/include/kernel/task.H b/src/include/kernel/task.H index cab925977..9ff0401ba 100644 --- a/src/include/kernel/task.H +++ b/src/include/kernel/task.H @@ -56,6 +56,30 @@ struct context_fp_t uint64_t fpscr; }; +enum task_states +{ + /** Task is currently running. */ + TASK_STATE_RUNNING = 'R', + /** Task is on scheduler queue ready to run. */ + TASK_STATE_READY = 'r', + /** Task has ended or crashed. */ + TASK_STATE_ENDED = 'E', + + /** Task is blocked on a futex. */ + TASK_STATE_BLOCK_FUTEX = 'f', + /** Task is blocked on a message queue. */ + TASK_STATE_BLOCK_MSG = 'M', + /** Task is defered due to a kernel->userspace request. */ + TASK_STATE_BLOCK_USRSPACE = 'u', + /** Task is blocked sleeping. */ + TASK_STATE_BLOCK_SLEEP = 's', + /** Task is blocked on join. */ + TASK_STATE_BLOCK_JOIN = 'j', +}; + + // Forward declaration. +struct task_tracking_t; + /** @struct task_t * @brief The kernel-level task structure. */ @@ -76,13 +100,24 @@ struct task_t * as been requested, so pinning can be used recursively. */ uint64_t affinity_pinned; + /** State of task */ + task_states state; + /** Extra info about the state. + * This is used when the task is blocked to give a pointer to the + * object the task is blocked on. */ + void* state_info; + + /** Pointer to tracking tree for joining, parent info, etc. */ + task_tracking_t* tracker; + + /** Detached state of the task. */ + bool detached; + // Pointers for queue containers. task_t* prev; task_t* next; }; -enum { TASK_DEFAULT_STACK_SIZE = 4 }; - // Macros for manipulating task's saved contexts. #define TASK_GETARGN(t, n) (t->context.gprs[n+4]) #define TASK_GETARG0(t) (TASK_GETARGN(t,0)) diff --git a/src/include/kernel/taskmgr.H b/src/include/kernel/taskmgr.H index db267523b..8f1deb264 100644 --- a/src/include/kernel/taskmgr.H +++ b/src/include/kernel/taskmgr.H @@ -26,31 +26,156 @@ #include <kernel/types.h> #include <util/lockfree/counter.H> #include <kernel/vmmmgr.H> +#include <sys/task.h> +#include <util/locked/list.H> +#include <kernel/spinlock.H> + // Forward declaration. +struct task_tracking_t; +struct task_wait_t; + /** Typedef for a list of task_tracking_t's. */ +typedef Util::Locked::List<task_tracking_t, tid_t> task_tracking_list_t; + +/** @struct task_tracking_t + * Stores the parent/child relationships and join information for tasks. + * + * The task_tracking_t's become a tree so that we can create a ps-like + * debug output and so that a task can do a wait() on all of its children. + */ +struct task_tracking_t +{ + typedef tid_t key_type; + + /** previous pointer for list. */ + task_tracking_t* prev; + /** next pointer for list. */ + task_tracking_t* next; + + /** pointer to parent's tracking info. */ + task_tracking_t* parent; + /** list of all children. */ + task_tracking_list_t children; + + /** tid as a key for list. */ + key_type key; + /** Pointer to task structure if it is still running. */ + task_t* task; + + /** Crash/clean-exit status if task has ended. */ + int status; + /** Return-value to task_end2() if task has ended. */ + void* retval; + /** Task-wait state object. */ + task_wait_t* wait_info; + + /** Record the original entry point of the thread for debug purpose. */ + void* entry_point; +}; + +/** @struct task_wait_t + * Stores the parameters for the task_wait syscall for a task which is + * deferred due to the syscall. + */ +struct task_wait_t +{ + /** Tid waiting on (or -1 for any). */ + int64_t tid; + /** Address to return the child status. */ + int* status; + /** Address to return the child return-value. */ + void** retval; +}; + +/** @class TaskManager + * Kernel management class to deal with task creation / exit. + */ class TaskManager { public: + /** @brief Returns a pointer to the currently running task on this + * CPU. + * + * @retval NULL - Kernel has never started running any tasks. + * @retval non-NULL - The task most recently running and/or will be + * running when kernel returns to user-space. + */ static task_t* getCurrentTask(); + + /** @brief Sets the current task pointer in this CPU object. + * + * @param[in] t - The task to assign on this CPU. + */ static void setCurrentTask(task_t* t); + /** Typedef for task entry points. */ typedef void(*task_fn_t)(void*); - static task_t* createTask(task_fn_t, void*); + + /** @brief Create a new task object. + * + * @param[in] t - The entry point to start the task at. + * @param[in] p - An argument pointer to pass to the task. + */ + static task_t* createTask(task_fn_t t, void* p); + + /** @brief End / destroy a task object. + * + * @param[in] t - The task to end. + * @param[in] retval - Return value from the task. + * @param[in] status - TASK_STATUS_* enumeration of how the task ended. + */ + static void endTask(task_t* t, void* retval, int status); + + /** @brief Perform the 'task_wait' for a task. + * + * Returns the child information if the requested child has already + * ended or defers the task if the child requested is still running. + * + * @param[in] t - The task requesting the wait. + * @param[in] tid - The child task requested to wait on or -1 for any. + * @param[out] status - The address to write the child status. + * @param[out] retval - The address to write the child retval. + */ + static void waitTask(task_t* t, int64_t tid, + int* status, void** retval); friend class CpuManager; + protected: TaskManager(); ~TaskManager() {}; + /** Create a new task where the entry point is idleTaskLoop. */ static task_t* createIdleTask(); private: - tid_t getNextTid() - { return iv_nextTid.next(); }; - Util::Lockfree::Counter<tid_t> iv_nextTid; + /** Get the next TID in the task sequence. */ + tid_t getNextTid() { return iv_nextTid.next(); }; + /** Run the idle task loop */ static void idleTaskLoop(void*); + + // Internal implementations of non-static / non-_ functions. task_t* _createIdleTask(); task_t* _createTask(task_fn_t, void*, bool); + void _endTask(task_t*, void*, int); + void _waitTask(task_t*, int64_t, int*, void**); + + /** Remove a tracker from the tracker-tree and delete it. + * + * @param[in] t - The tracker to remove. + * @note Spinlock-locking of the tracker-tree is the + * responsibility of the caller. + */ + void removeTracker(task_tracking_t* t); + + /** Atomic monotonically increasing counter to use for TIDs. */ + Util::Lockfree::Counter<tid_t> iv_nextTid; + + /** Task-tracking tree spinlock. */ + Spinlock iv_spinlock; + /** Task-tracking tree. */ + task_tracking_list_t iv_taskList; + }; #endif diff --git a/src/include/sys/syscall.h b/src/include/sys/syscall.h index f0972e949..b3d435edf 100644 --- a/src/include/sys/syscall.h +++ b/src/include/sys/syscall.h @@ -23,13 +23,21 @@ #ifndef __SYS_SYSCALL_H #define __SYS_SYSCALL_H +/** @file syscall.h + * @brief Defines syscall wrapper functions to get C-caller to put syscall + * parameters in the correct spots for ABI so kernel can pull them + * from the right position in the task structs. + */ + #ifdef __cplusplus -extern "C" +extern "C" { #endif #include <stdint.h> +#include <builtins.h> +// Normal system calls. void* _syscall0(uint64_t); void* _syscall1(uint64_t, void*); void* _syscall2(uint64_t, void*, void*); @@ -39,6 +47,19 @@ void* _syscall5(uint64_t, void*, void*, void*, void*, void*); void* _syscall6(uint64_t, void*, void*, void*, void*, void*, void*); void* _syscall7(uint64_t, void*, void*, void*, void*, void*, void*, void*); +// System calls which never return. Marked NO_RETURN so the compiler +// can make additional optimizations. +void* _syscall0_nr(uint64_t) NO_RETURN; +void* _syscall1_nr(uint64_t, void*) NO_RETURN; +void* _syscall2_nr(uint64_t, void*, void*) NO_RETURN; +void* _syscall3_nr(uint64_t, void*, void*, void*) NO_RETURN; +void* _syscall4_nr(uint64_t, void*, void*, void*, void*) NO_RETURN; +void* _syscall5_nr(uint64_t, void*, void*, void*, void*, void*) NO_RETURN; +void* _syscall6_nr(uint64_t, void*, void*, void*, void*, void*, void*) + NO_RETURN; +void* _syscall7_nr(uint64_t, void*, void*, void*, void*, void*, void*, void*) + NO_RETURN; + #ifdef __cplusplus } #include <kernel/syscalls.H> diff --git a/src/include/sys/task.h b/src/include/sys/task.h index 9f43996c4..0627784f1 100644 --- a/src/include/sys/task.h +++ b/src/include/sys/task.h @@ -27,6 +27,7 @@ #define __SYS_TASK_H #include <stdint.h> +#include <builtins.h> #include <kernel/types.h> #ifdef __cplusplus @@ -64,7 +65,17 @@ tid_t task_create(void(*start_routine)(void*), void* arg); * function. Therefore, returning from the entry point function will * also cause the task to end cleanly using this function. */ -void task_end(); +void task_end() NO_RETURN; + +/** @fn task_end2 + * @brief End the calling task with a return value. + * + * See POSIX pthread_exit. + * + * @param[in] retval - A pointer to return to the task performing task_join / + * task_wait on this task. + */ +void task_end2(void* retval) NO_RETURN; /** @fn task_gettid * @brief Get task ID of calling task. @@ -130,6 +141,75 @@ void task_affinity_unpin(); */ void task_affinity_migrate_to_master(); +/** @enum task_status + * @brief Status of how a task exited. + */ +enum task_status +{ + /** Task called task_end cleanly. */ + TASK_STATUS_EXITED_CLEAN, + /** Task crashed. Ended by the kernel due to error. */ + TASK_STATUS_CRASHED, +}; + +/** @fn task_detach + * @brief Sets the calling task to the 'detached' state, meaning no parent + * may task_wait_tid on it. + */ +void task_detach(); + +/** @fn task_wait_tid + * @brief Block calling task until a requested child process exits. + * + * See also: POSIX 'waitid' / Linux 'waitpid'. + * + * @param[in] tid - Task to wait for completion. + * + * @param[out] status - Optional address to write child status. + * @param[out] retval - Optional address to write return-value. + * + * Status values come from task_status enumeration. + * Retval values come from child's 'task_end2' parameter. + * + * @note All non-detached tasks must be waited on by their parent to ensure + * there are not kernel memory-leaks. + * + * @note If a parent task ends prior to waiting on its children, the children + * become parented by their grand-parents, who must do the wait. + * + * @return tid of child waited on or negative number on error. + * + * @retval EDEADLK - Performing this wait would deadlock the caller such as + * when it has no children. + * @retval EFAULT - Bad memory address given for status or retval parameter. + */ +tid_t task_wait_tid(tid_t tid, int* status, void** retval); + +/** @fn task_wait + * @brief Block calling task until any child process exits. + * + * See also: Linux 'wait'. + * + * @param[out] status - Optional address to write child status. + * @param[out] retval - Optional address to write return-value. + * + * Status values come from task_status enumeration. + * Retval values come from child's 'task_end2' parameter. + * + * @note All non-detached tasks must be waited on by their parent to ensure + * there are not kernel memory-leaks. + * + * @note If a parent task ends prior to waiting on its children, the children + * become parented by their grand-parents, who must do the wait. + * + * @return tid of child waited on or negative number on error. + * + * @retval EDEADLK - Performing this wait would deadlock the caller such as + * when it has no children. + * @retval EFAULT - Bad memory address given for status or retval parameter. + */ +tid_t task_wait(int* status, void** retval); + #ifdef __cplusplus } #endif diff --git a/src/include/sys/vfs.h b/src/include/sys/vfs.h index 6ab24c9b8..d427c0abb 100644 --- a/src/include/sys/vfs.h +++ b/src/include/sys/vfs.h @@ -102,14 +102,13 @@ extern uint64_t VFS_LAST_ADDRESS; VfsSystemModule * vfs_find_module(VfsSystemModule * i_table, const char * i_name); /** - * Call the module start routine + * Get the module's start routine * @param[in] i_module VfsSystemModule data for the module - * @param[in] i_param parameter to pass to task_create() for this module - * @return tid_t of started task or negative value on error. + * @return Function pointer of module's start or negative value on error. * @retval -ENOENT if i_module is NULL * @retval -ENOEXEC if there is no start() */ -tid_t vfs_exec(VfsSystemModule * i_module, void* i_param); +void* vfs_start_entrypoint(VfsSystemModule * i_module); /** * Change permissions on the virtual pages associated with the module diff --git a/src/include/util/locked/list.H b/src/include/util/locked/list.H index 5e06c5e8e..dc19ff193 100644 --- a/src/include/util/locked/list.H +++ b/src/include/util/locked/list.H @@ -46,6 +46,9 @@ namespace Util _T* find(_K& key) const; + bool empty(); + _T* begin(); + protected: _T* head; _T* tail; @@ -56,6 +59,31 @@ namespace Util void __unlock() const; }; + // SFINAE template to ensure compile fails if functions which are not + // SMP-safe are used on a 'locked' instance. + template<bool locked> + class __verify_list_is_smp_safe + { + public: + __verify_list_is_smp_safe() + { + class __util_locked_list_is_not_smp_safe; + __util_locked_list_is_not_smp_safe(); + } + }; + + // SFINAE template implementation to allow certain functions when the + // instance is not 'locked', assuming that caller is ensuring safety + // in some other way. + template<> + class __verify_list_is_smp_safe<false> + { + public: + __verify_list_is_smp_safe() + { + } + }; + template <typename _T, typename _K, bool locked, typename _S> _T* List<_T,_K,locked,_S>::remove() { @@ -169,6 +197,31 @@ namespace Util return node; } + + template <typename _T, typename _K, bool locked, typename _S> + bool List<_T, _K,locked,_S>::empty() + { + bool isEmpty = false; + + __lock(); + isEmpty = (head == NULL); + __unlock(); + + return isEmpty; + } + + template <typename _T, typename _K, bool locked, typename _S> + _T* List<_T, _K,locked,_S>::begin() + { + // Entirely not SMP-safe to return a pointer to a node if + // we are a locking instance. If we aren't locking we + // have to assume that the caller is ensuring SMP-safety + // globally in some other way. Use SFINAE technique to + // ensure begin() fails on locked lists. + __verify_list_is_smp_safe<locked>(); + return head; + } + } } |