bc0b84057e2752e687c44ccb75253b181c89ea6d
[dpdk.git] / lib / librte_eal / common / include / rte_spinlock.h
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2013 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  * 
7  *   Redistribution and use in source and binary forms, with or without 
8  *   modification, are permitted provided that the following conditions 
9  *   are met:
10  * 
11  *     * Redistributions of source code must retain the above copyright 
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright 
14  *       notice, this list of conditions and the following disclaimer in 
15  *       the documentation and/or other materials provided with the 
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its 
18  *       contributors may be used to endorse or promote products derived 
19  *       from this software without specific prior written permission.
20  * 
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  * 
33  */
34
35 #ifndef _RTE_SPINLOCK_H_
36 #define _RTE_SPINLOCK_H_
37
38 /**
39  * @file
40  *
41  * RTE Spinlocks
42  *
43  * This file defines an API for read-write locks, which are implemented
44  * in an architecture-specific way. This kind of lock simply waits in
45  * a loop repeatedly checking until the lock becomes available.
46  *
47  * All locks must be initialised before use, and only initialised once.
48  *
49  */
50
51 #ifdef __cplusplus
52 extern "C" {
53 #endif
54
55 #include <rte_lcore.h>
56 #ifdef RTE_FORCE_INTRINSICS
57 #include <rte_common.h>
58 #endif
59
60 /**
61  * The rte_spinlock_t type.
62  */
63 typedef struct {
64         volatile int locked; /**< lock status 0 = unlocked, 1 = locked */
65 } rte_spinlock_t;
66
67 /**
68  * A static spinlock initializer.
69  */
70 #define RTE_SPINLOCK_INITIALIZER { 0 }
71
72 /**
73  * Initialize the spinlock to an unlocked state.
74  *
75  * @param sl
76  *   A pointer to the spinlock.
77  */
78 static inline void
79 rte_spinlock_init(rte_spinlock_t *sl)
80 {
81         sl->locked = 0;
82 }
83
84 /**
85  * Take the spinlock.
86  *
87  * @param sl
88  *   A pointer to the spinlock.
89  */
90 static inline void
91 rte_spinlock_lock(rte_spinlock_t *sl)
92 {
93 #ifndef RTE_FORCE_INTRINSICS
94         int lock_val = 1;
95         asm volatile (
96                         "1:\n"
97                         "xchg %[locked], %[lv]\n"
98                         "test %[lv], %[lv]\n"
99                         "jz 3f\n"
100                         "2:\n"
101                         "pause\n"
102                         "cmp $0, %[locked]\n"
103                         "jnz 2b\n"
104                         "jmp 1b\n"
105                         "3:\n"
106                         : [locked] "=m" (sl->locked), [lv] "=q" (lock_val)
107                         : "[lv]" (lock_val)
108                         : "memory");
109 #else
110         while (__sync_lock_test_and_set(&sl->locked, 1))
111                 while(sl->locked)
112                         rte_pause();
113 #endif
114 }
115
116 /**
117  * Release the spinlock.
118  *
119  * @param sl
120  *   A pointer to the spinlock.
121  */
122 static inline void
123 rte_spinlock_unlock (rte_spinlock_t *sl)
124 {
125 #ifndef RTE_FORCE_INTRINSICS
126         int unlock_val = 0;
127         asm volatile (
128                         "xchg %[locked], %[ulv]\n"
129                         : [locked] "=m" (sl->locked), [ulv] "=q" (unlock_val)
130                         : "[ulv]" (unlock_val)
131                         : "memory");
132 #else
133         __sync_lock_release(&sl->locked);
134 #endif
135 }
136
137 /**
138  * Try to take the lock.
139  *
140  * @param sl
141  *   A pointer to the spinlock.
142  * @return
143  *   1 if the lock is successfully taken; 0 otherwise.
144  */
145 static inline int
146 rte_spinlock_trylock (rte_spinlock_t *sl)
147 {
148 #ifndef RTE_FORCE_INTRINSICS
149         int lockval = 1;
150
151         asm volatile (
152                         "xchg %[locked], %[lockval]"
153                         : [locked] "=m" (sl->locked), [lockval] "=q" (lockval)
154                         : "[lockval]" (lockval)
155                         : "memory");
156
157         return (lockval == 0);
158 #else
159         return (__sync_lock_test_and_set(&sl->locked,1) == 0);
160 #endif
161 }
162
163 /**
164  * Test if the lock is taken.
165  *
166  * @param sl
167  *   A pointer to the spinlock.
168  * @return
169  *   1 if the lock is currently taken; 0 otherwise.
170  */
171 static inline int rte_spinlock_is_locked (rte_spinlock_t *sl)
172 {
173         return sl->locked;
174 }
175
176 /**
177  * The rte_spinlock_recursive_t type.
178  */
179 typedef struct {
180         rte_spinlock_t sl; /**< the actual spinlock */
181         volatile int user; /**< core id using lock, -1 for unused */
182         volatile int count; /**< count of time this lock has been called */
183 } rte_spinlock_recursive_t;
184
185 /**
186  * A static recursive spinlock initializer.
187  */
188 #define RTE_SPINLOCK_RECURSIVE_INITIALIZER {RTE_SPINLOCK_INITIALIZER, -1, 0}
189
190 /**
191  * Initialize the recursive spinlock to an unlocked state.
192  *
193  * @param slr
194  *   A pointer to the recursive spinlock.
195  */
196 static inline void rte_spinlock_recursive_init(rte_spinlock_recursive_t *slr)
197 {
198         rte_spinlock_init(&slr->sl);
199         slr->user = -1;
200         slr->count = 0;
201 }
202
203 /**
204  * Take the recursive spinlock.
205  *
206  * @param slr
207  *   A pointer to the recursive spinlock.
208  */
209 static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr)
210 {
211         int id = rte_lcore_id();
212
213         if (slr->user != id) {
214                 rte_spinlock_lock(&slr->sl);
215                 slr->user = id;
216         }
217         slr->count++;
218 }
219 /**
220  * Release the recursive spinlock.
221  *
222  * @param slr
223  *   A pointer to the recursive spinlock.
224  */
225 static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr)
226 {
227         if (--(slr->count) == 0) {
228                 slr->user = -1;
229                 rte_spinlock_unlock(&slr->sl);
230         }
231
232 }
233
234 /**
235  * Try to take the recursive lock.
236  *
237  * @param slr
238  *   A pointer to the recursive spinlock.
239  * @return
240  *   1 if the lock is successfully taken; 0 otherwise.
241  */
242 static inline int rte_spinlock_recursive_trylock(rte_spinlock_recursive_t *slr)
243 {
244         int id = rte_lcore_id();
245
246         if (slr->user != id) {
247                 if (rte_spinlock_trylock(&slr->sl) == 0)
248                         return 0;
249                 slr->user = id;
250         }
251         slr->count++;
252         return 1;
253 }
254
255 #ifdef __cplusplus
256 }
257 #endif
258
259 #endif /* _RTE_SPINLOCK_H_ */