53f4b219336be527e017c821eee57b7bcd1bd966
[cascardo/linux.git] / arch / x86 / include / asm / cmpxchg_32.h
1 #ifndef _ASM_X86_CMPXCHG_32_H
2 #define _ASM_X86_CMPXCHG_32_H
3
4 /*
5  * Note: if you use set64_bit(), __cmpxchg64(), or their variants, you
6  *       you need to test for the feature in boot_cpu_data.
7  */
8
9 /*
10  * CMPXCHG8B only writes to the target if we had the previous
11  * value in registers, otherwise it acts as a read and gives us the
12  * "new previous" value.  That is why there is a loop.  Preloading
13  * EDX:EAX is a performance optimization: in the common case it means
14  * we need only one locked operation.
15  *
16  * A SIMD/3DNOW!/MMX/FPU 64-bit store here would require at the very
17  * least an FPU save and/or %cr0.ts manipulation.
18  *
19  * cmpxchg8b must be used with the lock prefix here to allow the
20  * instruction to be executed atomically.  We need to have the reader
21  * side to see the coherent 64bit value.
22  */
23 static inline void set_64bit(volatile u64 *ptr, u64 value)
24 {
25         u32 low  = value;
26         u32 high = value >> 32;
27         u64 prev = *ptr;
28
29         asm volatile("\n1:\t"
30                      LOCK_PREFIX "cmpxchg8b %0\n\t"
31                      "jnz 1b"
32                      : "=m" (*ptr), "+A" (prev)
33                      : "b" (low), "c" (high)
34                      : "memory");
35 }
36
37 #ifdef CONFIG_X86_CMPXCHG
38 #define __HAVE_ARCH_CMPXCHG 1
39 #endif
40
41 #ifdef CONFIG_X86_CMPXCHG64
42 #define cmpxchg64(ptr, o, n)                                            \
43         ((__typeof__(*(ptr)))__cmpxchg64((ptr), (unsigned long long)(o), \
44                                          (unsigned long long)(n)))
45 #define cmpxchg64_local(ptr, o, n)                                      \
46         ((__typeof__(*(ptr)))__cmpxchg64_local((ptr), (unsigned long long)(o), \
47                                                (unsigned long long)(n)))
48 #endif
49
50 static inline u64 __cmpxchg64(volatile u64 *ptr, u64 old, u64 new)
51 {
52         u64 prev;
53         asm volatile(LOCK_PREFIX "cmpxchg8b %1"
54                      : "=A" (prev),
55                        "+m" (*ptr)
56                      : "b" ((u32)new),
57                        "c" ((u32)(new >> 32)),
58                        "0" (old)
59                      : "memory");
60         return prev;
61 }
62
63 static inline u64 __cmpxchg64_local(volatile u64 *ptr, u64 old, u64 new)
64 {
65         u64 prev;
66         asm volatile("cmpxchg8b %1"
67                      : "=A" (prev),
68                        "+m" (*ptr)
69                      : "b" ((u32)new),
70                        "c" ((u32)(new >> 32)),
71                        "0" (old)
72                      : "memory");
73         return prev;
74 }
75
76 #ifndef CONFIG_X86_CMPXCHG
77 /*
78  * Building a kernel capable running on 80386. It may be necessary to
79  * simulate the cmpxchg on the 80386 CPU. For that purpose we define
80  * a function for each of the sizes we support.
81  */
82
83 extern unsigned long cmpxchg_386_u8(volatile void *, u8, u8);
84 extern unsigned long cmpxchg_386_u16(volatile void *, u16, u16);
85 extern unsigned long cmpxchg_386_u32(volatile void *, u32, u32);
86
87 static inline unsigned long cmpxchg_386(volatile void *ptr, unsigned long old,
88                                         unsigned long new, int size)
89 {
90         switch (size) {
91         case 1:
92                 return cmpxchg_386_u8(ptr, old, new);
93         case 2:
94                 return cmpxchg_386_u16(ptr, old, new);
95         case 4:
96                 return cmpxchg_386_u32(ptr, old, new);
97         }
98         return old;
99 }
100
101 #define cmpxchg(ptr, o, n)                                              \
102 ({                                                                      \
103         __typeof__(*(ptr)) __ret;                                       \
104         if (likely(boot_cpu_data.x86 > 3))                              \
105                 __ret = (__typeof__(*(ptr)))__cmpxchg((ptr),            \
106                                 (unsigned long)(o), (unsigned long)(n), \
107                                 sizeof(*(ptr)));                        \
108         else                                                            \
109                 __ret = (__typeof__(*(ptr)))cmpxchg_386((ptr),          \
110                                 (unsigned long)(o), (unsigned long)(n), \
111                                 sizeof(*(ptr)));                        \
112         __ret;                                                          \
113 })
114 #define cmpxchg_local(ptr, o, n)                                        \
115 ({                                                                      \
116         __typeof__(*(ptr)) __ret;                                       \
117         if (likely(boot_cpu_data.x86 > 3))                              \
118                 __ret = (__typeof__(*(ptr)))__cmpxchg_local((ptr),      \
119                                 (unsigned long)(o), (unsigned long)(n), \
120                                 sizeof(*(ptr)));                        \
121         else                                                            \
122                 __ret = (__typeof__(*(ptr)))cmpxchg_386((ptr),          \
123                                 (unsigned long)(o), (unsigned long)(n), \
124                                 sizeof(*(ptr)));                        \
125         __ret;                                                          \
126 })
127 #endif
128
129 #ifndef CONFIG_X86_CMPXCHG64
130 /*
131  * Building a kernel capable running on 80386 and 80486. It may be necessary
132  * to simulate the cmpxchg8b on the 80386 and 80486 CPU.
133  */
134
135 #define cmpxchg64(ptr, o, n)                                    \
136 ({                                                              \
137         __typeof__(*(ptr)) __ret;                               \
138         __typeof__(*(ptr)) __old = (o);                         \
139         __typeof__(*(ptr)) __new = (n);                         \
140         alternative_io(LOCK_PREFIX_HERE                         \
141                         "call cmpxchg8b_emu",                   \
142                         "lock; cmpxchg8b (%%esi)" ,             \
143                        X86_FEATURE_CX8,                         \
144                        "=A" (__ret),                            \
145                        "S" ((ptr)), "0" (__old),                \
146                        "b" ((unsigned int)__new),               \
147                        "c" ((unsigned int)(__new>>32))          \
148                        : "memory");                             \
149         __ret; })
150
151
152 #define cmpxchg64_local(ptr, o, n)                              \
153 ({                                                              \
154         __typeof__(*(ptr)) __ret;                               \
155         __typeof__(*(ptr)) __old = (o);                         \
156         __typeof__(*(ptr)) __new = (n);                         \
157         alternative_io("call cmpxchg8b_emu",                    \
158                        "cmpxchg8b (%%esi)" ,                    \
159                        X86_FEATURE_CX8,                         \
160                        "=A" (__ret),                            \
161                        "S" ((ptr)), "0" (__old),                \
162                        "b" ((unsigned int)__new),               \
163                        "c" ((unsigned int)(__new>>32))          \
164                        : "memory");                             \
165         __ret; })
166
167 #endif
168
169 #define system_has_cmpxchg_double() cpu_has_cx8
170
171 #endif /* _ASM_X86_CMPXCHG_32_H */