blob: 5f9af3081d667fb71b23477eab3ab4162230b91d [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* rwsem.h: R/W semaphores implemented using XADD/CMPXCHG for i486+
2 *
3 * Written by David Howells (dhowells@redhat.com).
4 *
Dave Jones99122a32008-01-30 13:30:28 +01005 * Derived from asm-x86/semaphore.h
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
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
H. Peter Anvin1965aae2008-10-22 22:26:29 -070032#ifndef _ASM_X86_RWSEM_H
33#define _ASM_X86_RWSEM_H
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
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>
Ingo Molnar4ea21762006-07-03 00:24:53 -070043#include <linux/lockdep.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45struct rwsem_waiter;
46
Ingo Molnard50efc62008-01-30 13:33:00 +010047extern asmregparm struct rw_semaphore *
48 rwsem_down_read_failed(struct rw_semaphore *sem);
49extern asmregparm struct rw_semaphore *
50 rwsem_down_write_failed(struct rw_semaphore *sem);
51extern asmregparm struct rw_semaphore *
52 rwsem_wake(struct rw_semaphore *);
53extern asmregparm struct rw_semaphore *
54 rwsem_downgrade_wake(struct rw_semaphore *sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
56/*
57 * the semaphore definition
Linus Torvalds5d0b7232010-01-12 17:57:35 -080058 *
59 * The bias values and the counter type needs to be extended to 64 bits
60 * if we want to have more than 32767 potential readers/writers
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 */
Joe Perches6e5609a2008-03-23 01:03:21 -070062
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#define RWSEM_UNLOCKED_VALUE 0x00000000
64#define RWSEM_ACTIVE_BIAS 0x00000001
65#define RWSEM_ACTIVE_MASK 0x0000ffff
66#define RWSEM_WAITING_BIAS (-0x00010000)
67#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS
68#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
Joe Perches6e5609a2008-03-23 01:03:21 -070069
Linus Torvalds5d0b7232010-01-12 17:57:35 -080070typedef signed int rwsem_count_t;
71
Joe Perches6e5609a2008-03-23 01:03:21 -070072struct rw_semaphore {
Linus Torvalds5d0b7232010-01-12 17:57:35 -080073 rwsem_count_t count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 spinlock_t wait_lock;
75 struct list_head wait_list;
Ingo Molnar4ea21762006-07-03 00:24:53 -070076#ifdef CONFIG_DEBUG_LOCK_ALLOC
77 struct lockdep_map dep_map;
78#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070079};
80
Ingo Molnar4ea21762006-07-03 00:24:53 -070081#ifdef CONFIG_DEBUG_LOCK_ALLOC
82# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname }
83#else
84# define __RWSEM_DEP_MAP_INIT(lockname)
85#endif
86
87
Joe Perches6e5609a2008-03-23 01:03:21 -070088#define __RWSEM_INITIALIZER(name) \
89{ \
90 RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \
91 LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) \
92}
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
Joe Perches6e5609a2008-03-23 01:03:21 -070094#define DECLARE_RWSEM(name) \
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 struct rw_semaphore name = __RWSEM_INITIALIZER(name)
96
Ingo Molnar4ea21762006-07-03 00:24:53 -070097extern void __init_rwsem(struct rw_semaphore *sem, const char *name,
98 struct lock_class_key *key);
99
100#define init_rwsem(sem) \
101do { \
102 static struct lock_class_key __key; \
103 \
104 __init_rwsem((sem), #sem, &__key); \
105} while (0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106
107/*
108 * lock for reading
109 */
110static inline void __down_read(struct rw_semaphore *sem)
111{
Joe Perches6e5609a2008-03-23 01:03:21 -0700112 asm volatile("# beginning down_read\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800113 LOCK_PREFIX " inc%z0 (%1)\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700114 /* adds 0x00000001, returns the old value */
115 " jns 1f\n"
116 " call call_rwsem_down_read_failed\n"
117 "1:\n\t"
118 "# ending down_read\n\t"
119 : "+m" (sem->count)
120 : "a" (sem)
121 : "memory", "cc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122}
123
124/*
125 * trylock for reading -- returns 1 if successful, 0 if contention
126 */
127static inline int __down_read_trylock(struct rw_semaphore *sem)
128{
Linus Torvalds5d0b7232010-01-12 17:57:35 -0800129 rwsem_count_t result, tmp;
Joe Perches6e5609a2008-03-23 01:03:21 -0700130 asm volatile("# beginning __down_read_trylock\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800131 " mov %0,%1\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700132 "1:\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800133 " mov %1,%2\n\t"
134 " add %3,%2\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700135 " jle 2f\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800136 LOCK_PREFIX " cmpxchg %2,%0\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700137 " jnz 1b\n\t"
138 "2:\n\t"
139 "# ending __down_read_trylock\n\t"
140 : "+m" (sem->count), "=&a" (result), "=&r" (tmp)
141 : "i" (RWSEM_ACTIVE_READ_BIAS)
142 : "memory", "cc");
143 return result >= 0 ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144}
145
146/*
147 * lock for writing
148 */
Ingo Molnar4ea21762006-07-03 00:24:53 -0700149static inline void __down_write_nested(struct rw_semaphore *sem, int subclass)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150{
Linus Torvalds5d0b7232010-01-12 17:57:35 -0800151 rwsem_count_t tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
153 tmp = RWSEM_ACTIVE_WRITE_BIAS;
Joe Perches6e5609a2008-03-23 01:03:21 -0700154 asm volatile("# beginning down_write\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800155 LOCK_PREFIX " xadd %1,(%2)\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700156 /* subtract 0x0000ffff, returns the old value */
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800157 " test %1,%1\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700158 /* was the count 0 before? */
159 " jz 1f\n"
160 " call call_rwsem_down_write_failed\n"
161 "1:\n"
162 "# ending down_write"
163 : "+m" (sem->count), "=d" (tmp)
164 : "a" (sem), "1" (tmp)
165 : "memory", "cc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166}
167
Ingo Molnar4ea21762006-07-03 00:24:53 -0700168static inline void __down_write(struct rw_semaphore *sem)
169{
170 __down_write_nested(sem, 0);
171}
172
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173/*
174 * trylock for writing -- returns 1 if successful, 0 if contention
175 */
176static inline int __down_write_trylock(struct rw_semaphore *sem)
177{
Linus Torvalds5d0b7232010-01-12 17:57:35 -0800178 rwsem_count_t ret = cmpxchg(&sem->count,
179 RWSEM_UNLOCKED_VALUE,
180 RWSEM_ACTIVE_WRITE_BIAS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 if (ret == RWSEM_UNLOCKED_VALUE)
182 return 1;
183 return 0;
184}
185
186/*
187 * unlock after reading
188 */
189static inline void __up_read(struct rw_semaphore *sem)
190{
Linus Torvalds5d0b7232010-01-12 17:57:35 -0800191 rwsem_count_t tmp = -RWSEM_ACTIVE_READ_BIAS;
Joe Perches6e5609a2008-03-23 01:03:21 -0700192 asm volatile("# beginning __up_read\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800193 LOCK_PREFIX " xadd %1,(%2)\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700194 /* subtracts 1, returns the old value */
195 " jns 1f\n\t"
196 " call call_rwsem_wake\n"
197 "1:\n"
198 "# ending __up_read\n"
199 : "+m" (sem->count), "=d" (tmp)
200 : "a" (sem), "1" (tmp)
201 : "memory", "cc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202}
203
204/*
205 * unlock after writing
206 */
207static inline void __up_write(struct rw_semaphore *sem)
208{
Linus Torvalds5d0b7232010-01-12 17:57:35 -0800209 rwsem_count_t tmp;
Joe Perches6e5609a2008-03-23 01:03:21 -0700210 asm volatile("# beginning __up_write\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800211 LOCK_PREFIX " xadd %1,(%2)\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700212 /* tries to transition
213 0xffff0001 -> 0x00000000 */
214 " jz 1f\n"
215 " call call_rwsem_wake\n"
216 "1:\n\t"
217 "# ending __up_write\n"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800218 : "+m" (sem->count), "=d" (tmp)
219 : "a" (sem), "1" (-RWSEM_ACTIVE_WRITE_BIAS)
220 : "memory", "cc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221}
222
223/*
224 * downgrade write lock to read lock
225 */
226static inline void __downgrade_write(struct rw_semaphore *sem)
227{
Joe Perches6e5609a2008-03-23 01:03:21 -0700228 asm volatile("# beginning __downgrade_write\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800229 LOCK_PREFIX " add%z0 %2,(%1)\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700230 /* transitions 0xZZZZ0001 -> 0xYYYY0001 */
231 " jns 1f\n\t"
232 " call call_rwsem_downgrade_wake\n"
233 "1:\n\t"
234 "# ending __downgrade_write\n"
235 : "+m" (sem->count)
236 : "a" (sem), "i" (-RWSEM_WAITING_BIAS)
237 : "memory", "cc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238}
239
240/*
241 * implement atomic add functionality
242 */
243static inline void rwsem_atomic_add(int delta, struct rw_semaphore *sem)
244{
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800245 asm volatile(LOCK_PREFIX "add%z0 %1,%0"
Joe Perches6e5609a2008-03-23 01:03:21 -0700246 : "+m" (sem->count)
247 : "ir" (delta));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248}
249
250/*
251 * implement exchange and add functionality
252 */
Linus Torvalds5d0b7232010-01-12 17:57:35 -0800253static inline rwsem_count_t rwsem_atomic_update(int delta, struct rw_semaphore *sem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254{
Linus Torvalds5d0b7232010-01-12 17:57:35 -0800255 rwsem_count_t tmp = delta;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
Joe Perches6e5609a2008-03-23 01:03:21 -0700257 asm volatile(LOCK_PREFIX "xadd %0,%1"
258 : "+r" (tmp), "+m" (sem->count)
259 : : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
Joe Perches6e5609a2008-03-23 01:03:21 -0700261 return tmp + delta;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262}
263
Rik Van Rieleb92f4e2005-10-29 18:15:44 -0700264static inline int rwsem_is_locked(struct rw_semaphore *sem)
265{
266 return (sem->count != 0);
267}
268
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269#endif /* __KERNEL__ */
H. Peter Anvin1965aae2008-10-22 22:26:29 -0700270#endif /* _ASM_X86_RWSEM_H */