diff options
| author | Patrick Williams <iawillia@us.ibm.com> | 2011-10-03 16:12:51 -0500 |
|---|---|---|
| committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2011-10-24 13:33:20 -0500 |
| commit | 4962a22309cd7e3586aa57817689b18d67ca71c7 (patch) | |
| tree | 2bfd610d6ed048f7d4a35717211eca06b15d1f69 /src/kernel/taskmgr.C | |
| parent | 21185b30cd99a00f01e15edba28402cdc00de1d1 (diff) | |
| download | talos-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.C | 216 |
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; +} |

