Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6] / 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 "       membar  #StoreLoad | #StoreStore\n"
36 "       bne,pn  %%icc, 1b\n"
37 "        nop\n"
38         : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count)
39         : "r" (&sem->count), "r" (incr), "m" (sem->count)
40         : "cc");
41
42         return old_count;
43 }
44
45 static void __up(struct semaphore *sem)
46 {
47         __sem_update_count(sem, 1);
48         wake_up(&sem->wait);
49 }
50
51 void up(struct semaphore *sem)
52 {
53         /* This atomically does:
54          *      old_val = sem->count;
55          *      new_val = sem->count + 1;
56          *      sem->count = new_val;
57          *      if (old_val < 0)
58          *              __up(sem);
59          *
60          * The (old_val < 0) test is equivalent to
61          * the more straightforward (new_val <= 0),
62          * but it is easier to test the former because
63          * of how the CAS instruction works.
64          */
65
66         __asm__ __volatile__("\n"
67 "       ! up sem(%0)\n"
68 "       membar  #StoreLoad | #LoadLoad\n"
69 "1:     lduw    [%0], %%g1\n"
70 "       add     %%g1, 1, %%g7\n"
71 "       cas     [%0], %%g1, %%g7\n"
72 "       cmp     %%g1, %%g7\n"
73 "       bne,pn  %%icc, 1b\n"
74 "        addcc  %%g7, 1, %%g0\n"
75 "       membar  #StoreLoad | #StoreStore\n"
76 "       ble,pn  %%icc, 3f\n"
77 "        nop\n"
78 "2:\n"
79 "       .subsection 2\n"
80 "3:     mov     %0, %%g1\n"
81 "       save    %%sp, -160, %%sp\n"
82 "       call    %1\n"
83 "        mov    %%g1, %%o0\n"
84 "       ba,pt   %%xcc, 2b\n"
85 "        restore\n"
86 "       .previous\n"
87         : : "r" (sem), "i" (__up)
88         : "g1", "g2", "g3", "g7", "memory", "cc");
89 }
90
91 static void __sched __down(struct semaphore * sem)
92 {
93         struct task_struct *tsk = current;
94         DECLARE_WAITQUEUE(wait, tsk);
95
96         tsk->state = TASK_UNINTERRUPTIBLE;
97         add_wait_queue_exclusive(&sem->wait, &wait);
98
99         while (__sem_update_count(sem, -1) <= 0) {
100                 schedule();
101                 tsk->state = TASK_UNINTERRUPTIBLE;
102         }
103         remove_wait_queue(&sem->wait, &wait);
104         tsk->state = TASK_RUNNING;
105
106         wake_up(&sem->wait);
107 }
108
109 void __sched down(struct semaphore *sem)
110 {
111         might_sleep();
112         /* This atomically does:
113          *      old_val = sem->count;
114          *      new_val = sem->count - 1;
115          *      sem->count = new_val;
116          *      if (old_val < 1)
117          *              __down(sem);
118          *
119          * The (old_val < 1) test is equivalent to
120          * the more straightforward (new_val < 0),
121          * but it is easier to test the former because
122          * of how the CAS instruction works.
123          */
124
125         __asm__ __volatile__("\n"
126 "       ! down sem(%0)\n"
127 "1:     lduw    [%0], %%g1\n"
128 "       sub     %%g1, 1, %%g7\n"
129 "       cas     [%0], %%g1, %%g7\n"
130 "       cmp     %%g1, %%g7\n"
131 "       bne,pn  %%icc, 1b\n"
132 "        cmp    %%g7, 1\n"
133 "       membar  #StoreLoad | #StoreStore\n"
134 "       bl,pn   %%icc, 3f\n"
135 "        nop\n"
136 "2:\n"
137 "       .subsection 2\n"
138 "3:     mov     %0, %%g1\n"
139 "       save    %%sp, -160, %%sp\n"
140 "       call    %1\n"
141 "        mov    %%g1, %%o0\n"
142 "       ba,pt   %%xcc, 2b\n"
143 "        restore\n"
144 "       .previous\n"
145         : : "r" (sem), "i" (__down)
146         : "g1", "g2", "g3", "g7", "memory", "cc");
147 }
148
149 int down_trylock(struct semaphore *sem)
150 {
151         int ret;
152
153         /* This atomically does:
154          *      old_val = sem->count;
155          *      new_val = sem->count - 1;
156          *      if (old_val < 1) {
157          *              ret = 1;
158          *      } else {
159          *              sem->count = new_val;
160          *              ret = 0;
161          *      }
162          *
163          * The (old_val < 1) test is equivalent to
164          * the more straightforward (new_val < 0),
165          * but it is easier to test the former because
166          * of how the CAS instruction works.
167          */
168
169         __asm__ __volatile__("\n"
170 "       ! down_trylock sem(%1) ret(%0)\n"
171 "1:     lduw    [%1], %%g1\n"
172 "       sub     %%g1, 1, %%g7\n"
173 "       cmp     %%g1, 1\n"
174 "       bl,pn   %%icc, 2f\n"
175 "        mov    1, %0\n"
176 "       cas     [%1], %%g1, %%g7\n"
177 "       cmp     %%g1, %%g7\n"
178 "       bne,pn  %%icc, 1b\n"
179 "        mov    0, %0\n"
180 "       membar  #StoreLoad | #StoreStore\n"
181 "2:\n"
182         : "=&r" (ret)
183         : "r" (sem)
184         : "g1", "g7", "memory", "cc");
185
186         return ret;
187 }
188
189 static int __sched __down_interruptible(struct semaphore * sem)
190 {
191         int retval = 0;
192         struct task_struct *tsk = current;
193         DECLARE_WAITQUEUE(wait, tsk);
194
195         tsk->state = TASK_INTERRUPTIBLE;
196         add_wait_queue_exclusive(&sem->wait, &wait);
197
198         while (__sem_update_count(sem, -1) <= 0) {
199                 if (signal_pending(current)) {
200                         __sem_update_count(sem, 0);
201                         retval = -EINTR;
202                         break;
203                 }
204                 schedule();
205                 tsk->state = TASK_INTERRUPTIBLE;
206         }
207         tsk->state = TASK_RUNNING;
208         remove_wait_queue(&sem->wait, &wait);
209         wake_up(&sem->wait);
210         return retval;
211 }
212
213 int __sched down_interruptible(struct semaphore *sem)
214 {
215         int ret = 0;
216         
217         might_sleep();
218         /* This atomically does:
219          *      old_val = sem->count;
220          *      new_val = sem->count - 1;
221          *      sem->count = new_val;
222          *      if (old_val < 1)
223          *              ret = __down_interruptible(sem);
224          *
225          * The (old_val < 1) test is equivalent to
226          * the more straightforward (new_val < 0),
227          * but it is easier to test the former because
228          * of how the CAS instruction works.
229          */
230
231         __asm__ __volatile__("\n"
232 "       ! down_interruptible sem(%2) ret(%0)\n"
233 "1:     lduw    [%2], %%g1\n"
234 "       sub     %%g1, 1, %%g7\n"
235 "       cas     [%2], %%g1, %%g7\n"
236 "       cmp     %%g1, %%g7\n"
237 "       bne,pn  %%icc, 1b\n"
238 "        cmp    %%g7, 1\n"
239 "       membar  #StoreLoad | #StoreStore\n"
240 "       bl,pn   %%icc, 3f\n"
241 "        nop\n"
242 "2:\n"
243 "       .subsection 2\n"
244 "3:     mov     %2, %%g1\n"
245 "       save    %%sp, -160, %%sp\n"
246 "       call    %3\n"
247 "        mov    %%g1, %%o0\n"
248 "       ba,pt   %%xcc, 2b\n"
249 "        restore\n"
250 "       .previous\n"
251         : "=r" (ret)
252         : "0" (ret), "r" (sem), "i" (__down_interruptible)
253         : "g1", "g2", "g3", "g7", "memory", "cc");
254         return ret;
255 }