blob: fd28fd3fb742a93cf4da957e3a16f7a05199e1be [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 Dumazetaacf6822009-07-03 12:14:27 +020065 u64 old = 1LL << 32;
Ingo Molnarb7882b72009-07-03 13:26:39 +020066
Eric Dumazetaacf6822009-07-03 12:14:27 +020067 return cmpxchg8b(&ptr->counter, old, old);
Ingo Molnarb7882b72009-07-03 13:26:39 +020068}
69
70/**
71 * atomic64_add_return - add and return
72 * @delta: integer value to add
73 * @ptr: pointer to type atomic64_t
74 *
75 * Atomically adds @delta to @ptr and returns @delta + *@ptr
76 */
Ingo Molnar3ac805d2009-07-03 12:51:19 +020077noinline u64 atomic64_add_return(u64 delta, atomic64_t *ptr)
Ingo Molnarb7882b72009-07-03 13:26:39 +020078{
Ingo Molnar824975e2009-07-03 12:39:07 +020079 /*
80 * Try first with a (probably incorrect) assumption about
81 * what we have there. We'll do two loops most likely,
82 * but we'll get an ownership MESI transaction straight away
83 * instead of a read transaction followed by a
84 * flush-for-ownership transaction:
85 */
86 u64 old_val, new_val, real_val = 1ULL << 32;
Ingo Molnarb7882b72009-07-03 13:26:39 +020087
88 do {
Ingo Molnar824975e2009-07-03 12:39:07 +020089 old_val = real_val;
Ingo Molnarb7882b72009-07-03 13:26:39 +020090 new_val = old_val + delta;
91
Ingo Molnar824975e2009-07-03 12:39:07 +020092 real_val = atomic64_cmpxchg(ptr, old_val, new_val);
93
94 } while (real_val != old_val);
Ingo Molnarb7882b72009-07-03 13:26:39 +020095
96 return new_val;
97}
98
99u64 atomic64_sub_return(u64 delta, atomic64_t *ptr)
100{
101 return atomic64_add_return(-delta, ptr);
102}
103
104u64 atomic64_inc_return(atomic64_t *ptr)
105{
106 return atomic64_add_return(1, ptr);
107}
108
109u64 atomic64_dec_return(atomic64_t *ptr)
110{
111 return atomic64_sub_return(1, ptr);
112}
113
114/**
115 * atomic64_add - add integer to atomic64 variable
116 * @delta: integer value to add
117 * @ptr: pointer to type atomic64_t
118 *
119 * Atomically adds @delta to @ptr.
120 */
121void atomic64_add(u64 delta, atomic64_t *ptr)
122{
123 atomic64_add_return(delta, ptr);
124}
125
126/**
127 * atomic64_sub - subtract the atomic64 variable
128 * @delta: integer value to subtract
129 * @ptr: pointer to type atomic64_t
130 *
131 * Atomically subtracts @delta from @ptr.
132 */
133void atomic64_sub(u64 delta, atomic64_t *ptr)
134{
135 atomic64_add(-delta, ptr);
136}
137
138/**
139 * atomic64_sub_and_test - subtract value from variable and test result
140 * @delta: integer value to subtract
141 * @ptr: pointer to type atomic64_t
142 *
143 * Atomically subtracts @delta from @ptr and returns
144 * true if the result is zero, or false for all
145 * other cases.
146 */
147int atomic64_sub_and_test(u64 delta, atomic64_t *ptr)
148{
149 u64 old_val = atomic64_sub_return(delta, ptr);
150
151 return old_val == 0;
152}
153
154/**
155 * atomic64_inc - increment atomic64 variable
156 * @ptr: pointer to type atomic64_t
157 *
158 * Atomically increments @ptr by 1.
159 */
160void atomic64_inc(atomic64_t *ptr)
161{
162 atomic64_add(1, ptr);
163}
164
165/**
166 * atomic64_dec - decrement atomic64 variable
167 * @ptr: pointer to type atomic64_t
168 *
169 * Atomically decrements @ptr by 1.
170 */
171void atomic64_dec(atomic64_t *ptr)
172{
173 atomic64_sub(1, ptr);
174}
175
176/**
177 * atomic64_dec_and_test - decrement and test
178 * @ptr: pointer to type atomic64_t
179 *
180 * Atomically decrements @ptr by 1 and
181 * returns true if the result is 0, or false for all other
182 * cases.
183 */
184int atomic64_dec_and_test(atomic64_t *ptr)
185{
186 return atomic64_sub_and_test(1, ptr);
187}
188
189/**
190 * atomic64_inc_and_test - increment and test
191 * @ptr: pointer to type atomic64_t
192 *
193 * Atomically increments @ptr by 1
194 * and returns true if the result is zero, or false for all
195 * other cases.
196 */
197int atomic64_inc_and_test(atomic64_t *ptr)
198{
199 return atomic64_sub_and_test(-1, ptr);
200}
201
202/**
203 * atomic64_add_negative - add and test if negative
204 * @delta: integer value to add
205 * @ptr: pointer to type atomic64_t
206 *
207 * Atomically adds @delta to @ptr and returns true
208 * if the result is negative, or false when
209 * result is greater than or equal to zero.
210 */
211int atomic64_add_negative(u64 delta, atomic64_t *ptr)
212{
213 long long old_val = atomic64_add_return(delta, ptr);
214
215 return old_val < 0;
216}