/* * FMLP implementation. * Much of the code here is borrowed from include/asm-i386/semaphore.h */ #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_FMLP static void* create_fmlp_semaphore(void) { struct pi_semaphore* sem; int i; sem = kmalloc(sizeof(*sem), GFP_KERNEL); if (!sem) return NULL; atomic_set(&sem->count, 1); sem->sleepers = 0; init_waitqueue_head(&sem->wait); sem->hp.task = NULL; sem->holder = NULL; for (i = 0; i < NR_CPUS; i++) sem->hp.cpu_task[i] = NULL; return sem; } static int open_fmlp_semaphore(struct od_table_entry* entry, void* __user arg) { if (!fmlp_active()) return -EBUSY; return 0; } static void destroy_fmlp_semaphore(void* sem) { /* XXX assert invariants */ kfree(sem); } struct fdso_ops fmlp_sem_ops = { .create = create_fmlp_semaphore, .open = open_fmlp_semaphore, .destroy = destroy_fmlp_semaphore }; struct wq_pair { struct task_struct* tsk; struct pi_semaphore* sem; }; static int rt_pi_wake_up(wait_queue_t *wait, unsigned mode, int sync, void *key) { struct wq_pair* wqp = (struct wq_pair*) wait->private; set_rt_flags(wqp->tsk, RT_F_EXIT_SEM); litmus->inherit_priority(wqp->sem, wqp->tsk); TRACE_TASK(wqp->tsk, "woken up by rt_pi_wake_up() (RT_F_SEM_EXIT, PI)\n"); /* point to task for default_wake_function() */ wait->private = wqp->tsk; default_wake_function(wait, mode, sync, key); /* Always return true since we know that if we encountered a task * that was already running the wake_up raced with the schedule in * rt_pi_down(). In that case the task in rt_pi_down() will be scheduled * immediately and own the lock. We must not wake up another task in * any case. */ return 1; } /* caller is responsible for locking */ int edf_set_hp_task(struct pi_semaphore *sem) { struct list_head *tmp, *next; struct task_struct *queued; int ret = 0; sem->hp.task = NULL; list_for_each_safe(tmp, next, &sem->wait.task_list) { queued = ((struct wq_pair*) list_entry(tmp, wait_queue_t, task_list)->private)->tsk; /* Compare task prios, find high prio task. */ if (edf_higher_prio(queued, sem->hp.task)) { sem->hp.task = queued; ret = 1; } } return ret; } /* caller is responsible for locking */ int edf_set_hp_cpu_task(struct pi_semaphore *sem, int cpu) { struct list_head *tmp, *next; struct task_struct *queued; int ret = 0; sem->hp.cpu_task[cpu] = NULL; list_for_each_safe(tmp, next, &sem->wait.task_list) { queued = ((struct wq_pair*) list_entry(tmp, wait_queue_t, task_list)->private)->tsk; /* Compare task prios, find high prio task. */ if (get_partition(queued) == cpu && edf_higher_prio(queued, sem->hp.cpu_task[cpu])) { sem->hp.cpu_task[cpu] = queued; ret = 1; } } return ret; } static int do_fmlp_down(struct pi_semaphore* sem) { unsigned long flags; struct task_struct *tsk = current; struct wq_pair pair; int suspended = 1; wait_queue_t wait = { .private = &pair, .func = rt_pi_wake_up, .task_list = {NULL, NULL} }; pair.tsk = tsk; pair.sem = sem; spin_lock_irqsave(&sem->wait.lock, flags); if (atomic_dec_return(&sem->count) < 0 || waitqueue_active(&sem->wait)) { /* we need to suspend */ tsk->state = TASK_UNINTERRUPTIBLE; add_wait_queue_exclusive_locked(&sem->wait, &wait); TRACE_CUR("suspends on PI lock %p\n", sem); litmus->pi_block(sem, tsk); /* release lock before sleeping */ spin_unlock_irqrestore(&sem->wait.lock, flags); TS_PI_DOWN_END; preempt_enable_no_resched(); /* we depend on the FIFO order * Thus, we don't need to recheck when we wake up, we * are guaranteed to have the lock since there is only one * wake up per release */ schedule(); TRACE_CUR("woke up, now owns PI lock %p\n", sem); /* try_to_wake_up() set our state to TASK_RUNNING, * all we need to do is to remove our wait queue entry */ remove_wait_queue(&sem->wait, &wait); } else { /* no priority inheritance necessary, since there are no queued * tasks. */ suspended = 0; TRACE_CUR("acquired PI lock %p, no contention\n", sem); sem->holder = tsk; /* don't know if we're global or partitioned. */ sem->hp.task = tsk; sem->hp.cpu_task[get_partition(tsk)] = tsk; litmus->inherit_priority(sem, tsk); spin_unlock_irqrestore(&sem->wait.lock, flags); } return suspended; } static void do_fmlp_up(struct pi_semaphore* sem) { unsigned long flags; spin_lock_irqsave(&sem->wait.lock, flags); TRACE_CUR("releases PI lock %p\n", sem); litmus->return_priority(sem); sem->holder = NULL; if (atomic_inc_return(&sem->count) < 1) /* there is a task queued */ wake_up_locked(&sem->wait); spin_unlock_irqrestore(&sem->wait.lock, flags); } asmlinkage long sys_fmlp_down(int sem_od) { long ret = 0; struct pi_semaphore * sem; int suspended = 0; preempt_disable(); TS_PI_DOWN_START; sem = lookup_fmlp_sem(sem_od); if (sem) suspended = do_fmlp_down(sem); else ret = -EINVAL; if (!suspended) { TS_PI_DOWN_END; preempt_enable(); } return ret; } asmlinkage long sys_fmlp_up(int sem_od) { long ret = 0; struct pi_semaphore * sem; preempt_disable(); TS_PI_UP_START; sem = lookup_fmlp_sem(sem_od); if (sem) do_fmlp_up(sem); else ret = -EINVAL; TS_PI_UP_END; preempt_enable(); return ret; } #else struct fdso_ops fmlp_sem_ops = {}; asmlinkage long sys_fmlp_down(int sem_od) { return -ENOSYS; } asmlinkage long sys_fmlp_up(int sem_od) { return -ENOSYS; } #endif