2007-10-01 10:15:00 +07:00
|
|
|
/*
|
|
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
|
|
* for more details.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
|
|
|
|
*/
|
|
|
|
#ifndef __ASM_CMPXCHG_H
|
|
|
|
#define __ASM_CMPXCHG_H
|
|
|
|
|
2012-07-19 14:11:15 +07:00
|
|
|
#include <linux/bug.h>
|
2007-10-01 10:15:00 +07:00
|
|
|
#include <linux/irqflags.h>
|
2014-11-16 05:08:48 +07:00
|
|
|
#include <asm/compiler.h>
|
2019-10-02 04:53:05 +07:00
|
|
|
#include <asm/llsc.h>
|
2019-10-02 04:53:37 +07:00
|
|
|
#include <asm/sync.h>
|
2012-03-29 00:30:02 +07:00
|
|
|
#include <asm/war.h>
|
|
|
|
|
2017-06-10 07:26:36 +07:00
|
|
|
/*
|
|
|
|
* These functions doesn't exist, so if they are called you'll either:
|
|
|
|
*
|
|
|
|
* - Get an error at compile-time due to __compiletime_error, if supported by
|
|
|
|
* your compiler.
|
|
|
|
*
|
|
|
|
* or:
|
|
|
|
*
|
|
|
|
* - Get an error at link-time due to the call to the missing function.
|
|
|
|
*/
|
2017-06-10 07:26:38 +07:00
|
|
|
extern unsigned long __cmpxchg_called_with_bad_pointer(void)
|
2017-06-10 07:26:36 +07:00
|
|
|
__compiletime_error("Bad argument size for cmpxchg");
|
2019-02-07 05:38:56 +07:00
|
|
|
extern unsigned long __cmpxchg64_unsupported(void)
|
|
|
|
__compiletime_error("cmpxchg64 not available; cpu_has_64bits may be false");
|
2017-06-10 07:26:36 +07:00
|
|
|
extern unsigned long __xchg_called_with_bad_pointer(void)
|
|
|
|
__compiletime_error("Bad argument size for xchg");
|
|
|
|
|
2017-06-10 07:26:34 +07:00
|
|
|
#define __xchg_asm(ld, st, m, val) \
|
|
|
|
({ \
|
|
|
|
__typeof(*(m)) __ret; \
|
|
|
|
\
|
|
|
|
if (kernel_uses_llsc) { \
|
|
|
|
__asm__ __volatile__( \
|
|
|
|
" .set push \n" \
|
|
|
|
" .set noat \n" \
|
2018-11-09 03:14:38 +07:00
|
|
|
" .set push \n" \
|
2017-06-10 07:26:34 +07:00
|
|
|
" .set " MIPS_ISA_ARCH_LEVEL " \n" \
|
2019-10-02 04:53:37 +07:00
|
|
|
" " __SYNC(full, loongson3_war) " \n" \
|
2017-06-10 07:26:34 +07:00
|
|
|
"1: " ld " %0, %2 # __xchg_asm \n" \
|
2018-11-09 03:14:38 +07:00
|
|
|
" .set pop \n" \
|
2017-06-10 07:26:34 +07:00
|
|
|
" move $1, %z3 \n" \
|
|
|
|
" .set " MIPS_ISA_ARCH_LEVEL " \n" \
|
|
|
|
" " st " $1, %1 \n" \
|
2019-10-02 04:53:05 +07:00
|
|
|
"\t" __SC_BEQZ "$1, 1b \n" \
|
2017-06-10 07:26:34 +07:00
|
|
|
" .set pop \n" \
|
|
|
|
: "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
|
|
|
|
: GCC_OFF_SMALL_ASM() (*m), "Jr" (val) \
|
2019-06-13 20:43:20 +07:00
|
|
|
: __LLSC_CLOBBER); \
|
2017-06-10 07:26:34 +07:00
|
|
|
} else { \
|
|
|
|
unsigned long __flags; \
|
|
|
|
\
|
|
|
|
raw_local_irq_save(__flags); \
|
|
|
|
__ret = *m; \
|
|
|
|
*m = val; \
|
|
|
|
raw_local_irq_restore(__flags); \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
__ret; \
|
|
|
|
})
|
|
|
|
|
2017-06-10 07:26:39 +07:00
|
|
|
extern unsigned long __xchg_small(volatile void *ptr, unsigned long val,
|
|
|
|
unsigned int size);
|
|
|
|
|
2019-10-09 17:06:00 +07:00
|
|
|
static __always_inline
|
|
|
|
unsigned long __xchg(volatile void *ptr, unsigned long x, int size)
|
2012-03-29 00:30:02 +07:00
|
|
|
{
|
|
|
|
switch (size) {
|
2017-06-10 07:26:39 +07:00
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
return __xchg_small(ptr, x, size);
|
|
|
|
|
2012-03-29 00:30:02 +07:00
|
|
|
case 4:
|
2017-06-10 07:26:37 +07:00
|
|
|
return __xchg_asm("ll", "sc", (volatile u32 *)ptr, x);
|
|
|
|
|
2012-03-29 00:30:02 +07:00
|
|
|
case 8:
|
2017-06-10 07:26:37 +07:00
|
|
|
if (!IS_ENABLED(CONFIG_64BIT))
|
|
|
|
return __xchg_called_with_bad_pointer();
|
|
|
|
|
|
|
|
return __xchg_asm("lld", "scd", (volatile u64 *)ptr, x);
|
|
|
|
|
2017-06-10 07:26:36 +07:00
|
|
|
default:
|
|
|
|
return __xchg_called_with_bad_pointer();
|
2012-03-29 00:30:02 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define xchg(ptr, x) \
|
|
|
|
({ \
|
2017-06-10 07:26:37 +07:00
|
|
|
__typeof__(*(ptr)) __res; \
|
|
|
|
\
|
2019-10-02 04:53:38 +07:00
|
|
|
/* \
|
|
|
|
* In the Loongson3 workaround case __xchg_asm() already \
|
|
|
|
* contains a completion barrier prior to the LL, so we don't \
|
|
|
|
* need to emit an extra one here. \
|
|
|
|
*/ \
|
|
|
|
if (!__SYNC_loongson3_war) \
|
|
|
|
smp_mb__before_llsc(); \
|
2017-06-10 07:26:37 +07:00
|
|
|
\
|
|
|
|
__res = (__typeof__(*(ptr))) \
|
2017-06-10 07:26:41 +07:00
|
|
|
__xchg((ptr), (unsigned long)(x), sizeof(*(ptr))); \
|
2017-06-10 07:26:37 +07:00
|
|
|
\
|
|
|
|
smp_llsc_mb(); \
|
|
|
|
\
|
|
|
|
__res; \
|
2012-03-29 00:30:02 +07:00
|
|
|
})
|
2007-10-01 10:15:00 +07:00
|
|
|
|
|
|
|
#define __cmpxchg_asm(ld, st, m, old, new) \
|
|
|
|
({ \
|
|
|
|
__typeof(*(m)) __ret; \
|
|
|
|
\
|
2017-06-10 07:26:33 +07:00
|
|
|
if (kernel_uses_llsc) { \
|
2007-10-01 10:15:00 +07:00
|
|
|
__asm__ __volatile__( \
|
|
|
|
" .set push \n" \
|
|
|
|
" .set noat \n" \
|
2018-11-09 03:14:38 +07:00
|
|
|
" .set push \n" \
|
2014-11-20 20:31:48 +07:00
|
|
|
" .set "MIPS_ISA_ARCH_LEVEL" \n" \
|
2019-10-02 04:53:37 +07:00
|
|
|
" " __SYNC(full, loongson3_war) " \n" \
|
2013-01-22 18:59:30 +07:00
|
|
|
"1: " ld " %0, %2 # __cmpxchg_asm \n" \
|
2007-10-01 10:15:00 +07:00
|
|
|
" bne %0, %z3, 2f \n" \
|
2018-11-09 03:14:38 +07:00
|
|
|
" .set pop \n" \
|
2007-10-01 10:15:00 +07:00
|
|
|
" move $1, %z4 \n" \
|
2014-11-20 20:31:48 +07:00
|
|
|
" .set "MIPS_ISA_ARCH_LEVEL" \n" \
|
2007-10-01 10:15:00 +07:00
|
|
|
" " st " $1, %1 \n" \
|
2019-10-02 04:53:05 +07:00
|
|
|
"\t" __SC_BEQZ "$1, 1b \n" \
|
2007-10-01 10:15:00 +07:00
|
|
|
" .set pop \n" \
|
2019-10-02 04:53:37 +07:00
|
|
|
"2: " __SYNC(full, loongson3_war) " \n" \
|
2015-01-26 19:44:11 +07:00
|
|
|
: "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
|
2019-06-13 20:43:20 +07:00
|
|
|
: GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new) \
|
|
|
|
: __LLSC_CLOBBER); \
|
2007-10-01 10:15:00 +07:00
|
|
|
} else { \
|
|
|
|
unsigned long __flags; \
|
|
|
|
\
|
|
|
|
raw_local_irq_save(__flags); \
|
|
|
|
__ret = *m; \
|
|
|
|
if (__ret == old) \
|
|
|
|
*m = new; \
|
|
|
|
raw_local_irq_restore(__flags); \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
__ret; \
|
|
|
|
})
|
|
|
|
|
2017-06-10 07:26:40 +07:00
|
|
|
extern unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old,
|
|
|
|
unsigned long new, unsigned int size);
|
|
|
|
|
2019-10-06 20:12:32 +07:00
|
|
|
static __always_inline
|
|
|
|
unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
|
|
|
|
unsigned long new, unsigned int size)
|
2017-06-10 07:26:38 +07:00
|
|
|
{
|
|
|
|
switch (size) {
|
2017-06-10 07:26:40 +07:00
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
return __cmpxchg_small(ptr, old, new, size);
|
|
|
|
|
2017-06-10 07:26:38 +07:00
|
|
|
case 4:
|
MIPS: Fix cmpxchg on 32b signed ints for 64b kernel with !kernel_uses_llsc
Commit 8263db4d7768 ("MIPS: cmpxchg: Implement __cmpxchg() as a
function") refactored our implementation of __cmpxchg() to be a function
rather than a macro, with the aim of making it easier to read & modify.
Unfortunately the commit breaks use of cmpxchg() for signed 32 bit
values when we have a 64 bit kernel with kernel_uses_llsc == false,
because:
- In cmpxchg_local() we cast the old value to the type the pointer
points to, and then to an unsigned long. If the pointer points to a
signed type smaller than 64 bits then the old value will be sign
extended to 64 bits. That is, bits beyond the size of the pointed to
type will be set to 1 if the old value is negative. In the case of a
signed 32 bit integer with a negative value, bits 63:32 will all be
set.
- In __cmpxchg_asm() we load the value from memory, ie. dereference the
pointer, and store the value as an unsigned integer (__ret) whose
size matches the pointer. For a 32 bit cmpxchg() this means we store
the value in a u32, because the pointer provided to __cmpxchg_asm()
by __cmpxchg() is of type volatile u32 *.
- __cmpxchg_asm() then checks whether the value in memory (__ret)
matches the provided old value, by comparing the two values. This
results in the u32 being promoted to a 64 bit unsigned long to match
the old argument - however because both types are unsigned the value
is zero extended, which does not match the sign extension performed
on the old value in cmpxchg_local() earlier.
This mismatch means that unfortunate cmpxchg() calls can incorrectly
fail for 64 bit kernels with kernel_uses_llsc == false. This is the case
on at least non-SMP Cavium Octeon kernels, which hardcode
kernel_uses_llsc in their cpu-feature-overrides.h header. Using a
v4.13-rc7 kernel configured using cavium_octeon_defconfig with SMP
manually disabled, this presents itself as oddity when we reach
userland - for example:
can't run '/bin/mount': Text file busy
can't run '/bin/mkdir': Text file busy
can't run '/bin/mkdir': Text file busy
can't run '/bin/mount': Text file busy
can't run '/bin/hostname': Text file busy
can't run '/etc/init.d/rcS': Text file busy
can't run '/sbin/getty': Text file busy
can't run '/sbin/getty': Text file busy
It appears that some part of the init process, which is in this case
buildroot's busybox init, is running successfully. It never manages to
reach the login prompt though, and complains about /sbin/getty being
busy repeatedly and indefinitely.
Fix this by casting the old value provided to __cmpxchg_asm() to an
appropriately sized unsigned integer, such that we consistently
zero-extend avoiding the mismatch. The __cmpxchg_small() case for 8 & 16
bit values is unaffected because __cmpxchg_small() already masks
provided values appropriately.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Fixes: 8263db4d7768 ("MIPS: cmpxchg: Implement __cmpxchg() as a function")
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/17226/
Cc: linux-mips@linux-mips.org
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2017-09-02 04:46:50 +07:00
|
|
|
return __cmpxchg_asm("ll", "sc", (volatile u32 *)ptr,
|
|
|
|
(u32)old, new);
|
2017-06-10 07:26:38 +07:00
|
|
|
|
|
|
|
case 8:
|
|
|
|
/* lld/scd are only available for MIPS64 */
|
|
|
|
if (!IS_ENABLED(CONFIG_64BIT))
|
|
|
|
return __cmpxchg_called_with_bad_pointer();
|
|
|
|
|
MIPS: Fix cmpxchg on 32b signed ints for 64b kernel with !kernel_uses_llsc
Commit 8263db4d7768 ("MIPS: cmpxchg: Implement __cmpxchg() as a
function") refactored our implementation of __cmpxchg() to be a function
rather than a macro, with the aim of making it easier to read & modify.
Unfortunately the commit breaks use of cmpxchg() for signed 32 bit
values when we have a 64 bit kernel with kernel_uses_llsc == false,
because:
- In cmpxchg_local() we cast the old value to the type the pointer
points to, and then to an unsigned long. If the pointer points to a
signed type smaller than 64 bits then the old value will be sign
extended to 64 bits. That is, bits beyond the size of the pointed to
type will be set to 1 if the old value is negative. In the case of a
signed 32 bit integer with a negative value, bits 63:32 will all be
set.
- In __cmpxchg_asm() we load the value from memory, ie. dereference the
pointer, and store the value as an unsigned integer (__ret) whose
size matches the pointer. For a 32 bit cmpxchg() this means we store
the value in a u32, because the pointer provided to __cmpxchg_asm()
by __cmpxchg() is of type volatile u32 *.
- __cmpxchg_asm() then checks whether the value in memory (__ret)
matches the provided old value, by comparing the two values. This
results in the u32 being promoted to a 64 bit unsigned long to match
the old argument - however because both types are unsigned the value
is zero extended, which does not match the sign extension performed
on the old value in cmpxchg_local() earlier.
This mismatch means that unfortunate cmpxchg() calls can incorrectly
fail for 64 bit kernels with kernel_uses_llsc == false. This is the case
on at least non-SMP Cavium Octeon kernels, which hardcode
kernel_uses_llsc in their cpu-feature-overrides.h header. Using a
v4.13-rc7 kernel configured using cavium_octeon_defconfig with SMP
manually disabled, this presents itself as oddity when we reach
userland - for example:
can't run '/bin/mount': Text file busy
can't run '/bin/mkdir': Text file busy
can't run '/bin/mkdir': Text file busy
can't run '/bin/mount': Text file busy
can't run '/bin/hostname': Text file busy
can't run '/etc/init.d/rcS': Text file busy
can't run '/sbin/getty': Text file busy
can't run '/sbin/getty': Text file busy
It appears that some part of the init process, which is in this case
buildroot's busybox init, is running successfully. It never manages to
reach the login prompt though, and complains about /sbin/getty being
busy repeatedly and indefinitely.
Fix this by casting the old value provided to __cmpxchg_asm() to an
appropriately sized unsigned integer, such that we consistently
zero-extend avoiding the mismatch. The __cmpxchg_small() case for 8 & 16
bit values is unaffected because __cmpxchg_small() already masks
provided values appropriately.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Fixes: 8263db4d7768 ("MIPS: cmpxchg: Implement __cmpxchg() as a function")
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/17226/
Cc: linux-mips@linux-mips.org
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2017-09-02 04:46:50 +07:00
|
|
|
return __cmpxchg_asm("lld", "scd", (volatile u64 *)ptr,
|
|
|
|
(u64)old, new);
|
2017-06-10 07:26:38 +07:00
|
|
|
|
|
|
|
default:
|
|
|
|
return __cmpxchg_called_with_bad_pointer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define cmpxchg_local(ptr, old, new) \
|
|
|
|
((__typeof__(*(ptr))) \
|
|
|
|
__cmpxchg((ptr), \
|
|
|
|
(unsigned long)(__typeof__(*(ptr)))(old), \
|
|
|
|
(unsigned long)(__typeof__(*(ptr)))(new), \
|
|
|
|
sizeof(*(ptr))))
|
|
|
|
|
|
|
|
#define cmpxchg(ptr, old, new) \
|
2007-10-01 10:15:00 +07:00
|
|
|
({ \
|
2017-06-10 07:26:38 +07:00
|
|
|
__typeof__(*(ptr)) __res; \
|
2007-10-01 10:15:00 +07:00
|
|
|
\
|
2019-10-02 04:53:38 +07:00
|
|
|
/* \
|
|
|
|
* In the Loongson3 workaround case __cmpxchg_asm() already \
|
|
|
|
* contains a completion barrier prior to the LL, so we don't \
|
|
|
|
* need to emit an extra one here. \
|
|
|
|
*/ \
|
|
|
|
if (!__SYNC_loongson3_war) \
|
|
|
|
smp_mb__before_llsc(); \
|
|
|
|
\
|
2017-06-10 07:26:38 +07:00
|
|
|
__res = cmpxchg_local((ptr), (old), (new)); \
|
2019-10-02 04:53:38 +07:00
|
|
|
\
|
|
|
|
/* \
|
|
|
|
* In the Loongson3 workaround case __cmpxchg_asm() already \
|
|
|
|
* contains a completion barrier after the SC, so we don't \
|
|
|
|
* need to emit an extra one here. \
|
|
|
|
*/ \
|
|
|
|
if (!__SYNC_loongson3_war) \
|
|
|
|
smp_llsc_mb(); \
|
2007-10-01 10:15:00 +07:00
|
|
|
\
|
|
|
|
__res; \
|
|
|
|
})
|
|
|
|
|
2015-03-08 01:30:20 +07:00
|
|
|
#ifdef CONFIG_64BIT
|
|
|
|
#define cmpxchg64_local(ptr, o, n) \
|
2008-02-07 15:16:09 +07:00
|
|
|
({ \
|
|
|
|
BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
|
2015-03-08 01:30:20 +07:00
|
|
|
cmpxchg_local((ptr), (o), (n)); \
|
2008-02-07 15:16:09 +07:00
|
|
|
})
|
|
|
|
|
2015-03-08 01:30:20 +07:00
|
|
|
#define cmpxchg64(ptr, o, n) \
|
2008-02-07 15:16:09 +07:00
|
|
|
({ \
|
|
|
|
BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
|
2015-03-08 01:30:20 +07:00
|
|
|
cmpxchg((ptr), (o), (n)); \
|
2008-02-07 15:16:09 +07:00
|
|
|
})
|
|
|
|
#else
|
2019-02-07 05:38:56 +07:00
|
|
|
|
|
|
|
# include <asm-generic/cmpxchg-local.h>
|
|
|
|
# define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
|
|
|
|
|
|
|
|
# ifdef CONFIG_SMP
|
|
|
|
|
|
|
|
static inline unsigned long __cmpxchg64(volatile void *ptr,
|
|
|
|
unsigned long long old,
|
|
|
|
unsigned long long new)
|
|
|
|
{
|
|
|
|
unsigned long long tmp, ret;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The assembly below has to combine 32 bit values into a 64 bit
|
|
|
|
* register, and split 64 bit values from one register into two. If we
|
|
|
|
* were to take an interrupt in the middle of this we'd only save the
|
|
|
|
* least significant 32 bits of each register & probably clobber the
|
|
|
|
* most significant 32 bits of the 64 bit values we're using. In order
|
|
|
|
* to avoid this we must disable interrupts.
|
|
|
|
*/
|
|
|
|
local_irq_save(flags);
|
|
|
|
|
|
|
|
asm volatile(
|
|
|
|
" .set push \n"
|
|
|
|
" .set " MIPS_ISA_ARCH_LEVEL " \n"
|
|
|
|
/* Load 64 bits from ptr */
|
2019-10-02 04:53:37 +07:00
|
|
|
" " __SYNC(full, loongson3_war) " \n"
|
2019-02-07 05:38:56 +07:00
|
|
|
"1: lld %L0, %3 # __cmpxchg64 \n"
|
|
|
|
/*
|
|
|
|
* Split the 64 bit value we loaded into the 2 registers that hold the
|
|
|
|
* ret variable.
|
|
|
|
*/
|
|
|
|
" dsra %M0, %L0, 32 \n"
|
|
|
|
" sll %L0, %L0, 0 \n"
|
|
|
|
/*
|
|
|
|
* Compare ret against old, breaking out of the loop if they don't
|
|
|
|
* match.
|
|
|
|
*/
|
|
|
|
" bne %M0, %M4, 2f \n"
|
|
|
|
" bne %L0, %L4, 2f \n"
|
|
|
|
/*
|
|
|
|
* Combine the 32 bit halves from the 2 registers that hold the new
|
|
|
|
* variable into a single 64 bit register.
|
|
|
|
*/
|
|
|
|
# if MIPS_ISA_REV >= 2
|
|
|
|
" move %L1, %L5 \n"
|
|
|
|
" dins %L1, %M5, 32, 32 \n"
|
|
|
|
# else
|
|
|
|
" dsll %L1, %L5, 32 \n"
|
|
|
|
" dsrl %L1, %L1, 32 \n"
|
|
|
|
" .set noat \n"
|
|
|
|
" dsll $at, %M5, 32 \n"
|
|
|
|
" or %L1, %L1, $at \n"
|
|
|
|
" .set at \n"
|
|
|
|
# endif
|
|
|
|
/* Attempt to store new at ptr */
|
|
|
|
" scd %L1, %2 \n"
|
|
|
|
/* If we failed, loop! */
|
2019-10-02 04:53:05 +07:00
|
|
|
"\t" __SC_BEQZ "%L1, 1b \n"
|
2019-02-07 05:38:56 +07:00
|
|
|
" .set pop \n"
|
2019-10-02 04:53:37 +07:00
|
|
|
"2: " __SYNC(full, loongson3_war) " \n"
|
2019-02-07 05:38:56 +07:00
|
|
|
: "=&r"(ret),
|
|
|
|
"=&r"(tmp),
|
|
|
|
"=" GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr)
|
|
|
|
: GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr),
|
|
|
|
"r" (old),
|
|
|
|
"r" (new)
|
|
|
|
: "memory");
|
|
|
|
|
|
|
|
local_irq_restore(flags);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
# define cmpxchg64(ptr, o, n) ({ \
|
|
|
|
unsigned long long __old = (__typeof__(*(ptr)))(o); \
|
|
|
|
unsigned long long __new = (__typeof__(*(ptr)))(n); \
|
|
|
|
__typeof__(*(ptr)) __res; \
|
|
|
|
\
|
|
|
|
/* \
|
|
|
|
* We can only use cmpxchg64 if we know that the CPU supports \
|
|
|
|
* 64-bits, ie. lld & scd. Our call to __cmpxchg64_unsupported \
|
|
|
|
* will cause a build error unless cpu_has_64bits is a \
|
|
|
|
* compile-time constant 1. \
|
|
|
|
*/ \
|
2019-06-13 20:43:18 +07:00
|
|
|
if (cpu_has_64bits && kernel_uses_llsc) { \
|
|
|
|
smp_mb__before_llsc(); \
|
2019-02-07 05:38:56 +07:00
|
|
|
__res = __cmpxchg64((ptr), __old, __new); \
|
2019-06-13 20:43:18 +07:00
|
|
|
smp_llsc_mb(); \
|
|
|
|
} else { \
|
2019-02-07 05:38:56 +07:00
|
|
|
__res = __cmpxchg64_unsupported(); \
|
2019-06-13 20:43:18 +07:00
|
|
|
} \
|
2019-02-07 05:38:56 +07:00
|
|
|
\
|
|
|
|
__res; \
|
|
|
|
})
|
|
|
|
|
|
|
|
# else /* !CONFIG_SMP */
|
|
|
|
# define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n))
|
|
|
|
# endif /* !CONFIG_SMP */
|
|
|
|
#endif /* !CONFIG_64BIT */
|
2008-02-07 15:16:09 +07:00
|
|
|
|
2007-10-01 10:15:00 +07:00
|
|
|
#endif /* __ASM_CMPXCHG_H */
|