blob: cd11803f9448fc034fc8bb284518e64e94e58dd3 [file] [log] [blame]
Ingo Molnarb7882b72009-07-03 13:26:39 +02001#include <linux/compiler.h>
2#include <linux/types.h>
3#include <asm/processor.h>
4#include <asm/cmpxchg.h>
5#include <asm/atomic.h>
6
Ingo Molnar3ac805d2009-07-03 12:51:19 +02007static noinline u64 cmpxchg8b(u64 *ptr, u64 old, u64 new)
Ingo Molnarb7882b72009-07-03 13:26:39 +02008{
Eric Dumazet69237f92009-07-03 13:26:41 +02009 u32 low = new;
10 u32 high = new >> 32;
11
Ingo Molnarb7882b72009-07-03 13:26:39 +020012 asm volatile(
Eric Dumazet69237f92009-07-03 13:26:41 +020013 LOCK_PREFIX "cmpxchg8b %1\n"
14 : "+A" (old), "+m" (*ptr)
15 : "b" (low), "c" (high)
16 );
Ingo Molnarb7882b72009-07-03 13:26:39 +020017 return old;
18}
19
20u64 atomic64_cmpxchg(atomic64_t *ptr, u64 old_val, u64 new_val)
21{
22 return cmpxchg8b(&ptr->counter, old_val, new_val);
23}
24
25/**
26 * atomic64_xchg - xchg atomic64 variable
27 * @ptr: pointer to type atomic64_t
28 * @new_val: value to assign
29 *
30 * Atomically xchgs the value of @ptr to @new_val and returns
31 * the old value.
32 */
33
34u64 atomic64_xchg(atomic64_t *ptr, u64 new_val)
35{
36 u64 old_val;
37
38 do {
Ingo Molnar199e2372009-07-03 13:02:39 +020039 old_val = __atomic64_read(ptr);
Ingo Molnarb7882b72009-07-03 13:26:39 +020040 } while (atomic64_cmpxchg(ptr, old_val, new_val) != old_val);
41
42 return old_val;
43}
44
45/**
46 * atomic64_set - set atomic64 variable
47 * @ptr: pointer to type atomic64_t
48 * @new_val: value to assign
49 *
50 * Atomically sets the value of @ptr to @new_val.
51 */
52void atomic64_set(atomic64_t *ptr, u64 new_val)
53{
54 atomic64_xchg(ptr, new_val);
55}
56
57/**
58 * atomic64_read - read atomic64 variable
59 * @ptr: pointer to type atomic64_t
60 *
61 * Atomically reads the value of @ptr and returns it.
62 */
63u64 atomic64_read(atomic64_t *ptr)
64{
Eric Dumazet67d71782009-07-03 13:23:02 +020065 u64 res;
Ingo Molnarb7882b72009-07-03 13:26:39 +020066
Eric Dumazet67d71782009-07-03 13:23:02 +020067 asm volatile(
68 "mov %%ebx, %%eax\n\t"
69 "mov %%ecx, %%edx\n\t"
70 LOCK_PREFIX "cmpxchg8b %1\n"
71 : "+A" (res)
72 : "m" (*ptr)
73 );
74
75 return res;
Ingo Molnarb7882b72009-07-03 13:26:39 +020076}
77
78/**
79 * atomic64_add_return - add and return
80 * @delta: integer value to add
81 * @ptr: pointer to type atomic64_t
82 *
83 * Atomically adds @delta to @ptr and returns @delta + *@ptr
84 */
Ingo Molnar3ac805d2009-07-03 12:51:19 +020085noinline u64 atomic64_add_return(u64 delta, atomic64_t *ptr)
Ingo Molnarb7882b72009-07-03 13:26:39 +020086{
Ingo Molnar824975e2009-07-03 12:39:07 +020087 /*
88 * Try first with a (probably incorrect) assumption about
89 * what we have there. We'll do two loops most likely,
90 * but we'll get an ownership MESI transaction straight away
91 * instead of a read transaction followed by a
92 * flush-for-ownership transaction:
93 */
94 u64 old_val, new_val, real_val = 1ULL << 32;
Ingo Molnarb7882b72009-07-03 13:26:39 +020095
96 do {
Ingo Molnar824975e2009-07-03 12:39:07 +020097 old_val = real_val;
Ingo Molnarb7882b72009-07-03 13:26:39 +020098 new_val = old_val + delta;
99
Ingo Molnar824975e2009-07-03 12:39:07 +0200100 real_val = atomic64_cmpxchg(ptr, old_val, new_val);
101
102 } while (real_val != old_val);
Ingo Molnarb7882b72009-07-03 13:26:39 +0200103
104 return new_val;
105}
106
107u64 atomic64_sub_return(u64 delta, atomic64_t *ptr)
108{
109 return atomic64_add_return(-delta, ptr);
110}
111
112u64 atomic64_inc_return(atomic64_t *ptr)
113{
114 return atomic64_add_return(1, ptr);
115}
116
117u64 atomic64_dec_return(atomic64_t *ptr)
118{
119 return atomic64_sub_return(1, ptr);
120}
121
122/**
123 * atomic64_add - add integer to atomic64 variable
124 * @delta: integer value to add
125 * @ptr: pointer to type atomic64_t
126 *
127 * Atomically adds @delta to @ptr.
128 */
129void atomic64_add(u64 delta, atomic64_t *ptr)
130{
131 atomic64_add_return(delta, ptr);
132}
133
134/**
135 * atomic64_sub - subtract the atomic64 variable
136 * @delta: integer value to subtract
137 * @ptr: pointer to type atomic64_t
138 *
139 * Atomically subtracts @delta from @ptr.
140 */
141void atomic64_sub(u64 delta, atomic64_t *ptr)
142{
143 atomic64_add(-delta, ptr);
144}
145
146/**
147 * atomic64_sub_and_test - subtract value from variable and test result
148 * @delta: integer value to subtract
149 * @ptr: pointer to type atomic64_t
150 *
151 * Atomically subtracts @delta from @ptr and returns
152 * true if the result is zero, or false for all
153 * other cases.
154 */
155int atomic64_sub_and_test(u64 delta, atomic64_t *ptr)
156{
157 u64 old_val = atomic64_sub_return(delta, ptr);
158
159 return old_val == 0;
160}
161
162/**
163 * atomic64_inc - increment atomic64 variable
164 * @ptr: pointer to type atomic64_t
165 *
166 * Atomically increments @ptr by 1.
167 */
168void atomic64_inc(atomic64_t *ptr)
169{
170 atomic64_add(1, ptr);
171}
172
173/**
174 * atomic64_dec - decrement atomic64 variable
175 * @ptr: pointer to type atomic64_t
176 *
177 * Atomically decrements @ptr by 1.
178 */
179void atomic64_dec(atomic64_t *ptr)
180{
181 atomic64_sub(1, ptr);
182}
183
184/**
185 * atomic64_dec_and_test - decrement and test
186 * @ptr: pointer to type atomic64_t
187 *
188 * Atomically decrements @ptr by 1 and
189 * returns true if the result is 0, or false for all other
190 * cases.
191 */
192int atomic64_dec_and_test(atomic64_t *ptr)
193{
194 return atomic64_sub_and_test(1, ptr);
195}
196
197/**
198 * atomic64_inc_and_test - increment and test
199 * @ptr: pointer to type atomic64_t
200 *
201 * Atomically increments @ptr by 1
202 * and returns true if the result is zero, or false for all
203 * other cases.
204 */
205int atomic64_inc_and_test(atomic64_t *ptr)
206{
207 return atomic64_sub_and_test(-1, ptr);
208}
209
210/**
211 * atomic64_add_negative - add and test if negative
212 * @delta: integer value to add
213 * @ptr: pointer to type atomic64_t
214 *
215 * Atomically adds @delta to @ptr and returns true
216 * if the result is negative, or false when
217 * result is greater than or equal to zero.
218 */
219int atomic64_add_negative(u64 delta, atomic64_t *ptr)
220{
221 long long old_val = atomic64_add_return(delta, ptr);
222
223 return old_val < 0;
224}