Generic semaphore implementation
Semaphores are no longer performance-critical, so a generic C implementation is better for maintainability, debuggability and extensibility. Thanks to Peter Zijlstra for fixing the lockdep warning. Thanks to Harvey Harrison for pointing out that the unlikely() was unnecessary. Signed-off-by: Matthew Wilcox <willy@linux.intel.com> Acked-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
committed by
Matthew Wilcox
parent
e48b3deee4
commit
64ac24e738
@@ -7,7 +7,7 @@ EXTRA_AFLAGS := $(KBUILD_CFLAGS)
|
||||
EXTRA_CFLAGS := -Werror -Wno-sign-compare
|
||||
|
||||
obj-y := entry.o traps.o process.o init_task.o osf_sys.o irq.o \
|
||||
irq_alpha.o signal.o setup.o ptrace.o time.o semaphore.o \
|
||||
irq_alpha.o signal.o setup.o ptrace.o time.o \
|
||||
alpha_ksyms.o systbls.o err_common.o io.o
|
||||
|
||||
obj-$(CONFIG_VGA_HOSE) += console.o
|
||||
|
||||
@@ -77,15 +77,6 @@ EXPORT_SYMBOL(__do_clear_user);
|
||||
EXPORT_SYMBOL(__strncpy_from_user);
|
||||
EXPORT_SYMBOL(__strnlen_user);
|
||||
|
||||
/* Semaphore helper functions. */
|
||||
EXPORT_SYMBOL(__down_failed);
|
||||
EXPORT_SYMBOL(__down_failed_interruptible);
|
||||
EXPORT_SYMBOL(__up_wakeup);
|
||||
EXPORT_SYMBOL(down);
|
||||
EXPORT_SYMBOL(down_interruptible);
|
||||
EXPORT_SYMBOL(down_trylock);
|
||||
EXPORT_SYMBOL(up);
|
||||
|
||||
/*
|
||||
* SMP-specific symbols.
|
||||
*/
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
Reference in New Issue
Block a user