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