blob: b551bb1ae3cf9e97a3c18f9571982b884bcdf9ab [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 */
34#define atomic_set(v, i) (((v)->counter) = (i))
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
36/**
37 * atomic_add - add integer to atomic variable
38 * @i: integer value to add
39 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -070040 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 * Atomically adds @i to @v.
42 */
Joe Perches78ff12e2008-03-23 01:01:41 -070043static inline void atomic_add(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -070044{
Joe Perches78ff12e2008-03-23 01:01:41 -070045 asm volatile(LOCK_PREFIX "addl %1,%0"
46 : "+m" (v->counter)
47 : "ir" (i));
Linus Torvalds1da177e2005-04-16 15:20:36 -070048}
49
50/**
Robert P. J. Daycc386822007-05-08 00:35:08 -070051 * atomic_sub - subtract integer from atomic variable
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 * @i: integer value to subtract
53 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -070054 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 * Atomically subtracts @i from @v.
56 */
Joe Perches78ff12e2008-03-23 01:01:41 -070057static inline void atomic_sub(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -070058{
Joe Perches78ff12e2008-03-23 01:01:41 -070059 asm volatile(LOCK_PREFIX "subl %1,%0"
60 : "+m" (v->counter)
61 : "ir" (i));
Linus Torvalds1da177e2005-04-16 15:20:36 -070062}
63
64/**
65 * atomic_sub_and_test - subtract value from variable and test result
66 * @i: integer value to subtract
67 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -070068 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 * Atomically subtracts @i from @v and returns
70 * true if the result is zero, or false for all
71 * other cases.
72 */
Joe Perches78ff12e2008-03-23 01:01:41 -070073static inline int atomic_sub_and_test(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -070074{
75 unsigned char c;
76
Joe Perches78ff12e2008-03-23 01:01:41 -070077 asm volatile(LOCK_PREFIX "subl %2,%0; sete %1"
78 : "+m" (v->counter), "=qm" (c)
79 : "ir" (i) : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 return c;
81}
82
83/**
84 * atomic_inc - increment atomic variable
85 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -070086 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 * Atomically increments @v by 1.
Joe Perches78ff12e2008-03-23 01:01:41 -070088 */
89static inline void atomic_inc(atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -070090{
Joe Perches78ff12e2008-03-23 01:01:41 -070091 asm volatile(LOCK_PREFIX "incl %0"
92 : "+m" (v->counter));
Linus Torvalds1da177e2005-04-16 15:20:36 -070093}
94
95/**
96 * atomic_dec - decrement atomic variable
97 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -070098 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 * Atomically decrements @v by 1.
Joe Perches78ff12e2008-03-23 01:01:41 -0700100 */
101static inline void atomic_dec(atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102{
Joe Perches78ff12e2008-03-23 01:01:41 -0700103 asm volatile(LOCK_PREFIX "decl %0"
104 : "+m" (v->counter));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105}
106
107/**
108 * atomic_dec_and_test - decrement and test
109 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -0700110 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 * Atomically decrements @v by 1 and
112 * returns true if the result is 0, or false for all other
113 * cases.
Joe Perches78ff12e2008-03-23 01:01:41 -0700114 */
115static inline int atomic_dec_and_test(atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116{
117 unsigned char c;
118
Joe Perches78ff12e2008-03-23 01:01:41 -0700119 asm volatile(LOCK_PREFIX "decl %0; sete %1"
120 : "+m" (v->counter), "=qm" (c)
121 : : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 return c != 0;
123}
124
125/**
Joe Perches78ff12e2008-03-23 01:01:41 -0700126 * atomic_inc_and_test - increment and test
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 * @v: pointer of type atomic_t
Joe Perches78ff12e2008-03-23 01:01:41 -0700128 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 * Atomically increments @v by 1
130 * and returns true if the result is zero, or false for all
131 * other cases.
Joe Perches78ff12e2008-03-23 01:01:41 -0700132 */
133static inline int atomic_inc_and_test(atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134{
135 unsigned char c;
136
Joe Perches78ff12e2008-03-23 01:01:41 -0700137 asm volatile(LOCK_PREFIX "incl %0; sete %1"
138 : "+m" (v->counter), "=qm" (c)
139 : : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 return c != 0;
141}
142
143/**
144 * atomic_add_negative - add and test if negative
145 * @v: pointer of type atomic_t
146 * @i: integer value to add
Joe Perches78ff12e2008-03-23 01:01:41 -0700147 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 * Atomically adds @i to @v and returns true
149 * if the result is negative, or false when
150 * result is greater than or equal to zero.
Joe Perches78ff12e2008-03-23 01:01:41 -0700151 */
152static inline int atomic_add_negative(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153{
154 unsigned char c;
155
Joe Perches78ff12e2008-03-23 01:01:41 -0700156 asm volatile(LOCK_PREFIX "addl %2,%0; sets %1"
157 : "+m" (v->counter), "=qm" (c)
158 : "ir" (i) : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 return c;
160}
161
162/**
Robert P. J. Daycc386822007-05-08 00:35:08 -0700163 * atomic_add_return - add integer and return
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 * @v: pointer of type atomic_t
165 * @i: integer value to add
166 *
167 * Atomically adds @i to @v and returns @i + @v
168 */
Joe Perches78ff12e2008-03-23 01:01:41 -0700169static inline int atomic_add_return(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170{
171 int __i;
172#ifdef CONFIG_M386
lepton1bb858f2006-04-18 22:21:10 -0700173 unsigned long flags;
Joe Perches78ff12e2008-03-23 01:01:41 -0700174 if (unlikely(boot_cpu_data.x86 <= 3))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 goto no_xadd;
176#endif
177 /* Modern 486+ processor */
178 __i = i;
Joe Perches78ff12e2008-03-23 01:01:41 -0700179 asm volatile(LOCK_PREFIX "xaddl %0, %1"
180 : "+r" (i), "+m" (v->counter)
181 : : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 return i + __i;
183
184#ifdef CONFIG_M386
185no_xadd: /* Legacy 386 processor */
lepton1bb858f2006-04-18 22:21:10 -0700186 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 __i = atomic_read(v);
188 atomic_set(v, i + __i);
lepton1bb858f2006-04-18 22:21:10 -0700189 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 return i + __i;
191#endif
192}
193
Robert P. J. Daycc386822007-05-08 00:35:08 -0700194/**
195 * atomic_sub_return - subtract integer and return
196 * @v: pointer of type atomic_t
197 * @i: integer value to subtract
198 *
199 * Atomically subtracts @i from @v and returns @v - @i
200 */
Joe Perches78ff12e2008-03-23 01:01:41 -0700201static inline int atomic_sub_return(int i, atomic_t *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202{
Joe Perches78ff12e2008-03-23 01:01:41 -0700203 return atomic_add_return(-i, v);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204}
205
Mathieu Desnoyerse656e242007-05-08 00:34:20 -0700206#define atomic_cmpxchg(v, old, new) (cmpxchg(&((v)->counter), (old), (new)))
207#define atomic_xchg(v, new) (xchg(&((v)->counter), (new)))
Nick Piggin4a6dae62005-11-13 16:07:24 -0800208
Nick Piggin8426e1f2005-11-13 16:07:25 -0800209/**
Robert P. J. Day72fd4a32007-02-10 01:45:59 -0800210 * atomic_add_unless - add unless the number is already a given value
Nick Piggin8426e1f2005-11-13 16:07:25 -0800211 * @v: pointer of type atomic_t
212 * @a: the amount to add to v...
213 * @u: ...unless v is equal to u.
214 *
Robert P. J. Day72fd4a32007-02-10 01:45:59 -0800215 * Atomically adds @a to @v, so long as @v was not already @u.
Nick Piggin8426e1f2005-11-13 16:07:25 -0800216 * Returns non-zero if @v was not @u, and zero otherwise.
217 */
Joe Perches78ff12e2008-03-23 01:01:41 -0700218static inline int atomic_add_unless(atomic_t *v, int a, int u)
Mathieu Desnoyers2856f5e2007-05-08 00:34:38 -0700219{
220 int c, old;
221 c = atomic_read(v);
222 for (;;) {
223 if (unlikely(c == (u)))
224 break;
225 old = atomic_cmpxchg((v), c, c + (a));
226 if (likely(old == c))
227 break;
228 c = old;
229 }
230 return c != (u);
231}
232
Nick Piggin8426e1f2005-11-13 16:07:25 -0800233#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
234
Joe Perches78ff12e2008-03-23 01:01:41 -0700235#define atomic_inc_return(v) (atomic_add_return(1, v))
236#define atomic_dec_return(v) (atomic_sub_return(1, v))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
238/* These are x86-specific, used by some header files */
Joe Perches78ff12e2008-03-23 01:01:41 -0700239#define atomic_clear_mask(mask, addr) \
240 asm volatile(LOCK_PREFIX "andl %0,%1" \
241 : : "r" (~(mask)), "m" (*(addr)) : "memory")
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
Joe Perches78ff12e2008-03-23 01:01:41 -0700243#define atomic_set_mask(mask, addr) \
244 asm volatile(LOCK_PREFIX "orl %0,%1" \
245 : : "r" (mask), "m" (*(addr)) : "memory")
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247/* Atomic operations are already serializing on x86 */
248#define smp_mb__before_atomic_dec() barrier()
249#define smp_mb__after_atomic_dec() barrier()
250#define smp_mb__before_atomic_inc() barrier()
251#define smp_mb__after_atomic_inc() barrier()
252
Ingo Molnar9b194e82008-12-14 20:22:35 +0100253/* An 64bit atomic type */
254
255typedef struct {
Ingo Molnarb7882b72009-07-03 13:26:39 +0200256 u64 __aligned(8) counter;
Ingo Molnar9b194e82008-12-14 20:22:35 +0100257} atomic64_t;
258
259#define ATOMIC64_INIT(val) { (val) }
260
261/**
262 * atomic64_read - read atomic64 variable
Randy Dunlap46e44322009-06-13 20:00:54 -0700263 * @ptr: pointer of type atomic64_t
Ingo Molnar9b194e82008-12-14 20:22:35 +0100264 *
265 * Atomically reads the value of @v.
266 * Doesn't imply a read memory barrier.
267 */
268#define __atomic64_read(ptr) ((ptr)->counter)
269
Ingo Molnarb7882b72009-07-03 13:26:39 +0200270extern u64 atomic64_cmpxchg(atomic64_t *ptr, u64 old_val, u64 new_val);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100271
272/**
Ingo Molnar98c2aaf2009-04-07 11:30:17 +0200273 * atomic64_xchg - xchg atomic64 variable
274 * @ptr: pointer to type atomic64_t
275 * @new_val: value to assign
Ingo Molnar98c2aaf2009-04-07 11:30:17 +0200276 *
277 * Atomically xchgs the value of @ptr to @new_val and returns
278 * the old value.
279 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200280extern u64 atomic64_xchg(atomic64_t *ptr, u64 new_val);
Ingo Molnar98c2aaf2009-04-07 11:30:17 +0200281
282/**
Ingo Molnar9b194e82008-12-14 20:22:35 +0100283 * atomic64_set - set atomic64 variable
284 * @ptr: pointer to type atomic64_t
285 * @new_val: value to assign
286 *
287 * Atomically sets the value of @ptr to @new_val.
288 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200289extern void atomic64_set(atomic64_t *ptr, u64 new_val);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100290
291/**
292 * atomic64_read - read atomic64 variable
293 * @ptr: pointer to type atomic64_t
294 *
295 * Atomically reads the value of @ptr and returns it.
296 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200297extern u64 atomic64_read(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100298
299/**
300 * atomic64_add_return - add and return
301 * @delta: integer value to add
302 * @ptr: pointer to type atomic64_t
303 *
304 * Atomically adds @delta to @ptr and returns @delta + *@ptr
305 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200306extern u64 atomic64_add_return(u64 delta, atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100307
Ingo Molnarb7882b72009-07-03 13:26:39 +0200308/*
309 * Other variants with different arithmetic operators:
310 */
311extern u64 atomic64_sub_return(u64 delta, atomic64_t *ptr);
312extern u64 atomic64_inc_return(atomic64_t *ptr);
313extern u64 atomic64_dec_return(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100314
315/**
316 * atomic64_add - add integer to atomic64 variable
317 * @delta: integer value to add
318 * @ptr: pointer to type atomic64_t
319 *
320 * Atomically adds @delta to @ptr.
321 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200322extern void atomic64_add(u64 delta, atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100323
324/**
325 * atomic64_sub - subtract the atomic64 variable
326 * @delta: integer value to subtract
327 * @ptr: pointer to type atomic64_t
328 *
329 * Atomically subtracts @delta from @ptr.
330 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200331extern void atomic64_sub(u64 delta, atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100332
333/**
334 * atomic64_sub_and_test - subtract value from variable and test result
335 * @delta: integer value to subtract
336 * @ptr: pointer to type atomic64_t
337 *
338 * Atomically subtracts @delta from @ptr and returns
339 * true if the result is zero, or false for all
340 * other cases.
341 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200342extern int atomic64_sub_and_test(u64 delta, atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100343
344/**
345 * atomic64_inc - increment atomic64 variable
346 * @ptr: pointer to type atomic64_t
347 *
348 * Atomically increments @ptr by 1.
349 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200350extern void atomic64_inc(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100351
352/**
353 * atomic64_dec - decrement atomic64 variable
354 * @ptr: pointer to type atomic64_t
355 *
356 * Atomically decrements @ptr by 1.
357 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200358extern void atomic64_dec(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100359
360/**
361 * atomic64_dec_and_test - decrement and test
362 * @ptr: pointer to type atomic64_t
363 *
364 * Atomically decrements @ptr by 1 and
365 * returns true if the result is 0, or false for all other
366 * cases.
367 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200368extern int atomic64_dec_and_test(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100369
370/**
371 * atomic64_inc_and_test - increment and test
372 * @ptr: pointer to type atomic64_t
373 *
374 * Atomically increments @ptr by 1
375 * and returns true if the result is zero, or false for all
376 * other cases.
377 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200378extern int atomic64_inc_and_test(atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100379
380/**
381 * atomic64_add_negative - add and test if negative
382 * @delta: integer value to add
383 * @ptr: pointer to type atomic64_t
384 *
385 * Atomically adds @delta to @ptr and returns true
386 * if the result is negative, or false when
387 * result is greater than or equal to zero.
388 */
Ingo Molnarb7882b72009-07-03 13:26:39 +0200389extern int atomic64_add_negative(u64 delta, atomic64_t *ptr);
Ingo Molnar9b194e82008-12-14 20:22:35 +0100390
Arnd Bergmann72099ed2009-05-13 22:56:29 +0000391#include <asm-generic/atomic-long.h>
H. Peter Anvin1965aae2008-10-22 22:26:29 -0700392#endif /* _ASM_X86_ATOMIC_32_H */