18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef ASM_X86_CMPXCHG_H
38c2ecf20Sopenharmony_ci#define ASM_X86_CMPXCHG_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/compiler.h>
68c2ecf20Sopenharmony_ci#include <asm/cpufeatures.h>
78c2ecf20Sopenharmony_ci#include <asm/alternative.h> /* Provides LOCK_PREFIX */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * Non-existent functions to indicate usage errors at link time
118c2ecf20Sopenharmony_ci * (or compile-time if the compiler implements __compiletime_error().
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ciextern void __xchg_wrong_size(void)
148c2ecf20Sopenharmony_ci	__compiletime_error("Bad argument size for xchg");
158c2ecf20Sopenharmony_ciextern void __cmpxchg_wrong_size(void)
168c2ecf20Sopenharmony_ci	__compiletime_error("Bad argument size for cmpxchg");
178c2ecf20Sopenharmony_ciextern void __xadd_wrong_size(void)
188c2ecf20Sopenharmony_ci	__compiletime_error("Bad argument size for xadd");
198c2ecf20Sopenharmony_ciextern void __add_wrong_size(void)
208c2ecf20Sopenharmony_ci	__compiletime_error("Bad argument size for add");
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/*
238c2ecf20Sopenharmony_ci * Constants for operation sizes. On 32-bit, the 64-bit size it set to
248c2ecf20Sopenharmony_ci * -1 because sizeof will never return -1, thereby making those switch
258c2ecf20Sopenharmony_ci * case statements guaranteeed dead code which the compiler will
268c2ecf20Sopenharmony_ci * eliminate, and allowing the "missing symbol in the default case" to
278c2ecf20Sopenharmony_ci * indicate a usage error.
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci#define __X86_CASE_B	1
308c2ecf20Sopenharmony_ci#define __X86_CASE_W	2
318c2ecf20Sopenharmony_ci#define __X86_CASE_L	4
328c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
338c2ecf20Sopenharmony_ci#define __X86_CASE_Q	8
348c2ecf20Sopenharmony_ci#else
358c2ecf20Sopenharmony_ci#define	__X86_CASE_Q	-1		/* sizeof will never return -1 */
368c2ecf20Sopenharmony_ci#endif
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * An exchange-type operation, which takes a value and a pointer, and
408c2ecf20Sopenharmony_ci * returns the old value.
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ci#define __xchg_op(ptr, arg, op, lock)					\
438c2ecf20Sopenharmony_ci	({								\
448c2ecf20Sopenharmony_ci	        __typeof__ (*(ptr)) __ret = (arg);			\
458c2ecf20Sopenharmony_ci		switch (sizeof(*(ptr))) {				\
468c2ecf20Sopenharmony_ci		case __X86_CASE_B:					\
478c2ecf20Sopenharmony_ci			asm volatile (lock #op "b %b0, %1\n"		\
488c2ecf20Sopenharmony_ci				      : "+q" (__ret), "+m" (*(ptr))	\
498c2ecf20Sopenharmony_ci				      : : "memory", "cc");		\
508c2ecf20Sopenharmony_ci			break;						\
518c2ecf20Sopenharmony_ci		case __X86_CASE_W:					\
528c2ecf20Sopenharmony_ci			asm volatile (lock #op "w %w0, %1\n"		\
538c2ecf20Sopenharmony_ci				      : "+r" (__ret), "+m" (*(ptr))	\
548c2ecf20Sopenharmony_ci				      : : "memory", "cc");		\
558c2ecf20Sopenharmony_ci			break;						\
568c2ecf20Sopenharmony_ci		case __X86_CASE_L:					\
578c2ecf20Sopenharmony_ci			asm volatile (lock #op "l %0, %1\n"		\
588c2ecf20Sopenharmony_ci				      : "+r" (__ret), "+m" (*(ptr))	\
598c2ecf20Sopenharmony_ci				      : : "memory", "cc");		\
608c2ecf20Sopenharmony_ci			break;						\
618c2ecf20Sopenharmony_ci		case __X86_CASE_Q:					\
628c2ecf20Sopenharmony_ci			asm volatile (lock #op "q %q0, %1\n"		\
638c2ecf20Sopenharmony_ci				      : "+r" (__ret), "+m" (*(ptr))	\
648c2ecf20Sopenharmony_ci				      : : "memory", "cc");		\
658c2ecf20Sopenharmony_ci			break;						\
668c2ecf20Sopenharmony_ci		default:						\
678c2ecf20Sopenharmony_ci			__ ## op ## _wrong_size();			\
688c2ecf20Sopenharmony_ci		}							\
698c2ecf20Sopenharmony_ci		__ret;							\
708c2ecf20Sopenharmony_ci	})
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/*
738c2ecf20Sopenharmony_ci * Note: no "lock" prefix even on SMP: xchg always implies lock anyway.
748c2ecf20Sopenharmony_ci * Since this is generally used to protect other memory information, we
758c2ecf20Sopenharmony_ci * use "asm volatile" and "memory" clobbers to prevent gcc from moving
768c2ecf20Sopenharmony_ci * information around.
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_ci#define arch_xchg(ptr, v)	__xchg_op((ptr), (v), xchg, "")
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * Atomic compare and exchange.  Compare OLD with MEM, if identical,
828c2ecf20Sopenharmony_ci * store NEW in MEM.  Return the initial value in MEM.  Success is
838c2ecf20Sopenharmony_ci * indicated by comparing RETURN with OLD.
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_ci#define __raw_cmpxchg(ptr, old, new, size, lock)			\
868c2ecf20Sopenharmony_ci({									\
878c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __ret;					\
888c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __old = (old);				\
898c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __new = (new);				\
908c2ecf20Sopenharmony_ci	switch (size) {							\
918c2ecf20Sopenharmony_ci	case __X86_CASE_B:						\
928c2ecf20Sopenharmony_ci	{								\
938c2ecf20Sopenharmony_ci		volatile u8 *__ptr = (volatile u8 *)(ptr);		\
948c2ecf20Sopenharmony_ci		asm volatile(lock "cmpxchgb %2,%1"			\
958c2ecf20Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
968c2ecf20Sopenharmony_ci			     : "q" (__new), "0" (__old)			\
978c2ecf20Sopenharmony_ci			     : "memory");				\
988c2ecf20Sopenharmony_ci		break;							\
998c2ecf20Sopenharmony_ci	}								\
1008c2ecf20Sopenharmony_ci	case __X86_CASE_W:						\
1018c2ecf20Sopenharmony_ci	{								\
1028c2ecf20Sopenharmony_ci		volatile u16 *__ptr = (volatile u16 *)(ptr);		\
1038c2ecf20Sopenharmony_ci		asm volatile(lock "cmpxchgw %2,%1"			\
1048c2ecf20Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
1058c2ecf20Sopenharmony_ci			     : "r" (__new), "0" (__old)			\
1068c2ecf20Sopenharmony_ci			     : "memory");				\
1078c2ecf20Sopenharmony_ci		break;							\
1088c2ecf20Sopenharmony_ci	}								\
1098c2ecf20Sopenharmony_ci	case __X86_CASE_L:						\
1108c2ecf20Sopenharmony_ci	{								\
1118c2ecf20Sopenharmony_ci		volatile u32 *__ptr = (volatile u32 *)(ptr);		\
1128c2ecf20Sopenharmony_ci		asm volatile(lock "cmpxchgl %2,%1"			\
1138c2ecf20Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
1148c2ecf20Sopenharmony_ci			     : "r" (__new), "0" (__old)			\
1158c2ecf20Sopenharmony_ci			     : "memory");				\
1168c2ecf20Sopenharmony_ci		break;							\
1178c2ecf20Sopenharmony_ci	}								\
1188c2ecf20Sopenharmony_ci	case __X86_CASE_Q:						\
1198c2ecf20Sopenharmony_ci	{								\
1208c2ecf20Sopenharmony_ci		volatile u64 *__ptr = (volatile u64 *)(ptr);		\
1218c2ecf20Sopenharmony_ci		asm volatile(lock "cmpxchgq %2,%1"			\
1228c2ecf20Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
1238c2ecf20Sopenharmony_ci			     : "r" (__new), "0" (__old)			\
1248c2ecf20Sopenharmony_ci			     : "memory");				\
1258c2ecf20Sopenharmony_ci		break;							\
1268c2ecf20Sopenharmony_ci	}								\
1278c2ecf20Sopenharmony_ci	default:							\
1288c2ecf20Sopenharmony_ci		__cmpxchg_wrong_size();					\
1298c2ecf20Sopenharmony_ci	}								\
1308c2ecf20Sopenharmony_ci	__ret;								\
1318c2ecf20Sopenharmony_ci})
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#define __cmpxchg(ptr, old, new, size)					\
1348c2ecf20Sopenharmony_ci	__raw_cmpxchg((ptr), (old), (new), (size), LOCK_PREFIX)
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci#define __sync_cmpxchg(ptr, old, new, size)				\
1378c2ecf20Sopenharmony_ci	__raw_cmpxchg((ptr), (old), (new), (size), "lock; ")
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#define __cmpxchg_local(ptr, old, new, size)				\
1408c2ecf20Sopenharmony_ci	__raw_cmpxchg((ptr), (old), (new), (size), "")
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32
1438c2ecf20Sopenharmony_ci# include <asm/cmpxchg_32.h>
1448c2ecf20Sopenharmony_ci#else
1458c2ecf20Sopenharmony_ci# include <asm/cmpxchg_64.h>
1468c2ecf20Sopenharmony_ci#endif
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci#define arch_cmpxchg(ptr, old, new)					\
1498c2ecf20Sopenharmony_ci	__cmpxchg(ptr, old, new, sizeof(*(ptr)))
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci#define arch_sync_cmpxchg(ptr, old, new)				\
1528c2ecf20Sopenharmony_ci	__sync_cmpxchg(ptr, old, new, sizeof(*(ptr)))
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci#define arch_cmpxchg_local(ptr, old, new)				\
1558c2ecf20Sopenharmony_ci	__cmpxchg_local(ptr, old, new, sizeof(*(ptr)))
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci#define __raw_try_cmpxchg(_ptr, _pold, _new, size, lock)		\
1598c2ecf20Sopenharmony_ci({									\
1608c2ecf20Sopenharmony_ci	bool success;							\
1618c2ecf20Sopenharmony_ci	__typeof__(_ptr) _old = (__typeof__(_ptr))(_pold);		\
1628c2ecf20Sopenharmony_ci	__typeof__(*(_ptr)) __old = *_old;				\
1638c2ecf20Sopenharmony_ci	__typeof__(*(_ptr)) __new = (_new);				\
1648c2ecf20Sopenharmony_ci	switch (size) {							\
1658c2ecf20Sopenharmony_ci	case __X86_CASE_B:						\
1668c2ecf20Sopenharmony_ci	{								\
1678c2ecf20Sopenharmony_ci		volatile u8 *__ptr = (volatile u8 *)(_ptr);		\
1688c2ecf20Sopenharmony_ci		asm volatile(lock "cmpxchgb %[new], %[ptr]"		\
1698c2ecf20Sopenharmony_ci			     CC_SET(z)					\
1708c2ecf20Sopenharmony_ci			     : CC_OUT(z) (success),			\
1718c2ecf20Sopenharmony_ci			       [ptr] "+m" (*__ptr),			\
1728c2ecf20Sopenharmony_ci			       [old] "+a" (__old)			\
1738c2ecf20Sopenharmony_ci			     : [new] "q" (__new)			\
1748c2ecf20Sopenharmony_ci			     : "memory");				\
1758c2ecf20Sopenharmony_ci		break;							\
1768c2ecf20Sopenharmony_ci	}								\
1778c2ecf20Sopenharmony_ci	case __X86_CASE_W:						\
1788c2ecf20Sopenharmony_ci	{								\
1798c2ecf20Sopenharmony_ci		volatile u16 *__ptr = (volatile u16 *)(_ptr);		\
1808c2ecf20Sopenharmony_ci		asm volatile(lock "cmpxchgw %[new], %[ptr]"		\
1818c2ecf20Sopenharmony_ci			     CC_SET(z)					\
1828c2ecf20Sopenharmony_ci			     : CC_OUT(z) (success),			\
1838c2ecf20Sopenharmony_ci			       [ptr] "+m" (*__ptr),			\
1848c2ecf20Sopenharmony_ci			       [old] "+a" (__old)			\
1858c2ecf20Sopenharmony_ci			     : [new] "r" (__new)			\
1868c2ecf20Sopenharmony_ci			     : "memory");				\
1878c2ecf20Sopenharmony_ci		break;							\
1888c2ecf20Sopenharmony_ci	}								\
1898c2ecf20Sopenharmony_ci	case __X86_CASE_L:						\
1908c2ecf20Sopenharmony_ci	{								\
1918c2ecf20Sopenharmony_ci		volatile u32 *__ptr = (volatile u32 *)(_ptr);		\
1928c2ecf20Sopenharmony_ci		asm volatile(lock "cmpxchgl %[new], %[ptr]"		\
1938c2ecf20Sopenharmony_ci			     CC_SET(z)					\
1948c2ecf20Sopenharmony_ci			     : CC_OUT(z) (success),			\
1958c2ecf20Sopenharmony_ci			       [ptr] "+m" (*__ptr),			\
1968c2ecf20Sopenharmony_ci			       [old] "+a" (__old)			\
1978c2ecf20Sopenharmony_ci			     : [new] "r" (__new)			\
1988c2ecf20Sopenharmony_ci			     : "memory");				\
1998c2ecf20Sopenharmony_ci		break;							\
2008c2ecf20Sopenharmony_ci	}								\
2018c2ecf20Sopenharmony_ci	case __X86_CASE_Q:						\
2028c2ecf20Sopenharmony_ci	{								\
2038c2ecf20Sopenharmony_ci		volatile u64 *__ptr = (volatile u64 *)(_ptr);		\
2048c2ecf20Sopenharmony_ci		asm volatile(lock "cmpxchgq %[new], %[ptr]"		\
2058c2ecf20Sopenharmony_ci			     CC_SET(z)					\
2068c2ecf20Sopenharmony_ci			     : CC_OUT(z) (success),			\
2078c2ecf20Sopenharmony_ci			       [ptr] "+m" (*__ptr),			\
2088c2ecf20Sopenharmony_ci			       [old] "+a" (__old)			\
2098c2ecf20Sopenharmony_ci			     : [new] "r" (__new)			\
2108c2ecf20Sopenharmony_ci			     : "memory");				\
2118c2ecf20Sopenharmony_ci		break;							\
2128c2ecf20Sopenharmony_ci	}								\
2138c2ecf20Sopenharmony_ci	default:							\
2148c2ecf20Sopenharmony_ci		__cmpxchg_wrong_size();					\
2158c2ecf20Sopenharmony_ci	}								\
2168c2ecf20Sopenharmony_ci	if (unlikely(!success))						\
2178c2ecf20Sopenharmony_ci		*_old = __old;						\
2188c2ecf20Sopenharmony_ci	likely(success);						\
2198c2ecf20Sopenharmony_ci})
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci#define __try_cmpxchg(ptr, pold, new, size)				\
2228c2ecf20Sopenharmony_ci	__raw_try_cmpxchg((ptr), (pold), (new), (size), LOCK_PREFIX)
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci#define try_cmpxchg(ptr, pold, new) 					\
2258c2ecf20Sopenharmony_ci	__try_cmpxchg((ptr), (pold), (new), sizeof(*(ptr)))
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/*
2288c2ecf20Sopenharmony_ci * xadd() adds "inc" to "*ptr" and atomically returns the previous
2298c2ecf20Sopenharmony_ci * value of "*ptr".
2308c2ecf20Sopenharmony_ci *
2318c2ecf20Sopenharmony_ci * xadd() is locked when multiple CPUs are online
2328c2ecf20Sopenharmony_ci */
2338c2ecf20Sopenharmony_ci#define __xadd(ptr, inc, lock)	__xchg_op((ptr), (inc), xadd, lock)
2348c2ecf20Sopenharmony_ci#define xadd(ptr, inc)		__xadd((ptr), (inc), LOCK_PREFIX)
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci#define __cmpxchg_double(pfx, p1, p2, o1, o2, n1, n2)			\
2378c2ecf20Sopenharmony_ci({									\
2388c2ecf20Sopenharmony_ci	bool __ret;							\
2398c2ecf20Sopenharmony_ci	__typeof__(*(p1)) __old1 = (o1), __new1 = (n1);			\
2408c2ecf20Sopenharmony_ci	__typeof__(*(p2)) __old2 = (o2), __new2 = (n2);			\
2418c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(*(p1)) != sizeof(long));			\
2428c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(*(p2)) != sizeof(long));			\
2438c2ecf20Sopenharmony_ci	VM_BUG_ON((unsigned long)(p1) % (2 * sizeof(long)));		\
2448c2ecf20Sopenharmony_ci	VM_BUG_ON((unsigned long)((p1) + 1) != (unsigned long)(p2));	\
2458c2ecf20Sopenharmony_ci	asm volatile(pfx "cmpxchg%c5b %1"				\
2468c2ecf20Sopenharmony_ci		     CC_SET(e)						\
2478c2ecf20Sopenharmony_ci		     : CC_OUT(e) (__ret),				\
2488c2ecf20Sopenharmony_ci		       "+m" (*(p1)), "+m" (*(p2)),			\
2498c2ecf20Sopenharmony_ci		       "+a" (__old1), "+d" (__old2)			\
2508c2ecf20Sopenharmony_ci		     : "i" (2 * sizeof(long)),				\
2518c2ecf20Sopenharmony_ci		       "b" (__new1), "c" (__new2));			\
2528c2ecf20Sopenharmony_ci	__ret;								\
2538c2ecf20Sopenharmony_ci})
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci#define arch_cmpxchg_double(p1, p2, o1, o2, n1, n2) \
2568c2ecf20Sopenharmony_ci	__cmpxchg_double(LOCK_PREFIX, p1, p2, o1, o2, n1, n2)
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci#define arch_cmpxchg_double_local(p1, p2, o1, o2, n1, n2) \
2598c2ecf20Sopenharmony_ci	__cmpxchg_double(, p1, p2, o1, o2, n1, n2)
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci#endif	/* ASM_X86_CMPXCHG_H */
262