diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-18 11:40:05 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-18 11:40:05 -0400 |
commit | 93c9d7f60c0cb7715890b1f9e159da6f4d1f5a65 (patch) | |
tree | 6be428ca5fe52f14ebb78a8e695cec59d2f21c26 | |
parent | 7421a10de7a525f67cc082fca7a91011d00eada4 (diff) | |
parent | d9c5841e22231e4e49fd0a1004164e6fce59b7a6 (diff) |
Merge branch 'x86-atomic-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'x86-atomic-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
x86: Fix LOCK_PREFIX_HERE for uniprocessor build
x86, atomic64: In selftest, distinguish x86-64 from 586+
x86-32: Fix atomic64_inc_not_zero return value convention
lib: Fix atomic64_inc_not_zero test
lib: Fix atomic64_add_unless return value convention
x86-32: Fix atomic64_add_unless return value convention
lib: Fix atomic64_add_unless test
x86: Implement atomic[64]_dec_if_positive()
lib: Only test atomic64_dec_if_positive on archs having it
x86-32: Rewrite 32-bit atomic64 functions in assembly
lib: Add self-test for atomic64_t
x86-32: Allow UP/SMP lock replacement in cmpxchg64
x86: Add support for lock prefix in alternatives
-rw-r--r-- | arch/x86/include/asm/alternative.h | 9 | ||||
-rw-r--r-- | arch/x86/include/asm/atomic.h | 23 | ||||
-rw-r--r-- | arch/x86/include/asm/atomic64_32.h | 278 | ||||
-rw-r--r-- | arch/x86/include/asm/atomic64_64.h | 23 | ||||
-rw-r--r-- | arch/x86/include/asm/cmpxchg_32.h | 3 | ||||
-rw-r--r-- | arch/x86/kernel/alternative.c | 6 | ||||
-rw-r--r-- | arch/x86/lib/Makefile | 3 | ||||
-rw-r--r-- | arch/x86/lib/atomic64_32.c | 273 | ||||
-rw-r--r-- | arch/x86/lib/atomic64_386_32.S | 174 | ||||
-rw-r--r-- | arch/x86/lib/atomic64_cx8_32.S | 224 | ||||
-rw-r--r-- | lib/Kconfig.debug | 7 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/atomic64.c | 4 | ||||
-rw-r--r-- | lib/atomic64_test.c | 164 |
14 files changed, 895 insertions, 298 deletions
diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h index 714bf2417284..92a9033c14d1 100644 --- a/arch/x86/include/asm/alternative.h +++ b/arch/x86/include/asm/alternative.h | |||
@@ -28,14 +28,17 @@ | |||
28 | */ | 28 | */ |
29 | 29 | ||
30 | #ifdef CONFIG_SMP | 30 | #ifdef CONFIG_SMP |
31 | #define LOCK_PREFIX \ | 31 | #define LOCK_PREFIX_HERE \ |
32 | ".section .smp_locks,\"a\"\n" \ | 32 | ".section .smp_locks,\"a\"\n" \ |
33 | ".balign 4\n" \ | 33 | ".balign 4\n" \ |
34 | ".long 661f - .\n" /* offset */ \ | 34 | ".long 671f - .\n" /* offset */ \ |
35 | ".previous\n" \ | 35 | ".previous\n" \ |
36 | "661:\n\tlock; " | 36 | "671:" |
37 | |||
38 | #define LOCK_PREFIX LOCK_PREFIX_HERE "\n\tlock; " | ||
37 | 39 | ||
38 | #else /* ! CONFIG_SMP */ | 40 | #else /* ! CONFIG_SMP */ |
41 | #define LOCK_PREFIX_HERE "" | ||
39 | #define LOCK_PREFIX "" | 42 | #define LOCK_PREFIX "" |
40 | #endif | 43 | #endif |
41 | 44 | ||
diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h index 37b39d27abe0..952a826ac4e5 100644 --- a/arch/x86/include/asm/atomic.h +++ b/arch/x86/include/asm/atomic.h | |||
@@ -246,6 +246,29 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u) | |||
246 | 246 | ||
247 | #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) | 247 | #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) |
248 | 248 | ||
249 | /* | ||
250 | * atomic_dec_if_positive - decrement by 1 if old value positive | ||
251 | * @v: pointer of type atomic_t | ||
252 | * | ||
253 | * The function returns the old value of *v minus 1, even if | ||
254 | * the atomic variable, v, was not decremented. | ||
255 | */ | ||
256 | static inline int atomic_dec_if_positive(atomic_t *v) | ||
257 | { | ||
258 | int c, old, dec; | ||
259 | c = atomic_read(v); | ||
260 | for (;;) { | ||
261 | dec = c - 1; | ||
262 | if (unlikely(dec < 0)) | ||
263 | break; | ||
264 | old = atomic_cmpxchg((v), c, dec); | ||
265 | if (likely(old == c)) | ||
266 | break; | ||
267 | c = old; | ||
268 | } | ||
269 | return dec; | ||
270 | } | ||
271 | |||
249 | /** | 272 | /** |
250 | * atomic_inc_short - increment of a short integer | 273 | * atomic_inc_short - increment of a short integer |
251 | * @v: pointer to type int | 274 | * @v: pointer to type int |
diff --git a/arch/x86/include/asm/atomic64_32.h b/arch/x86/include/asm/atomic64_32.h index 03027bf28de5..2a934aa19a43 100644 --- a/arch/x86/include/asm/atomic64_32.h +++ b/arch/x86/include/asm/atomic64_32.h | |||
@@ -14,109 +14,193 @@ typedef struct { | |||
14 | 14 | ||
15 | #define ATOMIC64_INIT(val) { (val) } | 15 | #define ATOMIC64_INIT(val) { (val) } |
16 | 16 | ||
17 | extern u64 atomic64_cmpxchg(atomic64_t *ptr, u64 old_val, u64 new_val); | 17 | #ifdef CONFIG_X86_CMPXCHG64 |
18 | #define ATOMIC64_ALTERNATIVE_(f, g) "call atomic64_" #g "_cx8" | ||
19 | #else | ||
20 | #define ATOMIC64_ALTERNATIVE_(f, g) ALTERNATIVE("call atomic64_" #f "_386", "call atomic64_" #g "_cx8", X86_FEATURE_CX8) | ||
21 | #endif | ||
22 | |||
23 | #define ATOMIC64_ALTERNATIVE(f) ATOMIC64_ALTERNATIVE_(f, f) | ||
24 | |||
25 | /** | ||
26 | * atomic64_cmpxchg - cmpxchg atomic64 variable | ||
27 | * @p: pointer to type atomic64_t | ||
28 | * @o: expected value | ||
29 | * @n: new value | ||
30 | * | ||
31 | * Atomically sets @v to @n if it was equal to @o and returns | ||
32 | * the old value. | ||
33 | */ | ||
34 | |||
35 | static inline long long atomic64_cmpxchg(atomic64_t *v, long long o, long long n) | ||
36 | { | ||
37 | return cmpxchg64(&v->counter, o, n); | ||
38 | } | ||
18 | 39 | ||
19 | /** | 40 | /** |
20 | * atomic64_xchg - xchg atomic64 variable | 41 | * atomic64_xchg - xchg atomic64 variable |
21 | * @ptr: pointer to type atomic64_t | 42 | * @v: pointer to type atomic64_t |
22 | * @new_val: value to assign | 43 | * @n: value to assign |
23 | * | 44 | * |
24 | * Atomically xchgs the value of @ptr to @new_val and returns | 45 | * Atomically xchgs the value of @v to @n and returns |
25 | * the old value. | 46 | * the old value. |
26 | */ | 47 | */ |
27 | extern u64 atomic64_xchg(atomic64_t *ptr, u64 new_val); | 48 | static inline long long atomic64_xchg(atomic64_t *v, long long n) |
49 | { | ||
50 | long long o; | ||
51 | unsigned high = (unsigned)(n >> 32); | ||
52 | unsigned low = (unsigned)n; | ||
53 | asm volatile(ATOMIC64_ALTERNATIVE(xchg) | ||
54 | : "=A" (o), "+b" (low), "+c" (high) | ||
55 | : "S" (v) | ||
56 | : "memory" | ||
57 | ); | ||
58 | return o; | ||
59 | } | ||
28 | 60 | ||
29 | /** | 61 | /** |
30 | * atomic64_set - set atomic64 variable | 62 | * atomic64_set - set atomic64 variable |
31 | * @ptr: pointer to type atomic64_t | 63 | * @v: pointer to type atomic64_t |
32 | * @new_val: value to assign | 64 | * @n: value to assign |
33 | * | 65 | * |
34 | * Atomically sets the value of @ptr to @new_val. | 66 | * Atomically sets the value of @v to @n. |
35 | */ | 67 | */ |
36 | extern void atomic64_set(atomic64_t *ptr, u64 new_val); | 68 | static inline void atomic64_set(atomic64_t *v, long long i) |
69 | { | ||
70 | unsigned high = (unsigned)(i >> 32); | ||
71 | unsigned low = (unsigned)i; | ||
72 | asm volatile(ATOMIC64_ALTERNATIVE(set) | ||
73 | : "+b" (low), "+c" (high) | ||
74 | : "S" (v) | ||
75 | : "eax", "edx", "memory" | ||
76 | ); | ||
77 | } | ||
37 | 78 | ||
38 | /** | 79 | /** |
39 | * atomic64_read - read atomic64 variable | 80 | * atomic64_read - read atomic64 variable |
40 | * @ptr: pointer to type atomic64_t | 81 | * @v: pointer to type atomic64_t |
41 | * | 82 | * |
42 | * Atomically reads the value of @ptr and returns it. | 83 | * Atomically reads the value of @v and returns it. |
43 | */ | 84 | */ |
44 | static inline u64 atomic64_read(atomic64_t *ptr) | 85 | static inline long long atomic64_read(atomic64_t *v) |
45 | { | 86 | { |
46 | u64 res; | 87 | long long r; |
47 | 88 | asm volatile(ATOMIC64_ALTERNATIVE(read) | |
48 | /* | 89 | : "=A" (r), "+c" (v) |
49 | * Note, we inline this atomic64_t primitive because | 90 | : : "memory" |
50 | * it only clobbers EAX/EDX and leaves the others | 91 | ); |
51 | * untouched. We also (somewhat subtly) rely on the | 92 | return r; |
52 | * fact that cmpxchg8b returns the current 64-bit value | 93 | } |
53 | * of the memory location we are touching: | ||
54 | */ | ||
55 | asm volatile( | ||
56 | "mov %%ebx, %%eax\n\t" | ||
57 | "mov %%ecx, %%edx\n\t" | ||
58 | LOCK_PREFIX "cmpxchg8b %1\n" | ||
59 | : "=&A" (res) | ||
60 | : "m" (*ptr) | ||
61 | ); | ||
62 | |||
63 | return res; | ||
64 | } | ||
65 | |||
66 | extern u64 atomic64_read(atomic64_t *ptr); | ||
67 | 94 | ||
68 | /** | 95 | /** |
69 | * atomic64_add_return - add and return | 96 | * atomic64_add_return - add and return |
70 | * @delta: integer value to add | 97 | * @i: integer value to add |
71 | * @ptr: pointer to type atomic64_t | 98 | * @v: pointer to type atomic64_t |
72 | * | 99 | * |
73 | * Atomically adds @delta to @ptr and returns @delta + *@ptr | 100 | * Atomically adds @i to @v and returns @i + *@v |
74 | */ | 101 | */ |
75 | extern u64 atomic64_add_return(u64 delta, atomic64_t *ptr); | 102 | static inline long long atomic64_add_return(long long i, atomic64_t *v) |
103 | { | ||
104 | asm volatile(ATOMIC64_ALTERNATIVE(add_return) | ||
105 | : "+A" (i), "+c" (v) | ||
106 | : : "memory" | ||
107 | ); | ||
108 | return i; | ||
109 | } | ||
76 | 110 | ||
77 | /* | 111 | /* |
78 | * Other variants with different arithmetic operators: | 112 | * Other variants with different arithmetic operators: |
79 | */ | 113 | */ |
80 | extern u64 atomic64_sub_return(u64 delta, atomic64_t *ptr); | 114 | static inline long long atomic64_sub_return(long long i, atomic64_t *v) |
81 | extern u64 atomic64_inc_return(atomic64_t *ptr); | 115 | { |
82 | extern u64 atomic64_dec_return(atomic64_t *ptr); | 116 | asm volatile(ATOMIC64_ALTERNATIVE(sub_return) |
117 | : "+A" (i), "+c" (v) | ||
118 | : : "memory" | ||
119 | ); | ||
120 | return i; | ||
121 | } | ||
122 | |||
123 | static inline long long atomic64_inc_return(atomic64_t *v) | ||
124 | { | ||
125 | long long a; | ||
126 | asm volatile(ATOMIC64_ALTERNATIVE(inc_return) | ||
127 | : "=A" (a) | ||
128 | : "S" (v) | ||
129 | : "memory", "ecx" | ||
130 | ); | ||
131 | return a; | ||
132 | } | ||
133 | |||
134 | static inline long long atomic64_dec_return(atomic64_t *v) | ||
135 | { | ||
136 | long long a; | ||
137 | asm volatile(ATOMIC64_ALTERNATIVE(dec_return) | ||
138 | : "=A" (a) | ||
139 | : "S" (v) | ||
140 | : "memory", "ecx" | ||
141 | ); | ||
142 | return a; | ||
143 | } | ||
83 | 144 | ||
84 | /** | 145 | /** |
85 | * atomic64_add - add integer to atomic64 variable | 146 | * atomic64_add - add integer to atomic64 variable |
86 | * @delta: integer value to add | 147 | * @i: integer value to add |
87 | * @ptr: pointer to type atomic64_t | 148 | * @v: pointer to type atomic64_t |
88 | * | 149 | * |
89 | * Atomically adds @delta to @ptr. | 150 | * Atomically adds @i to @v. |
90 | */ | 151 | */ |
91 | extern void atomic64_add(u64 delta, atomic64_t *ptr); | 152 | static inline long long atomic64_add(long long i, atomic64_t *v) |
153 | { | ||
154 | asm volatile(ATOMIC64_ALTERNATIVE_(add, add_return) | ||
155 | : "+A" (i), "+c" (v) | ||
156 | : : "memory" | ||
157 | ); | ||
158 | return i; | ||
159 | } | ||
92 | 160 | ||
93 | /** | 161 | /** |
94 | * atomic64_sub - subtract the atomic64 variable | 162 | * atomic64_sub - subtract the atomic64 variable |
95 | * @delta: integer value to subtract | 163 | * @i: integer value to subtract |
96 | * @ptr: pointer to type atomic64_t | 164 | * @v: pointer to type atomic64_t |
97 | * | 165 | * |
98 | * Atomically subtracts @delta from @ptr. | 166 | * Atomically subtracts @i from @v. |
99 | */ | 167 | */ |
100 | extern void atomic64_sub(u64 delta, atomic64_t *ptr); | 168 | static inline long long atomic64_sub(long long i, atomic64_t *v) |
169 | { | ||
170 | asm volatile(ATOMIC64_ALTERNATIVE_(sub, sub_return) | ||
171 | : "+A" (i), "+c" (v) | ||
172 | : : "memory" | ||
173 | ); | ||
174 | return i; | ||
175 | } | ||
101 | 176 | ||
102 | /** | 177 | /** |
103 | * atomic64_sub_and_test - subtract value from variable and test result | 178 | * atomic64_sub_and_test - subtract value from variable and test result |
104 | * @delta: integer value to subtract | 179 | * @i: integer value to subtract |
105 | * @ptr: pointer to type atomic64_t | 180 | * @v: pointer to type atomic64_t |
106 | * | 181 | * |
107 | * Atomically subtracts @delta from @ptr and returns | 182 | * Atomically subtracts @i from @v and returns |
108 | * true if the result is zero, or false for all | 183 | * true if the result is zero, or false for all |
109 | * other cases. | 184 | * other cases. |
110 | */ | 185 | */ |
111 | extern int atomic64_sub_and_test(u64 delta, atomic64_t *ptr); | 186 | static inline int atomic64_sub_and_test(long long i, atomic64_t *v) |
187 | { | ||
188 | return atomic64_sub_return(i, v) == 0; | ||
189 | } | ||
112 | 190 | ||
113 | /** | 191 | /** |
114 | * atomic64_inc - increment atomic64 variable | 192 | * atomic64_inc - increment atomic64 variable |
115 | * @ptr: pointer to type atomic64_t | 193 | * @v: pointer to type atomic64_t |
116 | * | 194 | * |
117 | * Atomically increments @ptr by 1. | 195 | * Atomically increments @v by 1. |
118 | */ | 196 | */ |
119 | extern void atomic64_inc(atomic64_t *ptr); | 197 | static inline void atomic64_inc(atomic64_t *v) |
198 | { | ||
199 | asm volatile(ATOMIC64_ALTERNATIVE_(inc, inc_return) | ||
200 | : : "S" (v) | ||
201 | : "memory", "eax", "ecx", "edx" | ||
202 | ); | ||
203 | } | ||
120 | 204 | ||
121 | /** | 205 | /** |
122 | * atomic64_dec - decrement atomic64 variable | 206 | * atomic64_dec - decrement atomic64 variable |
@@ -124,37 +208,97 @@ extern void atomic64_inc(atomic64_t *ptr); | |||
124 | * | 208 | * |
125 | * Atomically decrements @ptr by 1. | 209 | * Atomically decrements @ptr by 1. |
126 | */ | 210 | */ |
127 | extern void atomic64_dec(atomic64_t *ptr); | 211 | static inline void atomic64_dec(atomic64_t *v) |
212 | { | ||
213 | asm volatile(ATOMIC64_ALTERNATIVE_(dec, dec_return) | ||
214 | : : "S" (v) | ||
215 | : "memory", "eax", "ecx", "edx" | ||
216 | ); | ||
217 | } | ||
128 | 218 | ||
129 | /** | 219 | /** |
130 | * atomic64_dec_and_test - decrement and test | 220 | * atomic64_dec_and_test - decrement and test |
131 | * @ptr: pointer to type atomic64_t | 221 | * @v: pointer to type atomic64_t |
132 | * | 222 | * |
133 | * Atomically decrements @ptr by 1 and | 223 | * Atomically decrements @v by 1 and |
134 | * returns true if the result is 0, or false for all other | 224 | * returns true if the result is 0, or false for all other |
135 | * cases. | 225 | * cases. |
136 | */ | 226 | */ |
137 | extern int atomic64_dec_and_test(atomic64_t *ptr); | 227 | static inline int atomic64_dec_and_test(atomic64_t *v) |
228 | { | ||
229 | return atomic64_dec_return(v) == 0; | ||
230 | } | ||
138 | 231 | ||
139 | /** | 232 | /** |
140 | * atomic64_inc_and_test - increment and test | 233 | * atomic64_inc_and_test - increment and test |
141 | * @ptr: pointer to type atomic64_t | 234 | * @v: pointer to type atomic64_t |
142 | * | 235 | * |
143 | * Atomically increments @ptr by 1 | 236 | * Atomically increments @v by 1 |
144 | * and returns true if the result is zero, or false for all | 237 | * and returns true if the result is zero, or false for all |
145 | * other cases. | 238 | * other cases. |
146 | */ | 239 | */ |
147 | extern int atomic64_inc_and_test(atomic64_t *ptr); | 240 | static inline int atomic64_inc_and_test(atomic64_t *v) |
241 | { | ||
242 | return atomic64_inc_return(v) == 0; | ||
243 | } | ||
148 | 244 | ||
149 | /** | 245 | /** |
150 | * atomic64_add_negative - add and test if negative | 246 | * atomic64_add_negative - add and test if negative |
151 | * @delta: integer value to add | 247 | * @i: integer value to add |
152 | * @ptr: pointer to type atomic64_t | 248 | * @v: pointer to type atomic64_t |
153 | * | 249 | * |
154 | * Atomically adds @delta to @ptr and returns true | 250 | * Atomically adds @i to @v and returns true |
155 | * if the result is negative, or false when | 251 | * if the result is negative, or false when |
156 | * result is greater than or equal to zero. | 252 | * result is greater than or equal to zero. |
157 | */ | 253 | */ |
158 | extern int atomic64_add_negative(u64 delta, atomic64_t *ptr); | 254 | static inline int atomic64_add_negative(long long i, atomic64_t *v) |
255 | { | ||
256 | return atomic64_add_return(i, v) < 0; | ||
257 | } | ||
258 | |||
259 | /** | ||
260 | * atomic64_add_unless - add unless the number is a given value | ||
261 | * @v: pointer of type atomic64_t | ||
262 | * @a: the amount to add to v... | ||
263 | * @u: ...unless v is equal to u. | ||
264 | * | ||
265 | * Atomically adds @a to @v, so long as it was not @u. | ||
266 | * Returns non-zero if @v was not @u, and zero otherwise. | ||
267 | */ | ||
268 | static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u) | ||
269 | { | ||
270 | unsigned low = (unsigned)u; | ||
271 | unsigned high = (unsigned)(u >> 32); | ||
272 | asm volatile(ATOMIC64_ALTERNATIVE(add_unless) "\n\t" | ||
273 | : "+A" (a), "+c" (v), "+S" (low), "+D" (high) | ||
274 | : : "memory"); | ||
275 | return (int)a; | ||
276 | } | ||
277 | |||
278 | |||
279 | static inline int atomic64_inc_not_zero(atomic64_t *v) | ||
280 | { | ||
281 | int r; | ||
282 | asm volatile(ATOMIC64_ALTERNATIVE(inc_not_zero) | ||
283 | : "=a" (r) | ||
284 | : "S" (v) | ||
285 | : "ecx", "edx", "memory" | ||
286 | ); | ||
287 | return r; | ||
288 | } | ||
289 | |||
290 | static inline long long atomic64_dec_if_positive(atomic64_t *v) | ||
291 | { | ||
292 | long long r; | ||
293 | asm volatile(ATOMIC64_ALTERNATIVE(dec_if_positive) | ||
294 | : "=A" (r) | ||
295 | : "S" (v) | ||
296 | : "ecx", "memory" | ||
297 | ); | ||
298 | return r; | ||
299 | } | ||
300 | |||
301 | #undef ATOMIC64_ALTERNATIVE | ||
302 | #undef ATOMIC64_ALTERNATIVE_ | ||
159 | 303 | ||
160 | #endif /* _ASM_X86_ATOMIC64_32_H */ | 304 | #endif /* _ASM_X86_ATOMIC64_32_H */ |
diff --git a/arch/x86/include/asm/atomic64_64.h b/arch/x86/include/asm/atomic64_64.h index b014e235ea8d..49fd1ea22951 100644 --- a/arch/x86/include/asm/atomic64_64.h +++ b/arch/x86/include/asm/atomic64_64.h | |||
@@ -221,4 +221,27 @@ static inline int atomic64_add_unless(atomic64_t *v, long a, long u) | |||
221 | 221 | ||
222 | #define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0) | 222 | #define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0) |
223 | 223 | ||
224 | /* | ||
225 | * atomic64_dec_if_positive - decrement by 1 if old value positive | ||
226 | * @v: pointer of type atomic_t | ||
227 | * | ||
228 | * The function returns the old value of *v minus 1, even if | ||
229 | * the atomic variable, v, was not decremented. | ||
230 | */ | ||
231 | static inline long atomic64_dec_if_positive(atomic64_t *v) | ||
232 | { | ||
233 | long c, old, dec; | ||
234 | c = atomic64_read(v); | ||
235 | for (;;) { | ||
236 | dec = c - 1; | ||
237 | if (unlikely(dec < 0)) | ||
238 | break; | ||
239 | old = atomic64_cmpxchg((v), c, dec); | ||
240 | if (likely(old == c)) | ||
241 | break; | ||
242 | c = old; | ||
243 | } | ||
244 | return dec; | ||
245 | } | ||
246 | |||
224 | #endif /* _ASM_X86_ATOMIC64_64_H */ | 247 | #endif /* _ASM_X86_ATOMIC64_64_H */ |
diff --git a/arch/x86/include/asm/cmpxchg_32.h b/arch/x86/include/asm/cmpxchg_32.h index ffb9bb6b6c37..8859e12dd3cf 100644 --- a/arch/x86/include/asm/cmpxchg_32.h +++ b/arch/x86/include/asm/cmpxchg_32.h | |||
@@ -271,7 +271,8 @@ extern unsigned long long cmpxchg_486_u64(volatile void *, u64, u64); | |||
271 | __typeof__(*(ptr)) __ret; \ | 271 | __typeof__(*(ptr)) __ret; \ |
272 | __typeof__(*(ptr)) __old = (o); \ | 272 | __typeof__(*(ptr)) __old = (o); \ |
273 | __typeof__(*(ptr)) __new = (n); \ | 273 | __typeof__(*(ptr)) __new = (n); \ |
274 | alternative_io("call cmpxchg8b_emu", \ | 274 | alternative_io(LOCK_PREFIX_HERE \ |
275 | "call cmpxchg8b_emu", \ | ||
275 | "lock; cmpxchg8b (%%esi)" , \ | 276 | "lock; cmpxchg8b (%%esi)" , \ |
276 | X86_FEATURE_CX8, \ | 277 | X86_FEATURE_CX8, \ |
277 | "=A" (__ret), \ | 278 | "=A" (__ret), \ |
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index 936738427223..70237732a6c7 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c | |||
@@ -247,7 +247,8 @@ static void alternatives_smp_lock(const s32 *start, const s32 *end, | |||
247 | if (!*poff || ptr < text || ptr >= text_end) | 247 | if (!*poff || ptr < text || ptr >= text_end) |
248 | continue; | 248 | continue; |
249 | /* turn DS segment override prefix into lock prefix */ | 249 | /* turn DS segment override prefix into lock prefix */ |
250 | text_poke(ptr, ((unsigned char []){0xf0}), 1); | 250 | if (*ptr == 0x3e) |
251 | text_poke(ptr, ((unsigned char []){0xf0}), 1); | ||
251 | }; | 252 | }; |
252 | mutex_unlock(&text_mutex); | 253 | mutex_unlock(&text_mutex); |
253 | } | 254 | } |
@@ -267,7 +268,8 @@ static void alternatives_smp_unlock(const s32 *start, const s32 *end, | |||
267 | if (!*poff || ptr < text || ptr >= text_end) | 268 | if (!*poff || ptr < text || ptr >= text_end) |
268 | continue; | 269 | continue; |
269 | /* turn lock prefix into DS segment override prefix */ | 270 | /* turn lock prefix into DS segment override prefix */ |
270 | text_poke(ptr, ((unsigned char []){0x3E}), 1); | 271 | if (*ptr == 0xf0) |
272 | text_poke(ptr, ((unsigned char []){0x3E}), 1); | ||
271 | }; | 273 | }; |
272 | mutex_unlock(&text_mutex); | 274 | mutex_unlock(&text_mutex); |
273 | } | 275 | } |
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index cbaf8f2b83df..f871e04b6965 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile | |||
@@ -26,11 +26,12 @@ obj-y += msr.o msr-reg.o msr-reg-export.o | |||
26 | 26 | ||
27 | ifeq ($(CONFIG_X86_32),y) | 27 | ifeq ($(CONFIG_X86_32),y) |
28 | obj-y += atomic64_32.o | 28 | obj-y += atomic64_32.o |
29 | lib-y += atomic64_cx8_32.o | ||
29 | lib-y += checksum_32.o | 30 | lib-y += checksum_32.o |
30 | lib-y += strstr_32.o | 31 | lib-y += strstr_32.o |
31 | lib-y += semaphore_32.o string_32.o | 32 | lib-y += semaphore_32.o string_32.o |
32 | ifneq ($(CONFIG_X86_CMPXCHG64),y) | 33 | ifneq ($(CONFIG_X86_CMPXCHG64),y) |
33 | lib-y += cmpxchg8b_emu.o | 34 | lib-y += cmpxchg8b_emu.o atomic64_386_32.o |
34 | endif | 35 | endif |
35 | lib-$(CONFIG_X86_USE_3DNOW) += mmx_32.o | 36 | lib-$(CONFIG_X86_USE_3DNOW) += mmx_32.o |
36 | else | 37 | else |
diff --git a/arch/x86/lib/atomic64_32.c b/arch/x86/lib/atomic64_32.c index 824fa0be55a3..540179e8e9fa 100644 --- a/arch/x86/lib/atomic64_32.c +++ b/arch/x86/lib/atomic64_32.c | |||
@@ -6,225 +6,54 @@ | |||
6 | #include <asm/cmpxchg.h> | 6 | #include <asm/cmpxchg.h> |
7 | #include <asm/atomic.h> | 7 | #include <asm/atomic.h> |
8 | 8 | ||
9 | static noinline u64 cmpxchg8b(u64 *ptr, u64 old, u64 new) | 9 | long long atomic64_read_cx8(long long, const atomic64_t *v); |
10 | { | 10 | EXPORT_SYMBOL(atomic64_read_cx8); |
11 | u32 low = new; | 11 | long long atomic64_set_cx8(long long, const atomic64_t *v); |
12 | u32 high = new >> 32; | 12 | EXPORT_SYMBOL(atomic64_set_cx8); |
13 | 13 | long long atomic64_xchg_cx8(long long, unsigned high); | |
14 | asm volatile( | 14 | EXPORT_SYMBOL(atomic64_xchg_cx8); |
15 | LOCK_PREFIX "cmpxchg8b %1\n" | 15 | long long atomic64_add_return_cx8(long long a, atomic64_t *v); |
16 | : "+A" (old), "+m" (*ptr) | 16 | EXPORT_SYMBOL(atomic64_add_return_cx8); |
17 | : "b" (low), "c" (high) | 17 | long long atomic64_sub_return_cx8(long long a, atomic64_t *v); |
18 | ); | 18 | EXPORT_SYMBOL(atomic64_sub_return_cx8); |
19 | return old; | 19 | long long atomic64_inc_return_cx8(long long a, atomic64_t *v); |
20 | } | 20 | EXPORT_SYMBOL(atomic64_inc_return_cx8); |
21 | 21 | long long atomic64_dec_return_cx8(long long a, atomic64_t *v); | |
22 | u64 atomic64_cmpxchg(atomic64_t *ptr, u64 old_val, u64 new_val) | 22 | EXPORT_SYMBOL(atomic64_dec_return_cx8); |
23 | { | 23 | long long atomic64_dec_if_positive_cx8(atomic64_t *v); |
24 | return cmpxchg8b(&ptr->counter, old_val, new_val); | 24 | EXPORT_SYMBOL(atomic64_dec_if_positive_cx8); |
25 | } | 25 | int atomic64_inc_not_zero_cx8(atomic64_t *v); |
26 | EXPORT_SYMBOL(atomic64_cmpxchg); | 26 | EXPORT_SYMBOL(atomic64_inc_not_zero_cx8); |
27 | 27 | int atomic64_add_unless_cx8(atomic64_t *v, long long a, long long u); | |
28 | /** | 28 | EXPORT_SYMBOL(atomic64_add_unless_cx8); |
29 | * atomic64_xchg - xchg atomic64 variable | 29 | |
30 | * @ptr: pointer to type atomic64_t | 30 | #ifndef CONFIG_X86_CMPXCHG64 |
31 | * @new_val: value to assign | 31 | long long atomic64_read_386(long long, const atomic64_t *v); |
32 | * | 32 | EXPORT_SYMBOL(atomic64_read_386); |
33 | * Atomically xchgs the value of @ptr to @new_val and returns | 33 | long long atomic64_set_386(long long, const atomic64_t *v); |
34 | * the old value. | 34 | EXPORT_SYMBOL(atomic64_set_386); |
35 | */ | 35 | long long atomic64_xchg_386(long long, unsigned high); |
36 | u64 atomic64_xchg(atomic64_t *ptr, u64 new_val) | 36 | EXPORT_SYMBOL(atomic64_xchg_386); |
37 | { | 37 | long long atomic64_add_return_386(long long a, atomic64_t *v); |
38 | /* | 38 | EXPORT_SYMBOL(atomic64_add_return_386); |
39 | * Try first with a (possibly incorrect) assumption about | 39 | long long atomic64_sub_return_386(long long a, atomic64_t *v); |
40 | * what we have there. We'll do two loops most likely, | 40 | EXPORT_SYMBOL(atomic64_sub_return_386); |
41 | * but we'll get an ownership MESI transaction straight away | 41 | long long atomic64_inc_return_386(long long a, atomic64_t *v); |
42 | * instead of a read transaction followed by a | 42 | EXPORT_SYMBOL(atomic64_inc_return_386); |
43 | * flush-for-ownership transaction: | 43 | long long atomic64_dec_return_386(long long a, atomic64_t *v); |
44 | */ | 44 | EXPORT_SYMBOL(atomic64_dec_return_386); |
45 | u64 old_val, real_val = 0; | 45 | long long atomic64_add_386(long long a, atomic64_t *v); |
46 | 46 | EXPORT_SYMBOL(atomic64_add_386); | |
47 | do { | 47 | long long atomic64_sub_386(long long a, atomic64_t *v); |
48 | old_val = real_val; | 48 | EXPORT_SYMBOL(atomic64_sub_386); |
49 | 49 | long long atomic64_inc_386(long long a, atomic64_t *v); | |
50 | real_val = atomic64_cmpxchg(ptr, old_val, new_val); | 50 | EXPORT_SYMBOL(atomic64_inc_386); |
51 | 51 | long long atomic64_dec_386(long long a, atomic64_t *v); | |
52 | } while (real_val != old_val); | 52 | EXPORT_SYMBOL(atomic64_dec_386); |
53 | 53 | long long atomic64_dec_if_positive_386(atomic64_t *v); | |
54 | return old_val; | 54 | EXPORT_SYMBOL(atomic64_dec_if_positive_386); |
55 | } | 55 | int atomic64_inc_not_zero_386(atomic64_t *v); |
56 | EXPORT_SYMBOL(atomic64_xchg); | 56 | EXPORT_SYMBOL(atomic64_inc_not_zero_386); |
57 | 57 | int atomic64_add_unless_386(atomic64_t *v, long long a, long long u); | |
58 | /** | 58 | EXPORT_SYMBOL(atomic64_add_unless_386); |
59 | * atomic64_set - set atomic64 variable | 59 | #endif |
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 | */ | ||
65 | void atomic64_set(atomic64_t *ptr, u64 new_val) | ||
66 | { | ||
67 | atomic64_xchg(ptr, new_val); | ||
68 | } | ||
69 | EXPORT_SYMBOL(atomic64_set); | ||
70 | |||
71 | /** | ||
72 | EXPORT_SYMBOL(atomic64_read); | ||
73 | * atomic64_add_return - add and return | ||
74 | * @delta: integer value to add | ||
75 | * @ptr: pointer to type atomic64_t | ||
76 | * | ||
77 | * Atomically adds @delta to @ptr and returns @delta + *@ptr | ||
78 | */ | ||
79 | noinline u64 atomic64_add_return(u64 delta, atomic64_t *ptr) | ||
80 | { | ||
81 | /* | ||
82 | * Try first with a (possibly incorrect) assumption about | ||
83 | * what we have there. We'll do two loops most likely, | ||
84 | * but we'll get an ownership MESI transaction straight away | ||
85 | * instead of a read transaction followed by a | ||
86 | * flush-for-ownership transaction: | ||
87 | */ | ||
88 | u64 old_val, new_val, real_val = 0; | ||
89 | |||
90 | do { | ||
91 | old_val = real_val; | ||
92 | new_val = old_val + delta; | ||
93 | |||
94 | real_val = atomic64_cmpxchg(ptr, old_val, new_val); | ||
95 | |||
96 | } while (real_val != old_val); | ||
97 | |||
98 | return new_val; | ||
99 | } | ||
100 | EXPORT_SYMBOL(atomic64_add_return); | ||
101 | |||
102 | u64 atomic64_sub_return(u64 delta, atomic64_t *ptr) | ||
103 | { | ||
104 | return atomic64_add_return(-delta, ptr); | ||
105 | } | ||
106 | EXPORT_SYMBOL(atomic64_sub_return); | ||
107 | |||
108 | u64 atomic64_inc_return(atomic64_t *ptr) | ||
109 | { | ||
110 | return atomic64_add_return(1, ptr); | ||
111 | } | ||
112 | EXPORT_SYMBOL(atomic64_inc_return); | ||
113 | |||
114 | u64 atomic64_dec_return(atomic64_t *ptr) | ||
115 | { | ||
116 | return atomic64_sub_return(1, ptr); | ||
117 | } | ||
118 | EXPORT_SYMBOL(atomic64_dec_return); | ||
119 | |||
120 | /** | ||
121 | * atomic64_add - add integer to atomic64 variable | ||
122 | * @delta: integer value to add | ||
123 | * @ptr: pointer to type atomic64_t | ||
124 | * | ||
125 | * Atomically adds @delta to @ptr. | ||
126 | */ | ||
127 | void atomic64_add(u64 delta, atomic64_t *ptr) | ||
128 | { | ||
129 | atomic64_add_return(delta, ptr); | ||
130 | } | ||
131 | EXPORT_SYMBOL(atomic64_add); | ||
132 | |||
133 | /** | ||
134 | * atomic64_sub - subtract the atomic64 variable | ||
135 | * @delta: integer value to subtract | ||
136 | * @ptr: pointer to type atomic64_t | ||
137 | * | ||
138 | * Atomically subtracts @delta from @ptr. | ||
139 | */ | ||
140 | void atomic64_sub(u64 delta, atomic64_t *ptr) | ||
141 | { | ||
142 | atomic64_add(-delta, ptr); | ||
143 | } | ||
144 | EXPORT_SYMBOL(atomic64_sub); | ||
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 | */ | ||
155 | int atomic64_sub_and_test(u64 delta, atomic64_t *ptr) | ||
156 | { | ||
157 | u64 new_val = atomic64_sub_return(delta, ptr); | ||
158 | |||
159 | return new_val == 0; | ||
160 | } | ||
161 | EXPORT_SYMBOL(atomic64_sub_and_test); | ||
162 | |||
163 | /** | ||
164 | * atomic64_inc - increment atomic64 variable | ||
165 | * @ptr: pointer to type atomic64_t | ||
166 | * | ||
167 | * Atomically increments @ptr by 1. | ||
168 | */ | ||
169 | void atomic64_inc(atomic64_t *ptr) | ||
170 | { | ||
171 | atomic64_add(1, ptr); | ||
172 | } | ||
173 | EXPORT_SYMBOL(atomic64_inc); | ||
174 | |||
175 | /** | ||
176 | * atomic64_dec - decrement atomic64 variable | ||
177 | * @ptr: pointer to type atomic64_t | ||
178 | * | ||
179 | * Atomically decrements @ptr by 1. | ||
180 | */ | ||
181 | void atomic64_dec(atomic64_t *ptr) | ||
182 | { | ||
183 | atomic64_sub(1, ptr); | ||
184 | } | ||
185 | EXPORT_SYMBOL(atomic64_dec); | ||
186 | |||
187 | /** | ||
188 | * atomic64_dec_and_test - decrement and test | ||
189 | * @ptr: pointer to type atomic64_t | ||
190 | * | ||
191 | * Atomically decrements @ptr by 1 and | ||
192 | * returns true if the result is 0, or false for all other | ||
193 | * cases. | ||
194 | */ | ||
195 | int atomic64_dec_and_test(atomic64_t *ptr) | ||
196 | { | ||
197 | return atomic64_sub_and_test(1, ptr); | ||
198 | } | ||
199 | EXPORT_SYMBOL(atomic64_dec_and_test); | ||
200 | |||
201 | /** | ||
202 | * atomic64_inc_and_test - increment and test | ||
203 | * @ptr: pointer to type atomic64_t | ||
204 | * | ||
205 | * Atomically increments @ptr by 1 | ||
206 | * and returns true if the result is zero, or false for all | ||
207 | * other cases. | ||
208 | */ | ||
209 | int atomic64_inc_and_test(atomic64_t *ptr) | ||
210 | { | ||
211 | return atomic64_sub_and_test(-1, ptr); | ||
212 | } | ||
213 | EXPORT_SYMBOL(atomic64_inc_and_test); | ||
214 | |||
215 | /** | ||
216 | * atomic64_add_negative - add and test if negative | ||
217 | * @delta: integer value to add | ||
218 | * @ptr: pointer to type atomic64_t | ||
219 | * | ||
220 | * Atomically adds @delta to @ptr and returns true | ||
221 | * if the result is negative, or false when | ||
222 | * result is greater than or equal to zero. | ||
223 | */ | ||
224 | int atomic64_add_negative(u64 delta, atomic64_t *ptr) | ||
225 | { | ||
226 | s64 new_val = atomic64_add_return(delta, ptr); | ||
227 | |||
228 | return new_val < 0; | ||
229 | } | ||
230 | EXPORT_SYMBOL(atomic64_add_negative); | ||
diff --git a/arch/x86/lib/atomic64_386_32.S b/arch/x86/lib/atomic64_386_32.S new file mode 100644 index 000000000000..4a5979aa6883 --- /dev/null +++ b/arch/x86/lib/atomic64_386_32.S | |||
@@ -0,0 +1,174 @@ | |||
1 | /* | ||
2 | * atomic64_t for 386/486 | ||
3 | * | ||
4 | * Copyright © 2010 Luca Barbieri | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/linkage.h> | ||
13 | #include <asm/alternative-asm.h> | ||
14 | #include <asm/dwarf2.h> | ||
15 | |||
16 | /* if you want SMP support, implement these with real spinlocks */ | ||
17 | .macro LOCK reg | ||
18 | pushfl | ||
19 | CFI_ADJUST_CFA_OFFSET 4 | ||
20 | cli | ||
21 | .endm | ||
22 | |||
23 | .macro UNLOCK reg | ||
24 | popfl | ||
25 | CFI_ADJUST_CFA_OFFSET -4 | ||
26 | .endm | ||
27 | |||
28 | .macro BEGIN func reg | ||
29 | $v = \reg | ||
30 | |||
31 | ENTRY(atomic64_\func\()_386) | ||
32 | CFI_STARTPROC | ||
33 | LOCK $v | ||
34 | |||
35 | .macro RETURN | ||
36 | UNLOCK $v | ||
37 | ret | ||
38 | .endm | ||
39 | |||
40 | .macro END_ | ||
41 | CFI_ENDPROC | ||
42 | ENDPROC(atomic64_\func\()_386) | ||
43 | .purgem RETURN | ||
44 | .purgem END_ | ||
45 | .purgem END | ||
46 | .endm | ||
47 | |||
48 | .macro END | ||
49 | RETURN | ||
50 | END_ | ||
51 | .endm | ||
52 | .endm | ||
53 | |||
54 | BEGIN read %ecx | ||
55 | movl ($v), %eax | ||
56 | movl 4($v), %edx | ||
57 | END | ||
58 | |||
59 | BEGIN set %esi | ||
60 | movl %ebx, ($v) | ||
61 | movl %ecx, 4($v) | ||
62 | END | ||
63 | |||
64 | BEGIN xchg %esi | ||
65 | movl ($v), %eax | ||
66 | movl 4($v), %edx | ||
67 | movl %ebx, ($v) | ||
68 | movl %ecx, 4($v) | ||
69 | END | ||
70 | |||
71 | BEGIN add %ecx | ||
72 | addl %eax, ($v) | ||
73 | adcl %edx, 4($v) | ||
74 | END | ||
75 | |||
76 | BEGIN add_return %ecx | ||
77 | addl ($v), %eax | ||
78 | adcl 4($v), %edx | ||
79 | movl %eax, ($v) | ||
80 | movl %edx, 4($v) | ||
81 | END | ||
82 | |||
83 | BEGIN sub %ecx | ||
84 | subl %eax, ($v) | ||
85 | sbbl %edx, 4($v) | ||
86 | END | ||
87 | |||
88 | BEGIN sub_return %ecx | ||
89 | negl %edx | ||
90 | negl %eax | ||
91 | sbbl $0, %edx | ||
92 | addl ($v), %eax | ||
93 | adcl 4($v), %edx | ||
94 | movl %eax, ($v) | ||
95 | movl %edx, 4($v) | ||
96 | END | ||
97 | |||
98 | BEGIN inc %esi | ||
99 | addl $1, ($v) | ||
100 | adcl $0, 4($v) | ||
101 | END | ||
102 | |||
103 | BEGIN inc_return %esi | ||
104 | movl ($v), %eax | ||
105 | movl 4($v), %edx | ||
106 | addl $1, %eax | ||
107 | adcl $0, %edx | ||
108 | movl %eax, ($v) | ||
109 | movl %edx, 4($v) | ||
110 | END | ||
111 | |||
112 | BEGIN dec %esi | ||
113 | subl $1, ($v) | ||
114 | sbbl $0, 4($v) | ||
115 | END | ||
116 | |||
117 | BEGIN dec_return %esi | ||
118 | movl ($v), %eax | ||
119 | movl 4($v), %edx | ||
120 | subl $1, %eax | ||
121 | sbbl $0, %edx | ||
122 | movl %eax, ($v) | ||
123 | movl %edx, 4($v) | ||
124 | END | ||
125 | |||
126 | BEGIN add_unless %ecx | ||
127 | addl %eax, %esi | ||
128 | adcl %edx, %edi | ||
129 | addl ($v), %eax | ||
130 | adcl 4($v), %edx | ||
131 | cmpl %eax, %esi | ||
132 | je 3f | ||
133 | 1: | ||
134 | movl %eax, ($v) | ||
135 | movl %edx, 4($v) | ||
136 | movl $1, %eax | ||
137 | 2: | ||
138 | RETURN | ||
139 | 3: | ||
140 | cmpl %edx, %edi | ||
141 | jne 1b | ||
142 | xorl %eax, %eax | ||
143 | jmp 2b | ||
144 | END_ | ||
145 | |||
146 | BEGIN inc_not_zero %esi | ||
147 | movl ($v), %eax | ||
148 | movl 4($v), %edx | ||
149 | testl %eax, %eax | ||
150 | je 3f | ||
151 | 1: | ||
152 | addl $1, %eax | ||
153 | adcl $0, %edx | ||
154 | movl %eax, ($v) | ||
155 | movl %edx, 4($v) | ||
156 | movl $1, %eax | ||
157 | 2: | ||
158 | RETURN | ||
159 | 3: | ||
160 | testl %edx, %edx | ||
161 | jne 1b | ||
162 | jmp 2b | ||
163 | END_ | ||
164 | |||
165 | BEGIN dec_if_positive %esi | ||
166 | movl ($v), %eax | ||
167 | movl 4($v), %edx | ||
168 | subl $1, %eax | ||
169 | sbbl $0, %edx | ||
170 | js 1f | ||
171 | movl %eax, ($v) | ||
172 | movl %edx, 4($v) | ||
173 | 1: | ||
174 | END | ||
diff --git a/arch/x86/lib/atomic64_cx8_32.S b/arch/x86/lib/atomic64_cx8_32.S new file mode 100644 index 000000000000..71e080de3352 --- /dev/null +++ b/arch/x86/lib/atomic64_cx8_32.S | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * atomic64_t for 586+ | ||
3 | * | ||
4 | * Copyright © 2010 Luca Barbieri | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/linkage.h> | ||
13 | #include <asm/alternative-asm.h> | ||
14 | #include <asm/dwarf2.h> | ||
15 | |||
16 | .macro SAVE reg | ||
17 | pushl %\reg | ||
18 | CFI_ADJUST_CFA_OFFSET 4 | ||
19 | CFI_REL_OFFSET \reg, 0 | ||
20 | .endm | ||
21 | |||
22 | .macro RESTORE reg | ||
23 | popl %\reg | ||
24 | CFI_ADJUST_CFA_OFFSET -4 | ||
25 | CFI_RESTORE \reg | ||
26 | .endm | ||
27 | |||
28 | .macro read64 reg | ||
29 | movl %ebx, %eax | ||
30 | movl %ecx, %edx | ||
31 | /* we need LOCK_PREFIX since otherwise cmpxchg8b always does the write */ | ||
32 | LOCK_PREFIX | ||
33 | cmpxchg8b (\reg) | ||
34 | .endm | ||
35 | |||
36 | ENTRY(atomic64_read_cx8) | ||
37 | CFI_STARTPROC | ||
38 | |||
39 | read64 %ecx | ||
40 | ret | ||
41 | CFI_ENDPROC | ||
42 | ENDPROC(atomic64_read_cx8) | ||
43 | |||
44 | ENTRY(atomic64_set_cx8) | ||
45 | CFI_STARTPROC | ||
46 | |||
47 | 1: | ||
48 | /* we don't need LOCK_PREFIX since aligned 64-bit writes | ||
49 | * are atomic on 586 and newer */ | ||
50 | cmpxchg8b (%esi) | ||
51 | jne 1b | ||
52 | |||
53 | ret | ||
54 | CFI_ENDPROC | ||
55 | ENDPROC(atomic64_set_cx8) | ||
56 | |||
57 | ENTRY(atomic64_xchg_cx8) | ||
58 | CFI_STARTPROC | ||
59 | |||
60 | movl %ebx, %eax | ||
61 | movl %ecx, %edx | ||
62 | 1: | ||
63 | LOCK_PREFIX | ||
64 | cmpxchg8b (%esi) | ||
65 | jne 1b | ||
66 | |||
67 | ret | ||
68 | CFI_ENDPROC | ||
69 | ENDPROC(atomic64_xchg_cx8) | ||
70 | |||
71 | .macro addsub_return func ins insc | ||
72 | ENTRY(atomic64_\func\()_return_cx8) | ||
73 | CFI_STARTPROC | ||
74 | SAVE ebp | ||
75 | SAVE ebx | ||
76 | SAVE esi | ||
77 | SAVE edi | ||
78 | |||
79 | movl %eax, %esi | ||
80 | movl %edx, %edi | ||
81 | movl %ecx, %ebp | ||
82 | |||
83 | read64 %ebp | ||
84 | 1: | ||
85 | movl %eax, %ebx | ||
86 | movl %edx, %ecx | ||
87 | \ins\()l %esi, %ebx | ||
88 | \insc\()l %edi, %ecx | ||
89 | LOCK_PREFIX | ||
90 | cmpxchg8b (%ebp) | ||
91 | jne 1b | ||
92 | |||
93 | 10: | ||
94 | movl %ebx, %eax | ||
95 | movl %ecx, %edx | ||
96 | RESTORE edi | ||
97 | RESTORE esi | ||
98 | RESTORE ebx | ||
99 | RESTORE ebp | ||
100 | ret | ||
101 | CFI_ENDPROC | ||
102 | ENDPROC(atomic64_\func\()_return_cx8) | ||
103 | .endm | ||
104 | |||
105 | addsub_return add add adc | ||
106 | addsub_return sub sub sbb | ||
107 | |||
108 | .macro incdec_return func ins insc | ||
109 | ENTRY(atomic64_\func\()_return_cx8) | ||
110 | CFI_STARTPROC | ||
111 | SAVE ebx | ||
112 | |||
113 | read64 %esi | ||
114 | 1: | ||
115 | movl %eax, %ebx | ||
116 | movl %edx, %ecx | ||
117 | \ins\()l $1, %ebx | ||
118 | \insc\()l $0, %ecx | ||
119 | LOCK_PREFIX | ||
120 | cmpxchg8b (%esi) | ||
121 | jne 1b | ||
122 | |||
123 | 10: | ||
124 | movl %ebx, %eax | ||
125 | movl %ecx, %edx | ||
126 | RESTORE ebx | ||
127 | ret | ||
128 | CFI_ENDPROC | ||
129 | ENDPROC(atomic64_\func\()_return_cx8) | ||
130 | .endm | ||
131 | |||
132 | incdec_return inc add adc | ||
133 | incdec_return dec sub sbb | ||
134 | |||
135 | ENTRY(atomic64_dec_if_positive_cx8) | ||
136 | CFI_STARTPROC | ||
137 | SAVE ebx | ||
138 | |||
139 | read64 %esi | ||
140 | 1: | ||
141 | movl %eax, %ebx | ||
142 | movl %edx, %ecx | ||
143 | subl $1, %ebx | ||
144 | sbb $0, %ecx | ||
145 | js 2f | ||
146 | LOCK_PREFIX | ||
147 | cmpxchg8b (%esi) | ||
148 | jne 1b | ||
149 | |||
150 | 2: | ||
151 | movl %ebx, %eax | ||
152 | movl %ecx, %edx | ||
153 | RESTORE ebx | ||
154 | ret | ||
155 | CFI_ENDPROC | ||
156 | ENDPROC(atomic64_dec_if_positive_cx8) | ||
157 | |||
158 | ENTRY(atomic64_add_unless_cx8) | ||
159 | CFI_STARTPROC | ||
160 | SAVE ebp | ||
161 | SAVE ebx | ||
162 | /* these just push these two parameters on the stack */ | ||
163 | SAVE edi | ||
164 | SAVE esi | ||
165 | |||
166 | movl %ecx, %ebp | ||
167 | movl %eax, %esi | ||
168 | movl %edx, %edi | ||
169 | |||
170 | read64 %ebp | ||
171 | 1: | ||
172 | cmpl %eax, 0(%esp) | ||
173 | je 4f | ||
174 | 2: | ||
175 | movl %eax, %ebx | ||
176 | movl %edx, %ecx | ||
177 | addl %esi, %ebx | ||
178 | adcl %edi, %ecx | ||
179 | LOCK_PREFIX | ||
180 | cmpxchg8b (%ebp) | ||
181 | jne 1b | ||
182 | |||
183 | movl $1, %eax | ||
184 | 3: | ||
185 | addl $8, %esp | ||
186 | CFI_ADJUST_CFA_OFFSET -8 | ||
187 | RESTORE ebx | ||
188 | RESTORE ebp | ||
189 | ret | ||
190 | 4: | ||
191 | cmpl %edx, 4(%esp) | ||
192 | jne 2b | ||
193 | xorl %eax, %eax | ||
194 | jmp 3b | ||
195 | CFI_ENDPROC | ||
196 | ENDPROC(atomic64_add_unless_cx8) | ||
197 | |||
198 | ENTRY(atomic64_inc_not_zero_cx8) | ||
199 | CFI_STARTPROC | ||
200 | SAVE ebx | ||
201 | |||
202 | read64 %esi | ||
203 | 1: | ||
204 | testl %eax, %eax | ||
205 | je 4f | ||
206 | 2: | ||
207 | movl %eax, %ebx | ||
208 | movl %edx, %ecx | ||
209 | addl $1, %ebx | ||
210 | adcl $0, %ecx | ||
211 | LOCK_PREFIX | ||
212 | cmpxchg8b (%esi) | ||
213 | jne 1b | ||
214 | |||
215 | movl $1, %eax | ||
216 | 3: | ||
217 | RESTORE ebx | ||
218 | ret | ||
219 | 4: | ||
220 | testl %edx, %edx | ||
221 | jne 2b | ||
222 | jmp 3b | ||
223 | CFI_ENDPROC | ||
224 | ENDPROC(atomic64_inc_not_zero_cx8) | ||
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 930a9e5eae08..d85be90d5888 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
@@ -1098,6 +1098,13 @@ config DMA_API_DEBUG | |||
1098 | This option causes a performance degredation. Use only if you want | 1098 | This option causes a performance degredation. Use only if you want |
1099 | to debug device drivers. If unsure, say N. | 1099 | to debug device drivers. If unsure, say N. |
1100 | 1100 | ||
1101 | config ATOMIC64_SELFTEST | ||
1102 | bool "Perform an atomic64_t self-test at boot" | ||
1103 | help | ||
1104 | Enable this option to test the atomic64_t functions at boot. | ||
1105 | |||
1106 | If unsure, say N. | ||
1107 | |||
1101 | source "samples/Kconfig" | 1108 | source "samples/Kconfig" |
1102 | 1109 | ||
1103 | source "lib/Kconfig.kgdb" | 1110 | source "lib/Kconfig.kgdb" |
diff --git a/lib/Makefile b/lib/Makefile index 0d4015205c64..af759f39d4ae 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
@@ -101,6 +101,8 @@ obj-$(CONFIG_GENERIC_CSUM) += checksum.o | |||
101 | 101 | ||
102 | obj-$(CONFIG_GENERIC_ATOMIC64) += atomic64.o | 102 | obj-$(CONFIG_GENERIC_ATOMIC64) += atomic64.o |
103 | 103 | ||
104 | obj-$(CONFIG_ATOMIC64_SELFTEST) += atomic64_test.o | ||
105 | |||
104 | hostprogs-y := gen_crc32table | 106 | hostprogs-y := gen_crc32table |
105 | clean-files := crc32table.h | 107 | clean-files := crc32table.h |
106 | 108 | ||
diff --git a/lib/atomic64.c b/lib/atomic64.c index 8bee16ec7524..a21c12bc727c 100644 --- a/lib/atomic64.c +++ b/lib/atomic64.c | |||
@@ -162,12 +162,12 @@ int atomic64_add_unless(atomic64_t *v, long long a, long long u) | |||
162 | { | 162 | { |
163 | unsigned long flags; | 163 | unsigned long flags; |
164 | spinlock_t *lock = lock_addr(v); | 164 | spinlock_t *lock = lock_addr(v); |
165 | int ret = 1; | 165 | int ret = 0; |
166 | 166 | ||
167 | spin_lock_irqsave(lock, flags); | 167 | spin_lock_irqsave(lock, flags); |
168 | if (v->counter != u) { | 168 | if (v->counter != u) { |
169 | v->counter += a; | 169 | v->counter += a; |
170 | ret = 0; | 170 | ret = 1; |
171 | } | 171 | } |
172 | spin_unlock_irqrestore(lock, flags); | 172 | spin_unlock_irqrestore(lock, flags); |
173 | return ret; | 173 | return ret; |
diff --git a/lib/atomic64_test.c b/lib/atomic64_test.c new file mode 100644 index 000000000000..65e482caf5e9 --- /dev/null +++ b/lib/atomic64_test.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* | ||
2 | * Testsuite for atomic64_t functions | ||
3 | * | ||
4 | * Copyright © 2010 Luca Barbieri | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | #include <linux/init.h> | ||
12 | #include <asm/atomic.h> | ||
13 | |||
14 | #define INIT(c) do { atomic64_set(&v, c); r = c; } while (0) | ||
15 | static __init int test_atomic64(void) | ||
16 | { | ||
17 | long long v0 = 0xaaa31337c001d00dLL; | ||
18 | long long v1 = 0xdeadbeefdeafcafeLL; | ||
19 | long long v2 = 0xfaceabadf00df001LL; | ||
20 | long long onestwos = 0x1111111122222222LL; | ||
21 | long long one = 1LL; | ||
22 | |||
23 | atomic64_t v = ATOMIC64_INIT(v0); | ||
24 | long long r = v0; | ||
25 | BUG_ON(v.counter != r); | ||
26 | |||
27 | atomic64_set(&v, v1); | ||
28 | r = v1; | ||
29 | BUG_ON(v.counter != r); | ||
30 | BUG_ON(atomic64_read(&v) != r); | ||
31 | |||
32 | INIT(v0); | ||
33 | atomic64_add(onestwos, &v); | ||
34 | r += onestwos; | ||
35 | BUG_ON(v.counter != r); | ||
36 | |||
37 | INIT(v0); | ||
38 | atomic64_add(-one, &v); | ||
39 | r += -one; | ||
40 | BUG_ON(v.counter != r); | ||
41 | |||
42 | INIT(v0); | ||
43 | r += onestwos; | ||
44 | BUG_ON(atomic64_add_return(onestwos, &v) != r); | ||
45 | BUG_ON(v.counter != r); | ||
46 | |||
47 | INIT(v0); | ||
48 | r += -one; | ||
49 | BUG_ON(atomic64_add_return(-one, &v) != r); | ||
50 | BUG_ON(v.counter != r); | ||
51 | |||
52 | INIT(v0); | ||
53 | atomic64_sub(onestwos, &v); | ||
54 | r -= onestwos; | ||
55 | BUG_ON(v.counter != r); | ||
56 | |||
57 | INIT(v0); | ||
58 | atomic64_sub(-one, &v); | ||
59 | r -= -one; | ||
60 | BUG_ON(v.counter != r); | ||
61 | |||
62 | INIT(v0); | ||
63 | r -= onestwos; | ||
64 | BUG_ON(atomic64_sub_return(onestwos, &v) != r); | ||
65 | BUG_ON(v.counter != r); | ||
66 | |||
67 | INIT(v0); | ||
68 | r -= -one; | ||
69 | BUG_ON(atomic64_sub_return(-one, &v) != r); | ||
70 | BUG_ON(v.counter != r); | ||
71 | |||
72 | INIT(v0); | ||
73 | atomic64_inc(&v); | ||
74 | r += one; | ||
75 | BUG_ON(v.counter != r); | ||
76 | |||
77 | INIT(v0); | ||
78 | r += one; | ||
79 | BUG_ON(atomic64_inc_return(&v) != r); | ||
80 | BUG_ON(v.counter != r); | ||
81 | |||
82 | INIT(v0); | ||
83 | atomic64_dec(&v); | ||
84 | r -= one; | ||
85 | BUG_ON(v.counter != r); | ||
86 | |||
87 | INIT(v0); | ||
88 | r -= one; | ||
89 | BUG_ON(atomic64_dec_return(&v) != r); | ||
90 | BUG_ON(v.counter != r); | ||
91 | |||
92 | INIT(v0); | ||
93 | BUG_ON(atomic64_xchg(&v, v1) != v0); | ||
94 | r = v1; | ||
95 | BUG_ON(v.counter != r); | ||
96 | |||
97 | INIT(v0); | ||
98 | BUG_ON(atomic64_cmpxchg(&v, v0, v1) != v0); | ||
99 | r = v1; | ||
100 | BUG_ON(v.counter != r); | ||
101 | |||
102 | INIT(v0); | ||
103 | BUG_ON(atomic64_cmpxchg(&v, v2, v1) != v0); | ||
104 | BUG_ON(v.counter != r); | ||
105 | |||
106 | INIT(v0); | ||
107 | BUG_ON(atomic64_add_unless(&v, one, v0)); | ||
108 | BUG_ON(v.counter != r); | ||
109 | |||
110 | INIT(v0); | ||
111 | BUG_ON(!atomic64_add_unless(&v, one, v1)); | ||
112 | r += one; | ||
113 | BUG_ON(v.counter != r); | ||
114 | |||
115 | #if defined(CONFIG_X86) || defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(_ASM_GENERIC_ATOMIC64_H) | ||
116 | INIT(onestwos); | ||
117 | BUG_ON(atomic64_dec_if_positive(&v) != (onestwos - 1)); | ||
118 | r -= one; | ||
119 | BUG_ON(v.counter != r); | ||
120 | |||
121 | INIT(0); | ||
122 | BUG_ON(atomic64_dec_if_positive(&v) != -one); | ||
123 | BUG_ON(v.counter != r); | ||
124 | |||
125 | INIT(-one); | ||
126 | BUG_ON(atomic64_dec_if_positive(&v) != (-one - one)); | ||
127 | BUG_ON(v.counter != r); | ||
128 | #else | ||
129 | #warning Please implement atomic64_dec_if_positive for your architecture, and add it to the IF above | ||
130 | #endif | ||
131 | |||
132 | INIT(onestwos); | ||
133 | BUG_ON(!atomic64_inc_not_zero(&v)); | ||
134 | r += one; | ||
135 | BUG_ON(v.counter != r); | ||
136 | |||
137 | INIT(0); | ||
138 | BUG_ON(atomic64_inc_not_zero(&v)); | ||
139 | BUG_ON(v.counter != r); | ||
140 | |||
141 | INIT(-one); | ||
142 | BUG_ON(!atomic64_inc_not_zero(&v)); | ||
143 | r += one; | ||
144 | BUG_ON(v.counter != r); | ||
145 | |||
146 | #ifdef CONFIG_X86 | ||
147 | printk(KERN_INFO "atomic64 test passed for %s platform %s CX8 and %s SSE\n", | ||
148 | #ifdef CONFIG_X86_64 | ||
149 | "x86-64", | ||
150 | #elif defined(CONFIG_X86_CMPXCHG64) | ||
151 | "i586+", | ||
152 | #else | ||
153 | "i386+", | ||
154 | #endif | ||
155 | boot_cpu_has(X86_FEATURE_CX8) ? "with" : "without", | ||
156 | boot_cpu_has(X86_FEATURE_XMM) ? "with" : "without"); | ||
157 | #else | ||
158 | printk(KERN_INFO "atomic64 test passed\n"); | ||
159 | #endif | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | core_initcall(test_atomic64); | ||