import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / sparc64 / lib / debuglocks.c
1 /* $Id: debuglocks.c,v 1.9 2001/11/17 00:10:48 davem Exp $
2  * debuglocks.c: Debugging versions of SMP locking primitives.
3  *
4  * Copyright (C) 1998 David S. Miller (davem@redhat.com)
5  */
6
7 #include <linux/config.h>
8 #include <linux/kernel.h>
9 #include <linux/sched.h>
10 #include <linux/spinlock.h>
11 #include <asm/system.h>
12
13 #if defined(CONFIG_SMP) && defined(CONFIG_DEBUG_SPINLOCK)
14
15 #define GET_CALLER(PC) __asm__ __volatile__("mov %%i7, %0" : "=r" (PC))
16
17 static inline void show (char *str, spinlock_t *lock, unsigned long caller)
18 {
19         int cpu = smp_processor_id();
20
21         printk("%s(%p) CPU#%d stuck at %08x, owner PC(%08x):CPU(%x)\n",
22                str, lock, cpu, (unsigned int) caller,
23                lock->owner_pc, lock->owner_cpu);
24 }
25
26 static inline void show_read (char *str, rwlock_t *lock, unsigned long caller)
27 {
28         int cpu = smp_processor_id();
29
30         printk("%s(%p) CPU#%d stuck at %08x, writer PC(%08x):CPU(%x)\n",
31                str, lock, cpu, (unsigned int) caller,
32                lock->writer_pc, lock->writer_cpu);
33 }
34
35 static inline void show_write (char *str, rwlock_t *lock, unsigned long caller)
36 {
37         int cpu = smp_processor_id();
38
39         printk("%s(%p) CPU#%d stuck at %08x\n",
40                str, lock, cpu, (unsigned int) caller);
41         printk("Writer: PC(%08x):CPU(%x)\n",
42                lock->writer_pc, lock->writer_cpu);
43         printk("Readers: 0[%08x] 1[%08x] 2[%08x] 4[%08x]\n",
44                lock->reader_pc[0], lock->reader_pc[1],
45                lock->reader_pc[2], lock->reader_pc[3]);
46 }
47
48 #undef INIT_STUCK
49 #define INIT_STUCK 100000000
50
51 void _do_spin_lock(spinlock_t *lock, char *str)
52 {
53         unsigned long caller, val;
54         int stuck = INIT_STUCK;
55         int cpu = smp_processor_id();
56         int shown = 0;
57
58         GET_CALLER(caller);
59 again:
60         __asm__ __volatile__("ldstub [%1], %0"
61                              : "=r" (val)
62                              : "r" (&(lock->lock))
63                              : "memory");
64         membar_safe("#StoreLoad | #StoreStore");
65         if (val) {
66                 while (lock->lock) {
67                         if (!--stuck) {
68                                 if (shown++ <= 2)
69                                         show(str, lock, caller);
70                                 stuck = INIT_STUCK;
71                         }
72                         rmb();
73                 }
74                 goto again;
75         }
76         lock->owner_pc = ((unsigned int)caller);
77         lock->owner_cpu = cpu;
78         current->thread.smp_lock_count++;
79         current->thread.smp_lock_pc = ((unsigned int)caller);
80 }
81
82 int _spin_trylock(spinlock_t *lock)
83 {
84         unsigned long val, caller;
85         int cpu = smp_processor_id();
86
87         GET_CALLER(caller);
88         __asm__ __volatile__("ldstub [%1], %0"
89                              : "=r" (val)
90                              : "r" (&(lock->lock))
91                              : "memory");
92         membar_safe("#StoreLoad | #StoreStore");
93         if (!val) {
94                 lock->owner_pc = ((unsigned int)caller);
95                 lock->owner_cpu = cpu;
96                 current->thread.smp_lock_count++;
97                 current->thread.smp_lock_pc = ((unsigned int)caller);
98         }
99         return val == 0;
100 }
101
102 void _do_spin_unlock(spinlock_t *lock)
103 {
104         lock->owner_pc = 0;
105         lock->owner_cpu = NO_PROC_ID;
106         membar_safe("#StoreStore | #LoadStore");
107         lock->lock = 0;
108         current->thread.smp_lock_count--;
109 }
110
111 /* Keep INIT_STUCK the same... */
112
113 void _do_read_lock (rwlock_t *rw, char *str)
114 {
115         unsigned long caller, val;
116         int stuck = INIT_STUCK;
117         int cpu = smp_processor_id();
118         int shown = 0;
119
120         GET_CALLER(caller);
121 wlock_again:
122         /* Wait for any writer to go away.  */
123         while (((long)(rw->lock)) < 0) {
124                 if (!--stuck) {
125                         if (shown++ <= 2)
126                                 show_read(str, rw, caller);
127                         stuck = INIT_STUCK;
128                 }
129                 rmb();
130         }
131         /* Try once to increment the counter.  */
132         __asm__ __volatile__(
133 "       ldx             [%0], %%g5\n"
134 "       brlz,a,pn       %%g5, 2f\n"
135 "        mov            1, %0\n"
136 "       add             %%g5, 1, %%g7\n"
137 "       casx            [%0], %%g5, %%g7\n"
138 "       sub             %%g5, %%g7, %0\n"
139 "2:"    : "=r" (val)
140         : "0" (&(rw->lock))
141         : "g5", "g7", "memory");
142         membar_safe("#StoreLoad | #StoreStore");
143         if (val)
144                 goto wlock_again;
145         rw->reader_pc[cpu] = ((unsigned int)caller);
146         current->thread.smp_lock_count++;
147         current->thread.smp_lock_pc = ((unsigned int)caller);
148 }
149
150 void _do_read_unlock (rwlock_t *rw, char *str)
151 {
152         unsigned long caller, val;
153         int stuck = INIT_STUCK;
154         int cpu = smp_processor_id();
155         int shown = 0;
156
157         GET_CALLER(caller);
158
159         /* Drop our identity _first_. */
160         rw->reader_pc[cpu] = 0;
161         current->thread.smp_lock_count--;
162 runlock_again:
163         /* Spin trying to decrement the counter using casx.  */
164         __asm__ __volatile__(
165 "       membar  #StoreLoad | #LoadLoad\n"
166 "       ldx     [%0], %%g5\n"
167 "       sub     %%g5, 1, %%g7\n"
168 "       casx    [%0], %%g5, %%g7\n"
169 "       membar  #StoreLoad | #StoreStore\n"
170 "       sub     %%g5, %%g7, %0\n"
171         : "=r" (val)
172         : "0" (&(rw->lock))
173         : "g5", "g7", "memory");
174         if (val) {
175                 if (!--stuck) {
176                         if (shown++ <= 2)
177                                 show_read(str, rw, caller);
178                         stuck = INIT_STUCK;
179                 }
180                 goto runlock_again;
181         }
182 }
183
184 void _do_write_lock (rwlock_t *rw, char *str)
185 {
186         unsigned long caller, val;
187         int stuck = INIT_STUCK;
188         int cpu = smp_processor_id();
189         int shown = 0;
190
191         GET_CALLER(caller);
192 wlock_again:
193         /* Spin while there is another writer. */
194         while (((long)rw->lock) < 0) {
195                 if (!--stuck) {
196                         if (shown++ <= 2)
197                                 show_write(str, rw, caller);
198                         stuck = INIT_STUCK;
199                 }
200                 rmb();
201         }
202
203         /* Try to acuire the write bit.  */
204         __asm__ __volatile__(
205 "       mov     1, %%g3\n"
206 "       sllx    %%g3, 63, %%g3\n"
207 "       ldx     [%0], %%g5\n"
208 "       brlz,pn %%g5, 1f\n"
209 "        or     %%g5, %%g3, %%g7\n"
210 "       casx    [%0], %%g5, %%g7\n"
211 "       membar  #StoreLoad | #StoreStore\n"
212 "       ba,pt   %%xcc, 2f\n"
213 "        sub    %%g5, %%g7, %0\n"
214 "1:     mov     1, %0\n"
215 "2:"    : "=r" (val)
216         : "0" (&(rw->lock))
217         : "g3", "g5", "g7", "memory");
218         if (val) {
219                 /* We couldn't get the write bit. */
220                 if (!--stuck) {
221                         if (shown++ <= 2)
222                                 show_write(str, rw, caller);
223                         stuck = INIT_STUCK;
224                 }
225                 goto wlock_again;
226         }
227         if ((rw->lock & ((1UL<<63)-1UL)) != 0UL) {
228                 /* Readers still around, drop the write
229                  * lock, spin, and try again.
230                  */
231                 if (!--stuck) {
232                         if (shown++ <= 2)
233                                 show_write(str, rw, caller);
234                         stuck = INIT_STUCK;
235                 }
236                 __asm__ __volatile__(
237 "               mov     1, %%g3\n"
238 "               sllx    %%g3, 63, %%g3\n"
239 "1:             ldx     [%0], %%g5\n"
240 "               andn    %%g5, %%g3, %%g7\n"
241 "               casx    [%0], %%g5, %%g7\n"
242 "               cmp     %%g5, %%g7\n"
243 "               bne,pn  %%xcc, 1b\n"
244 "                membar #StoreLoad | #StoreStore"
245                 : /* no outputs */
246                 : "r" (&(rw->lock))
247                 : "g3", "g5", "g7", "cc", "memory");
248                 while(rw->lock != 0) {
249                         if (!--stuck) {
250                                 if (shown++ <= 2)
251                                         show_write(str, rw, caller);
252                                 stuck = INIT_STUCK;
253                         }
254                         rmb();
255                 }
256                 goto wlock_again;
257         }
258
259         /* We have it, say who we are. */
260         rw->writer_pc = ((unsigned int)caller);
261         rw->writer_cpu = cpu;
262         current->thread.smp_lock_count++;
263         current->thread.smp_lock_pc = ((unsigned int)caller);
264 }
265
266 void _do_write_unlock(rwlock_t *rw)
267 {
268         unsigned long caller, val;
269         int stuck = INIT_STUCK;
270         int shown = 0;
271
272         GET_CALLER(caller);
273
274         /* Drop our identity _first_ */
275         rw->writer_pc = 0;
276         rw->writer_cpu = NO_PROC_ID;
277         current->thread.smp_lock_count--;
278 wlock_again:
279         __asm__ __volatile__(
280 "       membar  #StoreLoad | #LoadLoad\n"
281 "       mov     1, %%g3\n"
282 "       sllx    %%g3, 63, %%g3\n"
283 "       ldx     [%0], %%g5\n"
284 "       andn    %%g5, %%g3, %%g7\n"
285 "       casx    [%0], %%g5, %%g7\n"
286 "       membar  #StoreLoad | #StoreStore\n"
287 "       sub     %%g5, %%g7, %0\n"
288         : "=r" (val)
289         : "0" (&(rw->lock))
290         : "g3", "g5", "g7", "memory");
291         if (val) {
292                 if (!--stuck) {
293                         if (shown++ <= 2)
294                                 show_write("write_unlock", rw, caller);
295                         stuck = INIT_STUCK;
296                 }
297                 goto wlock_again;
298         }
299 }
300
301 int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock)
302 {
303         spin_lock(lock);
304         if (atomic_dec_and_test(atomic))
305                 return 1;
306         spin_unlock(lock);
307         return 0;
308 }
309
310 #endif /* CONFIG_SMP && CONFIG_DEBUG_SPINLOCK */