diff options
Diffstat (limited to 'core/host/task.c')
-rw-r--r-- | core/host/task.c | 564 |
1 files changed, 0 insertions, 564 deletions
diff --git a/core/host/task.c b/core/host/task.c deleted file mode 100644 index be7ed3c579..0000000000 --- a/core/host/task.c +++ /dev/null @@ -1,564 +0,0 @@ -/* Copyright 2013 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* Task scheduling / events module for Chrome EC operating system */ - -#include <malloc.h> -#include <pthread.h> -#include <semaphore.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "atomic.h" -#include "common.h" -#include "console.h" -#include "host_task.h" -#include "task.h" -#include "task_id.h" -#include "test_util.h" -#include "timer.h" - -#define SIGNAL_INTERRUPT SIGUSR1 - -struct emu_task_t { - pthread_t thread; - pthread_cond_t resume; - uint32_t event; - timestamp_t wake_time; - uint8_t started; -}; - -struct task_args { - void (*routine)(void *); - void *d; -}; - -static struct emu_task_t tasks[TASK_ID_COUNT]; -static pthread_cond_t scheduler_cond; -static pthread_mutex_t run_lock; -static task_id_t running_task_id; -static int task_started; - -static sem_t interrupt_sem; -static pthread_mutex_t interrupt_lock; -static pthread_t interrupt_thread; -static int in_interrupt; -static int interrupt_disabled; -static void (*pending_isr)(void); -static int generator_sleeping; -static timestamp_t generator_sleep_deadline; -static int has_interrupt_generator = 1; - -/* thread local task id */ -static __thread task_id_t my_task_id = TASK_ID_INVALID; - -static void task_enable_all_tasks_callback(void); - -#define TASK(n, r, d, s) void r(void *); -CONFIG_TASK_LIST -CONFIG_TEST_TASK_LIST -CONFIG_CTS_TASK_LIST -#undef TASK - -/* usleep that uses OS functions, instead of emulated timer. */ -void _usleep(int usec) -{ - struct timespec req; - - req.tv_sec = usec / 1000000; - req.tv_nsec = (usec % 1000000) * 1000; - - nanosleep(&req, NULL); -} - -/* msleep that uses OS functions, instead of emulated timer. */ -void _msleep(int msec) -{ - _usleep(1000 * msec); -} - -/* Idle task */ -void __idle(void *d) -{ - while (1) - task_wait_event(-1); -} - -void _run_test(void *d) -{ - run_test(0, NULL); -} - -#define TASK(n, r, d, s) {r, d}, -const struct task_args task_info[TASK_ID_COUNT] = { - {__idle, NULL}, - CONFIG_TASK_LIST - CONFIG_TEST_TASK_LIST - CONFIG_CTS_TASK_LIST - {_run_test, NULL}, -}; -#undef TASK - -#define TASK(n, r, d, s) #n, -static const char * const task_names[] = { - "<< idle >>", - CONFIG_TASK_LIST - CONFIG_TEST_TASK_LIST - CONFIG_CTS_TASK_LIST - "<< test runner >>", -}; -#undef TASK - -void task_pre_init(void) -{ - /* Nothing */ -} - -int in_interrupt_context(void) -{ - return !!in_interrupt; -} - -test_mockable void interrupt_disable(void) -{ - pthread_mutex_lock(&interrupt_lock); - interrupt_disabled = 1; - pthread_mutex_unlock(&interrupt_lock); -} - -test_mockable void interrupt_enable(void) -{ - pthread_mutex_lock(&interrupt_lock); - interrupt_disabled = 0; - pthread_mutex_unlock(&interrupt_lock); -} - -inline int is_interrupt_enabled(void) -{ - return !interrupt_disabled; -} - -static void _task_execute_isr(int sig) -{ - in_interrupt = 1; - pending_isr(); - sem_post(&interrupt_sem); - in_interrupt = 0; -} - -void task_register_interrupt(void) -{ - sem_init(&interrupt_sem, 0, 0); - signal(SIGNAL_INTERRUPT, _task_execute_isr); -} - -void task_trigger_test_interrupt(void (*isr)(void)) -{ - pid_t main_pid; - pthread_mutex_lock(&interrupt_lock); - if (interrupt_disabled) { - pthread_mutex_unlock(&interrupt_lock); - return; - } - - /* Suspend current task and excute ISR */ - pending_isr = isr; - if (task_started) { - pthread_kill(tasks[running_task_id].thread, SIGNAL_INTERRUPT); - } else { - main_pid = getpid(); - kill(main_pid, SIGNAL_INTERRUPT); - } - - /* Wait for ISR to complete */ - sem_wait(&interrupt_sem); - while (in_interrupt) - _usleep(10); - pending_isr = NULL; - - pthread_mutex_unlock(&interrupt_lock); -} - -void interrupt_generator_udelay(unsigned us) -{ - generator_sleep_deadline.val = get_time().val + us; - generator_sleeping = 1; - while (get_time().val < generator_sleep_deadline.val) - ; - generator_sleeping = 0; -} - -const char *task_get_name(task_id_t tskid) -{ - return task_names[tskid]; -} - -pthread_t task_get_thread(task_id_t tskid) -{ - return tasks[tskid].thread; -} - -uint32_t task_set_event(task_id_t tskid, uint32_t event) -{ - atomic_or(&tasks[tskid].event, event); - return 0; -} - -uint32_t *task_get_event_bitmap(task_id_t tskid) -{ - return &tasks[tskid].event; -} - -uint32_t task_wait_event(int timeout_us) -{ - int tid = task_get_current(); - int ret; - pthread_mutex_lock(&interrupt_lock); - if (timeout_us > 0) - tasks[tid].wake_time.val = get_time().val + timeout_us; - - /* Transfer control to scheduler */ - pthread_cond_signal(&scheduler_cond); - pthread_cond_wait(&tasks[tid].resume, &run_lock); - - /* Resume */ - ret = atomic_clear(&tasks[tid].event); - pthread_mutex_unlock(&interrupt_lock); - return ret; -} - -uint32_t task_wait_event_mask(uint32_t event_mask, int timeout_us) -{ - uint64_t deadline = get_time().val + timeout_us; - uint32_t events = 0; - int time_remaining_us = timeout_us; - - /* Add the timer event to the mask so we can indicate a timeout */ - event_mask |= TASK_EVENT_TIMER; - - while (!(events & event_mask)) { - /* Collect events to re-post later */ - events |= task_wait_event(time_remaining_us); - - time_remaining_us = deadline - get_time().val; - if (timeout_us > 0 && time_remaining_us <= 0) { - /* Ensure we return a TIMER event if we timeout */ - events |= TASK_EVENT_TIMER; - break; - } - } - - /* Re-post any other events collected */ - if (events & ~event_mask) - atomic_or(&tasks[task_get_current()].event, - events & ~event_mask); - - return events & event_mask; -} - -void mutex_lock(struct mutex *mtx) -{ - int value = 0; - int id = 1 << task_get_current(); - - mtx->waiters |= id; - - do { - if (mtx->lock == 0) { - mtx->lock = 1; - value = 1; - } - - if (!value) - task_wait_event_mask(TASK_EVENT_MUTEX, 0); - } while (!value); - - mtx->waiters &= ~id; -} - -void mutex_unlock(struct mutex *mtx) -{ - int v; - mtx->lock = 0; - - for (v = 31; v >= 0; --v) - if ((1ul << v) & mtx->waiters) { - mtx->waiters &= ~(1ul << v); - task_set_event(v, TASK_EVENT_MUTEX); - break; - } -} - -task_id_t task_get_current(void) -{ - return my_task_id; -} - -task_id_t task_get_running(void) -{ - return running_task_id; -} - -void task_print_list(void) -{ - int i; - - ccputs("Name Events\n"); - - for (i = 0; i < TASK_ID_COUNT; i++) { - ccprintf("%4d %-16s %08x\n", i, task_names[i], tasks[i].event); - cflush(); - } -} - -int command_task_info(int argc, char **argv) -{ - task_print_list(); - - return EC_SUCCESS; -} -DECLARE_SAFE_CONSOLE_COMMAND(taskinfo, command_task_info, - NULL, - "Print task info"); - -static void _wait_for_task_started(int can_sleep) -{ - int i, ok; - - while (1) { - ok = 1; - for (i = 0; i < TASK_ID_COUNT - 1; ++i) { - if (!tasks[i].started) { - if (can_sleep) - msleep(10); - else - _msleep(10); - ok = 0; - break; - } - } - if (ok) - return; - } -} - -void wait_for_task_started(void) -{ - _wait_for_task_started(1); -} - -void wait_for_task_started_nosleep(void) -{ - _wait_for_task_started(0); -} - -static task_id_t task_get_next_wake(void) -{ - int i; - timestamp_t min_time; - int which_task = TASK_ID_INVALID; - - min_time.val = ~0ull; - - for (i = TASK_ID_COUNT - 1; i >= 0; --i) - if (min_time.val >= tasks[i].wake_time.val) { - min_time.val = tasks[i].wake_time.val; - which_task = i; - } - - return which_task; -} - -static int fast_forward(void) -{ - /* - * No task has event pending, and thus the next time we have an - * event to process must be either of: - * 1. Interrupt generator triggers an interrupt - * 2. The next wake alarm is reached - * So we should check whether an interrupt may happen, and fast - * forward to the nearest among: - * 1. When interrupt generator wakes up - * 2. When the next task wakes up - */ - int task_id = task_get_next_wake(); - - if (!has_interrupt_generator) { - if (task_id == TASK_ID_INVALID) { - return TASK_ID_IDLE; - } else { - force_time(tasks[task_id].wake_time); - return task_id; - } - } - - if (!generator_sleeping) - return TASK_ID_IDLE; - - if (task_id != TASK_ID_INVALID && - tasks[task_id].thread != (pthread_t)NULL && - tasks[task_id].wake_time.val < generator_sleep_deadline.val) { - force_time(tasks[task_id].wake_time); - return task_id; - } else { - force_time(generator_sleep_deadline); - return TASK_ID_IDLE; - } -} - -int task_start_called(void) -{ - return task_started; -} - -void task_scheduler(void) -{ - int i; - timestamp_t now; - - task_started = 1; - - while (1) { - now = get_time(); - i = TASK_ID_COUNT - 1; - while (i >= 0) { - /* - * Only tasks with spawned threads are valid to be - * resumed. - */ - if (tasks[i].thread) { - if (tasks[i].event || - now.val >= tasks[i].wake_time.val) - break; - } - --i; - } - if (i < 0) - i = fast_forward(); - - now = get_time(); - if (now.val >= tasks[i].wake_time.val) - tasks[i].event |= TASK_EVENT_TIMER; - tasks[i].wake_time.val = ~0ull; - running_task_id = i; - tasks[i].started = 1; - pthread_cond_signal(&tasks[i].resume); - pthread_cond_wait(&scheduler_cond, &run_lock); - } -} - -void *_task_start_impl(void *a) -{ - long tid = (long)a; - const struct task_args *arg = task_info + tid; - my_task_id = tid; - pthread_mutex_lock(&run_lock); - - /* Wait for scheduler */ - task_wait_event(1); - tasks[tid].event = 0; - - /* Start the task routine */ - (arg->routine)(arg->d); - - /* Catch exited routine */ - while (1) - task_wait_event(-1); -} - -test_mockable void interrupt_generator(void) -{ - has_interrupt_generator = 0; -} - -void *_task_int_generator_start(void *d) -{ - my_task_id = TASK_ID_INT_GEN; - interrupt_generator(); - return NULL; -} - -int task_start(void) -{ - int i = TASK_ID_HOOKS; - - pthread_mutex_init(&run_lock, NULL); - pthread_mutex_init(&interrupt_lock, NULL); - pthread_cond_init(&scheduler_cond, NULL); - - pthread_mutex_lock(&run_lock); - - /* - * Initialize the hooks task first. After its init, it will callback to - * enable the remaining tasks. - */ - tasks[i].event = TASK_EVENT_WAKE; - tasks[i].wake_time.val = ~0ull; - tasks[i].started = 0; - pthread_cond_init(&tasks[i].resume, NULL); - pthread_create(&tasks[i].thread, NULL, _task_start_impl, - (void *)(uintptr_t)i); - pthread_cond_wait(&scheduler_cond, &run_lock); - /* - * Interrupt lock is grabbed by the task which just started. - * Let's unlock it so the next task can be started. - */ - pthread_mutex_unlock(&interrupt_lock); - - /* - * The hooks task is waiting in task_wait_event(). Lock interrupt_lock - * here so the first task chosen sees it locked. - */ - pthread_mutex_lock(&interrupt_lock); - - pthread_create(&interrupt_thread, NULL, - _task_int_generator_start, NULL); - - /* - * Tell the hooks task to continue so that it can call back to enable - * the other tasks. - */ - pthread_cond_signal(&tasks[i].resume); - pthread_cond_wait(&scheduler_cond, &run_lock); - task_enable_all_tasks_callback(); - - task_scheduler(); - - return 0; -} - -static void task_enable_all_tasks_callback(void) -{ - int i; - - /* Initialize the remaning tasks. */ - for (i = 0; i < TASK_ID_COUNT; ++i) { - if (tasks[i].thread != (pthread_t)NULL) - continue; - - tasks[i].event = TASK_EVENT_WAKE; - tasks[i].wake_time.val = ~0ull; - tasks[i].started = 0; - pthread_cond_init(&tasks[i].resume, NULL); - pthread_create(&tasks[i].thread, NULL, _task_start_impl, - (void *)(uintptr_t)i); - /* - * Interrupt lock is grabbed by the task which just started. - * Let's unlock it so the next task can be started. - */ - pthread_mutex_unlock(&interrupt_lock); - pthread_cond_wait(&scheduler_cond, &run_lock); - } - -} - -void task_enable_all_tasks(void) -{ - /* Signal to the scheduler to enable the remaining tasks. */ - pthread_cond_signal(&scheduler_cond); -} |