summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--src/kernel/basesegment.C11
-rw-r--r--src/kernel/cpumgr.C2
-rw-r--r--src/kernel/exception.C15
-rw-r--r--src/kernel/futexmgr.C4
-rw-r--r--src/kernel/kernel.C16
-rw-r--r--src/kernel/msghandler.C9
-rw-r--r--src/kernel/scheduler.C4
-rw-r--r--src/kernel/syscall.C74
-rw-r--r--src/kernel/taskmgr.C216
-rw-r--r--src/kernel/timemgr.C8
-rw-r--r--src/lib/syscall_stub.S37
-rw-r--r--src/lib/syscall_task.C53
-rw-r--r--src/sys/vfs/vfs_main.C30
-rw-r--r--src/usr/testcore/kernel/taskwaittest.H177
-rw-r--r--src/usr/vfs/vfsrp.C8
23 files changed, 911 insertions, 93 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;
+ }
+
}
}
diff --git a/src/kernel/basesegment.C b/src/kernel/basesegment.C
index 4cb2c39cb..dee18600a 100644
--- a/src/kernel/basesegment.C
+++ b/src/kernel/basesegment.C
@@ -131,7 +131,14 @@ int BaseSegment::_mmAllocBlock(MessageQueue* i_mq,void* i_va,uint64_t i_size)
uint64_t BaseSegment::findPhysicalAddress(uint64_t i_vaddr) const
{
- if(i_vaddr < iv_physMemSize) return i_vaddr;
+ if(i_vaddr < iv_physMemSize)
+ {
+ // Anything in the physical address size is valid (and linear mapped)
+ // except NULL.
+ if (i_vaddr >= PAGE_SIZE)
+ return i_vaddr;
+ else return -EFAULT;
+ }
return (iv_block ? iv_block->findPhysicalAddress(i_vaddr) : -EFAULT);
}
@@ -170,7 +177,7 @@ int BaseSegment::_mmSetPermission(void* i_va, uint64_t i_size, uint64_t i_access
// Check to see if there is a next block
if (l_block->iv_nextBlock)
{
- // set local block to the next block
+ // set local block to the next block
l_block = l_block->iv_nextBlock;
}
else
diff --git a/src/kernel/cpumgr.C b/src/kernel/cpumgr.C
index 01af160f1..86e8653bf 100644
--- a/src/kernel/cpumgr.C
+++ b/src/kernel/cpumgr.C
@@ -149,7 +149,6 @@ void CpuManager::startCPU(ssize_t i)
if (currentCPU)
{
- setSPRG3((uint64_t) cv_cpus[i]->idle_task);
setDEC(TimeManager::getTimeSliceCount());
}
return;
@@ -157,7 +156,6 @@ void CpuManager::startCPU(ssize_t i)
void CpuManager::startSlaveCPU(cpu_t* cpu)
{
- setSPRG3((uint64_t) cpu->idle_task);
setDEC(TimeManager::getTimeSliceCount());
return;
diff --git a/src/kernel/exception.C b/src/kernel/exception.C
index 6f015a57c..cbe97b220 100644
--- a/src/kernel/exception.C
+++ b/src/kernel/exception.C
@@ -29,7 +29,6 @@
#include <kernel/vmmmgr.H>
#include <kernel/cpuid.H>
-namespace Systemcalls { void TaskEnd(task_t*); }
namespace ExceptionHandles
{
bool HvEmulation(task_t*);
@@ -54,7 +53,7 @@ void kernel_execute_prog_ex()
if (!handled)
{
printk("Program exception, killing task %d\n", t->tid);
- Systemcalls::TaskEnd(t);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
}
@@ -78,7 +77,7 @@ void kernel_execute_data_storage()
{
printk("Data Storage exception on %d: %lx, %lx\n",
t->tid, getDAR(), getDSISR());
- Systemcalls::TaskEnd(t);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
}
@@ -87,7 +86,7 @@ void kernel_execute_data_segment()
{
task_t* t = TaskManager::getCurrentTask();
printk("Data Segment exception, killing task %d\n", t->tid);
- Systemcalls::TaskEnd(t);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
const uint64_t EXCEPTION_SRR1_INSTR_MASK = 0x0000000040000000;
@@ -110,7 +109,7 @@ void kernel_execute_inst_storage()
{
printk("Inst Storage exception on %d: %lx, %lx\n",
t->tid, getSRR0(), getSRR1());
- Systemcalls::TaskEnd(t);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
}
@@ -119,7 +118,7 @@ void kernel_execute_inst_segment()
{
task_t* t = TaskManager::getCurrentTask();
printk("Inst Segment exception, killing task %d\n", t->tid);
- Systemcalls::TaskEnd(t);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
extern "C"
@@ -127,7 +126,7 @@ void kernel_execute_alignment()
{
task_t* t = TaskManager::getCurrentTask();
printk("Alignment exception, killing task %d\n", t->tid);
- Systemcalls::TaskEnd(t);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
extern "C"
@@ -136,7 +135,7 @@ void kernel_execute_hype_emu_assist()
task_t* t = TaskManager::getCurrentTask();
printk("HypeEmu: Illegal instruction in task %d\n"
"\tHSSR0 = %lx, HEIR = %lx\n", t->tid, getHSRR0(), getHEIR());
- Systemcalls::TaskEnd(t);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
namespace ExceptionHandles
diff --git a/src/kernel/futexmgr.C b/src/kernel/futexmgr.C
index e350e54a4..ec0b5ed64 100644
--- a/src/kernel/futexmgr.C
+++ b/src/kernel/futexmgr.C
@@ -68,6 +68,10 @@ uint64_t FutexManager::_wait(task_t* i_task, uint64_t * i_addr, uint64_t i_val)
waiter->key = i_addr;
waiter->task = i_task;
+ // Set blocked state.
+ i_task->state = TASK_STATE_BLOCK_FUTEX;
+ i_task->state_info = i_addr;
+
// Now add the futex/task it to the wait queue
iv_list.insert(waiter);
iv_lock.unlock();
diff --git a/src/kernel/kernel.C b/src/kernel/kernel.C
index 46ca11342..557a1c81c 100644
--- a/src/kernel/kernel.C
+++ b/src/kernel/kernel.C
@@ -43,7 +43,7 @@ extern uint64_t kernel_other_thread_spinlock;
class Kernel
{
public:
- void cppBootstrap();
+ void cppBootstrap();
void memBootstrap();
void cpuBootstrap();
void inittaskBootstrap();
@@ -56,19 +56,22 @@ extern "C"
int main()
{
printk("Booting %s kernel...\n\n", "Hostboot");
-
+
+ // Erase task-pointer so that TaskManager::getCurrentTask() returns NULL.
+ setSPRG3(NULL);
+
Kernel& kernel = Singleton<Kernel>::instance();
kernel.cppBootstrap();
- kernel.memBootstrap();
+ kernel.memBootstrap();
kernel.cpuBootstrap();
kernel.inittaskBootstrap();
-
+
// Ready to let the other CPUs go.
lwsync();
kernel_other_thread_spinlock = 1;
- kernel_dispatch_task(); // no return.
+ kernel_dispatch_task(); // no return.
while(1);
return 0;
}
@@ -76,6 +79,9 @@ int main()
extern "C"
int smp_slave_main(cpu_t* cpu)
{
+ // Erase task-pointer so that TaskManager::getCurrentTask() returns NULL.
+ setSPRG3(NULL);
+
CpuManager::init_slave_smp(cpu);
VmmManager::init_slb();
cpu->scheduler->setNextRunnable();
diff --git a/src/kernel/msghandler.C b/src/kernel/msghandler.C
index 32da05673..82dab7f8f 100644
--- a/src/kernel/msghandler.C
+++ b/src/kernel/msghandler.C
@@ -29,8 +29,6 @@
#include <kernel/taskmgr.H>
#include <kernel/console.H>
-namespace Systemcalls { void TaskEnd(task_t*); };
-
void MessageHandler::sendMessage(msg_sys_types_t i_type, void* i_key,
void* i_data, task_t* i_task)
{
@@ -74,6 +72,11 @@ void MessageHandler::sendMessage(msg_sys_types_t i_type, void* i_key,
// Defer task while waiting for message response.
if ((NULL != i_task) && (TaskManager::getCurrentTask() == i_task))
{
+ // Set block status.
+ i_task->state = TASK_STATE_BLOCK_USRSPACE;
+ i_task->state_info = i_key;
+
+ // Select next task off scheduler.
i_task->cpu->scheduler->setNextRunnable();
}
@@ -138,7 +141,7 @@ int MessageHandler::recvMessage(msg_t* i_msg)
// Unsuccessful, unhandled response. Kill task.
printk("Unhandled msg rc %d for key %p on task %d @ %p\n",
msg_rc, key, deferred_task->tid, deferred_task->context.nip);
- Systemcalls::TaskEnd(deferred_task);
+ TaskManager::endTask(deferred_task, NULL, TASK_STATUS_CRASHED);
}
else if (CONTINUE_DEFER == rc)
{
diff --git a/src/kernel/scheduler.C b/src/kernel/scheduler.C
index 28f70debd..8a8da89a4 100644
--- a/src/kernel/scheduler.C
+++ b/src/kernel/scheduler.C
@@ -31,6 +31,8 @@
void Scheduler::addTask(task_t* t)
{
+ t->state = TASK_STATE_READY;
+
if (t->cpu->idle_task != t)
{
// If task is pinned to this CPU, add to the per-CPU queue.
@@ -53,6 +55,8 @@ void Scheduler::addTask(task_t* t)
void Scheduler::addTaskMasterCPU(task_t* t)
{
+ t->state = TASK_STATE_READY;
+
if (t->cpu->idle_task != t)
{
cpu_t* master = CpuManager::getMasterCPU();
diff --git a/src/kernel/syscall.C b/src/kernel/syscall.C
index cb4650c7a..53fdb287e 100644
--- a/src/kernel/syscall.C
+++ b/src/kernel/syscall.C
@@ -62,6 +62,7 @@ namespace Systemcalls
void TaskStart(task_t*);
void TaskEnd(task_t*);
void TaskMigrateToMaster(task_t*);
+ void TaskWait(task_t*);
void MsgQCreate(task_t*);
void MsgQDestroy(task_t*);
void MsgQRegisterRoot(task_t*);
@@ -91,6 +92,7 @@ namespace Systemcalls
&TaskStart, // TASK_START
&TaskEnd, // TASK_END
&TaskMigrateToMaster, // TASK_MIGRATE_TO_MASTER
+ &TaskWait, // TASK_WAIT
&MsgQCreate, // MSGQ_CREATE
&MsgQDestroy, // MSGQ_DESTROY
@@ -163,19 +165,8 @@ namespace Systemcalls
void TaskEnd(task_t* t)
{
- // Make sure task pointers are updated before we delete this task.
- t->cpu->scheduler->setNextRunnable();
-
- // TODO: Deal with join.
-
- // Clean up task memory.
- // Delete FP context.
- if (t->fp_context)
- delete t->fp_context;
- // Delete stack.
- StackSegment::deleteStack(t->tid);
- // Delete task struct.
- delete t;
+ TaskManager::endTask(t, (void*)TASK_GETARG0(t),
+ TASK_STATUS_EXITED_CLEAN);
}
void TaskMigrateToMaster(task_t* t)
@@ -194,6 +185,46 @@ namespace Systemcalls
t->cpu->scheduler->setNextRunnable();
}
+ void TaskWait(task_t* t)
+ {
+ int64_t tid = static_cast<int64_t>(TASK_GETARG0(t));
+ int* status = reinterpret_cast<int*>(TASK_GETARG1(t));
+ void** retval = reinterpret_cast<void**>(TASK_GETARG2(t));
+
+ // Validate status address and convert to kernel address.
+ if (status != NULL)
+ {
+ uint64_t addr =
+ VmmManager::findPhysicalAddress(
+ reinterpret_cast<uint64_t>(status));
+
+ if (addr == (static_cast<uint64_t>(-EFAULT)))
+ {
+ TASK_SETRTN(t, -EFAULT);
+ return;
+ }
+ status = reinterpret_cast<int*>(addr);
+ }
+
+ // Validate retval address and convert to kernel address.
+ if (retval != NULL)
+ {
+ uint64_t addr =
+ VmmManager::findPhysicalAddress(
+ reinterpret_cast<uint64_t>(retval));
+
+ if (addr == (static_cast<uint64_t>(-EFAULT)))
+ {
+ TASK_SETRTN(t, -EFAULT);
+ return;
+ }
+ retval = reinterpret_cast<void**>(addr);
+ }
+
+ // Perform wait.
+ TaskManager::waitTask(t, tid, status, retval);
+ }
+
void MsgQCreate(task_t* t)
{
TASK_SETRTN(t, (uint64_t) new MessageQueue());
@@ -281,6 +312,8 @@ namespace Systemcalls
MessagePending* mp = new MessagePending();
mp->key = m;
mp->task = t;
+ t->state = TASK_STATE_BLOCK_MSG;
+ t->state_info = mq;
mq->lock.lock();
@@ -355,6 +388,8 @@ namespace Systemcalls
if (NULL == mp)
{
mq->waiting.insert(t);
+ t->state = TASK_STATE_BLOCK_MSG;
+ t->state_info = mq;
t->cpu->scheduler->setNextRunnable();
}
else
@@ -414,8 +449,7 @@ namespace Systemcalls
TimeManager::delayTask(t, TASK_GETARG0(t), TASK_GETARG1(t));
TASK_SETRTN(t, 0);
- Scheduler* s = t->cpu->scheduler;
- s->setNextRunnable();
+ t->cpu->scheduler->setNextRunnable();
}
/**
@@ -445,7 +479,7 @@ namespace Systemcalls
{
printk("Task %d terminated. No physical address found for address 0x%p",
t->tid, (void *) uaddr);
- TaskEnd(t);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
}
@@ -470,7 +504,7 @@ namespace Systemcalls
{
printk("Task %d terminated. No physical address found for address 0x%p",
t->tid, (void *) uaddr);
- TaskEnd(t);
+ TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED);
}
}
@@ -529,12 +563,12 @@ namespace Systemcalls
* @param[in] t: The task used to set Page Permissions for a given block
*/
void MmSetPermission(task_t* t)
- {
+ {
void* va = (void*)TASK_GETARG0(t);
uint64_t size = (uint64_t)TASK_GETARG1(t);
PAGE_PERMISSIONS access_type = (PAGE_PERMISSIONS)TASK_GETARG2(t);
-
+
TASK_SETRTN(t, VmmManager::mmSetPermission(va,size, access_type));
}
-
+
};
diff --git a/src/kernel/taskmgr.C b/src/kernel/taskmgr.C
index 04d91c4f8..0ed493801 100644
--- a/src/kernel/taskmgr.C
+++ b/src/kernel/taskmgr.C
@@ -26,6 +26,9 @@
#include <kernel/pagemgr.H>
#include <kernel/cpumgr.H>
#include <kernel/stacksegment.H>
+#include <kernel/stacksegment.H>
+#include <kernel/cpu.H>
+#include <kernel/scheduler.H>
#include <sys/task.h>
#include <arch/ppc.H>
#include <string.h>
@@ -50,6 +53,7 @@ task_t* TaskManager::getCurrentTask()
void TaskManager::setCurrentTask(task_t* t)
{
t->cpu = CpuManager::getCurrentCPU();
+ t->state = TASK_STATE_RUNNING;
setSPRG3((uint64_t)t);
return;
}
@@ -68,6 +72,16 @@ task_t* TaskManager::createTask(TaskManager::task_fn_t t, void* p)
return Singleton<TaskManager>::instance()._createTask(t, p, true);
}
+void TaskManager::endTask(task_t* t, void* retval, int status)
+{
+ Singleton<TaskManager>::instance()._endTask(t,retval,status);
+}
+
+void TaskManager::waitTask(task_t* t, int64_t tid, int* status, void** retval)
+{
+ Singleton<TaskManager>::instance()._waitTask(t,tid,status,retval);
+}
+
task_t* TaskManager::_createIdleTask()
{
return this->_createTask(&TaskManager::idleTaskLoop, NULL, false);
@@ -113,6 +127,208 @@ task_t* TaskManager::_createTask(TaskManager::task_fn_t t,
// Clear FP context (start with FP disabled on all tasks).
task->fp_context = NULL;
+ // Clear task state info.
+ task->state = TASK_STATE_READY;
+ task->state_info = NULL;
+
+ // Create tracker instance for this task.
+ task_tracking_t* tracker = new task_tracking_t;
+ tracker->key = task->tid;
+ tracker->task = task;
+ tracker->status = -1;
+ tracker->retval = NULL;
+ tracker->wait_info = NULL;
+ tracker->entry_point = reinterpret_cast<void*>(t);
+ task->tracker = tracker;
+
+ // Assign parent for tracker instance, add to task tree.
+ iv_spinlock.lock();
+ task_t* parent = getCurrentTask();
+ if (NULL == parent)
+ {
+ tracker->parent = NULL;
+ iv_taskList.insert(tracker);
+ }
+ else
+ {
+ tracker->parent = parent->tracker;
+ parent->tracker->children.insert(tracker);
+ }
+ iv_spinlock.unlock();
+
return task;
}
+
+void TaskManager::_endTask(task_t* t, void* retval, int status)
+{
+ // Update task state.
+ t->state = TASK_STATE_ENDED;
+
+ // Make sure task pointers are updated before we delete this task.
+ if (getCurrentTask() == t)
+ t->cpu->scheduler->setNextRunnable();
+
+ // Update status in tracker.
+ t->tracker->status = status;
+ t->tracker->retval = retval;
+ t->tracker->task = NULL; // NULL signifies task is complete for now.
+
+ iv_spinlock.lock();
+
+ if (t->detached) // If detached, just clean up the tracker.
+ {
+ removeTracker(t->tracker);
+ }
+ else // If not detached, do join.
+ {
+ if (t->tracker->parent && t->tracker->parent->wait_info)
+ {
+ task_tracking_t* parent = t->tracker->parent;
+
+ if ((parent->wait_info->tid < 0) ||
+ (parent->wait_info->tid == t->tid))
+ {
+ if (parent->wait_info->status)
+ {
+ *(parent->wait_info->status) = status;
+ }
+ if (parent->wait_info->retval)
+ {
+ *(parent->wait_info->retval) = retval;
+ }
+ delete parent->wait_info;
+ parent->wait_info = NULL;
+ lwsync(); // Ensure status is pushed to memory before parent
+ // task begins execution.
+
+ TASK_SETRTN(parent->task, t->tid);
+ removeTracker(t->tracker);
+ parent->task->cpu->scheduler->addTask(parent->task);
+ }
+ }
+ }
+ iv_spinlock.unlock();
+
+ // Clean up task memory.
+ // Delete FP context.
+ if (t->fp_context)
+ delete t->fp_context;
+ // Delete stack.
+ StackSegment::deleteStack(t->tid);
+ // Delete task struct.
+ delete t;
+}
+
+void TaskManager::_waitTask(task_t* t, int64_t tid, int* status, void** retval)
+{
+ iv_spinlock.lock();
+
+ do
+ {
+ // Search for a candidate completed child task.
+ task_tracking_t* child_task = NULL;
+
+ if (tid < 0) // -1 => Look for any task.
+ {
+ task_tracking_t* children = t->tracker->children.begin();
+ if (!children)
+ {
+ // No children at all, this is a deadlock.
+ TASK_SETRTN(t, -EDEADLK);
+ break;
+ }
+ while(children)
+ {
+ if (!children->task)
+ {
+ child_task = children;
+ children = NULL;
+ }
+ else
+ {
+ children = children->next;
+ }
+ }
+ }
+ else // Look for a specific task.
+ {
+ // This copy is needed to create a reference of the appropriate
+ // type to pass into the 'find' function. Otherwise, you get
+ // type-punned reference errors.
+ task_tracking_t::key_type _tid = tid;
+
+ child_task = t->tracker->children.find(_tid);
+
+ // Check that we aren't asking to wait on a task that isn't our
+ // child, potential deadlock.
+ if (NULL == child_task)
+ {
+ TASK_SETRTN(t, -EDEADLK);
+ break;
+ }
+ }
+
+ // If there was a finished task found return information to caller.
+ if (child_task && (child_task->task == NULL))
+ {
+ TASK_SETRTN(t, child_task->key);
+ if (status)
+ {
+ *status = child_task->status;
+ }
+ if (retval)
+ {
+ *retval = child_task->retval;
+ }
+ removeTracker(child_task);
+ }
+ else // Otherwise, create wait-info to defer task.
+ {
+ task_wait_t* tj = t->tracker->wait_info = new task_wait_t;
+ tj->tid = tid;
+ tj->status = status;
+ tj->retval = retval;
+
+ t->state = TASK_STATE_BLOCK_JOIN;
+ t->state_info = reinterpret_cast<void*>(tid);
+
+ t->cpu->scheduler->setNextRunnable();
+ }
+
+ } while(0);
+
+ iv_spinlock.unlock();
+
+ return;
+}
+
+void TaskManager::removeTracker(task_tracking_t* t)
+{
+ task_tracking_list_t* trackingList = NULL;
+
+ task_tracking_t* parent = NULL;
+
+ if (t->parent)
+ {
+ trackingList = &t->parent->children;
+ parent = t->parent;
+ }
+ else // Parent is kernel.
+ {
+ trackingList = &iv_taskList;
+ }
+
+ // Remove tracker from parent list.
+ trackingList->erase(t);
+
+ // Add children to parent.
+ while(task_tracking_t* child = t->children.remove())
+ {
+ child->parent = parent;
+ trackingList->insert(child);
+ }
+
+ // Delete tracker object.
+ delete t;
+}
diff --git a/src/kernel/timemgr.C b/src/kernel/timemgr.C
index cbe72440b..896871a4d 100644
--- a/src/kernel/timemgr.C
+++ b/src/kernel/timemgr.C
@@ -23,6 +23,7 @@
#include <kernel/timemgr.H>
#include <kernel/scheduler.H>
#include <util/singleton.H>
+#include <kernel/task.H>
uint64_t TimeManager::iv_timebaseFreq = 0xFFFFFFFF;
@@ -40,7 +41,7 @@ uint64_t TimeManager::convertSecToTicks(uint64_t i_sec, uint64_t i_nsec)
{
// This code will handle times almost up to a year without overflowing a
// uint64. This should be more than sufficient for our purposes.
-
+
// Result = ((sec * 10^9 + nsec) * tb) / 10^9.
uint64_t result = ((i_sec * 1000000000ULL) + i_nsec);
result *= (iv_timebaseFreq / 1000000);
@@ -57,10 +58,13 @@ void TimeManager::_delayTask(task_t* t, uint64_t i_sec, uint64_t i_nsec)
{
_TimeManager_Delay_t* node = new _TimeManager_Delay_t();
- node->key = this->getCurrentTimeBase() +
+ node->key = this->getCurrentTimeBase() +
this->convertSecToTicks(i_sec, i_nsec);
node->task = t;
+ t->state = TASK_STATE_BLOCK_SLEEP;
+ t->state_info = (void*)node->key;
+
iv_taskList[getPIR()].insert(node);
}
diff --git a/src/lib/syscall_stub.S b/src/lib/syscall_stub.S
index aca533338..6c839346c 100644
--- a/src/lib/syscall_stub.S
+++ b/src/lib/syscall_stub.S
@@ -1,24 +1,24 @@
# IBM_PROLOG_BEGIN_TAG
# This is an automatically generated prolog.
-#
+#
# $Source: src/lib/syscall_stub.S $
-#
+#
# IBM CONFIDENTIAL
-#
+#
# COPYRIGHT International Business Machines Corp. 2010 - 2011
-#
+#
# p1
-#
+#
# Object Code Only (OCO) source materials
# Licensed Internal Code Source Materials
# IBM HostBoot Licensed Internal Code
-#
+#
# The source code for this program is not published or other-
# wise divested of its trade secrets, irrespective of what has
# been deposited with the U.S. Copyright Office.
-#
+#
# Origin: 30
-#
+#
# IBM_PROLOG_END
.section .text
@@ -41,3 +41,24 @@ _syscall6:
_syscall7:
sc
blr
+
+.global _syscall0_nr
+.global _syscall1_nr
+.global _syscall2_nr
+.global _syscall3_nr
+.global _syscall4_nr
+.global _syscall5_nr
+.global _syscall6_nr
+.global _syscall7_nr
+
+_syscall0_nr:
+_syscall1_nr:
+_syscall2_nr:
+_syscall3_nr:
+_syscall4_nr:
+_syscall5_nr:
+_syscall6_nr:
+_syscall7_nr:
+ sc
+1:
+ b 1b
diff --git a/src/lib/syscall_task.C b/src/lib/syscall_task.C
index 38b8adee4..a00b779f7 100644
--- a/src/lib/syscall_task.C
+++ b/src/lib/syscall_task.C
@@ -49,8 +49,12 @@ tid_t task_create(void(*fn)(void*), void* ptr)
void task_end()
{
- _syscall0(TASK_END); // no return.
- return;
+ _syscall1_nr(TASK_END, NULL); // no return.
+}
+
+void task_end2(void* retval)
+{
+ _syscall1_nr(TASK_END, retval); // no return.
}
tid_t task_gettid()
@@ -69,8 +73,8 @@ cpuid_t task_getcpuid()
tid_t task_exec(const char* file, void* ptr)
{
- // The VFS process is responsible for finding the module and creating the
- // new process. So, we send a message over to that process.
+ // The VFS process is responsible for finding the module and entry point
+ // address, so we send a message over to that process.
tid_t child = 0;
int rc = 0;
@@ -80,7 +84,6 @@ tid_t task_exec(const char* file, void* ptr)
msg_t* msg = msg_allocate();
msg->type = VFS_MSG_EXEC;
msg->data[0] = (uint64_t) file;
- msg->data[1] = (uint64_t) ptr;
if (vfsQ)
{
msg_sendrecv(vfsQ, msg);
@@ -93,8 +96,17 @@ tid_t task_exec(const char* file, void* ptr)
if (0 == rc)
{
- // Get child ID from message data 0.
- child = (tid_t) msg->data[0];
+ // Get fn ptr or error from message data 0.
+ int64_t value = *reinterpret_cast<int64_t*>(&msg->data[0]);
+ if (value < 0)
+ {
+ child = value;
+ }
+ else
+ {
+ child = task_create(reinterpret_cast<void(*)(void*)>(msg->data[0]),
+ ptr);
+ }
}
else
{
@@ -129,3 +141,30 @@ void task_affinity_migrate_to_master()
{
_syscall0(TASK_MIGRATE_TO_MASTER);
}
+
+void task_detach()
+{
+ // Get task structure.
+ register task_t* task;
+ asm volatile("mr %0, 13" : "=r"(task));
+
+ task->detached = true;
+ // This does not need any sync instruction because the setting is
+ // only used by the kernel and it requires a context-sync operation
+ // to enter kernel mode.
+}
+
+tid_t task_wait_tid(tid_t tid, int* status, void** retval)
+{
+ return static_cast<tid_t>(
+ reinterpret_cast<uint64_t>(
+ _syscall3(TASK_WAIT,(void*)tid,status,retval)));
+}
+
+tid_t task_wait(int* status, void** retval)
+{
+ return static_cast<tid_t>(
+ reinterpret_cast<uint64_t>(
+ _syscall3(TASK_WAIT,(void*)-1,status,retval)));
+}
+
diff --git a/src/sys/vfs/vfs_main.C b/src/sys/vfs/vfs_main.C
index 06a7f9d07..17de12295 100644
--- a/src/sys/vfs/vfs_main.C
+++ b/src/sys/vfs/vfs_main.C
@@ -39,16 +39,6 @@ const char* VFS_ROOT_MSG_VFS = "/msg/vfs";
void vfs_module_init();
-/**
- * Call the module 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.
- * @retval -ENOENT if i_module is NULL
- * @retval -ENOEXEC if there is no start()
- */
-tid_t vfs_exec(VfsSystemModule * i_module, void* i_param);
-
struct VfsPath
{
char key[64];
@@ -123,13 +113,14 @@ void vfs_main(void* i_barrier)
vfs_find_module(VFS_MODULES,
(const char *) msg->data[0]);
- tid_t child = vfs_exec(module,(void*) msg->data[1]);
+ void* fnptr = vfs_start_entrypoint(module);
// child == -ENOENT means module not found in base image
// so send a message to VFS_MSG queue to look in the
// extended image VFS_MSG queue will handle the
// msg_respond()
- if( child == (tid_t)-ENOENT ) // forward msg to usr vfs
+ if( fnptr == reinterpret_cast<void*>(-ENOENT) )
+ // forward msg to usr vfs
{
VfsEntry::key_type k;
strcpy(k.key, VFS_ROOT_MSG_VFS);
@@ -144,13 +135,13 @@ void vfs_main(void* i_barrier)
}
else // Cant find VFS_MSG queue - not started yet
{
- msg->data[0] = child;
+ msg->data[0] = (uint64_t) fnptr;
msg_respond(vfsMsgQ, msg);
}
}
else // send back child (or errno)
{
- msg->data[0] = child;
+ msg->data[0] = (uint64_t) fnptr;
msg_respond(vfsMsgQ, msg);
}
}
@@ -165,21 +156,22 @@ void vfs_main(void* i_barrier)
// ----------------------------------------------------------------------------
-tid_t vfs_exec(VfsSystemModule * i_module, void* i_param)
+void* vfs_start_entrypoint(VfsSystemModule * i_module)
{
- tid_t child = -ENOENT;
+ void* ptr = reinterpret_cast<void*>(-ENOENT);
if(i_module != NULL)
{
if (i_module->start == NULL)
{
- child = -ENOEXEC; // module has no start() routine
+ // module has no start() routine
+ ptr = reinterpret_cast<void*>(-ENOEXEC);
}
else
{
- child = task_create(i_module->start, i_param);
+ ptr = reinterpret_cast<void*>(i_module->start);
}
}
- return child;
+ return ptr;
}
diff --git a/src/usr/testcore/kernel/taskwaittest.H b/src/usr/testcore/kernel/taskwaittest.H
new file mode 100644
index 000000000..00ec0990f
--- /dev/null
+++ b/src/usr/testcore/kernel/taskwaittest.H
@@ -0,0 +1,177 @@
+// IBM_PROLOG_BEGIN_TAG
+// This is an automatically generated prolog.
+//
+// $Source: src/usr/testcore/kernel/tasktest.H $
+//
+// IBM CONFIDENTIAL
+//
+// COPYRIGHT International Business Machines Corp. 2011
+//
+// p1
+//
+// Object Code Only (OCO) source materials
+// Licensed Internal Code Source Materials
+// IBM HostBoot Licensed Internal Code
+//
+// The source code for this program is not published or other-
+// wise divested of its trade secrets, irrespective of what has
+// been deposited with the U.S. Copyright Office.
+//
+// Origin: 30
+//
+// IBM_PROLOG_END
+/** @file tasktest.H
+ * @brief Test cases for task interfaces.
+ */
+
+#include <cxxtest/TestSuite.H>
+#include <sys/task.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <kernel/console.H>
+#include <kernel/timemgr.H>
+
+#define NS_PER_SEC (1000000ull)
+#define TEN_CTX_SWITCHES ((NS_PER_SEC/TimeManager::TIMESLICE_PER_SEC)*10)
+
+class TaskWaitTest : public CxxTest::TestSuite
+{
+ public:
+
+ void testWaitAfterChildExit()
+ {
+ int status = 0x1234;
+ tid_t child = task_create(&WaitSomeTime, NULL);
+ WaitSomeLongerTime(NULL);
+
+ // Join with that child task to clean it up.
+ if (child != task_wait_tid(child, &status, NULL))
+ {
+ TS_FAIL("Failed to join with test-case child.");
+ }
+ if (status != TASK_STATUS_EXITED_CLEAN)
+ {
+ TS_FAIL("Task status is incorrect after wait.");
+ }
+ }
+
+ void testWaitBeforeChildExit()
+ {
+ tid_t child = task_create(&WaitSomeLongerTime, NULL);
+ WaitSomeTime(NULL);
+
+ // Join with that child task to clean it up.
+ if (child != task_wait_tid(child, NULL, NULL))
+ {
+ TS_FAIL("Failed to join with test-case child.");
+ }
+ }
+
+ void testWaitOnTwoChildren()
+ {
+ tid_t child[2] = { task_create(&WaitSomeTime, NULL),
+ task_create(&WaitSomeTime, NULL) };
+
+ tid_t completed = 0;
+
+ completed = task_wait(NULL, NULL);
+ if ((completed != child[0]) && (completed != child[1]))
+ {
+ TS_FAIL("Different child returned than one waited on.");
+ }
+
+ completed = task_wait(NULL, NULL);
+ if ((completed != child[0]) && (completed != child[1]))
+ {
+ TS_FAIL("Different child returned than one waited on.");
+ }
+ }
+
+ void testWaitOnGrandchild()
+ {
+ tid_t child = task_create(&TaskWithChild, NULL);
+ void* retval;
+
+ task_wait_tid(child, NULL, &retval);
+
+ tid_t grandchild = static_cast<tid_t>(
+ reinterpret_cast<uint64_t>(retval));
+
+ if (grandchild != task_wait_tid(grandchild, NULL, NULL))
+ {
+ TS_FAIL("Grand-child task was not joined with.");
+ }
+ }
+
+ void testWaitOnCrash()
+ {
+ int status = 1234;
+ tid_t child = task_create(&TaskThatCrashes, NULL);
+
+ task_wait_tid(child, &status, NULL);
+
+ if (status != TASK_STATUS_CRASHED)
+ {
+ TS_FAIL("Task wait status is incorrect.");
+ }
+ }
+
+ void testWaitDeadlock()
+ {
+ // Check for any thread when we don't have a child.
+ if (((tid_t)-EDEADLK) != task_wait(NULL, NULL))
+ {
+ TS_FAIL("Deadlock condition not detected.");
+ }
+
+ // Check for waiting on the wrong task.
+ tid_t child = task_create(&WaitSomeTime, NULL);
+ if (((tid_t)-EDEADLK) != task_wait_tid(0, NULL, NULL))
+ {
+ TS_FAIL("Deadlock condition not detected.");
+ }
+
+ // Join with that child task to clean it up.
+ if (child != task_wait_tid(child, NULL, NULL))
+ {
+ TS_FAIL("Failed to join with test-case child.");
+ }
+ }
+
+ void testWaitAddrFault()
+ {
+ if (((tid_t)-EFAULT) != task_wait((int*)4, NULL))
+ {
+ TS_FAIL("Bad address on status not caught.");
+ }
+
+ if (((tid_t)-EFAULT) != task_wait(NULL, (void**)4))
+ {
+ TS_FAIL("Bad address on ret-val not caught.");
+ }
+ }
+
+ static void WaitSomeTime(void* retval)
+ {
+ nanosleep(0,TEN_CTX_SWITCHES);
+ if (retval) task_end2(retval);
+ }
+
+ static void WaitSomeLongerTime(void* retval)
+ {
+ nanosleep(0, 2*TEN_CTX_SWITCHES);
+ if (retval) task_end2(retval);
+ }
+
+ static void TaskWithChild(void* unused)
+ {
+ tid_t child = task_create(&WaitSomeTime, NULL);
+ task_end2(reinterpret_cast<void*>(child));
+ }
+
+ static void TaskThatCrashes(void* unused)
+ {
+ printk("Test case: Expect to see uncaught exception!");
+ *(int64_t*)(0) = 0xDEADC0DE;
+ }
+};
diff --git a/src/usr/vfs/vfsrp.C b/src/usr/vfs/vfsrp.C
index 266cdecba..9c178e92a 100644
--- a/src/usr/vfs/vfsrp.C
+++ b/src/usr/vfs/vfsrp.C
@@ -314,8 +314,8 @@ void VfsRp::_load_unload(msg_t * i_msg)
{
// Module does not exist in extended image.
// If it exists then it is in the base image and it's already
- // initialized, however, we still should not be here, so put out a
- // trace msg;
+ // initialized, however, we still should not be here, so put out a
+ // trace msg;
// If it does not exist anywhere then also create an error log
TRACFCOMP(g_trac_vfs, ERR_MRK"load Module not found: %s",
(const char *) i_msg->data[0]);
@@ -362,11 +362,9 @@ void VfsRp::_exec(msg_t * i_msg)
VFS_EXTENDED_MODULE_TABLE_OFFSET),
(const char*) msg1->data[0]);
- tid_t child = vfs_exec(module, (void*) msg1->data[1]);
-
msg_q_t vfsRmsgQ = (msg_q_t) i_msg->data[1];
- msg1->data[0] = child;
+ msg1->data[0] = (uint64_t) vfs_start_entrypoint(module);
msg_respond(vfsRmsgQ,msg1);
msg_free(i_msg);
OpenPOWER on IntegriCloud