/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1996 Linus Torvalds * Copyright (C) 1998, 99, 2000, 01 Ralf Baechle * Copyright (C) 1999, 2000, 01 Silicon Graphics, Inc. * Copyright (C) 2000, 01 MIPS Technologies, Inc. */ #ifndef _ASM_SEMAPHORE_H #define _ASM_SEMAPHORE_H #include #include #include #include #include #include struct semaphore { #ifdef __MIPSEB__ atomic_t count; atomic_t waking; #else atomic_t waking; atomic_t count; #endif wait_queue_head_t wait; #if WAITQUEUE_DEBUG long __magic; #endif } __attribute__((aligned(8))); #if WAITQUEUE_DEBUG # define __SEM_DEBUG_INIT(name) , .__magic = (long)&(name).__magic #else # define __SEM_DEBUG_INIT(name) #endif #define __SEMAPHORE_INITIALIZER(name,_count) { \ .count = ATOMIC_INIT(_count), \ .waking = ATOMIC_INIT(0), \ .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ __SEM_DEBUG_INIT(name) \ } #define __MUTEX_INITIALIZER(name) __SEMAPHORE_INITIALIZER(name, 1) #define __DECLARE_SEMAPHORE_GENERIC(name,count) \ struct semaphore name = __SEMAPHORE_INITIALIZER(name, count) #define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name, 1) #define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0) static inline void sema_init (struct semaphore *sem, int val) { atomic_set(&sem->count, val); atomic_set(&sem->waking, 0); init_waitqueue_head(&sem->wait); #if WAITQUEUE_DEBUG sem->__magic = (long)&sem->__magic; #endif } static inline void init_MUTEX (struct semaphore *sem) { sema_init(sem, 1); } static inline void init_MUTEX_LOCKED (struct semaphore *sem) { sema_init(sem, 0); } #ifndef CONFIG_CPU_HAS_LLDSCD /* * On machines without lld/scd we need a spinlock to make the manipulation of * sem->count and sem->waking atomic. */ extern spinlock_t semaphore_lock; #endif extern void __down_failed(struct semaphore * sem); extern int __down_failed_interruptible(struct semaphore * sem); extern void __up_wakeup(struct semaphore * sem); static inline void down(struct semaphore * sem) { int count; #if WAITQUEUE_DEBUG CHECK_MAGIC(sem->__magic); #endif might_sleep(); count = atomic_dec_return(&sem->count); if (unlikely(count < 0)) __down_failed(sem); } /* * Interruptible try to acquire a semaphore. If we obtained * it, return zero. If we were interrupted, returns -EINTR */ static inline int down_interruptible(struct semaphore * sem) { int count; #if WAITQUEUE_DEBUG CHECK_MAGIC(sem->__magic); #endif might_sleep(); count = atomic_dec_return(&sem->count); if (unlikely(count < 0)) return __down_failed_interruptible(sem); return 0; } #ifdef CONFIG_CPU_HAS_LLDSCD /* * down_trylock returns 0 on success, 1 if we failed to get the lock. * * We must manipulate count and waking simultaneously and atomically. * Here, we do this by using lld/scd on the pair of 32-bit words. * * Pseudocode: * * Decrement(sem->count) * If(sem->count >=0) { * Return(SUCCESS) // resource is free * } else { * If(sem->waking <= 0) { // if no wakeup pending * Increment(sem->count) // undo decrement * Return(FAILURE) * } else { * Decrement(sem->waking) // otherwise "steal" wakeup * Return(SUCCESS) * } * } */ static inline int down_trylock(struct semaphore * sem) { long ret, tmp, tmp2, sub; #if WAITQUEUE_DEBUG CHECK_MAGIC(sem->__magic); #endif __asm__ __volatile__( " .set mips3 # down_trylock \n" "0: lld %1, %4 \n" " dli %3, 0x0000000100000000 # count -= 1 \n" " dsubu %1, %3 \n" " li %0, 0 # ret = 0 \n" " bgez %1, 2f # if count >= 0 \n" " sll %2, %1, 0 # extract waking \n" " blez %2, 1f # if waking < 0 -> 1f \n" " daddiu %1, %1, -1 # waking -= 1 \n" " b 2f \n" "1: daddu %1, %1, %3 # count += 1 \n" " li %0, 1 # ret = 1 \n" "2: scd %1, %4 \n" " beqz %1, 0b \n" " sync \n" " .set mips0 \n" : "=&r"(ret), "=&r"(tmp), "=&r"(tmp2), "=&r"(sub) : "m"(*sem) : "memory"); return ret; } /* * Note! This is subtle. We jump to wake people up only if * the semaphore was negative (== somebody was waiting on it). */ static inline void up(struct semaphore * sem) { unsigned long tmp, tmp2; int count; #if WAITQUEUE_DEBUG CHECK_MAGIC(sem->__magic); #endif /* * We must manipulate count and waking simultaneously and atomically. * Otherwise we have races between up and __down_failed_interruptible * waking up on a signal. */ __asm__ __volatile__( " .set mips3 \n" " sync # up \n" "1: lld %1, %3 \n" " dsra32 %0, %1, 0 # extract count to %0 \n" " daddiu %0, 1 # count += 1 \n" " slti %2, %0, 1 # %3 = (%0 <= 0) \n" " daddu %1, %2 # waking += %3 \n" " dsll32 %1, %1, 0 # zero-extend %1 \n" " dsrl32 %1, %1, 0 \n" " dsll32 %2, %0, 0 # Reassemble union \n" " or %1, %2 # from count and waking \n" " scd %1, %3 \n" " beqz %1, 1b \n" " .set mips0 \n" : "=&r"(count), "=&r"(tmp), "=&r"(tmp2), "+m"(*sem) : : "memory"); if (unlikely(count <= 0)) __up_wakeup(sem); } #else /* * Non-blockingly attempt to down() a semaphore. * Returns zero if we acquired it */ static inline int down_trylock(struct semaphore * sem) { unsigned long flags; int count, waking; int ret = 0; #if WAITQUEUE_DEBUG CHECK_MAGIC(sem->__magic); #endif spin_lock_irqsave(&semaphore_lock, flags); count = atomic_read(&sem->count) - 1; atomic_set(&sem->count, count); if (unlikely(count < 0)) { waking = atomic_read(&sem->waking); if (waking <= 0) { atomic_set(&sem->count, count + 1); ret = 1; } else { atomic_set(&sem->waking, waking - 1); ret = 0; } } spin_unlock_irqrestore(&semaphore_lock, flags); return ret; } /* * Note! This is subtle. We jump to wake people up only if * the semaphore was negative (== somebody was waiting on it). */ static inline void up(struct semaphore * sem) { unsigned long flags; int count, waking; #if WAITQUEUE_DEBUG CHECK_MAGIC(sem->__magic); #endif /* * We must manipulate count and waking simultaneously and atomically. * Otherwise we have races between up and __down_failed_interruptible * waking up on a signal. */ spin_lock_irqsave(&semaphore_lock, flags); count = atomic_read(&sem->count) + 1; waking = atomic_read(&sem->waking); if (count <= 0) waking++; atomic_set(&sem->count, count); atomic_set(&sem->waking, waking); spin_unlock_irqrestore(&semaphore_lock, flags); if (unlikely(count <= 0)) __up_wakeup(sem); } #endif /* CONFIG_CPU_HAS_LLDSCD */ #endif /* _ASM_SEMAPHORE_H */