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