]> Pileus Git - ~andy/linux/blob - drivers/tty/tty_ldsem.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph...
[~andy/linux] / drivers / tty / tty_ldsem.c
1 /*
2  * Ldisc rw semaphore
3  *
4  * The ldisc semaphore is semantically a rw_semaphore but which enforces
5  * an alternate policy, namely:
6  *   1) Supports lock wait timeouts
7  *   2) Write waiter has priority
8  *   3) Downgrading is not supported
9  *
10  * Implementation notes:
11  *   1) Upper half of semaphore count is a wait count (differs from rwsem
12  *      in that rwsem normalizes the upper half to the wait bias)
13  *   2) Lacks overflow checking
14  *
15  * The generic counting was copied and modified from include/asm-generic/rwsem.h
16  * by Paul Mackerras <paulus@samba.org>.
17  *
18  * The scheduling policy was copied and modified from lib/rwsem.c
19  * Written by David Howells (dhowells@redhat.com).
20  *
21  * This implementation incorporates the write lock stealing work of
22  * Michel Lespinasse <walken@google.com>.
23  *
24  * Copyright (C) 2013 Peter Hurley <peter@hurleysoftware.com>
25  *
26  * This file may be redistributed under the terms of the GNU General Public
27  * License v2.
28  */
29
30 #include <linux/list.h>
31 #include <linux/spinlock.h>
32 #include <linux/atomic.h>
33 #include <linux/tty.h>
34 #include <linux/sched.h>
35
36
37 #ifdef CONFIG_DEBUG_LOCK_ALLOC
38 # define __acq(l, s, t, r, c, n, i)             \
39                                 lock_acquire(&(l)->dep_map, s, t, r, c, n, i)
40 # define __rel(l, n, i)                         \
41                                 lock_release(&(l)->dep_map, n, i)
42 # ifdef CONFIG_PROVE_LOCKING
43 #  define lockdep_acquire(l, s, t, i)           __acq(l, s, t, 0, 2, NULL, i)
44 #  define lockdep_acquire_nest(l, s, t, n, i)   __acq(l, s, t, 0, 2, n, i)
45 #  define lockdep_acquire_read(l, s, t, i)      __acq(l, s, t, 1, 2, NULL, i)
46 #  define lockdep_release(l, n, i)              __rel(l, n, i)
47 # else
48 #  define lockdep_acquire(l, s, t, i)           __acq(l, s, t, 0, 1, NULL, i)
49 #  define lockdep_acquire_nest(l, s, t, n, i)   __acq(l, s, t, 0, 1, n, i)
50 #  define lockdep_acquire_read(l, s, t, i)      __acq(l, s, t, 1, 1, NULL, i)
51 #  define lockdep_release(l, n, i)              __rel(l, n, i)
52 # endif
53 #else
54 # define lockdep_acquire(l, s, t, i)            do { } while (0)
55 # define lockdep_acquire_nest(l, s, t, n, i)    do { } while (0)
56 # define lockdep_acquire_read(l, s, t, i)       do { } while (0)
57 # define lockdep_release(l, n, i)               do { } while (0)
58 #endif
59
60 #ifdef CONFIG_LOCK_STAT
61 # define lock_stat(_lock, stat)         lock_##stat(&(_lock)->dep_map, _RET_IP_)
62 #else
63 # define lock_stat(_lock, stat)         do { } while (0)
64 #endif
65
66
67 #if BITS_PER_LONG == 64
68 # define LDSEM_ACTIVE_MASK      0xffffffffL
69 #else
70 # define LDSEM_ACTIVE_MASK      0x0000ffffL
71 #endif
72
73 #define LDSEM_UNLOCKED          0L
74 #define LDSEM_ACTIVE_BIAS       1L
75 #define LDSEM_WAIT_BIAS         (-LDSEM_ACTIVE_MASK-1)
76 #define LDSEM_READ_BIAS         LDSEM_ACTIVE_BIAS
77 #define LDSEM_WRITE_BIAS        (LDSEM_WAIT_BIAS + LDSEM_ACTIVE_BIAS)
78
79 struct ldsem_waiter {
80         struct list_head list;
81         struct task_struct *task;
82 };
83
84 static inline long ldsem_atomic_update(long delta, struct ld_semaphore *sem)
85 {
86         return atomic_long_add_return(delta, (atomic_long_t *)&sem->count);
87 }
88
89 /*
90  * ldsem_cmpxchg() updates @*old with the last-known sem->count value.
91  * Returns 1 if count was successfully changed; @*old will have @new value.
92  * Returns 0 if count was not changed; @*old will have most recent sem->count
93  */
94 static inline int ldsem_cmpxchg(long *old, long new, struct ld_semaphore *sem)
95 {
96         long tmp = atomic_long_cmpxchg(&sem->count, *old, new);
97         if (tmp == *old) {
98                 *old = new;
99                 return 1;
100         } else {
101                 *old = tmp;
102                 return 0;
103         }
104 }
105
106 /*
107  * Initialize an ldsem:
108  */
109 void __init_ldsem(struct ld_semaphore *sem, const char *name,
110                   struct lock_class_key *key)
111 {
112 #ifdef CONFIG_DEBUG_LOCK_ALLOC
113         /*
114          * Make sure we are not reinitializing a held semaphore:
115          */
116         debug_check_no_locks_freed((void *)sem, sizeof(*sem));
117         lockdep_init_map(&sem->dep_map, name, key, 0);
118 #endif
119         sem->count = LDSEM_UNLOCKED;
120         sem->wait_readers = 0;
121         raw_spin_lock_init(&sem->wait_lock);
122         INIT_LIST_HEAD(&sem->read_wait);
123         INIT_LIST_HEAD(&sem->write_wait);
124 }
125
126 static void __ldsem_wake_readers(struct ld_semaphore *sem)
127 {
128         struct ldsem_waiter *waiter, *next;
129         struct task_struct *tsk;
130         long adjust, count;
131
132         /* Try to grant read locks to all readers on the read wait list.
133          * Note the 'active part' of the count is incremented by
134          * the number of readers before waking any processes up.
135          */
136         adjust = sem->wait_readers * (LDSEM_ACTIVE_BIAS - LDSEM_WAIT_BIAS);
137         count = ldsem_atomic_update(adjust, sem);
138         do {
139                 if (count > 0)
140                         break;
141                 if (ldsem_cmpxchg(&count, count - adjust, sem))
142                         return;
143         } while (1);
144
145         list_for_each_entry_safe(waiter, next, &sem->read_wait, list) {
146                 tsk = waiter->task;
147                 smp_mb();
148                 waiter->task = NULL;
149                 wake_up_process(tsk);
150                 put_task_struct(tsk);
151         }
152         INIT_LIST_HEAD(&sem->read_wait);
153         sem->wait_readers = 0;
154 }
155
156 static inline int writer_trylock(struct ld_semaphore *sem)
157 {
158         /* only wake this writer if the active part of the count can be
159          * transitioned from 0 -> 1
160          */
161         long count = ldsem_atomic_update(LDSEM_ACTIVE_BIAS, sem);
162         do {
163                 if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS)
164                         return 1;
165                 if (ldsem_cmpxchg(&count, count - LDSEM_ACTIVE_BIAS, sem))
166                         return 0;
167         } while (1);
168 }
169
170 static void __ldsem_wake_writer(struct ld_semaphore *sem)
171 {
172         struct ldsem_waiter *waiter;
173
174         waiter = list_entry(sem->write_wait.next, struct ldsem_waiter, list);
175         wake_up_process(waiter->task);
176 }
177
178 /*
179  * handle the lock release when processes blocked on it that can now run
180  * - if we come here from up_xxxx(), then:
181  *   - the 'active part' of count (&0x0000ffff) reached 0 (but may have changed)
182  *   - the 'waiting part' of count (&0xffff0000) is -ve (and will still be so)
183  * - the spinlock must be held by the caller
184  * - woken process blocks are discarded from the list after having task zeroed
185  */
186 static void __ldsem_wake(struct ld_semaphore *sem)
187 {
188         if (!list_empty(&sem->write_wait))
189                 __ldsem_wake_writer(sem);
190         else if (!list_empty(&sem->read_wait))
191                 __ldsem_wake_readers(sem);
192 }
193
194 static void ldsem_wake(struct ld_semaphore *sem)
195 {
196         unsigned long flags;
197
198         raw_spin_lock_irqsave(&sem->wait_lock, flags);
199         __ldsem_wake(sem);
200         raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
201 }
202
203 /*
204  * wait for the read lock to be granted
205  */
206 static struct ld_semaphore __sched *
207 down_read_failed(struct ld_semaphore *sem, long count, long timeout)
208 {
209         struct ldsem_waiter waiter;
210         struct task_struct *tsk = current;
211         long adjust = -LDSEM_ACTIVE_BIAS + LDSEM_WAIT_BIAS;
212
213         /* set up my own style of waitqueue */
214         raw_spin_lock_irq(&sem->wait_lock);
215
216         /* Try to reverse the lock attempt but if the count has changed
217          * so that reversing fails, check if there are are no waiters,
218          * and early-out if not */
219         do {
220                 if (ldsem_cmpxchg(&count, count + adjust, sem))
221                         break;
222                 if (count > 0) {
223                         raw_spin_unlock_irq(&sem->wait_lock);
224                         return sem;
225                 }
226         } while (1);
227
228         list_add_tail(&waiter.list, &sem->read_wait);
229         sem->wait_readers++;
230
231         waiter.task = tsk;
232         get_task_struct(tsk);
233
234         /* if there are no active locks, wake the new lock owner(s) */
235         if ((count & LDSEM_ACTIVE_MASK) == 0)
236                 __ldsem_wake(sem);
237
238         raw_spin_unlock_irq(&sem->wait_lock);
239
240         /* wait to be given the lock */
241         for (;;) {
242                 set_task_state(tsk, TASK_UNINTERRUPTIBLE);
243
244                 if (!waiter.task)
245                         break;
246                 if (!timeout)
247                         break;
248                 timeout = schedule_timeout(timeout);
249         }
250
251         __set_task_state(tsk, TASK_RUNNING);
252
253         if (!timeout) {
254                 /* lock timed out but check if this task was just
255                  * granted lock ownership - if so, pretend there
256                  * was no timeout; otherwise, cleanup lock wait */
257                 raw_spin_lock_irq(&sem->wait_lock);
258                 if (waiter.task) {
259                         ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem);
260                         list_del(&waiter.list);
261                         raw_spin_unlock_irq(&sem->wait_lock);
262                         put_task_struct(waiter.task);
263                         return NULL;
264                 }
265                 raw_spin_unlock_irq(&sem->wait_lock);
266         }
267
268         return sem;
269 }
270
271 /*
272  * wait for the write lock to be granted
273  */
274 static struct ld_semaphore __sched *
275 down_write_failed(struct ld_semaphore *sem, long count, long timeout)
276 {
277         struct ldsem_waiter waiter;
278         struct task_struct *tsk = current;
279         long adjust = -LDSEM_ACTIVE_BIAS;
280         int locked = 0;
281
282         /* set up my own style of waitqueue */
283         raw_spin_lock_irq(&sem->wait_lock);
284
285         /* Try to reverse the lock attempt but if the count has changed
286          * so that reversing fails, check if the lock is now owned,
287          * and early-out if so */
288         do {
289                 if (ldsem_cmpxchg(&count, count + adjust, sem))
290                         break;
291                 if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) {
292                         raw_spin_unlock_irq(&sem->wait_lock);
293                         return sem;
294                 }
295         } while (1);
296
297         list_add_tail(&waiter.list, &sem->write_wait);
298
299         waiter.task = tsk;
300
301         set_task_state(tsk, TASK_UNINTERRUPTIBLE);
302         for (;;) {
303                 if (!timeout)
304                         break;
305                 raw_spin_unlock_irq(&sem->wait_lock);
306                 timeout = schedule_timeout(timeout);
307                 raw_spin_lock_irq(&sem->wait_lock);
308                 set_task_state(tsk, TASK_UNINTERRUPTIBLE);
309                 if ((locked = writer_trylock(sem)))
310                         break;
311         }
312
313         if (!locked)
314                 ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem);
315         list_del(&waiter.list);
316         raw_spin_unlock_irq(&sem->wait_lock);
317
318         __set_task_state(tsk, TASK_RUNNING);
319
320         /* lock wait may have timed out */
321         if (!locked)
322                 return NULL;
323         return sem;
324 }
325
326
327
328 static inline int __ldsem_down_read_nested(struct ld_semaphore *sem,
329                                            int subclass, long timeout)
330 {
331         long count;
332
333         lockdep_acquire_read(sem, subclass, 0, _RET_IP_);
334
335         count = ldsem_atomic_update(LDSEM_READ_BIAS, sem);
336         if (count <= 0) {
337                 lock_stat(sem, contended);
338                 if (!down_read_failed(sem, count, timeout)) {
339                         lockdep_release(sem, 1, _RET_IP_);
340                         return 0;
341                 }
342         }
343         lock_stat(sem, acquired);
344         return 1;
345 }
346
347 static inline int __ldsem_down_write_nested(struct ld_semaphore *sem,
348                                             int subclass, long timeout)
349 {
350         long count;
351
352         lockdep_acquire(sem, subclass, 0, _RET_IP_);
353
354         count = ldsem_atomic_update(LDSEM_WRITE_BIAS, sem);
355         if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) {
356                 lock_stat(sem, contended);
357                 if (!down_write_failed(sem, count, timeout)) {
358                         lockdep_release(sem, 1, _RET_IP_);
359                         return 0;
360                 }
361         }
362         lock_stat(sem, acquired);
363         return 1;
364 }
365
366
367 /*
368  * lock for reading -- returns 1 if successful, 0 if timed out
369  */
370 int __sched ldsem_down_read(struct ld_semaphore *sem, long timeout)
371 {
372         might_sleep();
373         return __ldsem_down_read_nested(sem, 0, timeout);
374 }
375
376 /*
377  * trylock for reading -- returns 1 if successful, 0 if contention
378  */
379 int ldsem_down_read_trylock(struct ld_semaphore *sem)
380 {
381         long count = sem->count;
382
383         while (count >= 0) {
384                 if (ldsem_cmpxchg(&count, count + LDSEM_READ_BIAS, sem)) {
385                         lockdep_acquire_read(sem, 0, 1, _RET_IP_);
386                         lock_stat(sem, acquired);
387                         return 1;
388                 }
389         }
390         return 0;
391 }
392
393 /*
394  * lock for writing -- returns 1 if successful, 0 if timed out
395  */
396 int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout)
397 {
398         might_sleep();
399         return __ldsem_down_write_nested(sem, 0, timeout);
400 }
401
402 /*
403  * trylock for writing -- returns 1 if successful, 0 if contention
404  */
405 int ldsem_down_write_trylock(struct ld_semaphore *sem)
406 {
407         long count = sem->count;
408
409         while ((count & LDSEM_ACTIVE_MASK) == 0) {
410                 if (ldsem_cmpxchg(&count, count + LDSEM_WRITE_BIAS, sem)) {
411                         lockdep_acquire(sem, 0, 1, _RET_IP_);
412                         lock_stat(sem, acquired);
413                         return 1;
414                 }
415         }
416         return 0;
417 }
418
419 /*
420  * release a read lock
421  */
422 void ldsem_up_read(struct ld_semaphore *sem)
423 {
424         long count;
425
426         lockdep_release(sem, 1, _RET_IP_);
427
428         count = ldsem_atomic_update(-LDSEM_READ_BIAS, sem);
429         if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0)
430                 ldsem_wake(sem);
431 }
432
433 /*
434  * release a write lock
435  */
436 void ldsem_up_write(struct ld_semaphore *sem)
437 {
438         long count;
439
440         lockdep_release(sem, 1, _RET_IP_);
441
442         count = ldsem_atomic_update(-LDSEM_WRITE_BIAS, sem);
443         if (count < 0)
444                 ldsem_wake(sem);
445 }
446
447
448 #ifdef CONFIG_DEBUG_LOCK_ALLOC
449
450 int ldsem_down_read_nested(struct ld_semaphore *sem, int subclass, long timeout)
451 {
452         might_sleep();
453         return __ldsem_down_read_nested(sem, subclass, timeout);
454 }
455
456 int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass,
457                             long timeout)
458 {
459         might_sleep();
460         return __ldsem_down_write_nested(sem, subclass, timeout);
461 }
462
463 #endif