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