blob: dc5a667ff791388074804ee547b92370c07f59c6 [file] [log] [blame]
H. Peter Anvin1965aae2008-10-22 22:26:29 -07001#ifndef _ASM_X86_ATOMIC_32_H
2#define _ASM_X86_ATOMIC_32_H
Linus Torvalds1da177e2005-04-16 15:20:36 -07003
Linus Torvalds1da177e2005-04-16 15:20:36 -07004#include <linux/compiler.h>
Matthew Wilcoxea4354672009-01-06 14:40:39 -08005#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006#include <asm/processor.h>
Jeff Dikea436ed92007-05-08 00:35:02 -07007#include <asm/cmpxchg.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008
9/*
10 * Atomic operations that C can't guarantee us. Useful for
11 * resource counting etc..
12 */
13
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#define ATOMIC_INIT(i) { (i) }
15
16/**
17 * atomic_read - read atomic variable
18 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -070019 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 * Atomically reads the value of @v.
Joe Perches78ff12e2008-03-23 01:01:41 -070021 */
Ingo Molnar32171202009-07-03 13:06:01 +020022static inline int atomic_read(const atomic_t *v)
23{
24 return v->counter;
25}
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27/**
28 * atomic_set - set atomic variable
29 * @v: pointer of type atomic_t
30 * @i: required value
Joe Perches78ff12e2008-03-23 01:01:41 -070031 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070032 * Atomically sets the value of @v to @i.
Joe Perches78ff12e2008-03-23 01:01:41 -070033 */
Paul Mackerras8e049ef2009-07-02 08:57:12 +100034static inline void atomic_set(atomic_t *v, int i)
35{
36 v->counter = i;
37}
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
39/**
40 * atomic_add - add integer to atomic variable
41 * @i: integer value to add
42 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -070043 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 * Atomically adds @i to @v.
45 */
Joe Perches78ff12e2008-03-23 01:01:41 -070046static inline void atomic_add(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047{
Joe Perches78ff12e2008-03-23 01:01:41 -070048 asm volatile(LOCK_PREFIX "addl %1,%0"
49 : "+m" (v->counter)
50 : "ir" (i));
Linus Torvalds1da177e2005-04-16 15:20:36 -070051}
52
53/**
Robert P. J. Daycc386822007-05-08 00:35:08 -070054 * atomic_sub - subtract integer from atomic variable
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 * @i: integer value to subtract
56 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -070057 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 * Atomically subtracts @i from @v.
59 */
Joe Perches78ff12e2008-03-23 01:01:41 -070060static inline void atomic_sub(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -070061{
Joe Perches78ff12e2008-03-23 01:01:41 -070062 asm volatile(LOCK_PREFIX "subl %1,%0"
63 : "+m" (v->counter)
64 : "ir" (i));
Linus Torvalds1da177e2005-04-16 15:20:36 -070065}
66
67/**
68 * atomic_sub_and_test - subtract value from variable and test result
69 * @i: integer value to subtract
70 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -070071 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 * Atomically subtracts @i from @v and returns
73 * true if the result is zero, or false for all
74 * other cases.
75 */
Joe Perches78ff12e2008-03-23 01:01:41 -070076static inline int atomic_sub_and_test(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -070077{
78 unsigned char c;
79
Joe Perches78ff12e2008-03-23 01:01:41 -070080 asm volatile(LOCK_PREFIX "subl %2,%0; sete %1"
81 : "+m" (v->counter), "=qm" (c)
82 : "ir" (i) : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 return c;
84}
85
86/**
87 * atomic_inc - increment atomic variable
88 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -070089 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 * Atomically increments @v by 1.
Joe Perches78ff12e2008-03-23 01:01:41 -070091 */
92static inline void atomic_inc(atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -070093{
Joe Perches78ff12e2008-03-23 01:01:41 -070094 asm volatile(LOCK_PREFIX "incl %0"
95 : "+m" (v->counter));
Linus Torvalds1da177e2005-04-16 15:20:36 -070096}
97
98/**
99 * atomic_dec - decrement atomic variable
100 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -0700101 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 * Atomically decrements @v by 1.
Joe Perches78ff12e2008-03-23 01:01:41 -0700103 */
104static inline void atomic_dec(atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105{
Joe Perches78ff12e2008-03-23 01:01:41 -0700106 asm volatile(LOCK_PREFIX "decl %0"
107 : "+m" (v->counter));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108}
109
110/**
111 * atomic_dec_and_test - decrement and test
112 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -0700113 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 * Atomically decrements @v by 1 and
115 * returns true if the result is 0, or false for all other
116 * cases.
Joe Perches78ff12e2008-03-23 01:01:41 -0700117 */
118static inline int atomic_dec_and_test(atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119{
120 unsigned char c;
121
Joe Perches78ff12e2008-03-23 01:01:41 -0700122 asm volatile(LOCK_PREFIX "decl %0; sete %1"
123 : "+m" (v->counter), "=qm" (c)
124 : : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 return c != 0;
126}
127
128/**
Joe Perches78ff12e2008-03-23 01:01:41 -0700129 * atomic_inc_and_test - increment and test
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -0700131 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 * Atomically increments @v by 1
133 * and returns true if the result is zero, or false for all
134 * other cases.
Joe Perches78ff12e2008-03-23 01:01:41 -0700135 */
136static inline int atomic_inc_and_test(atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
138 unsigned char c;
139
Joe Perches78ff12e2008-03-23 01:01:41 -0700140 asm volatile(LOCK_PREFIX "incl %0; sete %1"
141 : "+m" (v->counter), "=qm" (c)
142 : : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 return c != 0;
144}
145
146/**
147 * atomic_add_negative - add and test if negative
148 * @v: pointer of type atomic_t
149 * @i: integer value to add
Joe Perches78ff12e2008-03-23 01:01:41 -0700150 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 * Atomically adds @i to @v and returns true
152 * if the result is negative, or false when
153 * result is greater than or equal to zero.
Joe Perches78ff12e2008-03-23 01:01:41 -0700154 */
155static inline int atomic_add_negative(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156{
157 unsigned char c;
158
Joe Perches78ff12e2008-03-23 01:01:41 -0700159 asm volatile(LOCK_PREFIX "addl %2,%0; sets %1"
160 : "+m" (v->counter), "=qm" (c)
161 : "ir" (i) : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 return c;
163}
164
165/**
Robert P. J. Daycc386822007-05-08 00:35:08 -0700166 * atomic_add_return - add integer and return
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 * @v: pointer of type atomic_t
168 * @i: integer value to add
169 *
170 * Atomically adds @i to @v and returns @i + @v
171 */
Joe Perches78ff12e2008-03-23 01:01:41 -0700172static inline int atomic_add_return(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173{
174 int __i;
175#ifdef CONFIG_M386
lepton1bb858f2006-04-18 22:21:10 -0700176 unsigned long flags;
Joe Perches78ff12e2008-03-23 01:01:41 -0700177 if (unlikely(boot_cpu_data.x86 <= 3))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 goto no_xadd;
179#endif
180 /* Modern 486+ processor */
181 __i = i;
Joe Perches78ff12e2008-03-23 01:01:41 -0700182 asm volatile(LOCK_PREFIX "xaddl %0, %1"
183 : "+r" (i), "+m" (v->counter)
184 : : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 return i + __i;
186
187#ifdef CONFIG_M386
188no_xadd: /* Legacy 386 processor */
lepton1bb858f2006-04-18 22:21:10 -0700189 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 __i = atomic_read(v);
191 atomic_set(v, i + __i);
lepton1bb858f2006-04-18 22:21:10 -0700192 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 return i + __i;
194#endif
195}
196
Robert P. J. Daycc386822007-05-08 00:35:08 -0700197/**
198 * atomic_sub_return - subtract integer and return
199 * @v: pointer of type atomic_t
200 * @i: integer value to subtract
201 *
202 * Atomically subtracts @i from @v and returns @v - @i
203 */
Joe Perches78ff12e2008-03-23 01:01:41 -0700204static inline int atomic_sub_return(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205{
Joe Perches78ff12e2008-03-23 01:01:41 -0700206 return atomic_add_return(-i, v);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207}
208
Paul Mackerras8e049ef2009-07-02 08:57:12 +1000209static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
210{
211 return cmpxchg(&v->counter, old, new);
212}
213
214static inline int atomic_xchg(atomic_t *v, int new)
215{
216 return xchg(&v->counter, new);
217}
Nick Piggin4a6dae62005-11-13 16:07:24 -0800218
Nick Piggin8426e1f2005-11-13 16:07:25 -0800219/**
Robert P. J. Day72fd4a32007-02-10 01:45:59 -0800220 * atomic_add_unless - add unless the number is already a given value
Nick Piggin8426e1f2005-11-13 16:07:25 -0800221 * @v: pointer of type atomic_t
222 * @a: the amount to add to v...
223 * @u: ...unless v is equal to u.
224 *
Robert P. J. Day72fd4a32007-02-10 01:45:59 -0800225 * Atomically adds @a to @v, so long as @v was not already @u.
Nick Piggin8426e1f2005-11-13 16:07:25 -0800226 * Returns non-zero if @v was not @u, and zero otherwise.
227 */
Joe Perches78ff12e2008-03-23 01:01:41 -0700228static inline int atomic_add_unless(atomic_t *v, int a, int u)
Mathieu Desnoyers2856f5e2007-05-08 00:34:38 -0700229{
230 int c, old;
231 c = atomic_read(v);
232 for (;;) {
233 if (unlikely(c == (u)))
234 break;
235 old = atomic_cmpxchg((v), c, c + (a));
236 if (likely(old == c))
237 break;
238 c = old;
239 }
240 return c != (u);
241}
242
Nick Piggin8426e1f2005-11-13 16:07:25 -0800243#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
244
Joe Perches78ff12e2008-03-23 01:01:41 -0700245#define atomic_inc_return(v) (atomic_add_return(1, v))
246#define atomic_dec_return(v) (atomic_sub_return(1, v))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
248/* These are x86-specific, used by some header files */
Joe Perches78ff12e2008-03-23 01:01:41 -0700249#define atomic_clear_mask(mask, addr) \
250 asm volatile(LOCK_PREFIX "andl %0,%1" \
251 : : "r" (~(mask)), "m" (*(addr)) : "memory")
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
Joe Perches78ff12e2008-03-23 01:01:41 -0700253#define atomic_set_mask(mask, addr) \
254 asm volatile(LOCK_PREFIX "orl %0,%1" \
255 : : "r" (mask), "m" (*(addr)) : "memory")
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
257/* Atomic operations are already serializing on x86 */
258#define smp_mb__before_atomic_dec() barrier()
259#define smp_mb__after_atomic_dec() barrier()
260#define smp_mb__before_atomic_inc() barrier()
261#define smp_mb__after_atomic_inc() barrier()
262
Ingo Molnar9b194e82008-12-14 20:22:35 +0100263/* An 64bit atomic type */
264
265typedef struct {
Ingo Molnarb7882b72009-07-03 13:26:39 +0200266 u64 __aligned(8) counter;
Ingo Molnar9b194e82008-12-14 20:22:35 +0100267} atomic64_t;
268
269#define ATOMIC64_INIT(val) { (val) }
270
Ingo Molnarb7882b72009-07-03 13:26:39 +0200271extern u64 atomic64_cmpxchg(atomic64_t *ptr, u64 old_val, u64 new_val);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100272
273/**
Ingo Molnar98c2aaf2009-04-07 11:30:17 +0200274 * atomic64_xchg - xchg atomic64 variable
275 * @ptr: pointer to type atomic64_t
276 * @new_val: value to assign
Ingo Molnar98c2aaf2009-04-07 11:30:17 +0200277 *
278 * Atomically xchgs the value of @ptr to @new_val and returns
279 * the old value.
280 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200281extern u64 atomic64_xchg(atomic64_t *ptr, u64 new_val);
Ingo Molnar98c2aaf2009-04-07 11:30:17 +0200282
283/**
Ingo Molnar9b194e82008-12-14 20:22:35 +0100284 * atomic64_set - set atomic64 variable
285 * @ptr: pointer to type atomic64_t
286 * @new_val: value to assign
287 *
288 * Atomically sets the value of @ptr to @new_val.
289 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200290extern void atomic64_set(atomic64_t *ptr, u64 new_val);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100291
292/**
293 * atomic64_read - read atomic64 variable
294 * @ptr: pointer to type atomic64_t
295 *
296 * Atomically reads the value of @ptr and returns it.
297 */
Eric Dumazeta79f0da2009-07-03 16:50:10 +0200298static inline u64 atomic64_read(atomic64_t *ptr)
299{
300 u64 res;
301
302 /*
303 * Note, we inline this atomic64_t primitive because
304 * it only clobbers EAX/EDX and leaves the others
305 * untouched. We also (somewhat subtly) rely on the
306 * fact that cmpxchg8b returns the current 64-bit value
307 * of the memory location we are touching:
308 */
309 asm volatile(
310 "mov %%ebx, %%eax\n\t"
311 "mov %%ecx, %%edx\n\t"
312 LOCK_PREFIX "cmpxchg8b %1\n"
313 : "=&A" (res)
314 : "m" (*ptr)
315 );
316
317 return res;
318}
319
Ingo Molnarb7882b72009-07-03 13:26:39 +0200320extern u64 atomic64_read(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100321
322/**
323 * atomic64_add_return - add and return
324 * @delta: integer value to add
325 * @ptr: pointer to type atomic64_t
326 *
327 * Atomically adds @delta to @ptr and returns @delta + *@ptr
328 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200329extern u64 atomic64_add_return(u64 delta, atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100330
Ingo Molnarb7882b72009-07-03 13:26:39 +0200331/*
332 * Other variants with different arithmetic operators:
333 */
334extern u64 atomic64_sub_return(u64 delta, atomic64_t *ptr);
335extern u64 atomic64_inc_return(atomic64_t *ptr);
336extern u64 atomic64_dec_return(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100337
338/**
339 * atomic64_add - add integer to atomic64 variable
340 * @delta: integer value to add
341 * @ptr: pointer to type atomic64_t
342 *
343 * Atomically adds @delta to @ptr.
344 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200345extern void atomic64_add(u64 delta, atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100346
347/**
348 * atomic64_sub - subtract the atomic64 variable
349 * @delta: integer value to subtract
350 * @ptr: pointer to type atomic64_t
351 *
352 * Atomically subtracts @delta from @ptr.
353 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200354extern void atomic64_sub(u64 delta, atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100355
356/**
357 * atomic64_sub_and_test - subtract value from variable and test result
358 * @delta: integer value to subtract
359 * @ptr: pointer to type atomic64_t
360 *
361 * Atomically subtracts @delta from @ptr and returns
362 * true if the result is zero, or false for all
363 * other cases.
364 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200365extern int atomic64_sub_and_test(u64 delta, atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100366
367/**
368 * atomic64_inc - increment atomic64 variable
369 * @ptr: pointer to type atomic64_t
370 *
371 * Atomically increments @ptr by 1.
372 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200373extern void atomic64_inc(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100374
375/**
376 * atomic64_dec - decrement atomic64 variable
377 * @ptr: pointer to type atomic64_t
378 *
379 * Atomically decrements @ptr by 1.
380 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200381extern void atomic64_dec(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100382
383/**
384 * atomic64_dec_and_test - decrement and test
385 * @ptr: pointer to type atomic64_t
386 *
387 * Atomically decrements @ptr by 1 and
388 * returns true if the result is 0, or false for all other
389 * cases.
390 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200391extern int atomic64_dec_and_test(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100392
393/**
394 * atomic64_inc_and_test - increment and test
395 * @ptr: pointer to type atomic64_t
396 *
397 * Atomically increments @ptr by 1
398 * and returns true if the result is zero, or false for all
399 * other cases.
400 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200401extern int atomic64_inc_and_test(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100402
403/**
404 * atomic64_add_negative - add and test if negative
405 * @delta: integer value to add
406 * @ptr: pointer to type atomic64_t
407 *
408 * Atomically adds @delta to @ptr and returns true
409 * if the result is negative, or false when
410 * result is greater than or equal to zero.
411 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200412extern int atomic64_add_negative(u64 delta, atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100413
Arnd Bergmann72099ed2009-05-13 22:56:29 +0000414#include <asm-generic/atomic-long.h>
H. Peter Anvin1965aae2008-10-22 22:26:29 -0700415#endif /* _ASM_X86_ATOMIC_32_H */