Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.17 293 lines 8.0 kB view raw
1/* rwsem.h: R/W semaphores implemented using XADD/CMPXCHG for i486+ 2 * 3 * Written by David Howells (dhowells@redhat.com). 4 * 5 * Derived from asm-i386/semaphore.h 6 * 7 * 8 * The MSW of the count is the negated number of active writers and waiting 9 * lockers, and the LSW is the total number of active locks 10 * 11 * The lock count is initialized to 0 (no active and no waiting lockers). 12 * 13 * When a writer subtracts WRITE_BIAS, it'll get 0xffff0001 for the case of an 14 * uncontended lock. This can be determined because XADD returns the old value. 15 * Readers increment by 1 and see a positive value when uncontended, negative 16 * if there are writers (and maybe) readers waiting (in which case it goes to 17 * sleep). 18 * 19 * The value of WAITING_BIAS supports up to 32766 waiting processes. This can 20 * be extended to 65534 by manually checking the whole MSW rather than relying 21 * on the S flag. 22 * 23 * The value of ACTIVE_BIAS supports up to 65535 active processes. 24 * 25 * This should be totally fair - if anything is waiting, a process that wants a 26 * lock will go to the back of the queue. When the currently active lock is 27 * released, if there's a writer at the front of the queue, then that and only 28 * that will be woken up; if there's a bunch of consequtive readers at the 29 * front, then they'll all be woken up, but no other readers will be. 30 */ 31 32#ifndef _I386_RWSEM_H 33#define _I386_RWSEM_H 34 35#ifndef _LINUX_RWSEM_H 36#error "please don't include asm/rwsem.h directly, use linux/rwsem.h instead" 37#endif 38 39#ifdef __KERNEL__ 40 41#include <linux/list.h> 42#include <linux/spinlock.h> 43 44struct rwsem_waiter; 45 46extern struct rw_semaphore *FASTCALL(rwsem_down_read_failed(struct rw_semaphore *sem)); 47extern struct rw_semaphore *FASTCALL(rwsem_down_write_failed(struct rw_semaphore *sem)); 48extern struct rw_semaphore *FASTCALL(rwsem_wake(struct rw_semaphore *)); 49extern struct rw_semaphore *FASTCALL(rwsem_downgrade_wake(struct rw_semaphore *sem)); 50 51/* 52 * the semaphore definition 53 */ 54struct rw_semaphore { 55 signed long count; 56#define RWSEM_UNLOCKED_VALUE 0x00000000 57#define RWSEM_ACTIVE_BIAS 0x00000001 58#define RWSEM_ACTIVE_MASK 0x0000ffff 59#define RWSEM_WAITING_BIAS (-0x00010000) 60#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS 61#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) 62 spinlock_t wait_lock; 63 struct list_head wait_list; 64#if RWSEM_DEBUG 65 int debug; 66#endif 67}; 68 69/* 70 * initialisation 71 */ 72#if RWSEM_DEBUG 73#define __RWSEM_DEBUG_INIT , 0 74#else 75#define __RWSEM_DEBUG_INIT /* */ 76#endif 77 78#define __RWSEM_INITIALIZER(name) \ 79{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) \ 80 __RWSEM_DEBUG_INIT } 81 82#define DECLARE_RWSEM(name) \ 83 struct rw_semaphore name = __RWSEM_INITIALIZER(name) 84 85static inline void init_rwsem(struct rw_semaphore *sem) 86{ 87 sem->count = RWSEM_UNLOCKED_VALUE; 88 spin_lock_init(&sem->wait_lock); 89 INIT_LIST_HEAD(&sem->wait_list); 90#if RWSEM_DEBUG 91 sem->debug = 0; 92#endif 93} 94 95/* 96 * lock for reading 97 */ 98static inline void __down_read(struct rw_semaphore *sem) 99{ 100 __asm__ __volatile__( 101 "# beginning down_read\n\t" 102LOCK_PREFIX " incl (%%eax)\n\t" /* adds 0x00000001, returns the old value */ 103 " js 2f\n\t" /* jump if we weren't granted the lock */ 104 "1:\n\t" 105 LOCK_SECTION_START("") 106 "2:\n\t" 107 " pushl %%ecx\n\t" 108 " pushl %%edx\n\t" 109 " call rwsem_down_read_failed\n\t" 110 " popl %%edx\n\t" 111 " popl %%ecx\n\t" 112 " jmp 1b\n" 113 LOCK_SECTION_END 114 "# ending down_read\n\t" 115 : "=m"(sem->count) 116 : "a"(sem), "m"(sem->count) 117 : "memory", "cc"); 118} 119 120/* 121 * trylock for reading -- returns 1 if successful, 0 if contention 122 */ 123static inline int __down_read_trylock(struct rw_semaphore *sem) 124{ 125 __s32 result, tmp; 126 __asm__ __volatile__( 127 "# beginning __down_read_trylock\n\t" 128 " movl %0,%1\n\t" 129 "1:\n\t" 130 " movl %1,%2\n\t" 131 " addl %3,%2\n\t" 132 " jle 2f\n\t" 133LOCK_PREFIX " cmpxchgl %2,%0\n\t" 134 " jnz 1b\n\t" 135 "2:\n\t" 136 "# ending __down_read_trylock\n\t" 137 : "+m"(sem->count), "=&a"(result), "=&r"(tmp) 138 : "i"(RWSEM_ACTIVE_READ_BIAS) 139 : "memory", "cc"); 140 return result>=0 ? 1 : 0; 141} 142 143/* 144 * lock for writing 145 */ 146static inline void __down_write(struct rw_semaphore *sem) 147{ 148 int tmp; 149 150 tmp = RWSEM_ACTIVE_WRITE_BIAS; 151 __asm__ __volatile__( 152 "# beginning down_write\n\t" 153LOCK_PREFIX " xadd %%edx,(%%eax)\n\t" /* subtract 0x0000ffff, returns the old value */ 154 " testl %%edx,%%edx\n\t" /* was the count 0 before? */ 155 " jnz 2f\n\t" /* jump if we weren't granted the lock */ 156 "1:\n\t" 157 LOCK_SECTION_START("") 158 "2:\n\t" 159 " pushl %%ecx\n\t" 160 " call rwsem_down_write_failed\n\t" 161 " popl %%ecx\n\t" 162 " jmp 1b\n" 163 LOCK_SECTION_END 164 "# ending down_write" 165 : "=m"(sem->count), "=d"(tmp) 166 : "a"(sem), "1"(tmp), "m"(sem->count) 167 : "memory", "cc"); 168} 169 170/* 171 * trylock for writing -- returns 1 if successful, 0 if contention 172 */ 173static inline int __down_write_trylock(struct rw_semaphore *sem) 174{ 175 signed long ret = cmpxchg(&sem->count, 176 RWSEM_UNLOCKED_VALUE, 177 RWSEM_ACTIVE_WRITE_BIAS); 178 if (ret == RWSEM_UNLOCKED_VALUE) 179 return 1; 180 return 0; 181} 182 183/* 184 * unlock after reading 185 */ 186static inline void __up_read(struct rw_semaphore *sem) 187{ 188 __s32 tmp = -RWSEM_ACTIVE_READ_BIAS; 189 __asm__ __volatile__( 190 "# beginning __up_read\n\t" 191LOCK_PREFIX " xadd %%edx,(%%eax)\n\t" /* subtracts 1, returns the old value */ 192 " js 2f\n\t" /* jump if the lock is being waited upon */ 193 "1:\n\t" 194 LOCK_SECTION_START("") 195 "2:\n\t" 196 " decw %%dx\n\t" /* do nothing if still outstanding active readers */ 197 " jnz 1b\n\t" 198 " pushl %%ecx\n\t" 199 " call rwsem_wake\n\t" 200 " popl %%ecx\n\t" 201 " jmp 1b\n" 202 LOCK_SECTION_END 203 "# ending __up_read\n" 204 : "=m"(sem->count), "=d"(tmp) 205 : "a"(sem), "1"(tmp), "m"(sem->count) 206 : "memory", "cc"); 207} 208 209/* 210 * unlock after writing 211 */ 212static inline void __up_write(struct rw_semaphore *sem) 213{ 214 __asm__ __volatile__( 215 "# beginning __up_write\n\t" 216 " movl %2,%%edx\n\t" 217LOCK_PREFIX " xaddl %%edx,(%%eax)\n\t" /* tries to transition 0xffff0001 -> 0x00000000 */ 218 " jnz 2f\n\t" /* jump if the lock is being waited upon */ 219 "1:\n\t" 220 LOCK_SECTION_START("") 221 "2:\n\t" 222 " decw %%dx\n\t" /* did the active count reduce to 0? */ 223 " jnz 1b\n\t" /* jump back if not */ 224 " pushl %%ecx\n\t" 225 " call rwsem_wake\n\t" 226 " popl %%ecx\n\t" 227 " jmp 1b\n" 228 LOCK_SECTION_END 229 "# ending __up_write\n" 230 : "=m"(sem->count) 231 : "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS), "m"(sem->count) 232 : "memory", "cc", "edx"); 233} 234 235/* 236 * downgrade write lock to read lock 237 */ 238static inline void __downgrade_write(struct rw_semaphore *sem) 239{ 240 __asm__ __volatile__( 241 "# beginning __downgrade_write\n\t" 242LOCK_PREFIX " addl %2,(%%eax)\n\t" /* transitions 0xZZZZ0001 -> 0xYYYY0001 */ 243 " js 2f\n\t" /* jump if the lock is being waited upon */ 244 "1:\n\t" 245 LOCK_SECTION_START("") 246 "2:\n\t" 247 " pushl %%ecx\n\t" 248 " pushl %%edx\n\t" 249 " call rwsem_downgrade_wake\n\t" 250 " popl %%edx\n\t" 251 " popl %%ecx\n\t" 252 " jmp 1b\n" 253 LOCK_SECTION_END 254 "# ending __downgrade_write\n" 255 : "=m"(sem->count) 256 : "a"(sem), "i"(-RWSEM_WAITING_BIAS), "m"(sem->count) 257 : "memory", "cc"); 258} 259 260/* 261 * implement atomic add functionality 262 */ 263static inline void rwsem_atomic_add(int delta, struct rw_semaphore *sem) 264{ 265 __asm__ __volatile__( 266LOCK_PREFIX "addl %1,%0" 267 : "=m"(sem->count) 268 : "ir"(delta), "m"(sem->count)); 269} 270 271/* 272 * implement exchange and add functionality 273 */ 274static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem) 275{ 276 int tmp = delta; 277 278 __asm__ __volatile__( 279LOCK_PREFIX "xadd %0,(%2)" 280 : "+r"(tmp), "=m"(sem->count) 281 : "r"(sem), "m"(sem->count) 282 : "memory"); 283 284 return tmp+delta; 285} 286 287static inline int rwsem_is_locked(struct rw_semaphore *sem) 288{ 289 return (sem->count != 0); 290} 291 292#endif /* __KERNEL__ */ 293#endif /* _I386_RWSEM_H */