ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / mips / kernel / semaphore.c
1 /*
2  * Copyright (C) 1999, 2001, 02, 03 Ralf Baechle
3  *
4  * Heavily inspired by the Alpha implementation
5  */
6 #include <linux/config.h>
7 #include <linux/errno.h>
8 #include <linux/module.h>
9 #include <linux/init.h>
10 #include <linux/sched.h>
11
12 #ifdef CONFIG_CPU_HAS_LLDSCD
13 /*
14  * On machines without lld/scd we need a spinlock to make the manipulation of
15  * sem->count and sem->waking atomic.  Scalability isn't an issue because
16  * this lock is used on UP only so it's just an empty variable.
17  */
18 spinlock_t semaphore_lock = SPIN_LOCK_UNLOCKED;
19
20 EXPORT_SYMBOL(semaphore_lock);
21 #endif
22
23 /*
24  * Semaphores are implemented using a two-way counter: The "count" variable is
25  * decremented for each process that tries to sleep, while the "waking" variable
26  * is incremented when the "up()" code goes to wake up waiting processes.
27  *
28  * Notably, the inline "up()" and "down()" functions can efficiently test if
29  * they need to do any extra work (up needs to do something only if count was
30  * negative before the increment operation.
31  *
32  * waking_non_zero() must execute atomically.
33  *
34  * When __up() is called, the count was negative before incrementing it, and we
35  * need to wake up somebody.
36  *
37  * This routine adds one to the count of processes that need to wake up and
38  * exit.  ALL waiting processes actually wake up but only the one that gets to
39  * the "waking" field first will gate through and acquire the semaphore.  The
40  * others will go back to sleep.
41  *
42  * Note that these functions are only called when there is contention on the
43  * lock, and as such all this is the "non-critical" part of the whole semaphore
44  * business. The critical part is the inline stuff in <asm/semaphore.h> where
45  * we want to avoid any extra jumps and calls.
46  */
47 void __up_wakeup(struct semaphore *sem)
48 {
49         wake_up(&sem->wait);
50 }
51
52 EXPORT_SYMBOL(__up_wakeup);
53
54 #ifdef CONFIG_CPU_HAS_LLSC
55
56 static inline int waking_non_zero(struct semaphore *sem)
57 {
58         int ret, tmp;
59
60         __asm__ __volatile__(
61         "1:     ll      %1, %2                  # waking_non_zero       \n"
62         "       blez    %1, 2f                                          \n"
63         "       subu    %0, %1, 1                                       \n"
64         "       sc      %0, %2                                          \n"
65         "       beqz    %0, 1b                                          \n"
66         "2:                                                             \n"
67         : "=r" (ret), "=r" (tmp), "+m" (sem->waking)
68         : "0" (0));
69
70         return ret;
71 }
72
73 #else /* !CONFIG_CPU_HAS_LLSC */
74
75 static inline int waking_non_zero(struct semaphore *sem)
76 {
77         unsigned long flags;
78         int waking, ret = 0;
79
80         spin_lock_irqsave(&semaphore_lock, flags);
81         waking = atomic_read(&sem->waking);
82         if (waking > 0) {
83                 atomic_set(&sem->waking, waking - 1);
84                 ret = 1;
85         }
86         spin_unlock_irqrestore(&semaphore_lock, flags);
87
88         return ret;
89 }
90
91 #endif /* !CONFIG_CPU_HAS_LLSC */
92
93 /*
94  * Perform the "down" function.  Return zero for semaphore acquired, return
95  * negative for signalled out of the function.
96  *
97  * If called from down, the return is ignored and the wait loop is not
98  * interruptible.  This means that a task waiting on a semaphore using "down()"
99  * cannot be killed until someone does an "up()" on the semaphore.
100  *
101  * If called from down_interruptible, the return value gets checked upon return.
102  * If the return value is negative then the task continues with the negative
103  * value in the return register (it can be tested by the caller).
104  *
105  * Either form may be used in conjunction with "up()".
106  */
107
108 void __sched __down_failed(struct semaphore * sem)
109 {
110         struct task_struct *tsk = current;
111         wait_queue_t wait;
112
113         init_waitqueue_entry(&wait, tsk);
114         __set_current_state(TASK_UNINTERRUPTIBLE);
115         add_wait_queue_exclusive(&sem->wait, &wait);
116
117         /*
118          * Ok, we're set up.  sem->count is known to be less than zero
119          * so we must wait.
120          *
121          * We can let go the lock for purposes of waiting.
122          * We re-acquire it after awaking so as to protect
123          * all semaphore operations.
124          *
125          * If "up()" is called before we call waking_non_zero() then
126          * we will catch it right away.  If it is called later then
127          * we will have to go through a wakeup cycle to catch it.
128          *
129          * Multiple waiters contend for the semaphore lock to see
130          * who gets to gate through and who has to wait some more.
131          */
132         for (;;) {
133                 if (waking_non_zero(sem))
134                         break;
135                 schedule();
136                 __set_current_state(TASK_UNINTERRUPTIBLE);
137         }
138         __set_current_state(TASK_RUNNING);
139         remove_wait_queue(&sem->wait, &wait);
140 }
141
142 EXPORT_SYMBOL(__down_failed);
143
144 #ifdef CONFIG_CPU_HAS_LLDSCD
145
146 /*
147  * waking_non_zero_interruptible:
148  *      1       got the lock
149  *      0       go to sleep
150  *      -EINTR  interrupted
151  *
152  * We must undo the sem->count down_interruptible decrement
153  * simultaneously and atomically with the sem->waking adjustment,
154  * otherwise we can race with wake_one_more.
155  *
156  * This is accomplished by doing a 64-bit lld/scd on the 2 32-bit words.
157  *
158  * This is crazy.  Normally it's strictly forbidden to use 64-bit operations
159  * in the 32-bit MIPS kernel.  In this case it's however ok because if an
160  * interrupt has destroyed the upper half of registers sc will fail.
161  * Note also that this will not work for MIPS32 CPUs!
162  *
163  * Pseudocode:
164  *
165  * If(sem->waking > 0) {
166  *      Decrement(sem->waking)
167  *      Return(SUCCESS)
168  * } else If(signal_pending(tsk)) {
169  *      Increment(sem->count)
170  *      Return(-EINTR)
171  * } else {
172  *      Return(SLEEP)
173  * }
174  */
175
176 static inline int
177 waking_non_zero_interruptible(struct semaphore *sem, struct task_struct *tsk)
178 {
179         long ret, tmp;
180
181         __asm__ __volatile__(
182         "       .set    push            # waking_non_zero_interruptible \n"
183         "       .set    mips3                                           \n"
184         "       .set    noat                                            \n"
185         "0:     lld     %1, %2                                          \n"
186         "       li      %0, 0                                           \n"
187         "       sll     $1, %1, 0                                       \n"
188         "       blez    $1, 1f                                          \n"
189         "       daddiu  %1, %1, -1                                      \n"
190         "       li      %0, 1                                           \n"
191         "       b       2f                                              \n"
192         "1:     beqz    %3, 2f                                          \n"
193         "       li      %0, %4                                          \n"
194         "       dli     $1, 0x0000000100000000                          \n"
195         "       daddu   %1, %1, $1                                      \n"
196         "2:     scd     %1, %2                                          \n"
197         "       beqz    %1, 0b                                          \n"
198         "       .set    pop                                             \n"
199         : "=&r" (ret), "=&r" (tmp), "=m" (*sem)
200         : "r" (signal_pending(tsk)), "i" (-EINTR));
201
202         return ret;
203 }
204
205 #else /* !CONFIG_CPU_HAS_LLDSCD */
206
207 static inline int waking_non_zero_interruptible(struct semaphore *sem,
208                                                 struct task_struct *tsk)
209 {
210         int waking, pending, ret = 0;
211         unsigned long flags;
212
213         pending = signal_pending(tsk);
214
215         spin_lock_irqsave(&semaphore_lock, flags);
216         waking = atomic_read(&sem->waking);
217         if (waking > 0) {
218                 atomic_set(&sem->waking, waking - 1);
219                 ret = 1;
220         } else if (pending) {
221                 atomic_set(&sem->count, atomic_read(&sem->count) + 1);
222                 ret = -EINTR;
223         }
224         spin_unlock_irqrestore(&semaphore_lock, flags);
225
226         return ret;
227 }
228
229 #endif /* !CONFIG_CPU_HAS_LLDSCD */
230
231 int __sched __down_failed_interruptible(struct semaphore * sem)
232 {
233         struct task_struct *tsk = current;
234         wait_queue_t wait;
235         int ret = 0;
236
237         init_waitqueue_entry(&wait, tsk);
238         __set_current_state(TASK_INTERRUPTIBLE);
239         add_wait_queue_exclusive(&sem->wait, &wait);
240
241         /*
242          * Ok, we're set up.  sem->count is known to be less than zero
243          * so we must wait.
244          *
245          * We can let go the lock for purposes of waiting.
246          * We re-acquire it after awaking so as to protect
247          * all semaphore operations.
248          *
249          * If "up()" is called before we call waking_non_zero() then
250          * we will catch it right away.  If it is called later then
251          * we will have to go through a wakeup cycle to catch it.
252          *
253          * Multiple waiters contend for the semaphore lock to see
254          * who gets to gate through and who has to wait some more.
255          */
256         for (;;) {
257                 ret = waking_non_zero_interruptible(sem, tsk);
258                 if (ret) {
259                         if (ret == 1)
260                                 /* ret != 0 only if we get interrupted -arca */
261                                 ret = 0;
262                         break;
263                 }
264                 schedule();
265                 __set_current_state(TASK_INTERRUPTIBLE);
266         }
267         __set_current_state(TASK_RUNNING);
268         remove_wait_queue(&sem->wait, &wait);
269
270         return ret;
271 }
272
273 EXPORT_SYMBOL(__down_failed_interruptible);