diff options
Diffstat (limited to 'kernel/locking/rwsem.h')
-rw-r--r-- | kernel/locking/rwsem.h | 174 |
1 files changed, 172 insertions, 2 deletions
diff --git a/kernel/locking/rwsem.h b/kernel/locking/rwsem.h index bad2bca0268b..64877f5294e3 100644 --- a/kernel/locking/rwsem.h +++ b/kernel/locking/rwsem.h | |||
@@ -23,15 +23,44 @@ | |||
23 | * is involved. Ideally we would like to track all the readers that own | 23 | * is involved. Ideally we would like to track all the readers that own |
24 | * a rwsem, but the overhead is simply too big. | 24 | * a rwsem, but the overhead is simply too big. |
25 | */ | 25 | */ |
26 | #include "lock_events.h" | ||
27 | |||
26 | #define RWSEM_READER_OWNED (1UL << 0) | 28 | #define RWSEM_READER_OWNED (1UL << 0) |
27 | #define RWSEM_ANONYMOUSLY_OWNED (1UL << 1) | 29 | #define RWSEM_ANONYMOUSLY_OWNED (1UL << 1) |
28 | 30 | ||
29 | #ifdef CONFIG_DEBUG_RWSEMS | 31 | #ifdef CONFIG_DEBUG_RWSEMS |
30 | # define DEBUG_RWSEMS_WARN_ON(c) DEBUG_LOCKS_WARN_ON(c) | 32 | # define DEBUG_RWSEMS_WARN_ON(c, sem) do { \ |
33 | if (!debug_locks_silent && \ | ||
34 | WARN_ONCE(c, "DEBUG_RWSEMS_WARN_ON(%s): count = 0x%lx, owner = 0x%lx, curr 0x%lx, list %sempty\n",\ | ||
35 | #c, atomic_long_read(&(sem)->count), \ | ||
36 | (long)((sem)->owner), (long)current, \ | ||
37 | list_empty(&(sem)->wait_list) ? "" : "not ")) \ | ||
38 | debug_locks_off(); \ | ||
39 | } while (0) | ||
40 | #else | ||
41 | # define DEBUG_RWSEMS_WARN_ON(c, sem) | ||
42 | #endif | ||
43 | |||
44 | /* | ||
45 | * R/W semaphores originally for PPC using the stuff in lib/rwsem.c. | ||
46 | * Adapted largely from include/asm-i386/rwsem.h | ||
47 | * by Paul Mackerras <paulus@samba.org>. | ||
48 | */ | ||
49 | |||
50 | /* | ||
51 | * the semaphore definition | ||
52 | */ | ||
53 | #ifdef CONFIG_64BIT | ||
54 | # define RWSEM_ACTIVE_MASK 0xffffffffL | ||
31 | #else | 55 | #else |
32 | # define DEBUG_RWSEMS_WARN_ON(c) | 56 | # define RWSEM_ACTIVE_MASK 0x0000ffffL |
33 | #endif | 57 | #endif |
34 | 58 | ||
59 | #define RWSEM_ACTIVE_BIAS 0x00000001L | ||
60 | #define RWSEM_WAITING_BIAS (-RWSEM_ACTIVE_MASK-1) | ||
61 | #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS | ||
62 | #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) | ||
63 | |||
35 | #ifdef CONFIG_RWSEM_SPIN_ON_OWNER | 64 | #ifdef CONFIG_RWSEM_SPIN_ON_OWNER |
36 | /* | 65 | /* |
37 | * All writes to owner are protected by WRITE_ONCE() to make sure that | 66 | * All writes to owner are protected by WRITE_ONCE() to make sure that |
@@ -132,3 +161,144 @@ static inline void rwsem_clear_reader_owned(struct rw_semaphore *sem) | |||
132 | { | 161 | { |
133 | } | 162 | } |
134 | #endif | 163 | #endif |
164 | |||
165 | extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); | ||
166 | extern struct rw_semaphore *rwsem_down_read_failed_killable(struct rw_semaphore *sem); | ||
167 | extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); | ||
168 | extern struct rw_semaphore *rwsem_down_write_failed_killable(struct rw_semaphore *sem); | ||
169 | extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); | ||
170 | extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); | ||
171 | |||
172 | /* | ||
173 | * lock for reading | ||
174 | */ | ||
175 | static inline void __down_read(struct rw_semaphore *sem) | ||
176 | { | ||
177 | if (unlikely(atomic_long_inc_return_acquire(&sem->count) <= 0)) { | ||
178 | rwsem_down_read_failed(sem); | ||
179 | DEBUG_RWSEMS_WARN_ON(!((unsigned long)sem->owner & | ||
180 | RWSEM_READER_OWNED), sem); | ||
181 | } else { | ||
182 | rwsem_set_reader_owned(sem); | ||
183 | } | ||
184 | } | ||
185 | |||
186 | static inline int __down_read_killable(struct rw_semaphore *sem) | ||
187 | { | ||
188 | if (unlikely(atomic_long_inc_return_acquire(&sem->count) <= 0)) { | ||
189 | if (IS_ERR(rwsem_down_read_failed_killable(sem))) | ||
190 | return -EINTR; | ||
191 | DEBUG_RWSEMS_WARN_ON(!((unsigned long)sem->owner & | ||
192 | RWSEM_READER_OWNED), sem); | ||
193 | } else { | ||
194 | rwsem_set_reader_owned(sem); | ||
195 | } | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static inline int __down_read_trylock(struct rw_semaphore *sem) | ||
200 | { | ||
201 | /* | ||
202 | * Optimize for the case when the rwsem is not locked at all. | ||
203 | */ | ||
204 | long tmp = RWSEM_UNLOCKED_VALUE; | ||
205 | |||
206 | lockevent_inc(rwsem_rtrylock); | ||
207 | do { | ||
208 | if (atomic_long_try_cmpxchg_acquire(&sem->count, &tmp, | ||
209 | tmp + RWSEM_ACTIVE_READ_BIAS)) { | ||
210 | rwsem_set_reader_owned(sem); | ||
211 | return 1; | ||
212 | } | ||
213 | } while (tmp >= 0); | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | /* | ||
218 | * lock for writing | ||
219 | */ | ||
220 | static inline void __down_write(struct rw_semaphore *sem) | ||
221 | { | ||
222 | long tmp; | ||
223 | |||
224 | tmp = atomic_long_add_return_acquire(RWSEM_ACTIVE_WRITE_BIAS, | ||
225 | &sem->count); | ||
226 | if (unlikely(tmp != RWSEM_ACTIVE_WRITE_BIAS)) | ||
227 | rwsem_down_write_failed(sem); | ||
228 | rwsem_set_owner(sem); | ||
229 | } | ||
230 | |||
231 | static inline int __down_write_killable(struct rw_semaphore *sem) | ||
232 | { | ||
233 | long tmp; | ||
234 | |||
235 | tmp = atomic_long_add_return_acquire(RWSEM_ACTIVE_WRITE_BIAS, | ||
236 | &sem->count); | ||
237 | if (unlikely(tmp != RWSEM_ACTIVE_WRITE_BIAS)) | ||
238 | if (IS_ERR(rwsem_down_write_failed_killable(sem))) | ||
239 | return -EINTR; | ||
240 | rwsem_set_owner(sem); | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static inline int __down_write_trylock(struct rw_semaphore *sem) | ||
245 | { | ||
246 | long tmp; | ||
247 | |||
248 | lockevent_inc(rwsem_wtrylock); | ||
249 | tmp = atomic_long_cmpxchg_acquire(&sem->count, RWSEM_UNLOCKED_VALUE, | ||
250 | RWSEM_ACTIVE_WRITE_BIAS); | ||
251 | if (tmp == RWSEM_UNLOCKED_VALUE) { | ||
252 | rwsem_set_owner(sem); | ||
253 | return true; | ||
254 | } | ||
255 | return false; | ||
256 | } | ||
257 | |||
258 | /* | ||
259 | * unlock after reading | ||
260 | */ | ||
261 | static inline void __up_read(struct rw_semaphore *sem) | ||
262 | { | ||
263 | long tmp; | ||
264 | |||
265 | DEBUG_RWSEMS_WARN_ON(!((unsigned long)sem->owner & RWSEM_READER_OWNED), | ||
266 | sem); | ||
267 | rwsem_clear_reader_owned(sem); | ||
268 | tmp = atomic_long_dec_return_release(&sem->count); | ||
269 | if (unlikely(tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0)) | ||
270 | rwsem_wake(sem); | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * unlock after writing | ||
275 | */ | ||
276 | static inline void __up_write(struct rw_semaphore *sem) | ||
277 | { | ||
278 | DEBUG_RWSEMS_WARN_ON(sem->owner != current, sem); | ||
279 | rwsem_clear_owner(sem); | ||
280 | if (unlikely(atomic_long_sub_return_release(RWSEM_ACTIVE_WRITE_BIAS, | ||
281 | &sem->count) < 0)) | ||
282 | rwsem_wake(sem); | ||
283 | } | ||
284 | |||
285 | /* | ||
286 | * downgrade write lock to read lock | ||
287 | */ | ||
288 | static inline void __downgrade_write(struct rw_semaphore *sem) | ||
289 | { | ||
290 | long tmp; | ||
291 | |||
292 | /* | ||
293 | * When downgrading from exclusive to shared ownership, | ||
294 | * anything inside the write-locked region cannot leak | ||
295 | * into the read side. In contrast, anything in the | ||
296 | * read-locked region is ok to be re-ordered into the | ||
297 | * write side. As such, rely on RELEASE semantics. | ||
298 | */ | ||
299 | DEBUG_RWSEMS_WARN_ON(sem->owner != current, sem); | ||
300 | tmp = atomic_long_add_return_release(-RWSEM_WAITING_BIAS, &sem->count); | ||
301 | rwsem_set_reader_owned(sem); | ||
302 | if (tmp < 0) | ||
303 | rwsem_downgrade_wake(sem); | ||
304 | } | ||