summaryrefslogtreecommitdiffstats
path: root/src/kernel/taskmgr.C
diff options
context:
space:
mode:
authorPatrick Williams <iawillia@us.ibm.com>2011-10-03 16:12:51 -0500
committerA. Patrick Williams III <iawillia@us.ibm.com>2011-10-24 13:33:20 -0500
commit4962a22309cd7e3586aa57817689b18d67ca71c7 (patch)
tree2bfd610d6ed048f7d4a35717211eca06b15d1f69 /src/kernel/taskmgr.C
parent21185b30cd99a00f01e15edba28402cdc00de1d1 (diff)
downloadtalos-hostboot-4962a22309cd7e3586aa57817689b18d67ca71c7.tar.gz
talos-hostboot-4962a22309cd7e3586aa57817689b18d67ca71c7.zip
Support task_wait / task_wait_tid syscalls:
- Add task_end2 syscall to allow pthread_exit-like retval. - Add/maintain task states. - Create task parent/child tracking tree. - Add task_detach function. - Implement wait syscalls. Make task_exec caller the parent of spawned task: Previously the task_exec call caused a message to the VFS task, which called task_create and returned the tid in response to the message. This causes the parent of the spawned task to appear to be the VFS task. Modify task_exec / VFS handling to instead return the entry point address on the message and have task_exec call task_create directly itself. Change-Id: I6b6796f45875de37b1ab01e7596639b073820b95 Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/443 Tested-by: Jenkins Server Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com> Reviewed-by: Andrew J. Geissler <andrewg@us.ibm.com>
Diffstat (limited to 'src/kernel/taskmgr.C')
-rw-r--r--src/kernel/taskmgr.C216
1 files changed, 216 insertions, 0 deletions
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;
+}
OpenPOWER on IntegriCloud