blob: c30206c2bbf95739c2537f84c2c82b643ccbb17b [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__
H. Peter Anvin1838ef12010-01-18 14:00:34 -080040#include <asm/asm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
Ingo Molnard50efc62008-01-30 13:33:00 +010042extern asmregparm struct rw_semaphore *
43 rwsem_down_read_failed(struct rw_semaphore *sem);
44extern asmregparm struct rw_semaphore *
45 rwsem_down_write_failed(struct rw_semaphore *sem);
46extern asmregparm struct rw_semaphore *
47 rwsem_wake(struct rw_semaphore *);
48extern asmregparm struct rw_semaphore *
49 rwsem_downgrade_wake(struct rw_semaphore *sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
51/*
52 * the semaphore definition
Linus Torvalds5d0b7232010-01-12 17:57:35 -080053 *
H. Peter Anvin1838ef12010-01-18 14:00:34 -080054 * The bias values and the counter type limits the number of
55 * potential readers/writers to 32767 for 32 bits and 2147483647
56 * for 64 bits.
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 */
Joe Perches6e5609a2008-03-23 01:03:21 -070058
H. Peter Anvin1838ef12010-01-18 14:00:34 -080059#ifdef CONFIG_X86_64
60# define RWSEM_ACTIVE_MASK 0xffffffffL
61#else
62# define RWSEM_ACTIVE_MASK 0x0000ffffL
63#endif
64
65#define RWSEM_UNLOCKED_VALUE 0x00000000L
66#define RWSEM_ACTIVE_BIAS 0x00000001L
67#define RWSEM_WAITING_BIAS (-RWSEM_ACTIVE_MASK-1)
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS
69#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
Joe Perches6e5609a2008-03-23 01:03:21 -070070
71struct rw_semaphore {
Thomas Gleixnerbde11ef2011-01-26 20:05:53 +000072 long count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 spinlock_t wait_lock;
74 struct list_head wait_list;
Ingo Molnar4ea21762006-07-03 00:24:53 -070075#ifdef CONFIG_DEBUG_LOCK_ALLOC
76 struct lockdep_map dep_map;
77#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070078};
79
Ingo Molnar4ea21762006-07-03 00:24:53 -070080#ifdef CONFIG_DEBUG_LOCK_ALLOC
81# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname }
82#else
83# define __RWSEM_DEP_MAP_INIT(lockname)
84#endif
85
86
Joe Perches6e5609a2008-03-23 01:03:21 -070087#define __RWSEM_INITIALIZER(name) \
88{ \
89 RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \
90 LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) \
91}
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
Joe Perches6e5609a2008-03-23 01:03:21 -070093#define DECLARE_RWSEM(name) \
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 struct rw_semaphore name = __RWSEM_INITIALIZER(name)
95
Ingo Molnar4ea21762006-07-03 00:24:53 -070096extern void __init_rwsem(struct rw_semaphore *sem, const char *name,
97 struct lock_class_key *key);
98
99#define init_rwsem(sem) \
100do { \
101 static struct lock_class_key __key; \
102 \
103 __init_rwsem((sem), #sem, &__key); \
104} while (0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
106/*
107 * lock for reading
108 */
109static inline void __down_read(struct rw_semaphore *sem)
110{
Joe Perches6e5609a2008-03-23 01:03:21 -0700111 asm volatile("# beginning down_read\n\t"
H. Peter Anvin1838ef12010-01-18 14:00:34 -0800112 LOCK_PREFIX _ASM_INC "(%1)\n\t"
Michel Lespinasseb4bcb4c2010-07-20 15:19:45 -0700113 /* adds 0x00000001 */
Joe Perches6e5609a2008-03-23 01:03:21 -0700114 " jns 1f\n"
115 " call call_rwsem_down_read_failed\n"
116 "1:\n\t"
117 "# ending down_read\n\t"
118 : "+m" (sem->count)
119 : "a" (sem)
120 : "memory", "cc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121}
122
123/*
124 * trylock for reading -- returns 1 if successful, 0 if contention
125 */
126static inline int __down_read_trylock(struct rw_semaphore *sem)
127{
Thomas Gleixnerbde11ef2011-01-26 20:05:53 +0000128 long result, tmp;
Joe Perches6e5609a2008-03-23 01:03:21 -0700129 asm volatile("# beginning __down_read_trylock\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800130 " mov %0,%1\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700131 "1:\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800132 " mov %1,%2\n\t"
133 " add %3,%2\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700134 " jle 2f\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800135 LOCK_PREFIX " cmpxchg %2,%0\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700136 " jnz 1b\n\t"
137 "2:\n\t"
138 "# ending __down_read_trylock\n\t"
139 : "+m" (sem->count), "=&a" (result), "=&r" (tmp)
140 : "i" (RWSEM_ACTIVE_READ_BIAS)
141 : "memory", "cc");
142 return result >= 0 ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143}
144
145/*
146 * lock for writing
147 */
Ingo Molnar4ea21762006-07-03 00:24:53 -0700148static inline void __down_write_nested(struct rw_semaphore *sem, int subclass)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149{
Thomas Gleixnerbde11ef2011-01-26 20:05:53 +0000150 long tmp;
Joe Perches6e5609a2008-03-23 01:03:21 -0700151 asm volatile("# beginning down_write\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800152 LOCK_PREFIX " xadd %1,(%2)\n\t"
Michel Lespinasseb4bcb4c2010-07-20 15:19:45 -0700153 /* adds 0xffff0001, returns the old value */
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800154 " test %1,%1\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700155 /* was the count 0 before? */
156 " jz 1f\n"
157 " call call_rwsem_down_write_failed\n"
158 "1:\n"
159 "# ending down_write"
160 : "+m" (sem->count), "=d" (tmp)
Michel Lespinasseb4bcb4c2010-07-20 15:19:45 -0700161 : "a" (sem), "1" (RWSEM_ACTIVE_WRITE_BIAS)
Joe Perches6e5609a2008-03-23 01:03:21 -0700162 : "memory", "cc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163}
164
Ingo Molnar4ea21762006-07-03 00:24:53 -0700165static inline void __down_write(struct rw_semaphore *sem)
166{
167 __down_write_nested(sem, 0);
168}
169
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170/*
171 * trylock for writing -- returns 1 if successful, 0 if contention
172 */
173static inline int __down_write_trylock(struct rw_semaphore *sem)
174{
Thomas Gleixnerbde11ef2011-01-26 20:05:53 +0000175 long ret = cmpxchg(&sem->count, RWSEM_UNLOCKED_VALUE,
176 RWSEM_ACTIVE_WRITE_BIAS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 if (ret == RWSEM_UNLOCKED_VALUE)
178 return 1;
179 return 0;
180}
181
182/*
183 * unlock after reading
184 */
185static inline void __up_read(struct rw_semaphore *sem)
186{
Thomas Gleixnerbde11ef2011-01-26 20:05:53 +0000187 long tmp;
Joe Perches6e5609a2008-03-23 01:03:21 -0700188 asm volatile("# beginning __up_read\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800189 LOCK_PREFIX " xadd %1,(%2)\n\t"
Joe Perches6e5609a2008-03-23 01:03:21 -0700190 /* subtracts 1, returns the old value */
191 " jns 1f\n\t"
Michel Lespinasseb4bcb4c2010-07-20 15:19:45 -0700192 " call call_rwsem_wake\n" /* expects old value in %edx */
Joe Perches6e5609a2008-03-23 01:03:21 -0700193 "1:\n"
194 "# ending __up_read\n"
195 : "+m" (sem->count), "=d" (tmp)
Michel Lespinasseb4bcb4c2010-07-20 15:19:45 -0700196 : "a" (sem), "1" (-RWSEM_ACTIVE_READ_BIAS)
Joe Perches6e5609a2008-03-23 01:03:21 -0700197 : "memory", "cc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198}
199
200/*
201 * unlock after writing
202 */
203static inline void __up_write(struct rw_semaphore *sem)
204{
Thomas Gleixnerbde11ef2011-01-26 20:05:53 +0000205 long tmp;
Joe Perches6e5609a2008-03-23 01:03:21 -0700206 asm volatile("# beginning __up_write\n\t"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800207 LOCK_PREFIX " xadd %1,(%2)\n\t"
Michel Lespinassea751bd82010-07-20 15:19:45 -0700208 /* subtracts 0xffff0001, returns the old value */
209 " jns 1f\n\t"
Michel Lespinasseb4bcb4c2010-07-20 15:19:45 -0700210 " call call_rwsem_wake\n" /* expects old value in %edx */
Joe Perches6e5609a2008-03-23 01:03:21 -0700211 "1:\n\t"
212 "# ending __up_write\n"
Linus Torvalds59c33fa2010-01-12 16:21:09 -0800213 : "+m" (sem->count), "=d" (tmp)
214 : "a" (sem), "1" (-RWSEM_ACTIVE_WRITE_BIAS)
215 : "memory", "cc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216}
217
218/*
219 * downgrade write lock to read lock
220 */
221static inline void __downgrade_write(struct rw_semaphore *sem)
222{
H. Peter Anvin1838ef12010-01-18 14:00:34 -0800223 asm volatile("# beginning __downgrade_write\n\t"
224 LOCK_PREFIX _ASM_ADD "%2,(%1)\n\t"
Avi Kivity0d1622d2010-02-13 10:33:12 +0200225 /*
226 * transitions 0xZZZZ0001 -> 0xYYYY0001 (i386)
227 * 0xZZZZZZZZ00000001 -> 0xYYYYYYYY00000001 (x86_64)
228 */
Joe Perches6e5609a2008-03-23 01:03:21 -0700229 " jns 1f\n\t"
230 " call call_rwsem_downgrade_wake\n"
231 "1:\n\t"
232 "# ending __downgrade_write\n"
233 : "+m" (sem->count)
Avi Kivity0d1622d2010-02-13 10:33:12 +0200234 : "a" (sem), "er" (-RWSEM_WAITING_BIAS)
Joe Perches6e5609a2008-03-23 01:03:21 -0700235 : "memory", "cc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236}
237
238/*
239 * implement atomic add functionality
240 */
Thomas Gleixnerbde11ef2011-01-26 20:05:53 +0000241static inline void rwsem_atomic_add(long delta, struct rw_semaphore *sem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
H. Peter Anvin1838ef12010-01-18 14:00:34 -0800243 asm volatile(LOCK_PREFIX _ASM_ADD "%1,%0"
Joe Perches6e5609a2008-03-23 01:03:21 -0700244 : "+m" (sem->count)
H. Peter Anvin1838ef12010-01-18 14:00:34 -0800245 : "er" (delta));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246}
247
248/*
249 * implement exchange and add functionality
250 */
Thomas Gleixnerbde11ef2011-01-26 20:05:53 +0000251static inline long rwsem_atomic_update(long delta, struct rw_semaphore *sem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252{
Thomas Gleixnerbde11ef2011-01-26 20:05:53 +0000253 long tmp = delta;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
Joe Perches6e5609a2008-03-23 01:03:21 -0700255 asm volatile(LOCK_PREFIX "xadd %0,%1"
256 : "+r" (tmp), "+m" (sem->count)
257 : : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
Joe Perches6e5609a2008-03-23 01:03:21 -0700259 return tmp + delta;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260}
261
Rik Van Rieleb92f4e2005-10-29 18:15:44 -0700262static inline int rwsem_is_locked(struct rw_semaphore *sem)
263{
264 return (sem->count != 0);
265}
266
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267#endif /* __KERNEL__ */
H. Peter Anvin1965aae2008-10-22 22:26:29 -0700268#endif /* _ASM_X86_RWSEM_H */