/* litmus/sync.c - Support for synchronous and asynchronous task system releases. * * */ #include #include #include #include #include #include #include #include #include #include struct ts_release_wait { struct list_head list; struct completion completion; lt_t ts_release_time; }; #define DECLARE_TS_RELEASE_WAIT(symb) \ struct ts_release_wait symb = \ { \ LIST_HEAD_INIT(symb.list), \ COMPLETION_INITIALIZER_ONSTACK(symb.completion), \ 0 \ } static LIST_HEAD(task_release_list); static DEFINE_MUTEX(task_release_lock); static long do_wait_for_ts_release(struct timespec *wake) { DECLARE_TS_RELEASE_WAIT(wait); long ret = -ERESTARTSYS; if (mutex_lock_interruptible(&task_release_lock)) goto out; list_add(&wait.list, &task_release_list); mutex_unlock(&task_release_lock); /* We are enqueued, now we wait for someone to wake us up. */ ret = wait_for_completion_interruptible(&wait.completion); if (!ret) { if (is_realtime(current)) { lt_t phasedRelease = wait.ts_release_time + current->rt_param.task_params.phase; *wake = ns_to_timespec(phasedRelease); /* Completion succeeded, setup release. */ litmus->release_at(current, phasedRelease - current->rt_param.task_params.period); /* trigger advance to next job release at the programmed time */ ret = complete_job(); } else *wake = ns_to_timespec(wait.ts_release_time); } else { /* We were interrupted, must cleanup list. */ mutex_lock(&task_release_lock); if (!wait.completion.done) list_del(&wait.list); mutex_unlock(&task_release_lock); } out: return ret; } int count_tasks_waiting_for_release(void) { int task_count = 0; struct list_head *pos; mutex_lock(&task_release_lock); list_for_each(pos, &task_release_list) { task_count++; } mutex_unlock(&task_release_lock); return task_count; } static long do_release_ts(lt_t start) { long task_count = 0; struct list_head *pos, *safe; struct ts_release_wait *wait; if (mutex_lock_interruptible(&task_release_lock)) { task_count = -ERESTARTSYS; goto out; } TRACE("<<<<<< synchronous task system release >>>>>>\n"); sched_trace_sys_release(&start); task_count = 0; list_for_each_safe(pos, safe, &task_release_list) { wait = (struct ts_release_wait*) list_entry(pos, struct ts_release_wait, list); task_count++; /* RT tasks can be delayed. Non-RT tasks are released immediately. */ wait->ts_release_time = start; complete(&wait->completion); } /* clear stale list */ INIT_LIST_HEAD(&task_release_list); mutex_unlock(&task_release_lock); out: return task_count; } asmlinkage long sys_wait_for_ts_release(struct timespec __user *__wake) { struct timespec wake; long ret = -EPERM; ret = do_wait_for_ts_release(&wake); if (__wake && access_ok(VERIFY_WRITE, __wake, sizeof(struct timespec))) { __copy_to_user(__wake, &wake, sizeof(wake)); } return ret; } #define ONE_MS 1000000 asmlinkage long sys_release_ts(lt_t __user *__delay) { long ret = 0; lt_t delay = 0; lt_t start_time; /* FIXME: check capabilities... */ if (__delay) ret = copy_from_user(&delay, __delay, sizeof(delay)); if (ret == 0) { /* round up to next larger integral millisecond */ // start_time = ((litmus_clock() / ONE_MS) + 1) * ONE_MS; start_time = litmus_clock(); /* Note: Non-rt tasks that participate in a sync release cannot be delayed. They will be released immediately. */ ret = do_release_ts(start_time + delay); } return ret; }