]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/contrib/octeon-sdk/cvmx-spinlock.h
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.git] / sys / contrib / octeon-sdk / cvmx-spinlock.h
1 /***********************license start***************
2  *  Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
3  *  reserved.
4  *
5  *
6  *  Redistribution and use in source and binary forms, with or without
7  *  modification, are permitted provided that the following conditions are
8  *  met:
9  *
10  *      * Redistributions of source code must retain the above copyright
11  *        notice, this list of conditions and the following disclaimer.
12  *
13  *      * Redistributions in binary form must reproduce the above
14  *        copyright notice, this list of conditions and the following
15  *        disclaimer in the documentation and/or other materials provided
16  *        with the distribution.
17  *
18  *      * Neither the name of Cavium Networks nor the names of
19  *        its contributors may be used to endorse or promote products
20  *        derived from this software without specific prior written
21  *        permission.
22  *
23  *  TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
24  *  AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
25  *  OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
26  *  RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
27  *  REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
28  *  DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
29  *  OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
30  *  PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
31  *  POSSESSION OR CORRESPONDENCE TO DESCRIPTION.  THE ENTIRE RISK ARISING OUT
32  *  OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
33  *
34  *
35  *  For any questions regarding licensing please contact marketing@caviumnetworks.com
36  *
37  ***********************license end**************************************/
38
39
40
41
42
43
44 /**
45  * @file
46  *
47  * Implementation of spinlocks.
48  *
49  * <hr>$Revision: 41586 $<hr>
50  */
51
52
53 #ifndef __CVMX_SPINLOCK_H__
54 #define __CVMX_SPINLOCK_H__
55
56 #include "cvmx-asm.h"
57
58 #ifdef  __cplusplus
59 extern "C" {
60 #endif
61
62 /* Spinlocks for Octeon */
63
64
65 // define these to enable recursive spinlock debugging
66 //#define CVMX_SPINLOCK_DEBUG
67
68
69 /**
70  * Spinlocks for Octeon
71  */
72 typedef struct {
73     volatile uint32_t value;
74 } cvmx_spinlock_t;
75
76 // note - macros not expanded in inline ASM, so values hardcoded
77 #define  CVMX_SPINLOCK_UNLOCKED_VAL  0
78 #define  CVMX_SPINLOCK_LOCKED_VAL    1
79
80
81 #define CVMX_SPINLOCK_UNLOCKED_INITIALIZER  {CVMX_SPINLOCK_UNLOCKED_VAL}
82
83
84 /**
85  * Initialize a spinlock
86  *
87  * @param lock   Lock to initialize
88  */
89 static inline void cvmx_spinlock_init(cvmx_spinlock_t *lock)
90 {
91     lock->value = CVMX_SPINLOCK_UNLOCKED_VAL;
92 }
93
94
95 /**
96  * Return non-zero if the spinlock is currently locked
97  *
98  * @param lock   Lock to check
99  * @return Non-zero if locked
100  */
101 static inline int cvmx_spinlock_locked(cvmx_spinlock_t *lock)
102 {
103     return (lock->value != CVMX_SPINLOCK_UNLOCKED_VAL);
104 }
105
106
107 /**
108  * Releases lock
109  *
110  * @param lock   pointer to lock structure
111  */
112 static inline void cvmx_spinlock_unlock(cvmx_spinlock_t *lock)
113 {
114     CVMX_SYNCWS;
115     lock->value = 0;
116     CVMX_SYNCWS;
117 }
118
119
120 /**
121  * Attempts to take the lock, but does not spin if lock is not available.
122  * May take some time to acquire the lock even if it is available
123  * due to the ll/sc not succeeding.
124  *
125  * @param lock   pointer to lock structure
126  *
127  * @return 0: lock successfully taken
128  *         1: lock not taken, held by someone else
129  * These return values match the Linux semantics.
130  */
131
132 static inline unsigned int cvmx_spinlock_trylock(cvmx_spinlock_t *lock)
133 {
134     unsigned int tmp;
135
136     __asm__ __volatile__(
137     ".set noreorder         \n"
138     "1: ll   %[tmp], %[val] \n"
139     "   bnez %[tmp], 2f     \n"  // if lock held, fail immediately
140     "   li   %[tmp], 1      \n"
141     "   sc   %[tmp], %[val] \n"
142     "   beqz %[tmp], 1b     \n"
143     "   li   %[tmp], 0      \n"
144     "2:                     \n"
145     ".set reorder           \n"
146     : [val] "+m" (lock->value), [tmp] "=&r" (tmp)
147     :
148     : "memory");
149
150     return (!!tmp);  /* normalize to 0 or 1 */
151 }
152
153 /**
154  * Gets lock, spins until lock is taken
155  *
156  * @param lock   pointer to lock structure
157  */
158 static inline void cvmx_spinlock_lock(cvmx_spinlock_t *lock)
159 {
160     unsigned int tmp;
161
162     __asm__ __volatile__(
163     ".set noreorder         \n"
164     "1: ll   %[tmp], %[val]  \n"
165     "   bnez %[tmp], 1b     \n"
166     "   li   %[tmp], 1      \n"
167     "   sc   %[tmp], %[val] \n"
168     "   beqz %[tmp], 1b     \n"
169     "   nop                \n"
170     ".set reorder           \n"
171     : [val] "+m" (lock->value), [tmp] "=&r" (tmp)
172     :
173     : "memory");
174
175 }
176
177
178
179 /** ********************************************************************
180  * Bit spinlocks
181  * These spinlocks use a single bit (bit 31) of a 32 bit word for locking.
182  * The rest of the bits in the word are left undisturbed.  This enables more
183  * compact data structures as only 1 bit is consumed for the lock.
184  *
185  */
186
187 /**
188  * Gets lock, spins until lock is taken
189  * Preserves the low 31 bits of the 32 bit
190  * word used for the lock.
191  *
192  *
193  * @param word  word to lock bit 31 of
194  */
195 static inline void cvmx_spinlock_bit_lock(uint32_t *word)
196 {
197     unsigned int tmp;
198     unsigned int sav;
199
200     __asm__ __volatile__(
201     ".set noreorder         \n"
202     ".set noat              \n"
203     "1: ll    %[tmp], %[val]  \n"
204     "   bbit1 %[tmp], 31, 1b    \n"
205     "   li    $at, 1      \n"
206     "   ins   %[tmp], $at, 31, 1  \n"
207     "   sc    %[tmp], %[val] \n"
208     "   beqz  %[tmp], 1b     \n"
209     "   nop                \n"
210     ".set at              \n"
211     ".set reorder           \n"
212     : [val] "+m" (*word), [tmp] "=&r" (tmp), [sav] "=&r" (sav)
213     :
214     : "memory");
215
216 }
217
218 /**
219  * Attempts to get lock, returns immediately with success/failure
220  * Preserves the low 31 bits of the 32 bit
221  * word used for the lock.
222  *
223  *
224  * @param word  word to lock bit 31 of
225  * @return 0: lock successfully taken
226  *         1: lock not taken, held by someone else
227  * These return values match the Linux semantics.
228  */
229 static inline unsigned int cvmx_spinlock_bit_trylock(uint32_t *word)
230 {
231     unsigned int tmp;
232
233     __asm__ __volatile__(
234     ".set noreorder         \n"
235     ".set noat              \n"
236     "1: ll    %[tmp], %[val] \n"
237     "   bbit1 %[tmp], 31, 2f     \n"  // if lock held, fail immediately
238     "   li    $at, 1      \n"
239     "   ins   %[tmp], $at, 31, 1  \n"
240     "   sc    %[tmp], %[val] \n"
241     "   beqz  %[tmp], 1b     \n"
242     "   li    %[tmp], 0      \n"
243     "2:                     \n"
244     ".set at              \n"
245     ".set reorder           \n"
246     : [val] "+m" (*word), [tmp] "=&r" (tmp)
247     :
248     : "memory");
249
250     return (!!tmp);  /* normalize to 0 or 1 */
251 }
252 /**
253  * Releases bit lock
254  *
255  * Unconditionally clears bit 31 of the lock word.  Note that this is
256  * done non-atomically, as this implementation assumes that the rest
257  * of the bits in the word are protected by the lock.
258  *
259  * @param word  word to unlock bit 31 in
260  */
261 static inline void cvmx_spinlock_bit_unlock(uint32_t *word)
262 {
263     CVMX_SYNCWS;
264     *word &= ~(1UL << 31) ;
265     CVMX_SYNCWS;
266 }
267
268
269
270 /** ********************************************************************
271  * Recursive spinlocks
272  */
273 typedef struct {
274         volatile unsigned int value;
275         volatile unsigned int core_num;
276 } cvmx_spinlock_rec_t;
277
278
279 /**
280  * Initialize a recursive spinlock
281  *
282  * @param lock   Lock to initialize
283  */
284 static inline void cvmx_spinlock_rec_init(cvmx_spinlock_rec_t *lock)
285 {
286     lock->value = CVMX_SPINLOCK_UNLOCKED_VAL;
287 }
288
289
290 /**
291  * Return non-zero if the recursive spinlock is currently locked
292  *
293  * @param lock   Lock to check
294  * @return Non-zero if locked
295  */
296 static inline int cvmx_spinlock_rec_locked(cvmx_spinlock_rec_t *lock)
297 {
298     return (lock->value != CVMX_SPINLOCK_UNLOCKED_VAL);
299 }
300
301
302 /**
303 * Unlocks one level of recursive spinlock.  Lock is not unlocked
304 * unless this is the final unlock call for that spinlock
305 *
306 * @param lock   ptr to recursive spinlock structure
307 */
308 static inline void cvmx_spinlock_rec_unlock(cvmx_spinlock_rec_t *lock);
309
310 #ifdef CVMX_SPINLOCK_DEBUG
311 #define cvmx_spinlock_rec_unlock(x)  _int_cvmx_spinlock_rec_unlock((x), __FILE__, __LINE__)
312 static inline void _int_cvmx_spinlock_rec_unlock(cvmx_spinlock_rec_t *lock, char *filename, int linenum)
313 #else
314 static inline void cvmx_spinlock_rec_unlock(cvmx_spinlock_rec_t *lock)
315 #endif
316 {
317
318         unsigned int temp, result;
319     int core_num;
320     core_num = cvmx_get_core_num();
321
322 #ifdef CVMX_SPINLOCK_DEBUG
323     {
324         if (lock->core_num != core_num)
325         {
326             cvmx_dprintf("ERROR: Recursive spinlock release attemped by non-owner! file: %s, line: %d\n", filename, linenum);
327             return;
328         }
329     }
330 #endif
331
332         __asm__ __volatile__(
333                 ".set  noreorder                 \n"
334                 "     addi  %[tmp], %[pid], 0x80 \n"
335                 "     sw    %[tmp], %[lid]       # set lid to invalid value\n"
336                 CVMX_SYNCWS_STR
337                 "1:   ll    %[tmp], %[val]       \n"
338                 "     addu  %[res], %[tmp], -1   # decrement lock count\n"
339                 "     sc    %[res], %[val]       \n"
340                 "     beqz  %[res], 1b           \n"
341                 "     nop                        \n"
342                 "     beq   %[tmp], %[res], 2f   # res is 1 on successful sc       \n"
343                 "     nop                        \n"
344                 "     sw   %[pid], %[lid]        # set lid to pid, only if lock still held\n"
345                 "2:                         \n"
346                 CVMX_SYNCWS_STR
347                 ".set  reorder                   \n"
348                 : [res] "=&r" (result), [tmp] "=&r" (temp), [val] "+m" (lock->value), [lid] "+m" (lock->core_num)
349                 : [pid] "r" (core_num)
350                 : "memory");
351
352
353 #ifdef CVMX_SPINLOCK_DEBUG
354     {
355         if (lock->value == ~0UL)
356         {
357             cvmx_dprintf("ERROR: Recursive spinlock released too many times! file: %s, line: %d\n", filename, linenum);
358         }
359     }
360 #endif
361
362
363 }
364
365 /**
366  * Takes recursive spinlock for a given core.  A core can take the lock multiple
367  * times, and the lock is released only when the corresponding number of
368  * unlocks have taken place.
369  *
370  * NOTE: This assumes only one thread per core, and that the core ID is used as
371  * the lock 'key'.  (This implementation cannot be generalized to allow
372  * multiple threads to use the same key (core id) .)
373  *
374  * @param lock   address of recursive spinlock structure.  Note that this is
375  *               distinct from the standard spinlock
376  */
377 static inline void cvmx_spinlock_rec_lock(cvmx_spinlock_rec_t *lock);
378
379 #ifdef CVMX_SPINLOCK_DEBUG
380 #define cvmx_spinlock_rec_lock(x)  _int_cvmx_spinlock_rec_lock((x), __FILE__, __LINE__)
381 static inline void _int_cvmx_spinlock_rec_lock(cvmx_spinlock_rec_t *lock, char *filename, int linenum)
382 #else
383 static inline void cvmx_spinlock_rec_lock(cvmx_spinlock_rec_t *lock)
384 #endif
385 {
386
387
388         volatile unsigned int tmp;
389         volatile int core_num;
390
391         core_num = cvmx_get_core_num();
392
393
394         __asm__ __volatile__(
395                 ".set  noreorder              \n"
396                 "1: ll   %[tmp], %[val]       # load the count\n"
397                 "   bnez %[tmp], 2f           # if count!=zero branch to 2\n"
398                 "   addu %[tmp], %[tmp], 1    \n"
399                 "   sc   %[tmp], %[val]       \n"
400                 "   beqz %[tmp], 1b           # go back if not success\n"
401                 "   nop                       \n"
402                 "   j    3f                   # go to write core_num \n"
403                 "2: lw   %[tmp], %[lid]       # load the core_num \n"
404                 "   bne  %[tmp], %[pid], 1b   # core_num no match, restart\n"
405                 "   nop                       \n"
406                 "   lw   %[tmp], %[val]       \n"
407                 "   addu %[tmp], %[tmp], 1    \n"
408                 "   sw   %[tmp], %[val]       # update the count\n"
409                 "3: sw   %[pid], %[lid]       # store the core_num\n"
410                 CVMX_SYNCWS_STR
411                 ".set  reorder                \n"
412                 : [tmp] "=&r" (tmp), [val] "+m" (lock->value), [lid] "+m" (lock->core_num)
413                 : [pid] "r" (core_num)
414                 : "memory");
415
416 #ifdef CVMX_SPINLOCK_DEBUG
417     if (lock->core_num != core_num)
418     {
419         cvmx_dprintf("cvmx_spinlock_rec_lock: lock taken, but core_num is incorrect. file: %s, line: %d\n", filename, linenum);
420     }
421 #endif
422
423
424 }
425
426 #ifdef  __cplusplus
427 }
428 #endif
429
430 #endif /* __CVMX_SPINLOCK_H__ */