1 /*
2 * Semaphore implementation Copyright (c) 2001 Matthew Wilcox, Hewlett-Packard
3 */
4
5 #include <linux/sched.h>
6 #include <linux/spinlock.h>
7
8 /*
9 * Semaphores are complex as we wish to avoid using two variables.
10 * `count' has multiple roles, depending on its value. If it is positive
11 * or zero, there are no waiters. The functions here will never be
12 * called; see <asm/semaphore.h>
13 *
14 * When count is -1 it indicates there is at least one task waiting
15 * for the semaphore.
16 *
17 * When count is less than that, there are '- count - 1' wakeups
18 * pending. ie if it has value -3, there are 2 wakeups pending.
19 *
20 * Note that these functions are only called when there is contention
21 * on the lock, and as such all this is the "non-critical" part of the
22 * whole semaphore business. The critical part is the inline stuff in
23 * <asm/semaphore.h> where we want to avoid any extra jumps and calls.
24 */
__up(struct semaphore * sem)25 void __up(struct semaphore *sem)
26 {
27 sem->count--;
28 wake_up(&sem->wait);
29 }
30
31 #define wakers(count) (-1 - count)
32
33 #define DOWN_HEAD \
34 int ret = 0; \
35 DECLARE_WAITQUEUE(wait, current); \
36 \
37 /* Note that someone is waiting */ \
38 if (sem->count == 0) \
39 sem->count = -1; \
40 \
41 /* protected by the sentry still -- use unlocked version */ \
42 wait.flags = WQ_FLAG_EXCLUSIVE; \
43 __add_wait_queue_tail(&sem->wait, &wait); \
44 lost_race: \
45 spin_unlock_irq(&sem->sentry); \
46
47 #define DOWN_TAIL \
48 spin_lock_irq(&sem->sentry); \
49 if (wakers(sem->count) == 0 && ret == 0) \
50 goto lost_race; /* Someone stole our wakeup */ \
51 __remove_wait_queue(&sem->wait, &wait); \
52 current->state = TASK_RUNNING; \
53 if (!waitqueue_active(&sem->wait) && (sem->count < 0)) \
54 sem->count = wakers(sem->count);
55
56 #define UPDATE_COUNT \
57 sem->count += (sem->count < 0) ? 1 : - 1;
58
59
__down(struct semaphore * sem)60 void __down(struct semaphore * sem)
61 {
62 DOWN_HEAD
63
64 for(;;) {
65 set_task_state(current, TASK_UNINTERRUPTIBLE);
66 /* we can _read_ this without the sentry */
67 if (sem->count != -1)
68 break;
69 schedule();
70 }
71
72 DOWN_TAIL
73 UPDATE_COUNT
74 }
75
__down_interruptible(struct semaphore * sem)76 int __down_interruptible(struct semaphore * sem)
77 {
78 DOWN_HEAD
79
80 for(;;) {
81 set_task_state(current, TASK_INTERRUPTIBLE);
82 /* we can _read_ this without the sentry */
83 if (sem->count != -1)
84 break;
85
86 if (signal_pending(current)) {
87 ret = -EINTR;
88 break;
89 }
90 schedule();
91 }
92
93 DOWN_TAIL
94
95 if (!ret) {
96 UPDATE_COUNT
97 }
98
99 return ret;
100 }
101