core: fix another bunch of 'no real prototype' warnings, add doxygen docs
[sip-router] / fastlock.h
1 /*
2  * fast architecture specific locking
3  *
4  * $Id$
5  *
6  * 
7  *
8  * Copyright (C) 2001-2003 FhG Fokus
9  *
10  * Permission to use, copy, modify, and distribute this software for any
11  * purpose with or without fee is hereby granted, provided that the above
12  * copyright notice and this permission notice appear in all copies.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 /*
23  *
24  *History:
25  *--------
26  *  2002-02-05  created by andrei
27  *  2003-01-16  added PPC locking code contributed by Dinos Dorkofikis
28  *               <kdor@intranet.gr>
29  *  2004-09-12  added MIPS locking for ISA>=2 (>r3000)  (andrei)
30  *  2004-12-16  for now use the same locking code for sparc32 as for sparc64
31  *               (it will work only if NOSMP is defined) (andrei)
32  *  2005-04-27  added alpha locking code (andrei)
33  *  2005-05-25  PPC locking code enabled for PPC64; added a lwsync to
34  *               the tsl part and replaced the sync with a lwsync for the
35  *               unlock part (andrei)
36  *  2006-03-08  mips2 NOSMP (skip sync), optimized x86 & mips clobbers and
37  *               input/output constraints (andrei)
38  *  2006-04-03  optimization: call lock_get memory barrier outside tsl,in the 
39  *               calling function, only if the lock operation succeeded
40  *               (membar_getlock()) (andrei)
41  *              added try_lock(); more x86 optimizations, x86  release_lock
42  *               fix (andrei)
43  * 2006-04-04  sparc* optimizations, sparc32 smp support, armv6 no smp support,
44  *              ppc, mips*, alpha optimizations (andrei)
45  * 2006-04-05  ppc fixes (s/stw/stwx/, s/lwz/lwzx), early clobber added
46  *             where needed (andrei)
47  * 2006-11-22  arm early clobber added: according to the swp instruction 
48  *              specification the address register must be != from the other 2
49  *              (Julien Blache <jblache@debian.org>)
50  *
51  */
52
53 /*
54  * WARNING: the code was not tested on the following architectures:
55  *           - arm6  (cross-compiles ok, no test)
56  *           - alpha (cross-compiles ok, no test)
57  *           - mips64 (cross-compiles ok)
58  *           - ppc64 (compiles ok)
59  *           - sparc32 (tested on a sparc64)
60  */
61
62
63 #ifndef fastlock_h
64 #define fastlock_h
65
66 #include "sched_yield.h"
67
68
69 #define SPIN_OPTIMIZE /* if defined optimize spining on the lock:
70                          try first the lock with non-atomic/non memory locking
71                          operations, and only if the lock appears to be free
72                          switch to the more expensive version */
73
74 typedef  volatile int fl_lock_t;
75
76
77
78 #define init_lock( l ) (l)=0
79
80
81 /* what membar to use (if any) after taking a lock. This
82  *  was separated from the lock code to allow better optimizations.
83  *  e.g.: use the membar_getlock only after getting the lock and don't use 
84  *  it if lock_get fails / when spinning on tsl.
85  *  There is no corresponding membar_release_lock (because lock_release
86  *  must always include the needed memory barrier).
87  *  WARNING: this is intended only for internal fastlock use*/
88 #if defined(__CPU_i386) || defined(__CPU_x86_64)
89 #define membar_getlock()   /* not needed on x86 */
90
91 #elif defined(__CPU_sparc64)
92 #ifndef NOSMP
93 #define membar_getlock() \
94         asm volatile ("membar #StoreStore | #StoreLoad \n\t" : : : "memory");
95         /* can be either StoreStore|StoreLoad or LoadStore|LoadLoad
96          * since ldstub acts both as a store and as a load */
97 #else
98 /* no need for a compiler barrier, that is already included in lock_get/tsl*/
99 #define membar_getlock() /* not needed if no smp*/
100 #endif /* NOSMP */
101
102 #elif  defined(__CPU_sparc)
103 #define membar_getlock()/* no need for a compiler barrier, already included */
104
105 #elif defined __CPU_arm || defined __CPU_arm6
106 #ifndef NOSMP
107 #warning smp not supported on arm* (no membars), try compiling with -DNOSMP
108 #endif /* NOSMP */
109 #define membar_getlock() 
110
111 #elif defined(__CPU_ppc) || defined(__CPU_ppc64)
112 #ifndef NOSMP
113 #define membar_getlock() \
114         asm volatile("lwsync \n\t" : : : "memory");
115 #else
116 #define membar_getlock() 
117 #endif /* NOSMP */
118
119 #elif defined __CPU_mips2 || defined __CPU_mips64
120 #ifndef NOSMP
121 #define membar_getlock() \
122         asm volatile("sync \n\t" : : : "memory");
123 #else
124 #define membar_getlock() 
125 #endif /* NOSMP */
126
127 #elif defined __CPU_mips
128 #ifndef NOSMP
129 #warning smp not supported on mips1 (no membars), try compiling with -DNOSMP
130 #endif
131 #define membar_getlock() 
132
133 #elif defined __CPU_alpha
134 #ifndef NOSMP
135 #define membar_getlock() \
136         asm volatile("mb \n\t" : : : "memory");
137 #else
138 #define membar_getlock() 
139 #endif /* NOSMP */
140
141 #else /* __CPU_xxx */
142 #error "unknown architecture"
143 #endif
144
145
146
147 /*test and set lock, ret !=0 if lock held by someone else, 0 otherwise
148  * WARNING: no memory barriers included, if you use this function directly
149  *          (not recommended) and it gets the lock (ret==0), you should call 
150  *          membar_getlock() after it */
151 inline static int tsl(fl_lock_t* lock)
152 {
153         int val;
154
155 #if defined(__CPU_i386) || defined(__CPU_x86_64)
156
157 #ifdef NOSMP
158         asm volatile(
159                 " xor %0, %0 \n\t"
160                 " btsl $0, %2 \n\t"
161                 " setc %b0 \n\t"
162                 : "=&q" (val), "=m" (*lock) : "m"(*lock) : "memory", "cc"
163         );
164 #else
165         asm volatile(
166 #ifdef SPIN_OPTIMIZE
167                 " cmpb $0, %2 \n\t"
168                 " mov $1, %0 \n\t"
169                 " jnz 1f \n\t"
170 #else
171                 " mov $1, %0 \n\t"
172 #endif
173                 " xchgb %2, %b0 \n\t"
174                 "1: \n\t"
175                 : "=&q" (val), "=m" (*lock) : "m"(*lock) : "memory"
176 #ifdef SPIN_OPTIMIZE
177                                 , "cc"
178 #endif
179         );
180 #endif /*NOSMP*/
181 #elif defined(__CPU_sparc64)
182         asm volatile(
183 #ifdef SPIN_OPTIMIZE
184                         "   ldub [%2], %0 \n\t"
185                         "   brnz,a,pn %0, 1f \n\t"
186                         "   nop \n\t"
187 #endif
188                         "   ldstub [%2], %0 \n\t"
189                         "1: \n\t"
190                         /* membar_getlock must be  called outside this function */
191                         : "=&r"(val), "=m"(*lock) : "r"(lock): "memory"
192         );
193 #elif defined(__CPU_sparc)
194         asm volatile(
195 #ifdef SPIN_OPTIMIZE
196                         "   ldub [%2], %0 \n\t"
197                         "   tst %0 \n\t"
198                         "   bne,a  1f \n\t"
199                         "   nop \n\t"
200 #endif
201                         "   ldstub [%2], %0 \n\t"
202                         "1: \n\t"
203                         /* membar_getlock must be  called outside this function */
204                         : "=&r"(val), "=m"(*lock) : "r"(lock): "memory"
205 #ifdef SPIN_OPTIMIZE
206                                 , "cc"
207 #endif
208         );
209 #elif defined __CPU_arm 
210         asm volatile(
211                         "swp %0, %2, [%3] \n\t"
212                         : "=&r" (val), "=m"(*lock) : "r"(1), "r" (lock) : "memory"
213         );
214 #elif defined __CPU_arm6
215         asm volatile(
216                         "   ldrex %0, [%2] \n\t" 
217                         "   cmp %0, #0 \n\t"
218                         "   strexeq %0, %3, [%2] \n\t" /* executed only if Z=1 */
219                         /* if %0!=0 => either it was 1 initially or was 0
220                          * and somebody changed it just before the strexeq (so the 
221                          * lock is taken) => it's safe to return %0 */
222                         : "=&r"(val), "=m"(*lock) : "r"(lock), "r"(1) : "cc"
223         );
224 #elif defined(__CPU_ppc) || defined(__CPU_ppc64)
225         asm volatile(
226                         "1: \n\t"
227 #ifdef SPIN_OPTIMIZE
228                         "   lwzx %0, 0, %2 \n\t"
229                         "   cmpwi %0, 0 \n\t"
230                         "   bne- 2f \n\t" /* predict: not taken */
231 #endif
232                         "   lwarx  %0, 0, %2\n\t"
233                         "   cmpwi  %0, 0\n\t"
234                         "   bne-    2f\n\t"
235                         "   stwcx. %3, 0, %2\n\t"
236                         "   bne-   1b\n\t"
237                         /* membar_getlock must be  called outside this function */
238                         "2:\n\t"
239                         : "=&r" (val), "=m"(*lock) :  "r"(lock), "r"(1) : "memory", "cc"
240         );
241 #elif defined __CPU_mips2 || ( defined __CPU_mips && defined MIPS_HAS_LLSC ) \
242         || defined __CPU_mips64
243         long tmp;
244         
245         asm volatile(
246                 ".set push \n\t"
247                 ".set noreorder\n\t"
248                 ".set mips2 \n\t"
249 #ifdef SPIN_OPTIMIZE
250                 "    lw %1, %2 \n\t"
251                 "    bne %1, $0, 2f \n\t"
252                 "    nop \n\t"
253 #endif
254                 "1:  ll %1, %2   \n\t"
255                 "    bne %1, $0, 2f \n\t"
256                 "    li %0, 1 \n\t"  /* delay slot */
257                 "    sc %0, %2  \n\t"
258                 "    beqz %0, 1b \n\t"
259                 "    nop \n\t"
260                 "2: \n\t"
261                 /* membar_getlock must be called outside this function */
262                 ".set pop\n\t"
263                 : "=&r" (tmp), "=&r" (val), "=m" (*lock) 
264                 : "m" (*lock) 
265                 : "memory"
266         );
267 #elif defined __CPU_alpha
268         long tmp;
269         tmp=0;
270         /* lock low bit set to 1 when the lock is hold and to 0 otherwise */
271         asm volatile(
272                 "1:  ldl %0, %1   \n\t"
273                 "    blbs %0, 2f  \n\t"  /* optimization if locked */
274                 "    ldl_l %0, %1 \n\t"
275                 "    blbs %0, 2f  \n\t" 
276                 "    lda %2, 1    \n\t"  /* or: or $31, 1, %2 ??? */
277                 "    stl_c %2, %1 \n\t"
278                 "    beq %2, 3f   \n\t" /* back cond. jumps are always predicted to be 
279                                                                    taken => make forward jump */
280                 /* membar_getlock must be called outside this function */
281                 "2:               \n\t"
282                 ".subsection 2 \n\t"
283                 "3:  br 1b \n\t"
284                 ".previous \n\t"
285                 :"=&r" (val), "=m"(*lock), "=&r"(tmp)
286                 :"m"(*lock) 
287                 : "memory"
288         );
289 #else
290 #error "unknown architecture"
291 #endif
292         return val;
293 }
294
295
296
297 inline static void get_lock(fl_lock_t* lock)
298 {
299 #ifdef ADAPTIVE_WAIT
300         int i=ADAPTIVE_WAIT_LOOPS;
301 #endif
302         
303         while(tsl(lock)){
304 #ifdef BUSY_WAIT
305 #elif defined ADAPTIVE_WAIT
306                 if (i>0) i--;
307                 else sched_yield();
308 #else
309                 sched_yield();
310 #endif
311         }
312         membar_getlock();
313 }
314
315
316
317 /* like get_lock, but it doesn't wait. If it gets the lock returns 0,
318  *  <0  otherwise (-1) */
319 inline static int try_lock(fl_lock_t* lock)
320 {
321         if (tsl(lock)){
322                 return -1;
323         }
324         membar_getlock();
325         return 0;
326 }
327
328
329
330 inline static void release_lock(fl_lock_t* lock)
331 {
332 #if defined(__CPU_i386) 
333 #ifdef NOSMP
334         asm volatile(
335                 " movb $0, %0 \n\t" 
336                 : "=m"(*lock) : : "memory"
337         ); 
338 #else /* ! NOSMP */
339         int val;
340         /* a simple mov $0, (lock) does not force StoreStore ordering on all
341            x86 versions and it doesn't seem to force LoadStore either */
342         asm volatile(
343                 " xchgb %b0, %1 \n\t"
344                 : "=q" (val), "=m" (*lock) : "0" (0) : "memory"
345         );
346 #endif /* NOSMP */
347 #elif defined(__CPU_x86_64)
348         asm volatile(
349                 " movb $0, %0 \n\t" /* on amd64 membar StoreStore | LoadStore is 
350                                                            implicit (at least on the same mem. type) */
351                 : "=m"(*lock) : : "memory"
352         );
353 #elif defined(__CPU_sparc64) || defined(__CPU_sparc)
354         asm volatile(
355 #ifndef NOSMP
356 #ifdef __CPU_sparc64
357                         "membar #LoadStore | #StoreStore \n\t"
358 #else /* __CPU_sparc */
359                         "stbar \n\t"
360 #endif /* __CPU_sparc64 */
361 #endif
362                         "stb %%g0, [%1] \n\t"
363                         : "=m"(*lock) : "r" (lock) : "memory"
364         );
365 #elif defined __CPU_arm || defined __CPU_arm6
366 #ifndef NOSMP
367 #warning arm* smp mode not supported (no membars), try compiling with -DNOSMP
368 #endif
369         asm volatile(
370                 " str %1, [%2] \n\r" 
371                 : "=m"(*lock) : "r"(0), "r"(lock) : "memory"
372         );
373 #elif defined(__CPU_ppc) || defined(__CPU_ppc64)
374         asm volatile(
375                         /* "sync\n\t"  lwsync is faster and will work
376                          *             here too
377                          *             [IBM Prgramming Environments Manual, D.4.2.2]
378                          */
379                         "lwsync\n\t"
380                         "stwx %1, 0, %2\n\t"
381                         : "=m"(*lock) : "r"(0), "r"(lock) : "memory"
382         );
383 #elif defined __CPU_mips2 || ( defined __CPU_mips && defined MIPS_HAS_LLSC ) \
384         || defined __CPU_mips64
385         asm volatile(
386                 ".set push \n\t"
387                 ".set noreorder \n\t"
388                 ".set mips2 \n\t"
389 #ifndef NOSMP
390 #ifdef __CPU_mips
391 #warning mips1 smp mode not supported (no membars), try compiling with -DNOSMP
392 #else
393                 "    sync \n\t"
394 #endif
395 #endif
396                 "    sw $0, %0 \n\t"
397                 ".set pop \n\t"
398                 : "=m" (*lock)  : /* no input */ : "memory"
399         );
400 #elif defined __CPU_alpha
401         asm volatile(
402 #ifndef  NOSMP
403                 "    mb          \n\t"
404 #endif
405                 "    stl $31, %0 \n\t"
406                 : "=m"(*lock) :/* no input*/ : "memory"  /* because of the mb */
407         );  
408 #else
409 #error "unknown architecture"
410 #endif
411 }
412
413
414
415 #endif