| /* | |
| * Alpha semaphore implementation. | |
| * | |
| * (C) Copyright 1996 Linus Torvalds | |
| * (C) Copyright 1999, 2000 Richard Henderson | |
| */ | |
| #include <linux/errno.h> | |
| #include <linux/sched.h> | |
| #include <linux/init.h> | |
| /* | |
| * This is basically the PPC semaphore scheme ported to use | |
| * the Alpha ll/sc sequences, so see the PPC code for | |
| * credits. | |
| */ | |
| /* | |
| * Atomically update sem->count. | |
| * This does the equivalent of the following: | |
| * | |
| * old_count = sem->count; | |
| * tmp = MAX(old_count, 0) + incr; | |
| * sem->count = tmp; | |
| * return old_count; | |
| */ | |
| static inline int __sem_update_count(struct semaphore *sem, int incr) | |
| { | |
| long old_count, tmp = 0; | |
| __asm__ __volatile__( | |
| "1: ldl_l %0,%2\n" | |
| " cmovgt %0,%0,%1\n" | |
| " addl %1,%3,%1\n" | |
| " stl_c %1,%2\n" | |
| " beq %1,2f\n" | |
| " mb\n" | |
| ".subsection 2\n" | |
| "2: br 1b\n" | |
| ".previous" | |
| : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) | |
| : "Ir" (incr), "1" (tmp), "m" (sem->count)); | |
| return old_count; | |
| } | |
| /* | |
| * Perform the "down" function. Return zero for semaphore acquired, | |
| * return negative for signalled out of the function. | |
| * | |
| * If called from down, the return is ignored and the wait loop is | |
| * not interruptible. This means that a task waiting on a semaphore | |
| * using "down()" cannot be killed until someone does an "up()" on | |
| * the semaphore. | |
| * | |
| * If called from down_interruptible, the return value gets checked | |
| * upon return. If the return value is negative then the task continues | |
| * with the negative value in the return register (it can be tested by | |
| * the caller). | |
| * | |
| * Either form may be used in conjunction with "up()". | |
| */ | |
| void __sched | |
| __down_failed(struct semaphore *sem) | |
| { | |
| struct task_struct *tsk = current; | |
| DECLARE_WAITQUEUE(wait, tsk); | |
| #ifdef CONFIG_DEBUG_SEMAPHORE | |
| printk("%s(%d): down failed(%p)\n", | |
| tsk->comm, task_pid_nr(tsk), sem); | |
| #endif | |
| tsk->state = TASK_UNINTERRUPTIBLE; | |
| wmb(); | |
| add_wait_queue_exclusive(&sem->wait, &wait); | |
| /* | |
| * Try to get the semaphore. If the count is > 0, then we've | |
| * got the semaphore; we decrement count and exit the loop. | |
| * If the count is 0 or negative, we set it to -1, indicating | |
| * that we are asleep, and then sleep. | |
| */ | |
| while (__sem_update_count(sem, -1) <= 0) { | |
| schedule(); | |
| set_task_state(tsk, TASK_UNINTERRUPTIBLE); | |
| } | |
| remove_wait_queue(&sem->wait, &wait); | |
| tsk->state = TASK_RUNNING; | |
| /* | |
| * If there are any more sleepers, wake one of them up so | |
| * that it can either get the semaphore, or set count to -1 | |
| * indicating that there are still processes sleeping. | |
| */ | |
| wake_up(&sem->wait); | |
| #ifdef CONFIG_DEBUG_SEMAPHORE | |
| printk("%s(%d): down acquired(%p)\n", | |
| tsk->comm, task_pid_nr(tsk), sem); | |
| #endif | |
| } | |
| int __sched | |
| __down_failed_interruptible(struct semaphore *sem) | |
| { | |
| struct task_struct *tsk = current; | |
| DECLARE_WAITQUEUE(wait, tsk); | |
| long ret = 0; | |
| #ifdef CONFIG_DEBUG_SEMAPHORE | |
| printk("%s(%d): down failed(%p)\n", | |
| tsk->comm, task_pid_nr(tsk), sem); | |
| #endif | |
| tsk->state = TASK_INTERRUPTIBLE; | |
| wmb(); | |
| add_wait_queue_exclusive(&sem->wait, &wait); | |
| while (__sem_update_count(sem, -1) <= 0) { | |
| if (signal_pending(current)) { | |
| /* | |
| * A signal is pending - give up trying. | |
| * Set sem->count to 0 if it is negative, | |
| * since we are no longer sleeping. | |
| */ | |
| __sem_update_count(sem, 0); | |
| ret = -EINTR; | |
| break; | |
| } | |
| schedule(); | |
| set_task_state(tsk, TASK_INTERRUPTIBLE); | |
| } | |
| remove_wait_queue(&sem->wait, &wait); | |
| tsk->state = TASK_RUNNING; | |
| wake_up(&sem->wait); | |
| #ifdef CONFIG_DEBUG_SEMAPHORE | |
| printk("%s(%d): down %s(%p)\n", | |
| current->comm, task_pid_nr(current), | |
| (ret < 0 ? "interrupted" : "acquired"), sem); | |
| #endif | |
| return ret; | |
| } | |
| void | |
| __up_wakeup(struct semaphore *sem) | |
| { | |
| /* | |
| * Note that we incremented count in up() before we came here, | |
| * but that was ineffective since the result was <= 0, and | |
| * any negative value of count is equivalent to 0. | |
| * This ends up setting count to 1, unless count is now > 0 | |
| * (i.e. because some other cpu has called up() in the meantime), | |
| * in which case we just increment count. | |
| */ | |
| __sem_update_count(sem, 1); | |
| wake_up(&sem->wait); | |
| } | |
| void __sched | |
| down(struct semaphore *sem) | |
| { | |
| #ifdef WAITQUEUE_DEBUG | |
| CHECK_MAGIC(sem->__magic); | |
| #endif | |
| #ifdef CONFIG_DEBUG_SEMAPHORE | |
| printk("%s(%d): down(%p) <count=%d> from %p\n", | |
| current->comm, task_pid_nr(current), sem, | |
| atomic_read(&sem->count), __builtin_return_address(0)); | |
| #endif | |
| __down(sem); | |
| } | |
| int __sched | |
| down_interruptible(struct semaphore *sem) | |
| { | |
| #ifdef WAITQUEUE_DEBUG | |
| CHECK_MAGIC(sem->__magic); | |
| #endif | |
| #ifdef CONFIG_DEBUG_SEMAPHORE | |
| printk("%s(%d): down(%p) <count=%d> from %p\n", | |
| current->comm, task_pid_nr(current), sem, | |
| atomic_read(&sem->count), __builtin_return_address(0)); | |
| #endif | |
| return __down_interruptible(sem); | |
| } | |
| int | |
| down_trylock(struct semaphore *sem) | |
| { | |
| int ret; | |
| #ifdef WAITQUEUE_DEBUG | |
| CHECK_MAGIC(sem->__magic); | |
| #endif | |
| ret = __down_trylock(sem); | |
| #ifdef CONFIG_DEBUG_SEMAPHORE | |
| printk("%s(%d): down_trylock %s from %p\n", | |
| current->comm, task_pid_nr(current), | |
| ret ? "failed" : "acquired", | |
| __builtin_return_address(0)); | |
| #endif | |
| return ret; | |
| } | |
| void | |
| up(struct semaphore *sem) | |
| { | |
| #ifdef WAITQUEUE_DEBUG | |
| CHECK_MAGIC(sem->__magic); | |
| #endif | |
| #ifdef CONFIG_DEBUG_SEMAPHORE | |
| printk("%s(%d): up(%p) <count=%d> from %p\n", | |
| current->comm, task_pid_nr(current), sem, | |
| atomic_read(&sem->count), __builtin_return_address(0)); | |
| #endif | |
| __up(sem); | |
| } |