162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef ASM_X86_CMPXCHG_H 362306a36Sopenharmony_ci#define ASM_X86_CMPXCHG_H 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/compiler.h> 662306a36Sopenharmony_ci#include <asm/cpufeatures.h> 762306a36Sopenharmony_ci#include <asm/alternative.h> /* Provides LOCK_PREFIX */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * Non-existent functions to indicate usage errors at link time 1162306a36Sopenharmony_ci * (or compile-time if the compiler implements __compiletime_error(). 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ciextern void __xchg_wrong_size(void) 1462306a36Sopenharmony_ci __compiletime_error("Bad argument size for xchg"); 1562306a36Sopenharmony_ciextern void __cmpxchg_wrong_size(void) 1662306a36Sopenharmony_ci __compiletime_error("Bad argument size for cmpxchg"); 1762306a36Sopenharmony_ciextern void __xadd_wrong_size(void) 1862306a36Sopenharmony_ci __compiletime_error("Bad argument size for xadd"); 1962306a36Sopenharmony_ciextern void __add_wrong_size(void) 2062306a36Sopenharmony_ci __compiletime_error("Bad argument size for add"); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * Constants for operation sizes. On 32-bit, the 64-bit size it set to 2462306a36Sopenharmony_ci * -1 because sizeof will never return -1, thereby making those switch 2562306a36Sopenharmony_ci * case statements guaranteed dead code which the compiler will 2662306a36Sopenharmony_ci * eliminate, and allowing the "missing symbol in the default case" to 2762306a36Sopenharmony_ci * indicate a usage error. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#define __X86_CASE_B 1 3062306a36Sopenharmony_ci#define __X86_CASE_W 2 3162306a36Sopenharmony_ci#define __X86_CASE_L 4 3262306a36Sopenharmony_ci#ifdef CONFIG_64BIT 3362306a36Sopenharmony_ci#define __X86_CASE_Q 8 3462306a36Sopenharmony_ci#else 3562306a36Sopenharmony_ci#define __X86_CASE_Q -1 /* sizeof will never return -1 */ 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * An exchange-type operation, which takes a value and a pointer, and 4062306a36Sopenharmony_ci * returns the old value. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci#define __xchg_op(ptr, arg, op, lock) \ 4362306a36Sopenharmony_ci ({ \ 4462306a36Sopenharmony_ci __typeof__ (*(ptr)) __ret = (arg); \ 4562306a36Sopenharmony_ci switch (sizeof(*(ptr))) { \ 4662306a36Sopenharmony_ci case __X86_CASE_B: \ 4762306a36Sopenharmony_ci asm volatile (lock #op "b %b0, %1\n" \ 4862306a36Sopenharmony_ci : "+q" (__ret), "+m" (*(ptr)) \ 4962306a36Sopenharmony_ci : : "memory", "cc"); \ 5062306a36Sopenharmony_ci break; \ 5162306a36Sopenharmony_ci case __X86_CASE_W: \ 5262306a36Sopenharmony_ci asm volatile (lock #op "w %w0, %1\n" \ 5362306a36Sopenharmony_ci : "+r" (__ret), "+m" (*(ptr)) \ 5462306a36Sopenharmony_ci : : "memory", "cc"); \ 5562306a36Sopenharmony_ci break; \ 5662306a36Sopenharmony_ci case __X86_CASE_L: \ 5762306a36Sopenharmony_ci asm volatile (lock #op "l %0, %1\n" \ 5862306a36Sopenharmony_ci : "+r" (__ret), "+m" (*(ptr)) \ 5962306a36Sopenharmony_ci : : "memory", "cc"); \ 6062306a36Sopenharmony_ci break; \ 6162306a36Sopenharmony_ci case __X86_CASE_Q: \ 6262306a36Sopenharmony_ci asm volatile (lock #op "q %q0, %1\n" \ 6362306a36Sopenharmony_ci : "+r" (__ret), "+m" (*(ptr)) \ 6462306a36Sopenharmony_ci : : "memory", "cc"); \ 6562306a36Sopenharmony_ci break; \ 6662306a36Sopenharmony_ci default: \ 6762306a36Sopenharmony_ci __ ## op ## _wrong_size(); \ 6862306a36Sopenharmony_ci } \ 6962306a36Sopenharmony_ci __ret; \ 7062306a36Sopenharmony_ci }) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* 7362306a36Sopenharmony_ci * Note: no "lock" prefix even on SMP: xchg always implies lock anyway. 7462306a36Sopenharmony_ci * Since this is generally used to protect other memory information, we 7562306a36Sopenharmony_ci * use "asm volatile" and "memory" clobbers to prevent gcc from moving 7662306a36Sopenharmony_ci * information around. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci#define arch_xchg(ptr, v) __xchg_op((ptr), (v), xchg, "") 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * Atomic compare and exchange. Compare OLD with MEM, if identical, 8262306a36Sopenharmony_ci * store NEW in MEM. Return the initial value in MEM. Success is 8362306a36Sopenharmony_ci * indicated by comparing RETURN with OLD. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci#define __raw_cmpxchg(ptr, old, new, size, lock) \ 8662306a36Sopenharmony_ci({ \ 8762306a36Sopenharmony_ci __typeof__(*(ptr)) __ret; \ 8862306a36Sopenharmony_ci __typeof__(*(ptr)) __old = (old); \ 8962306a36Sopenharmony_ci __typeof__(*(ptr)) __new = (new); \ 9062306a36Sopenharmony_ci switch (size) { \ 9162306a36Sopenharmony_ci case __X86_CASE_B: \ 9262306a36Sopenharmony_ci { \ 9362306a36Sopenharmony_ci volatile u8 *__ptr = (volatile u8 *)(ptr); \ 9462306a36Sopenharmony_ci asm volatile(lock "cmpxchgb %2,%1" \ 9562306a36Sopenharmony_ci : "=a" (__ret), "+m" (*__ptr) \ 9662306a36Sopenharmony_ci : "q" (__new), "0" (__old) \ 9762306a36Sopenharmony_ci : "memory"); \ 9862306a36Sopenharmony_ci break; \ 9962306a36Sopenharmony_ci } \ 10062306a36Sopenharmony_ci case __X86_CASE_W: \ 10162306a36Sopenharmony_ci { \ 10262306a36Sopenharmony_ci volatile u16 *__ptr = (volatile u16 *)(ptr); \ 10362306a36Sopenharmony_ci asm volatile(lock "cmpxchgw %2,%1" \ 10462306a36Sopenharmony_ci : "=a" (__ret), "+m" (*__ptr) \ 10562306a36Sopenharmony_ci : "r" (__new), "0" (__old) \ 10662306a36Sopenharmony_ci : "memory"); \ 10762306a36Sopenharmony_ci break; \ 10862306a36Sopenharmony_ci } \ 10962306a36Sopenharmony_ci case __X86_CASE_L: \ 11062306a36Sopenharmony_ci { \ 11162306a36Sopenharmony_ci volatile u32 *__ptr = (volatile u32 *)(ptr); \ 11262306a36Sopenharmony_ci asm volatile(lock "cmpxchgl %2,%1" \ 11362306a36Sopenharmony_ci : "=a" (__ret), "+m" (*__ptr) \ 11462306a36Sopenharmony_ci : "r" (__new), "0" (__old) \ 11562306a36Sopenharmony_ci : "memory"); \ 11662306a36Sopenharmony_ci break; \ 11762306a36Sopenharmony_ci } \ 11862306a36Sopenharmony_ci case __X86_CASE_Q: \ 11962306a36Sopenharmony_ci { \ 12062306a36Sopenharmony_ci volatile u64 *__ptr = (volatile u64 *)(ptr); \ 12162306a36Sopenharmony_ci asm volatile(lock "cmpxchgq %2,%1" \ 12262306a36Sopenharmony_ci : "=a" (__ret), "+m" (*__ptr) \ 12362306a36Sopenharmony_ci : "r" (__new), "0" (__old) \ 12462306a36Sopenharmony_ci : "memory"); \ 12562306a36Sopenharmony_ci break; \ 12662306a36Sopenharmony_ci } \ 12762306a36Sopenharmony_ci default: \ 12862306a36Sopenharmony_ci __cmpxchg_wrong_size(); \ 12962306a36Sopenharmony_ci } \ 13062306a36Sopenharmony_ci __ret; \ 13162306a36Sopenharmony_ci}) 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#define __cmpxchg(ptr, old, new, size) \ 13462306a36Sopenharmony_ci __raw_cmpxchg((ptr), (old), (new), (size), LOCK_PREFIX) 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define __sync_cmpxchg(ptr, old, new, size) \ 13762306a36Sopenharmony_ci __raw_cmpxchg((ptr), (old), (new), (size), "lock; ") 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define __cmpxchg_local(ptr, old, new, size) \ 14062306a36Sopenharmony_ci __raw_cmpxchg((ptr), (old), (new), (size), "") 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#ifdef CONFIG_X86_32 14362306a36Sopenharmony_ci# include <asm/cmpxchg_32.h> 14462306a36Sopenharmony_ci#else 14562306a36Sopenharmony_ci# include <asm/cmpxchg_64.h> 14662306a36Sopenharmony_ci#endif 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci#define arch_cmpxchg(ptr, old, new) \ 14962306a36Sopenharmony_ci __cmpxchg(ptr, old, new, sizeof(*(ptr))) 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#define arch_sync_cmpxchg(ptr, old, new) \ 15262306a36Sopenharmony_ci __sync_cmpxchg(ptr, old, new, sizeof(*(ptr))) 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#define arch_cmpxchg_local(ptr, old, new) \ 15562306a36Sopenharmony_ci __cmpxchg_local(ptr, old, new, sizeof(*(ptr))) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define __raw_try_cmpxchg(_ptr, _pold, _new, size, lock) \ 15962306a36Sopenharmony_ci({ \ 16062306a36Sopenharmony_ci bool success; \ 16162306a36Sopenharmony_ci __typeof__(_ptr) _old = (__typeof__(_ptr))(_pold); \ 16262306a36Sopenharmony_ci __typeof__(*(_ptr)) __old = *_old; \ 16362306a36Sopenharmony_ci __typeof__(*(_ptr)) __new = (_new); \ 16462306a36Sopenharmony_ci switch (size) { \ 16562306a36Sopenharmony_ci case __X86_CASE_B: \ 16662306a36Sopenharmony_ci { \ 16762306a36Sopenharmony_ci volatile u8 *__ptr = (volatile u8 *)(_ptr); \ 16862306a36Sopenharmony_ci asm volatile(lock "cmpxchgb %[new], %[ptr]" \ 16962306a36Sopenharmony_ci CC_SET(z) \ 17062306a36Sopenharmony_ci : CC_OUT(z) (success), \ 17162306a36Sopenharmony_ci [ptr] "+m" (*__ptr), \ 17262306a36Sopenharmony_ci [old] "+a" (__old) \ 17362306a36Sopenharmony_ci : [new] "q" (__new) \ 17462306a36Sopenharmony_ci : "memory"); \ 17562306a36Sopenharmony_ci break; \ 17662306a36Sopenharmony_ci } \ 17762306a36Sopenharmony_ci case __X86_CASE_W: \ 17862306a36Sopenharmony_ci { \ 17962306a36Sopenharmony_ci volatile u16 *__ptr = (volatile u16 *)(_ptr); \ 18062306a36Sopenharmony_ci asm volatile(lock "cmpxchgw %[new], %[ptr]" \ 18162306a36Sopenharmony_ci CC_SET(z) \ 18262306a36Sopenharmony_ci : CC_OUT(z) (success), \ 18362306a36Sopenharmony_ci [ptr] "+m" (*__ptr), \ 18462306a36Sopenharmony_ci [old] "+a" (__old) \ 18562306a36Sopenharmony_ci : [new] "r" (__new) \ 18662306a36Sopenharmony_ci : "memory"); \ 18762306a36Sopenharmony_ci break; \ 18862306a36Sopenharmony_ci } \ 18962306a36Sopenharmony_ci case __X86_CASE_L: \ 19062306a36Sopenharmony_ci { \ 19162306a36Sopenharmony_ci volatile u32 *__ptr = (volatile u32 *)(_ptr); \ 19262306a36Sopenharmony_ci asm volatile(lock "cmpxchgl %[new], %[ptr]" \ 19362306a36Sopenharmony_ci CC_SET(z) \ 19462306a36Sopenharmony_ci : CC_OUT(z) (success), \ 19562306a36Sopenharmony_ci [ptr] "+m" (*__ptr), \ 19662306a36Sopenharmony_ci [old] "+a" (__old) \ 19762306a36Sopenharmony_ci : [new] "r" (__new) \ 19862306a36Sopenharmony_ci : "memory"); \ 19962306a36Sopenharmony_ci break; \ 20062306a36Sopenharmony_ci } \ 20162306a36Sopenharmony_ci case __X86_CASE_Q: \ 20262306a36Sopenharmony_ci { \ 20362306a36Sopenharmony_ci volatile u64 *__ptr = (volatile u64 *)(_ptr); \ 20462306a36Sopenharmony_ci asm volatile(lock "cmpxchgq %[new], %[ptr]" \ 20562306a36Sopenharmony_ci CC_SET(z) \ 20662306a36Sopenharmony_ci : CC_OUT(z) (success), \ 20762306a36Sopenharmony_ci [ptr] "+m" (*__ptr), \ 20862306a36Sopenharmony_ci [old] "+a" (__old) \ 20962306a36Sopenharmony_ci : [new] "r" (__new) \ 21062306a36Sopenharmony_ci : "memory"); \ 21162306a36Sopenharmony_ci break; \ 21262306a36Sopenharmony_ci } \ 21362306a36Sopenharmony_ci default: \ 21462306a36Sopenharmony_ci __cmpxchg_wrong_size(); \ 21562306a36Sopenharmony_ci } \ 21662306a36Sopenharmony_ci if (unlikely(!success)) \ 21762306a36Sopenharmony_ci *_old = __old; \ 21862306a36Sopenharmony_ci likely(success); \ 21962306a36Sopenharmony_ci}) 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci#define __try_cmpxchg(ptr, pold, new, size) \ 22262306a36Sopenharmony_ci __raw_try_cmpxchg((ptr), (pold), (new), (size), LOCK_PREFIX) 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci#define __try_cmpxchg_local(ptr, pold, new, size) \ 22562306a36Sopenharmony_ci __raw_try_cmpxchg((ptr), (pold), (new), (size), "") 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci#define arch_try_cmpxchg(ptr, pold, new) \ 22862306a36Sopenharmony_ci __try_cmpxchg((ptr), (pold), (new), sizeof(*(ptr))) 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci#define arch_try_cmpxchg_local(ptr, pold, new) \ 23162306a36Sopenharmony_ci __try_cmpxchg_local((ptr), (pold), (new), sizeof(*(ptr))) 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* 23462306a36Sopenharmony_ci * xadd() adds "inc" to "*ptr" and atomically returns the previous 23562306a36Sopenharmony_ci * value of "*ptr". 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * xadd() is locked when multiple CPUs are online 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci#define __xadd(ptr, inc, lock) __xchg_op((ptr), (inc), xadd, lock) 24062306a36Sopenharmony_ci#define xadd(ptr, inc) __xadd((ptr), (inc), LOCK_PREFIX) 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci#endif /* ASM_X86_CMPXCHG_H */ 243