Skip to content

Commit

Permalink
futex: Ensure futex_atomic_cmpxchg_inatomic() is present
Browse files Browse the repository at this point in the history
The boot-time detection of futex_atomic_cmpxchg_inatomic() has a bug on
some 32-bit arm builds, and Thomas Gleixner suggested that setting
CONFIG_HAVE_FUTEX_CMPXCHG would avoid the problem, as it is always present
anyway.

Looking into which other architectures could do the same showed that almost
all architectures have it, the exceptions being:

 - some old 32-bit MIPS uniprocessor cores without ll/sc
 - one xtensa variant with no SMP
 - 32-bit SPARC when built for SMP

Fix MIPS And Xtensa by rearranging the generic code to let it be used
as a fallback.

For SPARC, the SMP definition just ends up turning off futex anyway, so
this can be done at Kconfig time instead. Note that sparc32 glibc requires
the CASA instruction for its mutexes anyway, which is only available when
running on SPARCv9 or LEON CPUs, but needs to be implemented in the sparc32
kernel for those.

Signed-off-by: Arnd Bergmann <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
Acked-by: Max Filippov <[email protected]>
Acked-by: Geert Uytterhoeven <[email protected]>
Acked-by: Rich Felker <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
  • Loading branch information
arndb authored and KAGA-KOKO committed Nov 24, 2021
1 parent 2202e15 commit 3f2beda
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 33 deletions.
29 changes: 18 additions & 11 deletions arch/mips/include/asm/futex.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@
#include <asm/sync.h>
#include <asm/war.h>

#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \
#define arch_futex_atomic_op_inuser arch_futex_atomic_op_inuser
#define futex_atomic_cmpxchg_inatomic futex_atomic_cmpxchg_inatomic
#include <asm-generic/futex.h>

#define __futex_atomic_op(op, insn, ret, oldval, uaddr, oparg) \
{ \
if (cpu_has_llsc && IS_ENABLED(CONFIG_WAR_R10000_LLSC)) { \
__asm__ __volatile__( \
Expand Down Expand Up @@ -80,9 +84,11 @@
: "0" (0), GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oparg), \
"i" (-EFAULT) \
: "memory"); \
} else \
ret = -ENOSYS; \
}
} else { \
/* fallback for non-SMP */ \
ret = arch_futex_atomic_op_inuser_local(op, oparg, oval,\
uaddr); \
}

static inline int
arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
Expand All @@ -94,23 +100,23 @@ arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)

switch (op) {
case FUTEX_OP_SET:
__futex_atomic_op("move $1, %z5", ret, oldval, uaddr, oparg);
__futex_atomic_op(op, "move $1, %z5", ret, oldval, uaddr, oparg);
break;

case FUTEX_OP_ADD:
__futex_atomic_op("addu $1, %1, %z5",
__futex_atomic_op(op, "addu $1, %1, %z5",
ret, oldval, uaddr, oparg);
break;
case FUTEX_OP_OR:
__futex_atomic_op("or $1, %1, %z5",
__futex_atomic_op(op, "or $1, %1, %z5",
ret, oldval, uaddr, oparg);
break;
case FUTEX_OP_ANDN:
__futex_atomic_op("and $1, %1, %z5",
__futex_atomic_op(op, "and $1, %1, %z5",
ret, oldval, uaddr, ~oparg);
break;
case FUTEX_OP_XOR:
__futex_atomic_op("xor $1, %1, %z5",
__futex_atomic_op(op, "xor $1, %1, %z5",
ret, oldval, uaddr, oparg);
break;
default:
Expand Down Expand Up @@ -193,8 +199,9 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
: GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
"i" (-EFAULT)
: "memory");
} else
return -ENOSYS;
} else {
return futex_atomic_cmpxchg_inatomic_local(uval, uaddr, oldval, newval);
}

*uval = val;
return ret;
Expand Down
8 changes: 6 additions & 2 deletions arch/xtensa/include/asm/futex.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
#include <linux/uaccess.h>
#include <linux/errno.h>

#define arch_futex_atomic_op_inuser arch_futex_atomic_op_inuser
#define futex_atomic_cmpxchg_inatomic futex_atomic_cmpxchg_inatomic
#include <asm-generic/futex.h>

#if XCHAL_HAVE_EXCLUSIVE
#define __futex_atomic_op(insn, ret, old, uaddr, arg) \
__asm__ __volatile( \
Expand Down Expand Up @@ -105,7 +109,7 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,

return ret;
#else
return -ENOSYS;
return arch_futex_atomic_op_inuser_local(op, oparg, oval, uaddr);
#endif
}

Expand Down Expand Up @@ -156,7 +160,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,

return ret;
#else
return -ENOSYS;
return futex_atomic_cmpxchg_inatomic_local(uval, uaddr, oldval, newval);
#endif
}

Expand Down
31 changes: 11 additions & 20 deletions include/asm-generic/futex.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@
#include <linux/uaccess.h>
#include <asm/errno.h>

#ifndef futex_atomic_cmpxchg_inatomic
#ifndef CONFIG_SMP
/*
* The following implementation only for uniprocessor machines.
* It relies on preempt_disable() ensuring mutual exclusion.
*
*/
#define futex_atomic_cmpxchg_inatomic(uval, uaddr, oldval, newval) \
futex_atomic_cmpxchg_inatomic_local_generic(uval, uaddr, oldval, newval)
#define arch_futex_atomic_op_inuser(op, oparg, oval, uaddr) \
arch_futex_atomic_op_inuser_local_generic(op, oparg, oval, uaddr)
#endif /* CONFIG_SMP */
#endif

/**
* arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
* arch_futex_atomic_op_inuser_local() - Atomic arithmetic operation with constant
* argument and comparison of the previous
* futex value with another constant.
*
Expand All @@ -28,7 +35,7 @@
* -ENOSYS - Operation not supported
*/
static inline int
arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
futex_atomic_op_inuser_local(int op, u32 oparg, int *oval, u32 __user *uaddr)
{
int oldval, ret;
u32 tmp;
Expand Down Expand Up @@ -75,7 +82,7 @@ arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
}

/**
* futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
* futex_atomic_cmpxchg_inatomic_local() - Compare and exchange the content of the
* uaddr with newval if the current value is
* oldval.
* @uval: pointer to store content of @uaddr
Expand All @@ -87,10 +94,9 @@ arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
* 0 - On success
* -EFAULT - User access resulted in a page fault
* -EAGAIN - Atomic operation was unable to complete due to contention
* -ENOSYS - Function not implemented (only if !HAVE_FUTEX_CMPXCHG)
*/
static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
futex_atomic_cmpxchg_inatomic_local(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval)
{
u32 val;
Expand All @@ -112,19 +118,4 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
return 0;
}

#else
static inline int
arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
{
return -ENOSYS;
}

static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval)
{
return -ENOSYS;
}

#endif /* CONFIG_SMP */
#endif
1 change: 1 addition & 0 deletions init/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,7 @@ config BASE_FULL

config FUTEX
bool "Enable futex support" if EXPERT
depends on !(SPARC32 && SMP)
default y
imply RT_MUTEXES
help
Expand Down

0 comments on commit 3f2beda

Please sign in to comment.