0cd04a5b0a247c25caf846f79ec311c8ce1e0b34
[dpdk.git] / lib / librte_eal / common / include / i686 / arch / rte_atomic.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 /*
36  * Inspired from FreeBSD src/sys/i386/include/atomic.h
37  * Copyright (c) 1998 Doug Rabson
38  * All rights reserved.
39  */
40
41 #ifndef _RTE_ATOMIC_H_
42 #error "don't include this file directly, please include generic <rte_atomic.h>"
43 #endif
44
45 #ifndef _RTE_I686_ATOMIC_H_
46 #define _RTE_I686_ATOMIC_H_
47
48
49 /**
50  * @file
51  * Atomic Operations on i686
52  */
53
54
55 /*------------------------- 64 bit atomic operations -------------------------*/
56
57 /**
58  * An atomic compare and set function used by the mutex functions.
59  * (atomic) equivalent to:
60  *   if (*dst == exp)
61  *     *dst = src (all 64-bit words)
62  *
63  * @param dst
64  *   The destination into which the value will be written.
65  * @param exp
66  *   The expected value.
67  * @param src
68  *   The new value.
69  * @return
70  *   Non-zero on success; 0 on failure.
71  */
72 static inline int
73 rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src)
74 {
75         uint8_t res;
76         union {
77                 struct {
78                         uint32_t l32;
79                         uint32_t h32;
80                 };
81                 uint64_t u64;
82         } _exp, _src;
83
84         _exp.u64 = exp;
85         _src.u64 = src;
86
87         asm volatile (
88                         MPLOCKED
89                         "cmpxchg8b (%[dst]);"
90                         "setz %[res];"
91                         : [res] "=a" (res)      /* result in eax */
92                         : [dst] "S" (dst),      /* esi */
93                           "b" (_src.l32),       /* ebx */
94                           "c" (_src.h32),       /* ecx */
95                           "a" (_exp.l32),       /* eax */
96                           "d" (_exp.h32)        /* edx */
97                         : "memory" );           /* no-clobber list */
98
99         return res;
100 }
101
102 /**
103  * The atomic counter structure.
104  */
105 typedef struct {
106         volatile int64_t cnt;  /**< Internal counter value. */
107 } rte_atomic64_t;
108
109 /**
110  * Static initializer for an atomic counter.
111  */
112 #define RTE_ATOMIC64_INIT(val) { (val) }
113
114 /**
115  * Initialize the atomic counter.
116  *
117  * @param v
118  *   A pointer to the atomic counter.
119  */
120 static inline void
121 rte_atomic64_init(rte_atomic64_t *v)
122 {
123         int success = 0;
124         uint64_t tmp;
125
126         while (success == 0) {
127                 tmp = v->cnt;
128                 success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
129                                               tmp, 0);
130         }
131 }
132
133 /**
134  * Atomically read a 64-bit counter.
135  *
136  * @param v
137  *   A pointer to the atomic counter.
138  * @return
139  *   The value of the counter.
140  */
141 static inline int64_t
142 rte_atomic64_read(rte_atomic64_t *v)
143 {
144         int success = 0;
145         uint64_t tmp;
146
147         while (success == 0) {
148                 tmp = v->cnt;
149                 /* replace the value by itself */
150                 success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
151                                               tmp, tmp);
152         }
153         return tmp;
154 }
155
156 /**
157  * Atomically set a 64-bit counter.
158  *
159  * @param v
160  *   A pointer to the atomic counter.
161  * @param new_value
162  *   The new value of the counter.
163  */
164 static inline void
165 rte_atomic64_set(rte_atomic64_t *v, int64_t new_value)
166 {
167         int success = 0;
168         uint64_t tmp;
169
170         while (success == 0) {
171                 tmp = v->cnt;
172                 success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
173                                               tmp, new_value);
174         }
175 }
176
177 /**
178  * Atomically add a 64-bit value to a counter.
179  *
180  * @param v
181  *   A pointer to the atomic counter.
182  * @param inc
183  *   The value to be added to the counter.
184  */
185 static inline void
186 rte_atomic64_add(rte_atomic64_t *v, int64_t inc)
187 {
188         int success = 0;
189         uint64_t tmp;
190
191         while (success == 0) {
192                 tmp = v->cnt;
193                 success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
194                                               tmp, tmp + inc);
195         }
196 }
197
198 /**
199  * Atomically subtract a 64-bit value from a counter.
200  *
201  * @param v
202  *   A pointer to the atomic counter.
203  * @param dec
204  *   The value to be substracted from the counter.
205  */
206 static inline void
207 rte_atomic64_sub(rte_atomic64_t *v, int64_t dec)
208 {
209         int success = 0;
210         uint64_t tmp;
211
212         while (success == 0) {
213                 tmp = v->cnt;
214                 success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
215                                               tmp, tmp - dec);
216         }
217 }
218
219 /**
220  * Atomically increment a 64-bit counter by one and test.
221  *
222  * @param v
223  *   A pointer to the atomic counter.
224  */
225 static inline void
226 rte_atomic64_inc(rte_atomic64_t *v)
227 {
228         rte_atomic64_add(v, 1);
229 }
230
231 /**
232  * Atomically decrement a 64-bit counter by one and test.
233  *
234  * @param v
235  *   A pointer to the atomic counter.
236  */
237 static inline void
238 rte_atomic64_dec(rte_atomic64_t *v)
239 {
240         rte_atomic64_sub(v, 1);
241 }
242
243 /**
244  * Add a 64-bit value to an atomic counter and return the result.
245  *
246  * Atomically adds the 64-bit value (inc) to the atomic counter (v) and
247  * returns the value of v after the addition.
248  *
249  * @param v
250  *   A pointer to the atomic counter.
251  * @param inc
252  *   The value to be added to the counter.
253  * @return
254  *   The value of v after the addition.
255  */
256 static inline int64_t
257 rte_atomic64_add_return(rte_atomic64_t *v, int64_t inc)
258 {
259         int success = 0;
260         uint64_t tmp;
261
262         while (success == 0) {
263                 tmp = v->cnt;
264                 success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
265                                               tmp, tmp + inc);
266         }
267
268         return tmp + inc;
269 }
270
271 /**
272  * Subtract a 64-bit value from an atomic counter and return the result.
273  *
274  * Atomically subtracts the 64-bit value (dec) from the atomic counter (v)
275  * and returns the value of v after the substraction.
276  *
277  * @param v
278  *   A pointer to the atomic counter.
279  * @param dec
280  *   The value to be substracted from the counter.
281  * @return
282  *   The value of v after the substraction.
283  */
284 static inline int64_t
285 rte_atomic64_sub_return(rte_atomic64_t *v, int64_t dec)
286 {
287         int success = 0;
288         uint64_t tmp;
289
290         while (success == 0) {
291                 tmp = v->cnt;
292                 success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
293                                               tmp, tmp - dec);
294         }
295
296         return tmp - dec;
297 }
298
299 /**
300  * Atomically increment a 64-bit counter by one and test.
301  *
302  * Atomically increments the atomic counter (v) by one and returns
303  * true if the result is 0, or false in all other cases.
304  *
305  * @param v
306  *   A pointer to the atomic counter.
307  * @return
308  *   True if the result after the addition is 0; false otherwise.
309  */
310 static inline int rte_atomic64_inc_and_test(rte_atomic64_t *v)
311 {
312         return rte_atomic64_add_return(v, 1) == 0;
313 }
314
315 /**
316  * Atomically decrement a 64-bit counter by one and test.
317  *
318  * Atomically decrements the atomic counter (v) by one and returns true if
319  * the result is 0, or false in all other cases.
320  *
321  * @param v
322  *   A pointer to the atomic counter.
323  * @return
324  *   True if the result after substraction is 0; false otherwise.
325  */
326 static inline int rte_atomic64_dec_and_test(rte_atomic64_t *v)
327 {
328         return rte_atomic64_sub_return(v, 1) == 0;
329 }
330
331 /**
332  * Atomically test and set a 64-bit atomic counter.
333  *
334  * If the counter value is already set, return 0 (failed). Otherwise, set
335  * the counter value to 1 and return 1 (success).
336  *
337  * @param v
338  *   A pointer to the atomic counter.
339  * @return
340  *   0 if failed; else 1, success.
341  */
342 static inline int rte_atomic64_test_and_set(rte_atomic64_t *v)
343 {
344         return rte_atomic64_cmpset((volatile uint64_t *)&v->cnt, 0, 1);
345 }
346
347 /**
348  * Atomically set a 64-bit counter to 0.
349  *
350  * @param v
351  *   A pointer to the atomic counter.
352  */
353 static inline void rte_atomic64_clear(rte_atomic64_t *v)
354 {
355         rte_atomic64_set(v, 0);
356 }
357
358 #endif /* _RTE_I686_ATOMIC_H_ */