1/* 2 * SMP- and interrupt-safe semaphores.. 3 * 4 * This file is subject to the terms and conditions of the GNU General Public 5 * License. See the file "COPYING" in the main directory of this archive 6 * for more details. 7 * 8 * (C) Copyright 1996 Linus Torvalds 9 * (C) Copyright 1998, 99, 2000, 01 Ralf Baechle 10 * (C) Copyright 1999, 2000 Silicon Graphics, Inc. 11 * Copyright (C) 2000, 01 MIPS Technologies, Inc. All rights reserved. 12 */ 13#ifndef _ASM_SEMAPHORE_H 14#define _ASM_SEMAPHORE_H 15 16#include <asm/system.h> 17#include <asm/atomic.h> 18#include <linux/spinlock.h> 19#include <linux/wait.h> 20#include <linux/config.h> 21#include <linux/rwsem.h> 22 23struct semaphore { 24#ifdef __MIPSEB__ 25 atomic_t count; 26 atomic_t waking; 27#else 28 atomic_t waking; 29 atomic_t count; 30#endif 31 wait_queue_head_t wait; 32#if WAITQUEUE_DEBUG 33 long __magic; 34#endif 35} __attribute__((aligned(8))); 36 37#if WAITQUEUE_DEBUG 38# define __SEM_DEBUG_INIT(name) \ 39 , (long)&(name).__magic 40#else 41# define __SEM_DEBUG_INIT(name) 42#endif 43 44#ifdef __MIPSEB__ 45#define __SEMAPHORE_INITIALIZER(name,count) \ 46{ ATOMIC_INIT(count), ATOMIC_INIT(0), __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ 47 __SEM_DEBUG_INIT(name) } 48#else 49#define __SEMAPHORE_INITIALIZER(name,count) \ 50{ ATOMIC_INIT(0), ATOMIC_INIT(count), __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ 51 __SEM_DEBUG_INIT(name) } 52#endif 53 54#define __MUTEX_INITIALIZER(name) \ 55 __SEMAPHORE_INITIALIZER(name,1) 56 57#define __DECLARE_SEMAPHORE_GENERIC(name,count) \ 58 struct semaphore name = __SEMAPHORE_INITIALIZER(name,count) 59 60#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1) 61#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0) 62 63static inline void sema_init (struct semaphore *sem, int val) 64{ 65 atomic_set(&sem->count, val); 66 atomic_set(&sem->waking, 0); 67 init_waitqueue_head(&sem->wait); 68#if WAITQUEUE_DEBUG 69 sem->__magic = (long)&sem->__magic; 70#endif 71} 72 73static inline void init_MUTEX (struct semaphore *sem) 74{ 75 sema_init(sem, 1); 76} 77 78static inline void init_MUTEX_LOCKED (struct semaphore *sem) 79{ 80 sema_init(sem, 0); 81} 82 83asmlinkage void __down(struct semaphore * sem); 84asmlinkage int __down_interruptible(struct semaphore * sem); 85asmlinkage int __down_trylock(struct semaphore * sem); 86asmlinkage void __up(struct semaphore * sem); 87 88static inline void down(struct semaphore * sem) 89{ 90#if WAITQUEUE_DEBUG 91 CHECK_MAGIC(sem->__magic); 92#endif 93 if (atomic_dec_return(&sem->count) < 0) 94 __down(sem); 95} 96 97/* 98 * Interruptible try to acquire a semaphore. If we obtained 99 * it, return zero. If we were interrupted, returns -EINTR 100 */ 101static inline int down_interruptible(struct semaphore * sem) 102{ 103 int ret = 0; 104 105#if WAITQUEUE_DEBUG 106 CHECK_MAGIC(sem->__magic); 107#endif 108 if (atomic_dec_return(&sem->count) < 0) 109 ret = __down_interruptible(sem); 110 return ret; 111} 112 113#ifndef CONFIG_CPU_HAS_LLDSCD 114 115/* 116 * Non-blockingly attempt to down() a semaphore. 117 * Returns zero if we acquired it 118 */ 119static inline int down_trylock(struct semaphore * sem) 120{ 121 int ret = 0; 122 if (atomic_dec_return(&sem->count) < 0) 123 ret = __down_trylock(sem); 124 return ret; 125} 126 127#else 128 129/* 130 * down_trylock returns 0 on success, 1 if we failed to get the lock. 131 * 132 * We must manipulate count and waking simultaneously and atomically. 133 * Here, we do this by using lld/scd on the pair of 32-bit words. This 134 * won't work on MIPS32 platforms, however, and must be rewritten. 135 * 136 * Pseudocode: 137 * 138 * Decrement(sem->count) 139 * If(sem->count >=0) { 140 * Return(SUCCESS) // resource is free 141 * } else { 142 * If(sem->waking <= 0) { // if no wakeup pending 143 * Increment(sem->count) // undo decrement 144 * Return(FAILURE) 145 * } else { 146 * Decrement(sem->waking) // otherwise "steal" wakeup 147 * Return(SUCCESS) 148 * } 149 * } 150 */ 151static inline int down_trylock(struct semaphore * sem) 152{ 153 long ret, tmp, tmp2, sub; 154 155#if WAITQUEUE_DEBUG 156 CHECK_MAGIC(sem->__magic); 157#endif 158 159 __asm__ __volatile__( 160 ".set\tmips3\t\t\t# down_trylock\n" 161 "0:\tlld\t%1, %4\n\t" 162 "dli\t%3, 0x0000000100000000\n\t" 163 "dsubu\t%1, %3\n\t" 164 "li\t%0, 0\n\t" 165 "bgez\t%1, 2f\n\t" 166 "sll\t%2, %1, 0\n\t" 167 "blez\t%2, 1f\n\t" 168 "daddiu\t%1, %1, -1\n\t" 169 "b\t2f\n\t" 170 "1:\tdaddu\t%1, %1, %3\n" 171 "li\t%0, 1\n" 172 "2:\tscd\t%1, %4\n\t" 173 "beqz\t%1, 0b\n\t" 174 ".set mips0" 175 : "=&r"(ret), "=&r"(tmp), "=&r"(tmp2), "=&r"(sub) 176 : "m"(*sem) 177 : "memory"); 178 179 return ret; 180} 181 182#endif /* CONFIG_CPU_HAS_LLDSCD */ 183 184/* 185 * Note! This is subtle. We jump to wake people up only if 186 * the semaphore was negative (== somebody was waiting on it). 187 */ 188static inline void up(struct semaphore * sem) 189{ 190#if WAITQUEUE_DEBUG 191 CHECK_MAGIC(sem->__magic); 192#endif 193 if (atomic_inc_return(&sem->count) <= 0) 194 __up(sem); 195} 196 197static inline int sem_getcount(struct semaphore *sem) 198{ 199 return atomic_read(&sem->count); 200} 201 202#endif /* _ASM_SEMAPHORE_H */ 203