/* * Copyright 2010 Stefan Lankes, Chair for Operating Systems, * RWTH Aachen University * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file is part of MetalSVM. */ #include #include #include #include #include #include #include #include #include #include #include #include #include DEFINE_PER_CORE(task_t*, current_task, NULL); static task_t task_table[MAX_TASKS] = {[0 ... MAX_TASKS-1] = {0, TASK_INVALID, ATOMIC_INIT(0), NULL, NULL}}; static spinlock_t table_lock = SPINLOCK_INIT; /* * helper function for the assembly code to determine the current task */ task_t* get_current_task(void) { return per_core(current_task); } int multitasking_init(void) { unsigned int i; for(i=0; imbox[i]) { mailbox_int32_post(per_core(current_task)->mbox[i], result); per_core(current_task)->mbox[i] = NULL; } } spinlock_unlock_irqsave(&table_lock); } static void NORETURN do_exit(int arg) { kprintf("Terminate task: %u, return value %d\n", per_core(current_task)->id, arg); wakeup_blocked_tasks(arg); if (atomic_int32_read(&per_core(current_task)->mem_usage)) kprintf("Memory leak! Task %d did not release %d bytes\n", per_core(current_task)->id, atomic_int32_read(&per_core(current_task)->mem_usage)); per_core(current_task)->status = TASK_FINISHED; reschedule(); kputs("Kernel panic: scheduler found no valid task\n"); while(1) { NOP8; } } void NORETURN leave_kernel_task(void) { int result; result = get_return_value(); do_exit(result); } void NORETURN sys_exit(int arg) { do_exit(arg); } void NORETURN abort(void) { do_exit(-1); } static int create_task(tid_t* id, entry_point_t ep, void* arg) { int ret = -ENOMEM; unsigned int i; if (BUILTIN_EXPECT(!ep, 0)) return -EINVAL; spinlock_lock_irqsave(&table_lock); for(i=0; itype == FS_FILE)) return -EINVAL; return create_task(id, user_entry, node); } int join_task(tid_t id, int* result) { int32_t tmp; mailbox_int32_t mbox; mailbox_int32_init(&mbox); spinlock_lock_irqsave(&table_lock); /* * idle tasks are not allowed to wait for another task * they should always run... */ if (BUILTIN_EXPECT(per_core(current_task)->status == TASK_IDLE, 0)) goto join_out; /* a task is not able to wait for itself */ if (BUILTIN_EXPECT(per_core(current_task)->id == id, 0)) goto join_out; /* invalid id */ if (BUILTIN_EXPECT(id >= MAX_TASKS, 0)) goto join_out; /* task already finished */ if (BUILTIN_EXPECT(task_table[id].status == TASK_INVALID, 0)) goto join_out; /* task already finished */ if (BUILTIN_EXPECT(task_table[id].status == TASK_FINISHED, 0)) goto join_out; task_table[id].mbox[per_core(current_task)->id] = &mbox; spinlock_unlock_irqsave(&table_lock); mailbox_int32_fetch(&mbox, &tmp); if (result) *result = tmp; mailbox_int32_destroy(&mbox); return 0; join_out: spinlock_unlock_irqsave(&table_lock); mailbox_int32_destroy(&mbox); return -EINVAL; } int wakeup_task(tid_t id) { int ret = -EINVAL; int need_lock = !spinlock_has_lock(&table_lock); /* avoid nested locking */ if (need_lock) spinlock_lock_irqsave(&table_lock); if (task_table[id].status != TASK_BLOCKED) { kprintf("Task %d is not blocked!\n", id); } else { task_table[id].status = TASK_READY; ret = 0; } if (need_lock) spinlock_unlock_irqsave(&table_lock); return ret; } int block_task(tid_t id) { int ret = -EINVAL; int need_lock = !spinlock_has_lock(&table_lock); /* avoid nested locking */ if (need_lock) spinlock_lock_irqsave(&table_lock); if ((task_table[id].status == TASK_RUNNING) || (task_table[id].status == TASK_READY)) { task_table[id].status = TASK_BLOCKED; ret = 0; } else kprintf("Unable to block task %d!\n", id); if (need_lock) spinlock_unlock_irqsave(&table_lock); return ret; } void scheduler(void) { unsigned int i; unsigned int new_id; spinlock_lock(&table_lock); /* signalize that this task could be reused */ if (per_core(current_task)->status == TASK_FINISHED) per_core(current_task)->status = TASK_INVALID; for(i=1, new_id=(per_core(current_task)->id + 1) % MAX_TASKS; istatus == TASK_RUNNING) per_core(current_task)->status = TASK_READY; task_table[new_id].status = TASK_RUNNING; per_core(current_task) = task_table+new_id; goto get_task_out; } } if ((per_core(current_task)->status == TASK_RUNNING) || (per_core(current_task)->status == TASK_IDLE)) goto get_task_out; /* * we switch to the idle task, if the current task terminates * and no other is ready */ for(i=0; i