summaryrefslogtreecommitdiffstats
path: root/src/include
diff options
context:
space:
mode:
Diffstat (limited to 'src/include')
-rw-r--r--src/include/errno.h1
-rw-r--r--src/include/kernel/syscalls.H2
-rw-r--r--src/include/kernel/task.H39
-rw-r--r--src/include/kernel/taskmgr.H133
-rw-r--r--src/include/sys/syscall.h23
-rw-r--r--src/include/sys/task.h82
-rw-r--r--src/include/sys/vfs.h7
-rw-r--r--src/include/util/locked/list.H53
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;
+ }
+
}
}
OpenPOWER on IntegriCloud