cmpxchg_32.h 8.4 KB
Newer Older
H
H. Peter Anvin 已提交
1 2
#ifndef _ASM_X86_CMPXCHG_32_H
#define _ASM_X86_CMPXCHG_32_H
J
Jeff Dike 已提交
3 4 5

#include <linux/bitops.h> /* for LOCK_PREFIX */

A
Avi Kivity 已提交
6 7 8 9 10
/*
 * Note: if you use set64_bit(), __cmpxchg64(), or their variants, you
 *       you need to test for the feature in boot_cpu_data.
 */

11 12 13 14 15 16 17
extern void __xchg_wrong_size(void);

/*
 * Note: no "lock" prefix even on SMP: xchg always implies lock anyway
 * Note 2: xchg has side effect, so that attribute volatile is necessary,
 *	  but generally the primitive is invalid, *ptr is output argument. --ANK
 */
J
Jeff Dike 已提交
18

19 20 21
struct __xchg_dummy {
	unsigned long a[100];
};
J
Jeff Dike 已提交
22 23
#define __xg(x) ((struct __xchg_dummy *)(x))

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
#define __xchg(x, ptr, size)						\
({									\
	__typeof(*(ptr)) __x = (x);					\
	switch (size) {							\
	case 1:								\
		asm volatile("xchgb %b0,%1"				\
			     : "=q" (__x)				\
			     : "m" (*__xg(ptr)), "0" (__x)		\
			     : "memory");				\
		break;							\
	case 2:								\
		asm volatile("xchgw %w0,%1"				\
			     : "=r" (__x)				\
			     : "m" (*__xg(ptr)), "0" (__x)		\
			     : "memory");				\
		break;							\
	case 4:								\
		asm volatile("xchgl %0,%1"				\
			     : "=r" (__x)				\
			     : "m" (*__xg(ptr)), "0" (__x)		\
			     : "memory");				\
		break;							\
	default:							\
		__xchg_wrong_size();					\
	}								\
	__x;								\
})

#define xchg(ptr, v)							\
	__xchg((v), (ptr), sizeof(*ptr))

J
Jeff Dike 已提交
55 56 57 58 59 60 61 62 63 64 65 66 67 68
/*
 * The semantics of XCHGCMP8B are a bit strange, this is why
 * there is a loop and the loading of %%eax and %%edx has to
 * be inside. This inlines well in most cases, the cached
 * cost is around ~38 cycles. (in the future we might want
 * to do an SIMD/3DNOW!/MMX/FPU 64-bit store here, but that
 * might have an implicit FPU-save as a cost, so it's not
 * clear which path to go.)
 *
 * cmpxchg8b must be used with the lock prefix here to allow
 * the instruction to be executed atomically, see page 3-102
 * of the instruction set reference 24319102.pdf. We need
 * the reader side to see the coherent 64bit value.
 */
69 70
static inline void __set_64bit(unsigned long long *ptr,
			       unsigned int low, unsigned int high)
J
Jeff Dike 已提交
71
{
72 73 74 75 76 77 78 79 80 81
	asm volatile("\n1:\t"
		     "movl (%0), %%eax\n\t"
		     "movl 4(%0), %%edx\n\t"
		     LOCK_PREFIX "cmpxchg8b (%0)\n\t"
		     "jnz 1b"
		     : /* no outputs */
		     : "D"(ptr),
		       "b"(low),
		       "c"(high)
		     : "ax", "dx", "memory");
J
Jeff Dike 已提交
82 83
}

84 85
static inline void __set_64bit_constant(unsigned long long *ptr,
					unsigned long long value)
J
Jeff Dike 已提交
86
{
87
	__set_64bit(ptr, (unsigned int)value, (unsigned int)(value >> 32));
J
Jeff Dike 已提交
88 89
}

90 91 92 93 94
#define ll_low(x)	*(((unsigned int *)&(x)) + 0)
#define ll_high(x)	*(((unsigned int *)&(x)) + 1)

static inline void __set_64bit_var(unsigned long long *ptr,
				   unsigned long long value)
J
Jeff Dike 已提交
95
{
96
	__set_64bit(ptr, ll_low(value), ll_high(value));
J
Jeff Dike 已提交
97 98
}

99 100 101 102
#define set_64bit(ptr, value)			\
	(__builtin_constant_p((value))		\
	 ? __set_64bit_constant((ptr), (value))	\
	 : __set_64bit_var((ptr), (value)))
J
Jeff Dike 已提交
103

