blob: 6722a092e407e8914823909f17ff1359caac71da [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 */
36
37u64 atomic64_xchg(atomic64_t *ptr, u64 new_val)
38{
39 u64 old_val;
40
41 do {
Ingo Molnar199e2372009-07-03 13:02:39 +020042 old_val = __atomic64_read(ptr);
Ingo Molnarb7882b72009-07-03 13:26:39 +020043 } while (atomic64_cmpxchg(ptr, old_val, new_val) != old_val);
44
45 return old_val;
46}
Ingo Molnar1fde9022009-07-03 17:28:57 +020047EXPORT_SYMBOL(atomic64_xchg);
Ingo Molnarb7882b72009-07-03 13:26:39 +020048
49/**
50 * atomic64_set - set atomic64 variable
51 * @ptr: pointer to type atomic64_t
52 * @new_val: value to assign
53 *
54 * Atomically sets the value of @ptr to @new_val.
55 */
56void atomic64_set(atomic64_t *ptr, u64 new_val)
57{
58 atomic64_xchg(ptr, new_val);
59}
Ingo Molnar1fde9022009-07-03 17:28:57 +020060EXPORT_SYMBOL(atomic64_read);
Ingo Molnarb7882b72009-07-03 13:26:39 +020061
62/**
63 * atomic64_read - read atomic64 variable
64 * @ptr: pointer to type atomic64_t
65 *
66 * Atomically reads the value of @ptr and returns it.
67 */
68u64 atomic64_read(atomic64_t *ptr)
69{
Eric Dumazet67d71782009-07-03 13:23:02 +020070 u64 res;
Ingo Molnarb7882b72009-07-03 13:26:39 +020071
Eric Dumazet67d71782009-07-03 13:23:02 +020072 asm volatile(
73 "mov %%ebx, %%eax\n\t"
74 "mov %%ecx, %%edx\n\t"
75 LOCK_PREFIX "cmpxchg8b %1\n"
76 : "+A" (res)
77 : "m" (*ptr)
78 );
79
80 return res;
Ingo Molnarb7882b72009-07-03 13:26:39 +020081}
Ingo Molnar1fde9022009-07-03 17:28:57 +020082EXPORT_SYMBOL(atomic64_read);
Ingo Molnarb7882b72009-07-03 13:26:39 +020083
84/**
85 * atomic64_add_return - add and return
86 * @delta: integer value to add
87 * @ptr: pointer to type atomic64_t
88 *
89 * Atomically adds @delta to @ptr and returns @delta + *@ptr
90 */
Ingo Molnar3ac805d2009-07-03 12:51:19 +020091noinline u64 atomic64_add_return(u64 delta, atomic64_t *ptr)
Ingo Molnarb7882b72009-07-03 13:26:39 +020092{
Ingo Molnar824975e2009-07-03 12:39:07 +020093 /*
94 * Try first with a (probably incorrect) assumption about
95 * what we have there. We'll do two loops most likely,
96 * but we'll get an ownership MESI transaction straight away
97 * instead of a read transaction followed by a
98 * flush-for-ownership transaction:
99 */
100 u64 old_val, new_val, real_val = 1ULL << 32;
Ingo Molnarb7882b72009-07-03 13:26:39 +0200101
102 do {
Ingo Molnar824975e2009-07-03 12:39:07 +0200103 old_val = real_val;
Ingo Molnarb7882b72009-07-03 13:26:39 +0200104 new_val = old_val + delta;
105
Ingo Molnar824975e2009-07-03 12:39:07 +0200106 real_val = atomic64_cmpxchg(ptr, old_val, new_val);
107
108 } while (real_val != old_val);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200109
110 return new_val;
111}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200112EXPORT_SYMBOL(atomic64_add_return);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200113
114u64 atomic64_sub_return(u64 delta, atomic64_t *ptr)
115{
116 return atomic64_add_return(-delta, ptr);
117}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200118EXPORT_SYMBOL(atomic64_sub_return);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200119
120u64 atomic64_inc_return(atomic64_t *ptr)
121{
122 return atomic64_add_return(1, ptr);
123}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200124EXPORT_SYMBOL(atomic64_inc_return);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200125
126u64 atomic64_dec_return(atomic64_t *ptr)
127{
128 return atomic64_sub_return(1, ptr);
129}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200130EXPORT_SYMBOL(atomic64_dec_return);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200131
132/**
133 * atomic64_add - add integer to atomic64 variable
134 * @delta: integer value to add
135 * @ptr: pointer to type atomic64_t
136 *
137 * Atomically adds @delta to @ptr.
138 */
139void atomic64_add(u64 delta, atomic64_t *ptr)
140{
141 atomic64_add_return(delta, ptr);
142}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200143EXPORT_SYMBOL(atomic64_add);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200144
145/**
146 * atomic64_sub - subtract the atomic64 variable
147 * @delta: integer value to subtract
148 * @ptr: pointer to type atomic64_t
149 *
150 * Atomically subtracts @delta from @ptr.
151 */
152void atomic64_sub(u64 delta, atomic64_t *ptr)
153{
154 atomic64_add(-delta, ptr);
155}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200156EXPORT_SYMBOL(atomic64_sub);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200157
158/**
159 * atomic64_sub_and_test - subtract value from variable and test result
160 * @delta: integer value to subtract
161 * @ptr: pointer to type atomic64_t
162 *
163 * Atomically subtracts @delta from @ptr and returns
164 * true if the result is zero, or false for all
165 * other cases.
166 */
167int atomic64_sub_and_test(u64 delta, atomic64_t *ptr)
168{
169 u64 old_val = atomic64_sub_return(delta, ptr);
170
171 return old_val == 0;
172}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200173EXPORT_SYMBOL(atomic64_sub_and_test);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200174
175/**
176 * atomic64_inc - increment atomic64 variable
177 * @ptr: pointer to type atomic64_t
178 *
179 * Atomically increments @ptr by 1.
180 */
181void atomic64_inc(atomic64_t *ptr)
182{
183 atomic64_add(1, ptr);
184}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200185EXPORT_SYMBOL(atomic64_inc);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200186
187/**
188 * atomic64_dec - decrement atomic64 variable
189 * @ptr: pointer to type atomic64_t
190 *
191 * Atomically decrements @ptr by 1.
192 */
193void atomic64_dec(atomic64_t *ptr)
194{
195 atomic64_sub(1, ptr);
196}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200197EXPORT_SYMBOL(atomic64_dec);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200198
199/**
200 * atomic64_dec_and_test - decrement and test
201 * @ptr: pointer to type atomic64_t
202 *
203 * Atomically decrements @ptr by 1 and
204 * returns true if the result is 0, or false for all other
205 * cases.
206 */
207int atomic64_dec_and_test(atomic64_t *ptr)
208{
209 return atomic64_sub_and_test(1, ptr);
210}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200211EXPORT_SYMBOL(atomic64_dec_and_test);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200212
213/**
214 * atomic64_inc_and_test - increment and test
215 * @ptr: pointer to type atomic64_t
216 *
217 * Atomically increments @ptr by 1
218 * and returns true if the result is zero, or false for all
219 * other cases.
220 */
221int atomic64_inc_and_test(atomic64_t *ptr)
222{
223 return atomic64_sub_and_test(-1, ptr);
224}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200225EXPORT_SYMBOL(atomic64_inc_and_test);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200226
227/**
228 * atomic64_add_negative - add and test if negative
229 * @delta: integer value to add
230 * @ptr: pointer to type atomic64_t
231 *
232 * Atomically adds @delta to @ptr and returns true
233 * if the result is negative, or false when
234 * result is greater than or equal to zero.
235 */
236int atomic64_add_negative(u64 delta, atomic64_t *ptr)
237{
238 long long old_val = atomic64_add_return(delta, ptr);
239
240 return old_val < 0;
241}
Ingo Molnar1fde9022009-07-03 17:28:57 +0200242EXPORT_SYMBOL(atomic64_add_negative);