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 | |
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')
-rw-r--r-- | src/kernel/basesegment.C | 11 | ||||
-rw-r--r-- | src/kernel/cpumgr.C | 2 | ||||
-rw-r--r-- | src/kernel/exception.C | 15 | ||||
-rw-r--r-- | src/kernel/futexmgr.C | 4 | ||||
-rw-r--r-- | src/kernel/kernel.C | 16 | ||||
-rw-r--r-- | src/kernel/msghandler.C | 9 | ||||
-rw-r--r-- | src/kernel/scheduler.C | 4 | ||||
-rw-r--r-- | src/kernel/syscall.C | 74 | ||||
-rw-r--r-- | src/kernel/taskmgr.C | 216 | ||||
-rw-r--r-- | src/kernel/timemgr.C | 8 |
10 files changed, 317 insertions, 42 deletions
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); } |