104 105 106 107 108
#define _set_64bit(ptr, value)						\
	(__builtin_constant_p(value)					\
	 ? __set_64bit(ptr, (unsigned int)(value),			\
		       (unsigned int)((value) >> 32))			\
	 : __set_64bit(ptr, ll_low((value)), ll_high((value))))
J
Jeff Dike 已提交
109

110
extern void __cmpxchg_wrong_size(void);
J
Jeff Dike 已提交
111 112 113 114 115 116

/*
 * Atomic compare and exchange.  Compare OLD with MEM, if identical,
 * store NEW in MEM.  Return the initial value in MEM.  Success is
 * indicated by comparing RETURN with OLD.
 */
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
#define __raw_cmpxchg(ptr, old, new, size, lock)			\
({									\
	__typeof__(*(ptr)) __ret;					\
	__typeof__(*(ptr)) __old = (old);				\
	__typeof__(*(ptr)) __new = (new);				\
	switch (size) {							\
	case 1:								\
		asm volatile(lock "cmpxchgb %b1,%2"			\
			     : "=a"(__ret)				\
			     : "q"(__new), "m"(*__xg(ptr)), "0"(__old)	\
			     : "memory");				\
		break;							\
	case 2:								\
		asm volatile(lock "cmpxchgw %w1,%2"			\
			     : "=a"(__ret)				\
			     : "r"(__new), "m"(*__xg(ptr)), "0"(__old)	\
			     : "memory");				\
		break;							\
	case 4:								\
		asm volatile(lock "cmpxchgl %1,%2"			\
			     : "=a"(__ret)				\
			     : "r"(__new), "m"(*__xg(ptr)), "0"(__old)	\
			     : "memory");				\
		break;							\
	default:							\
		__cmpxchg_wrong_size();					\
	}								\
	__ret;								\
})

#define __cmpxchg(ptr, old, new, size)					\
	__raw_cmpxchg((ptr), (old), (new), (size), LOCK_PREFIX)

#define __sync_cmpxchg(ptr, old, new, size)				\
	__raw_cmpxchg((ptr), (old), (new), (size), "lock; ")

#define __cmpxchg_local(ptr, old, new, size)				\
	__raw_cmpxchg((ptr), (old), (new), (size), "")
J
Jeff Dike 已提交
155 156 157

#ifdef CONFIG_X86_CMPXCHG
#define __HAVE_ARCH_CMPXCHG 1
158 159 160 161 162 163 164 165 166

#define cmpxchg(ptr, old, new)						\
	__cmpxchg((ptr), (old), (new), sizeof(*ptr))

#define sync_cmpxchg(ptr, old, new)					\
	__sync_cmpxchg((ptr), (old), (new), sizeof(*ptr))

#define cmpxchg_local(ptr, old, new)					\
	__cmpxchg_local((ptr), (old), (new), sizeof(*ptr))
167 168 169
#endif

#ifdef CONFIG_X86_CMPXCHG64
170 171 172 173 174 175
#define cmpxchg64(ptr, o, n)						\
	((__typeof__(*(ptr)))__cmpxchg64((ptr), (unsigned long long)(o), \
					 (unsigned long long)(n)))
#define cmpxchg64_local(ptr, o, n)					\
	((__typeof__(*(ptr)))__cmpxchg64_local((ptr), (unsigned long long)(o), \
					       (unsigned long long)(n)))
J
Jeff Dike 已提交
176 177
#endif

178
static inline unsigned long long __cmpxchg64(volatile void *ptr,
179 180
					     unsigned long long old,
					     unsigned long long new)
181 182
{
	unsigned long long prev;
183 184 185 186 187 188 189
	asm volatile(LOCK_PREFIX "cmpxchg8b %3"
		     : "=A"(prev)
		     : "b"((unsigned long)new),
		       "c"((unsigned long)(new >> 32)),
		       "m"(*__xg(ptr)),
		       "0"(old)
		     : "memory");
190 191 192 193
	return prev;
}

static inline unsigned long long __cmpxchg64_local(volatile void *ptr,
194 195
						   unsigned long long old,
						   unsigned long long new)
196 197
{
	unsigned long long prev;
198 199 200 201 202 203 204
	asm volatile("cmpxchg8b %3"
		     : "=A"(prev)
		     : "b"((unsigned long)new),
		       "c"((unsigned long)(new >> 32)),
		       "m"(*__xg(ptr)),
		       "0"(old)
		     : "memory");
205 206 207
	return prev;
}

