blob: 1d98c9eb6eacb284dcf846c703bd8979ce4b9fc2 [file] [log] [blame]
Ingo Molnarb7882b72009-07-03 13:26:39 +02001#include <linux/compiler.h>
Ingo Molnar1fde9022009-07-03 17:28:57 +02002#include <linux/module.h>
Ingo Molnarb7882b72009-07-03 13:26:39 +02003#include <linux/types.h>
Ingo Molnar1fde9022009-07-03 17:28:57 +02004
Ingo Molnarb7882b72009-07-03 13:26:39 +02005#include <asm/processor.h>
6#include <asm/cmpxchg.h>
7#include <asm/atomic.h>
8
Ingo Molnar3ac805d2009-07-03 12:51:19 +02009static noinline u64 cmpxchg8b(u64 *ptr, u64 old, u64 new)
Ingo Molnarb7882b72009-07-03 13:26:39 +020010{
Eric Dumazet69237f92009-07-03 13:26:41 +020011 u32 low = new;
12 u32 high = new >> 32;
13
Ingo Molnarb7882b72009-07-03 13:26:39 +020014 asm volatile(
Eric Dumazet69237f92009-07-03 13:26:41 +020015 LOCK_PREFIX "cmpxchg8b %1\n"
16 : "+A" (old), "+m" (*ptr)
17 : "b" (low), "c" (high)
18 );
Ingo Molnarb7882b72009-07-03 13:26:39 +020019 return old;
20}
21
22u64 atomic64_cmpxchg(atomic64_t *ptr, u64 old_val, u64 new_val)
23{
24 return cmpxchg8b(&ptr->counter, old_val, new_val);
25}
Ingo Molnar1fde9022009-07-03 17:28:57 +020026EXPORT_SYMBOL(atomic64_cmpxchg);
Ingo Molnarb7882b72009-07-03 13:26:39 +020027
28/**
29 * atomic64_xchg - xchg atomic64 variable
30 * @ptr: pointer to type atomic64_t
31 * @new_val: value to assign
32 *
33 * Atomically xchgs the value of @ptr to @new_val and returns
34 * the old value.
35 */
Ingo Molnarb7882b72009-07-03 13:26:39 +020036u64 atomic64_xchg(atomic64_t *ptr, u64 new_val)
37{
Ingo Molnar3a8d1782009-07-03 19:56:36 +020038 /*
39 * Try first with a (possibly incorrect) assumption about
40 * what we have there. We'll do two loops most likely,
41 * but we'll get an ownership MESI transaction straight away
42 * instead of a read transaction followed by a
43 * flush-for-ownership transaction:
44 */
45 u64 old_val, real_val = 0;
Ingo Molnarb7882b72009-07-03 13:26:39 +020046
47 do {
Ingo Molnar3a8d1782009-07-03 19:56:36 +020048 old_val = real_val;
49
50 real_val = atomic64_cmpxchg(ptr, old_val, new_val);
51
52 } while (real_val != old_val);
Ingo Molnarb7882b72009-07-03 13:26:39 +020053
54 return old_val;
55}
Ingo Molnar1fde9022009-07-03 17:28:57 +020056EXPORT_SYMBOL(atomic64_xchg);
Ingo Molnarb7882b72009-07-03 13:26:39 +020057
58/**
59 * atomic64_set - set atomic64 variable
60 * @ptr: pointer to type atomic64_t
61 * @new_val: value to assign
62 *
63 * Atomically sets the value of @ptr to @new_val.
64 */
65void atomic64_set(atomic64_t *ptr, u64 new_val)
66{
67 atomic64_xchg(ptr, new_val);
68}
Ingo Molnar1fde9022009-07-03 17:28:57 +020069EXPORT_SYMBOL(atomic64_read);
Ingo Molnarb7882b72009-07-03 13:26:39 +020070
71/**
72 * atomic64_read - read atomic64 variable
73 * @ptr: pointer to type atomic64_t
74 *
75 * Atomically reads the value of @ptr and returns it.
76 */
77u64 atomic64_read(atomic64_t *ptr)
78{
Eric Dumazet67d71782009-07-03 13:23:02 +020079 u64 res;
Ingo Molnarb7882b72009-07-03 13:26:39 +020080
Eric Dumazet67d71782009-07-03 13:23:02 +020081 asm volatile(
82 "mov %%ebx, %%eax\n\t"
83 "mov %%ecx, %%edx\n\t"
84 LOCK_PREFIX "cmpxchg8b %1\n"
85 : "+A" (res)
86 : "m" (*ptr)
87 );
88
89 return res;
Ingo Molnarb7882b72009-07-03 13:26:39 +020090}
Ingo Molnar1fde9022009-07-03 17:28:57 +020091EXPORT_SYMBOL(atomic64_read);
Ingo Molnarb7882b72009-07-03 13:26:39 +020092
93/**
94 * atomic64_add_return - add and return
95 * @delta: integer value to add
96 * @ptr: pointer to type atomic64_t
97 *
98 * Atomically adds @delta to @ptr and returns @delta + *@ptr
99 */
Ingo Molnar3ac805d2009-07-03 12:51:19 +0200100noinline u64 atomic64_add_return(u64 delta, atomic64_t *ptr)
Ingo Molnarb7882b72009-07-03 13:26:39 +0200101{
Ingo Molnar824975e2009-07-03 12:39:07 +0200102 /*
Ingo Molnar3a8d1782009-07-03 19:56:36 +0200103 * Try first with a (possibly incorrect) assumption about
Ingo Molnar824975e2009-07-03 12:39:07 +0200104 * what we have there. We'll do two loops most likely,
105 * but we'll get an ownership MESI transaction straight away
106 * instead of a read transaction followed by a
107 * flush-for-ownership transaction:
108 */
Ingo Molnar3a8d1782009-07-03 19:56:36 +0200109 u64 old_val, new_val, real_val = 0;
Ingo Molnarb7882b72009-07-03 13:26:39 +0200110
111 do {
Ingo Molnar824975e2009-07-03 12:39:07 +0200112 old_val = real_val;
Ingo Molnarb7882b72009-07-03 13:26:39 +0200113 new_val = old_val + delta;
114
Ingo Molnar824975e2009-07-03 12:39:07 +0200115 real_val = atomic64_cmpxchg(ptr, old_val, new_val);
116
117 } while (real_val != old_val);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200118
119 return new_val;
120}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200121EXPORT_SYMBOL(atomic64_add_return);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200122
123u64 atomic64_sub_return(u64 delta, atomic64_t *ptr)
124{
125 return atomic64_add_return(-delta, ptr);
126}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200127EXPORT_SYMBOL(atomic64_sub_return);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200128
129u64 atomic64_inc_return(atomic64_t *ptr)
130{
131 return atomic64_add_return(1, ptr);
132}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200133EXPORT_SYMBOL(atomic64_inc_return);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200134
135u64 atomic64_dec_return(atomic64_t *ptr)
136{
137 return atomic64_sub_return(1, ptr);
138}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200139EXPORT_SYMBOL(atomic64_dec_return);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200140
141/**
142 * atomic64_add - add integer to atomic64 variable
143 * @delta: integer value to add
144 * @ptr: pointer to type atomic64_t
145 *
146 * Atomically adds @delta to @ptr.
147 */
148void atomic64_add(u64 delta, atomic64_t *ptr)
149{
150 atomic64_add_return(delta, ptr);
151}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200152EXPORT_SYMBOL(atomic64_add);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200153
154/**
155 * atomic64_sub - subtract the atomic64 variable
156 * @delta: integer value to subtract
157 * @ptr: pointer to type atomic64_t
158 *
159 * Atomically subtracts @delta from @ptr.
160 */
161void atomic64_sub(u64 delta, atomic64_t *ptr)
162{
163 atomic64_add(-delta, ptr);
164}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200165EXPORT_SYMBOL(atomic64_sub);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200166
167/**
168 * atomic64_sub_and_test - subtract value from variable and test result
169 * @delta: integer value to subtract
170 * @ptr: pointer to type atomic64_t
171 *
172 * Atomically subtracts @delta from @ptr and returns
173 * true if the result is zero, or false for all
174 * other cases.
175 */
176int atomic64_sub_and_test(u64 delta, atomic64_t *ptr)
177{
Ingo Molnarddf9a002009-07-03 20:11:30 +0200178 u64 new_val = atomic64_sub_return(delta, ptr);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200179
Ingo Molnarddf9a002009-07-03 20:11:30 +0200180 return new_val == 0;
Ingo Molnarb7882b72009-07-03 13:26:39 +0200181}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200182EXPORT_SYMBOL(atomic64_sub_and_test);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200183
184/**
185 * atomic64_inc - increment atomic64 variable
186 * @ptr: pointer to type atomic64_t
187 *
188 * Atomically increments @ptr by 1.
189 */
190void atomic64_inc(atomic64_t *ptr)
191{
192 atomic64_add(1, ptr);
193}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200194EXPORT_SYMBOL(atomic64_inc);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200195
196/**
197 * atomic64_dec - decrement atomic64 variable
198 * @ptr: pointer to type atomic64_t
199 *
200 * Atomically decrements @ptr by 1.
201 */
202void atomic64_dec(atomic64_t *ptr)
203{
204 atomic64_sub(1, ptr);
205}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200206EXPORT_SYMBOL(atomic64_dec);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200207
208/**
209 * atomic64_dec_and_test - decrement and test
210 * @ptr: pointer to type atomic64_t
211 *
212 * Atomically decrements @ptr by 1 and
213 * returns true if the result is 0, or false for all other
214 * cases.
215 */
216int atomic64_dec_and_test(atomic64_t *ptr)
217{
218 return atomic64_sub_and_test(1, ptr);
219}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200220EXPORT_SYMBOL(atomic64_dec_and_test);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200221
222/**
223 * atomic64_inc_and_test - increment and test
224 * @ptr: pointer to type atomic64_t
225 *
226 * Atomically increments @ptr by 1
227 * and returns true if the result is zero, or false for all
228 * other cases.
229 */
230int atomic64_inc_and_test(atomic64_t *ptr)
231{
232 return atomic64_sub_and_test(-1, ptr);
233}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200234EXPORT_SYMBOL(atomic64_inc_and_test);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200235
236/**
237 * atomic64_add_negative - add and test if negative
238 * @delta: integer value to add
239 * @ptr: pointer to type atomic64_t
240 *
241 * Atomically adds @delta to @ptr and returns true
242 * if the result is negative, or false when
243 * result is greater than or equal to zero.
244 */
245int atomic64_add_negative(u64 delta, atomic64_t *ptr)
246{
Ingo Molnarddf9a002009-07-03 20:11:30 +0200247 s64 new_val = atomic64_add_return(delta, ptr);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200248
Ingo Molnarddf9a002009-07-03 20:11:30 +0200249 return new_val < 0;
Ingo Molnarb7882b72009-07-03 13:26:39 +0200250}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200251EXPORT_SYMBOL(atomic64_add_negative);