ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / sparc64 / kernel / semaphore.c
1 /* $Id: semaphore.c,v 1.9 2001/11/18 00:12:56 davem Exp $
2  * semaphore.c: Sparc64 semaphore implementation.
3  *
4  * This is basically the PPC semaphore scheme ported to use
5  * the sparc64 atomic instructions, so see the PPC code for
6  * credits.
7  */
8
9 #include <linux/sched.h>
10 #include <linux/errno.h>
11 #include <linux/init.h>
12
13 /*
14  * Atomically update sem->count.
15  * This does the equivalent of the following:
16  *
17  *      old_count = sem->count;
18  *      tmp = MAX(old_count, 0) + incr;
19  *      sem->count = tmp;
20  *      return old_count;
21  */
22 static __inline__ int __sem_update_count(struct semaphore *sem, int incr)
23 {
24         int old_count, tmp;
25
26         __asm__ __volatile__("\n"
27 "       ! __sem_update_count old_count(%0) tmp(%1) incr(%4) &sem->count(%3)\n"
28 "1:     ldsw    [%3], %0\n"
29 "       mov     %0, %1\n"
30 "       cmp     %0, 0\n"
31 "       movl    %%icc, 0, %1\n"
32 "       add     %1, %4, %1\n"
33 "       cas     [%3], %0, %1\n"
34 "       cmp     %0, %1\n"
35 "       bne,pn  %%icc, 1b\n"
36 "        membar #StoreLoad | #StoreStore\n"
37         : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count)
38         : "r" (&sem->count), "r" (incr), "m" (sem->count)
39         : "cc");
40
41         return old_count;
42 }
43
44 static void __up(struct semaphore *sem)
45 {
46         __sem_update_count(sem, 1);
47         wake_up(&sem->wait);
48 }
49
50 void up(struct semaphore *sem)
51 {
52         /* This atomically does:
53          *      old_val = sem->count;
54          *      new_val = sem->count + 1;
55          *      sem->count = new_val;
56          *      if (old_val < 0)
57          *              __up(sem);
58          *
59          * The (old_val < 0) test is equivalent to
60          * the more straightforward (new_val <= 0),
61          * but it is easier to test the former because
62          * of how the CAS instruction works.
63          */
64
65         __asm__ __volatile__("\n"
66 "       ! up sem(%0)\n"
67 "       membar  #StoreLoad | #LoadLoad\n"
68 "1:     lduw    [%0], %%g5\n"
69 "       add     %%g5, 1, %%g7\n"
70 "       cas     [%0], %%g5, %%g7\n"
71 "       cmp     %%g5, %%g7\n"
72 "       bne,pn  %%icc, 1b\n"
73 "        addcc  %%g7, 1, %%g0\n"
74 "       ble,pn  %%icc, 3f\n"
75 "        membar #StoreLoad | #StoreStore\n"
76 "2:\n"
77 "       .subsection 2\n"
78 "3:     mov     %0, %%g5\n"
79 "       save    %%sp, -160, %%sp\n"
80 "       mov     %%g1, %%l1\n"
81 "       mov     %%g2, %%l2\n"
82 "       mov     %%g3, %%l3\n"
83 "       call    %1\n"
84 "        mov    %%g5, %%o0\n"
85 "       mov     %%l1, %%g1\n"
86 "       mov     %%l2, %%g2\n"
87 "       ba,pt   %%xcc, 2b\n"
88 "        restore %%l3, %%g0, %%g3\n"
89 "       .previous\n"
90         : : "r" (sem), "i" (__up)
91         : "g5", "g7", "memory", "cc");
92 }
93
94 static void __sched __down(struct semaphore * sem)
95 {
96         struct task_struct *tsk = current;
97         DECLARE_WAITQUEUE(wait, tsk);
98
99         tsk->state = TASK_UNINTERRUPTIBLE;
100         add_wait_queue_exclusive(&sem->wait, &wait);
101
102         while (__sem_update_count(sem, -1) <= 0) {
103                 schedule();
104                 tsk->state = TASK_UNINTERRUPTIBLE;
105         }
106         remove_wait_queue(&sem->wait, &wait);
107         tsk->state = TASK_RUNNING;
108
109         wake_up(&sem->wait);
110 }
111
112 void __sched down(struct semaphore *sem)
113 {
114         might_sleep();
115         /* This atomically does:
116          *      old_val = sem->count;
117          *      new_val = sem->count - 1;
118          *      sem->count = new_val;
119          *      if (old_val < 1)
120          *              __down(sem);
121          *
122          * The (old_val < 1) test is equivalent to
123          * the more straightforward (new_val < 0),
124          * but it is easier to test the former because
125          * of how the CAS instruction works.
126          */
127
128         __asm__ __volatile__("\n"
129 "       ! down sem(%0)\n"
130 "1:     lduw    [%0], %%g5\n"
131 "       sub     %%g5, 1, %%g7\n"
132 "       cas     [%0], %%g5, %%g7\n"
133 "       cmp     %%g5, %%g7\n"
134 "       bne,pn  %%icc, 1b\n"
135 "        cmp    %%g7, 1\n"
136 "       bl,pn   %%icc, 3f\n"
137 "        membar #StoreLoad | #StoreStore\n"
138 "2:\n"
139 "       .subsection 2\n"
140 "3:     mov     %0, %%g5\n"
141 "       save    %%sp, -160, %%sp\n"
142 "       mov     %%g1, %%l1\n"
143 "       mov     %%g2, %%l2\n"
144 "       mov     %%g3, %%l3\n"
145 "       call    %1\n"
146 "        mov    %%g5, %%o0\n"
147 "       mov     %%l1, %%g1\n"
148 "       mov     %%l2, %%g2\n"
149 "       ba,pt   %%xcc, 2b\n"
150 "        restore %%l3, %%g0, %%g3\n"
151 "       .previous\n"
152         : : "r" (sem), "i" (__down)
153         : "g5", "g7", "memory", "cc");
154 }
155
156 int down_trylock(struct semaphore *sem)
157 {
158         int ret;
159
160         /* This atomically does:
161          *      old_val = sem->count;
162          *      new_val = sem->count - 1;
163          *      if (old_val < 1) {
164          *              ret = 1;
165          *      } else {
166          *              sem->count = new_val;
167          *              ret = 0;
168          *      }
169          *
170          * The (old_val < 1) test is equivalent to
171          * the more straightforward (new_val < 0),
172          * but it is easier to test the former because
173          * of how the CAS instruction works.
174          */
175
176         __asm__ __volatile__("\n"
177 "       ! down_trylock sem(%1) ret(%0)\n"
178 "1:     lduw    [%1], %%g5\n"
179 "       sub     %%g5, 1, %%g7\n"
180 "       cmp     %%g5, 1\n"
181 "       bl,pn   %%icc, 2f\n"
182 "        mov    1, %0\n"
183 "       cas     [%1], %%g5, %%g7\n"
184 "       cmp     %%g5, %%g7\n"
185 "       bne,pn  %%icc, 1b\n"
186 "        mov    0, %0\n"
187 "       membar  #StoreLoad | #StoreStore\n"
188 "2:\n"
189         : "=&r" (ret)
190         : "r" (sem)
191         : "g5", "g7", "memory", "cc");
192
193         return ret;
194 }
195
196 static int __sched __down_interruptible(struct semaphore * sem)
197 {
198         int retval = 0;
199         struct task_struct *tsk = current;
200         DECLARE_WAITQUEUE(wait, tsk);
201
202         tsk->state = TASK_INTERRUPTIBLE;
203         add_wait_queue_exclusive(&sem->wait, &wait);
204
205         while (__sem_update_count(sem, -1) <= 0) {
206                 if (signal_pending(current)) {
207                         __sem_update_count(sem, 0);
208                         retval = -EINTR;
209                         break;
210                 }
211                 schedule();
212                 tsk->state = TASK_INTERRUPTIBLE;
213         }
214         tsk->state = TASK_RUNNING;
215         remove_wait_queue(&sem->wait, &wait);
216         wake_up(&sem->wait);
217         return retval;
218 }
219
220 int __sched down_interruptible(struct semaphore *sem)
221 {
222         int ret = 0;
223         
224         might_sleep();
225         /* This atomically does:
226          *      old_val = sem->count;
227          *      new_val = sem->count - 1;
228          *      sem->count = new_val;
229          *      if (old_val < 1)
230          *              ret = __down_interruptible(sem);
231          *
232          * The (old_val < 1) test is equivalent to
233          * the more straightforward (new_val < 0),
234          * but it is easier to test the former because
235          * of how the CAS instruction works.
236          */
237
238         __asm__ __volatile__("\n"
239 "       ! down_interruptible sem(%2) ret(%0)\n"
240 "1:     lduw    [%2], %%g5\n"
241 "       sub     %%g5, 1, %%g7\n"
242 "       cas     [%2], %%g5, %%g7\n"
243 "       cmp     %%g5, %%g7\n"
244 "       bne,pn  %%icc, 1b\n"
245 "        cmp    %%g7, 1\n"
246 "       bl,pn   %%icc, 3f\n"
247 "        membar #StoreLoad | #StoreStore\n"
248 "2:\n"
249 "       .subsection 2\n"
250 "3:     mov     %2, %%g5\n"
251 "       save    %%sp, -160, %%sp\n"
252 "       mov     %%g1, %%l1\n"
253 "       mov     %%g2, %%l2\n"
254 "       mov     %%g3, %%l3\n"
255 "       call    %3\n"
256 "        mov    %%g5, %%o0\n"
257 "       mov     %%l1, %%g1\n"
258 "       mov     %%l2, %%g2\n"
259 "       mov     %%l3, %%g3\n"
260 "       ba,pt   %%xcc, 2b\n"
261 "        restore %%o0, %%g0, %0\n"
262 "       .previous\n"
263         : "=r" (ret)
264         : "0" (ret), "r" (sem), "i" (__down_interruptible)
265         : "g5", "g7", "memory", "cc");
266         return ret;
267 }