J
Jeff Dike 已提交
208 209 210 211 212 213 214 215 216 217 218 219
#ifndef CONFIG_X86_CMPXCHG
/*
 * Building a kernel capable running on 80386. It may be necessary to
 * simulate the cmpxchg on the 80386 CPU. For that purpose we define
 * a function for each of the sizes we support.
 */

extern unsigned long cmpxchg_386_u8(volatile void *, u8, u8);
extern unsigned long cmpxchg_386_u16(volatile void *, u16, u16);
extern unsigned long cmpxchg_386_u32(volatile void *, u32, u32);

static inline unsigned long cmpxchg_386(volatile void *ptr, unsigned long old,
220
					unsigned long new, int size)
J
Jeff Dike 已提交
221 222 223 224 225 226 227 228 229 230 231 232
{
	switch (size) {
	case 1:
		return cmpxchg_386_u8(ptr, old, new);
	case 2:
		return cmpxchg_386_u16(ptr, old, new);
	case 4:
		return cmpxchg_386_u32(ptr, old, new);
	}
	return old;
}

233
#define cmpxchg(ptr, o, n)						\
J
Jeff Dike 已提交
234 235 236
({									\
	__typeof__(*(ptr)) __ret;					\
	if (likely(boot_cpu_data.x86 > 3))				\
237 238 239
		__ret = (__typeof__(*(ptr)))__cmpxchg((ptr),		\
				(unsigned long)(o), (unsigned long)(n),	\
				sizeof(*(ptr)));			\
J
Jeff Dike 已提交
240
	else								\
241 242 243
		__ret = (__typeof__(*(ptr)))cmpxchg_386((ptr),		\
				(unsigned long)(o), (unsigned long)(n),	\
				sizeof(*(ptr)));			\
J
Jeff Dike 已提交
244 245
	__ret;								\
})
246
#define cmpxchg_local(ptr, o, n)					\
J
Jeff Dike 已提交
247 248 249
({									\
	__typeof__(*(ptr)) __ret;					\
	if (likely(boot_cpu_data.x86 > 3))				\
250 251 252
		__ret = (__typeof__(*(ptr)))__cmpxchg_local((ptr),	\
				(unsigned long)(o), (unsigned long)(n),	\
				sizeof(*(ptr)));			\
J
Jeff Dike 已提交
253
	else								\
254 255 256
		__ret = (__typeof__(*(ptr)))cmpxchg_386((ptr),		\
				(unsigned long)(o), (unsigned long)(n),	\
				sizeof(*(ptr)));			\
J
Jeff Dike 已提交
257 258 259 260
	__ret;								\
})
#endif

261 262 263 264 265
#ifndef CONFIG_X86_CMPXCHG64
/*
 * Building a kernel capable running on 80386 and 80486. It may be necessary
 * to simulate the cmpxchg8b on the 80386 and 80486 CPU.
 */
J
Jeff Dike 已提交
266

267 268
extern unsigned long long cmpxchg_486_u64(volatile void *, u64, u64);

269 270 271 272 273
#define cmpxchg64(ptr, o, n)					\
({								\
	__typeof__(*(ptr)) __ret;				\
	__typeof__(*(ptr)) __old = (o);				\
	__typeof__(*(ptr)) __new = (n);				\
274 275
	alternative_io(LOCK_PREFIX_HERE				\
			"call cmpxchg8b_emu",			\
276 277 278 279 280 281 282 283 284 285 286
			"lock; cmpxchg8b (%%esi)" ,		\
		       X86_FEATURE_CX8,				\
		       "=A" (__ret),				\
		       "S" ((ptr)), "0" (__old),		\
		       "b" ((unsigned int)__new),		\
		       "c" ((unsigned int)(__new>>32))		\
		       : "memory");				\
	__ret; })



287 288 289 290
#define cmpxchg64_local(ptr, o, n)					\
({									\
	__typeof__(*(ptr)) __ret;					\
	if (likely(boot_cpu_data.x86 > 4))				\
291 292
		__ret = (__typeof__(*(ptr)))__cmpxchg64_local((ptr),	\
				(unsigned long long)(o),		\
293 294
				(unsigned long long)(n));		\
	else								\
295 296
		__ret = (__typeof__(*(ptr)))cmpxchg_486_u64((ptr),	\
				(unsigned long long)(o),		\
297 298 299 300 301
				(unsigned long long)(n));		\
	__ret;								\
})

#endif
J
Jeff Dike 已提交
302

H
H. Peter Anvin 已提交
303
#endif /* _ASM_X86_CMPXCHG_32_H */