Branch data Line data Source code
1 : : #ifndef __ASM_ARM_CMPXCHG_H
2 : : #define __ASM_ARM_CMPXCHG_H
3 : :
4 : : #include <linux/irqflags.h>
5 : : #include <asm/barrier.h>
6 : :
7 : : #if defined(CONFIG_CPU_SA1100) || defined(CONFIG_CPU_SA110)
8 : : /*
9 : : * On the StrongARM, "swp" is terminally broken since it bypasses the
10 : : * cache totally. This means that the cache becomes inconsistent, and,
11 : : * since we use normal loads/stores as well, this is really bad.
12 : : * Typically, this causes oopsen in filp_close, but could have other,
13 : : * more disastrous effects. There are two work-arounds:
14 : : * 1. Disable interrupts and emulate the atomic swap
15 : : * 2. Clean the cache, perform atomic swap, flush the cache
16 : : *
17 : : * We choose (1) since its the "easiest" to achieve here and is not
18 : : * dependent on the processor type.
19 : : *
20 : : * NOTE that this solution won't work on an SMP system, so explcitly
21 : : * forbid it here.
22 : : */
23 : : #define swp_is_buggy
24 : : #endif
25 : :
26 : : static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size)
27 : : {
28 : : extern void __bad_xchg(volatile void *, int);
29 : : unsigned long ret;
30 : : #ifdef swp_is_buggy
31 : : unsigned long flags;
32 : : #endif
33 : : #if __LINUX_ARM_ARCH__ >= 6
34 : : unsigned int tmp;
35 : : #endif
36 : :
37 : 14852656 : smp_mb();
38 : :
39 : : switch (size) {
40 : : #if __LINUX_ARM_ARCH__ >= 6
41 : : case 1:
42 : : asm volatile("@ __xchg1\n"
43 : : "1: ldrexb %0, [%3]\n"
44 : : " strexb %1, %2, [%3]\n"
45 : : " teq %1, #0\n"
46 : : " bne 1b"
47 : : : "=&r" (ret), "=&r" (tmp)
48 : : : "r" (x), "r" (ptr)
49 : : : "memory", "cc");
50 : : break;
51 : : case 4:
52 : 14852680 : asm volatile("@ __xchg4\n"
53 : : "1: ldrex %0, [%3]\n"
54 : : " strex %1, %2, [%3]\n"
55 : : " teq %1, #0\n"
56 : : " bne 1b"
57 : : : "=&r" (ret), "=&r" (tmp)
58 : : : "r" (x), "r" (ptr)
59 : : : "memory", "cc");
60 : : break;
61 : : #elif defined(swp_is_buggy)
62 : : #ifdef CONFIG_SMP
63 : : #error SMP is not supported on this platform
64 : : #endif
65 : : case 1:
66 : : raw_local_irq_save(flags);
67 : : ret = *(volatile unsigned char *)ptr;
68 : : *(volatile unsigned char *)ptr = x;
69 : : raw_local_irq_restore(flags);
70 : : break;
71 : :
72 : : case 4:
73 : : raw_local_irq_save(flags);
74 : : ret = *(volatile unsigned long *)ptr;
75 : : *(volatile unsigned long *)ptr = x;
76 : : raw_local_irq_restore(flags);
77 : : break;
78 : : #else
79 : : case 1:
80 : : asm volatile("@ __xchg1\n"
81 : : " swpb %0, %1, [%2]"
82 : : : "=&r" (ret)
83 : : : "r" (x), "r" (ptr)
84 : : : "memory", "cc");
85 : : break;
86 : : case 4:
87 : : asm volatile("@ __xchg4\n"
88 : : " swp %0, %1, [%2]"
89 : : : "=&r" (ret)
90 : : : "r" (x), "r" (ptr)
91 : : : "memory", "cc");
92 : : break;
93 : : #endif
94 : : default:
95 : : __bad_xchg(ptr, size), ret = 0;
96 : : break;
97 : : }
98 : 14853171 : smp_mb();
99 : :
100 : : return ret;
101 : : }
102 : :
103 : : #define xchg(ptr,x) \
104 : : ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
105 : :
106 : : #include <asm-generic/cmpxchg-local.h>
107 : :
108 : : #if __LINUX_ARM_ARCH__ < 6
109 : : /* min ARCH < ARMv6 */
110 : :
111 : : #ifdef CONFIG_SMP
112 : : #error "SMP is not supported on this platform"
113 : : #endif
114 : :
115 : : /*
116 : : * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make
117 : : * them available.
118 : : */
119 : : #define cmpxchg_local(ptr, o, n) \
120 : : ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\
121 : : (unsigned long)(n), sizeof(*(ptr))))
122 : : #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
123 : :
124 : : #ifndef CONFIG_SMP
125 : : #include <asm-generic/cmpxchg.h>
126 : : #endif
127 : :
128 : : #else /* min ARCH >= ARMv6 */
129 : :
130 : : extern void __bad_cmpxchg(volatile void *ptr, int size);
131 : :
132 : : /*
133 : : * cmpxchg only support 32-bits operands on ARMv6.
134 : : */
135 : :
136 : : static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
137 : : unsigned long new, int size)
138 : : {
139 : : unsigned long oldval, res;
140 : :
141 : : switch (size) {
142 : : #ifndef CONFIG_CPU_V6 /* min ARCH >= ARMv6K */
143 : : case 1:
144 : : do {
145 : : asm volatile("@ __cmpxchg1\n"
146 : : " ldrexb %1, [%2]\n"
147 : : " mov %0, #0\n"
148 : : " teq %1, %3\n"
149 : : " strexbeq %0, %4, [%2]\n"
150 : : : "=&r" (res), "=&r" (oldval)
151 : : : "r" (ptr), "Ir" (old), "r" (new)
152 : : : "memory", "cc");
153 : : } while (res);
154 : : break;
155 : : case 2:
156 : : do {
157 : : asm volatile("@ __cmpxchg1\n"
158 : : " ldrexh %1, [%2]\n"
159 : : " mov %0, #0\n"
160 : : " teq %1, %3\n"
161 : : " strexheq %0, %4, [%2]\n"
162 : : : "=&r" (res), "=&r" (oldval)
163 : : : "r" (ptr), "Ir" (old), "r" (new)
164 : : : "memory", "cc");
165 : : } while (res);
166 : : break;
167 : : #endif
168 : : case 4:
169 : : do {
170 : 28378204 : asm volatile("@ __cmpxchg4\n"
171 : : " ldrex %1, [%2]\n"
172 : : " mov %0, #0\n"
173 : : " teq %1, %3\n"
174 : : " strexeq %0, %4, [%2]\n"
175 : : : "=&r" (res), "=&r" (oldval)
176 : : : "r" (ptr), "Ir" (old), "r" (new)
177 : : : "memory", "cc");
178 [ + + ][ + + ]: 28378831 : } while (res);
[ + + ][ # #
# # # # #
# ][ + + ]
179 : : break;
180 : : default:
181 : : __bad_cmpxchg(ptr, size);
182 : : oldval = 0;
183 : : }
184 : :
185 : : return oldval;
186 : : }
187 : :
188 : : static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old,
189 : : unsigned long new, int size)
190 : : {
191 : : unsigned long ret;
192 : :
193 : 17300153 : smp_mb();
194 : : ret = __cmpxchg(ptr, old, new, size);
195 : 28151494 : smp_mb();
196 : :
197 : : return ret;
198 : : }
199 : :
200 : : #define cmpxchg(ptr,o,n) \
201 : : ((__typeof__(*(ptr)))__cmpxchg_mb((ptr), \
202 : : (unsigned long)(o), \
203 : : (unsigned long)(n), \
204 : : sizeof(*(ptr))))
205 : :
206 : : static inline unsigned long __cmpxchg_local(volatile void *ptr,
207 : : unsigned long old,
208 : : unsigned long new, int size)
209 : : {
210 : : unsigned long ret;
211 : :
212 : : switch (size) {
213 : : #ifdef CONFIG_CPU_V6 /* min ARCH == ARMv6 */
214 : : case 1:
215 : : case 2:
216 : : ret = __cmpxchg_local_generic(ptr, old, new, size);
217 : : break;
218 : : #endif
219 : : default:
220 : : ret = __cmpxchg(ptr, old, new, size);
221 : : }
222 : :
223 : : return ret;
224 : : }
225 : :
226 : : static inline unsigned long long __cmpxchg64(unsigned long long *ptr,
227 : : unsigned long long old,
228 : : unsigned long long new)
229 : : {
230 : : unsigned long long oldval;
231 : : unsigned long res;
232 : :
233 : 42112913 : __asm__ __volatile__(
234 : : "1: ldrexd %1, %H1, [%3]\n"
235 : : " teq %1, %4\n"
236 : : " teqeq %H1, %H4\n"
237 : : " bne 2f\n"
238 : : " strexd %0, %5, %H5, [%3]\n"
239 : : " teq %0, #0\n"
240 : : " bne 1b\n"
241 : : "2:"
242 : : : "=&r" (res), "=&r" (oldval), "+Qo" (*ptr)
243 : : : "r" (ptr), "r" (old), "r" (new)
244 : : : "cc");
245 : :
246 : : return oldval;
247 : : }
248 : :
249 : : static inline unsigned long long __cmpxchg64_mb(unsigned long long *ptr,
250 : : unsigned long long old,
251 : : unsigned long long new)
252 : : {
253 : : unsigned long long ret;
254 : :
255 : : smp_mb();
256 : : ret = __cmpxchg64(ptr, old, new);
257 : : smp_mb();
258 : :
259 : : return ret;
260 : : }
261 : :
262 : : #define cmpxchg_local(ptr,o,n) \
263 : : ((__typeof__(*(ptr)))__cmpxchg_local((ptr), \
264 : : (unsigned long)(o), \
265 : : (unsigned long)(n), \
266 : : sizeof(*(ptr))))
267 : :
268 : : #define cmpxchg64(ptr, o, n) \
269 : : ((__typeof__(*(ptr)))__cmpxchg64_mb((ptr), \
270 : : (unsigned long long)(o), \
271 : : (unsigned long long)(n)))
272 : :
273 : : #define cmpxchg64_relaxed(ptr, o, n) \
274 : : ((__typeof__(*(ptr)))__cmpxchg64((ptr), \
275 : : (unsigned long long)(o), \
276 : : (unsigned long long)(n)))
277 : :
278 : : #define cmpxchg64_local(ptr, o, n) cmpxchg64_relaxed((ptr), (o), (n))
279 : :
280 : : #endif /* __LINUX_ARM_ARCH__ >= 6 */
281 : :
282 : : #endif /* __ASM_ARM_CMPXCHG_H */
|