mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-15 20:56:43 +07:00
8101b5a153
Stephen reported the following build warning on a ARM multi_v7_defconfig
build with GCC 9.2.1:
kernel/futex.c: In function 'do_futex':
kernel/futex.c:1676:17: warning: 'oldval' may be used uninitialized in this function [-Wmaybe-uninitialized]
1676 | return oldval == cmparg;
| ~~~~~~~^~~~~~~~~
kernel/futex.c:1652:6: note: 'oldval' was declared here
1652 | int oldval, ret;
| ^~~~~~
introduced by commit a08971e948
("futex: arch_futex_atomic_op_inuser()
calling conventions change").
While that change should not make any difference it confuses GCC which
fails to work out that oldval is not referenced when the return value is
not zero.
GCC fails to properly analyze arch_futex_atomic_op_inuser(). It's not the
early return, the issue is with the assembly macros. GCC fails to detect
that those either set 'ret' to 0 and set oldval or set 'ret' to -EFAULT
which makes oldval uninteresting. The store to the callsite supplied oldval
pointer is conditional on ret == 0.
The straight forward way to solve this is to make the store unconditional.
Aside of addressing the build warning this makes sense anyway because it
removes the conditional from the fastpath. In the error case the stored
value is uninteresting and the extra store does not matter at all.
Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/87pncao2ph.fsf@nanos.tec.linutronix.de
181 lines
4.3 KiB
C
181 lines
4.3 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _ASM_ARM_FUTEX_H
|
|
#define _ASM_ARM_FUTEX_H
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
#include <linux/futex.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/errno.h>
|
|
|
|
#define __futex_atomic_ex_table(err_reg) \
|
|
"3:\n" \
|
|
" .pushsection __ex_table,\"a\"\n" \
|
|
" .align 3\n" \
|
|
" .long 1b, 4f, 2b, 4f\n" \
|
|
" .popsection\n" \
|
|
" .pushsection .text.fixup,\"ax\"\n" \
|
|
" .align 2\n" \
|
|
"4: mov %0, " err_reg "\n" \
|
|
" b 3b\n" \
|
|
" .popsection"
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
|
|
({ \
|
|
unsigned int __ua_flags; \
|
|
smp_mb(); \
|
|
prefetchw(uaddr); \
|
|
__ua_flags = uaccess_save_and_enable(); \
|
|
__asm__ __volatile__( \
|
|
"1: ldrex %1, [%3]\n" \
|
|
" " insn "\n" \
|
|
"2: strex %2, %0, [%3]\n" \
|
|
" teq %2, #0\n" \
|
|
" bne 1b\n" \
|
|
" mov %0, #0\n" \
|
|
__futex_atomic_ex_table("%5") \
|
|
: "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \
|
|
: "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \
|
|
: "cc", "memory"); \
|
|
uaccess_restore(__ua_flags); \
|
|
})
|
|
|
|
static inline int
|
|
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|
u32 oldval, u32 newval)
|
|
{
|
|
unsigned int __ua_flags;
|
|
int ret;
|
|
u32 val;
|
|
|
|
if (!access_ok(uaddr, sizeof(u32)))
|
|
return -EFAULT;
|
|
|
|
smp_mb();
|
|
/* Prefetching cannot fault */
|
|
prefetchw(uaddr);
|
|
__ua_flags = uaccess_save_and_enable();
|
|
__asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
|
|
"1: ldrex %1, [%4]\n"
|
|
" teq %1, %2\n"
|
|
" ite eq @ explicit IT needed for the 2b label\n"
|
|
"2: strexeq %0, %3, [%4]\n"
|
|
" movne %0, #0\n"
|
|
" teq %0, #0\n"
|
|
" bne 1b\n"
|
|
__futex_atomic_ex_table("%5")
|
|
: "=&r" (ret), "=&r" (val)
|
|
: "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
|
|
: "cc", "memory");
|
|
uaccess_restore(__ua_flags);
|
|
smp_mb();
|
|
|
|
*uval = val;
|
|
return ret;
|
|
}
|
|
|
|
#else /* !SMP, we can work around lack of atomic ops by disabling preemption */
|
|
|
|
#include <linux/preempt.h>
|
|
#include <asm/domain.h>
|
|
|
|
#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
|
|
({ \
|
|
unsigned int __ua_flags = uaccess_save_and_enable(); \
|
|
__asm__ __volatile__( \
|
|
"1: " TUSER(ldr) " %1, [%3]\n" \
|
|
" " insn "\n" \
|
|
"2: " TUSER(str) " %0, [%3]\n" \
|
|
" mov %0, #0\n" \
|
|
__futex_atomic_ex_table("%5") \
|
|
: "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \
|
|
: "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \
|
|
: "cc", "memory"); \
|
|
uaccess_restore(__ua_flags); \
|
|
})
|
|
|
|
static inline int
|
|
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|
u32 oldval, u32 newval)
|
|
{
|
|
unsigned int __ua_flags;
|
|
int ret = 0;
|
|
u32 val;
|
|
|
|
if (!access_ok(uaddr, sizeof(u32)))
|
|
return -EFAULT;
|
|
|
|
preempt_disable();
|
|
__ua_flags = uaccess_save_and_enable();
|
|
__asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
|
|
" .syntax unified\n"
|
|
"1: " TUSER(ldr) " %1, [%4]\n"
|
|
" teq %1, %2\n"
|
|
" it eq @ explicit IT needed for the 2b label\n"
|
|
"2: " TUSERCOND(str, eq) " %3, [%4]\n"
|
|
__futex_atomic_ex_table("%5")
|
|
: "+r" (ret), "=&r" (val)
|
|
: "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
|
|
: "cc", "memory");
|
|
uaccess_restore(__ua_flags);
|
|
|
|
*uval = val;
|
|
preempt_enable();
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* !SMP */
|
|
|
|
static inline int
|
|
arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
|
|
{
|
|
int oldval = 0, ret, tmp;
|
|
|
|
if (!access_ok(uaddr, sizeof(u32)))
|
|
return -EFAULT;
|
|
|
|
#ifndef CONFIG_SMP
|
|
preempt_disable();
|
|
#endif
|
|
|
|
switch (op) {
|
|
case FUTEX_OP_SET:
|
|
__futex_atomic_op("mov %0, %4", ret, oldval, tmp, uaddr, oparg);
|
|
break;
|
|
case FUTEX_OP_ADD:
|
|
__futex_atomic_op("add %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
|
|
break;
|
|
case FUTEX_OP_OR:
|
|
__futex_atomic_op("orr %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
|
|
break;
|
|
case FUTEX_OP_ANDN:
|
|
__futex_atomic_op("and %0, %1, %4", ret, oldval, tmp, uaddr, ~oparg);
|
|
break;
|
|
case FUTEX_OP_XOR:
|
|
__futex_atomic_op("eor %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
|
|
break;
|
|
default:
|
|
ret = -ENOSYS;
|
|
}
|
|
|
|
#ifndef CONFIG_SMP
|
|
preempt_enable();
|
|
#endif
|
|
|
|
/*
|
|
* Store unconditionally. If ret != 0 the extra store is the least
|
|
* of the worries but GCC cannot figure out that __futex_atomic_op()
|
|
* is either setting ret to -EFAULT or storing the old value in
|
|
* oldval which results in a uninitialized warning at the call site.
|
|
*/
|
|
*oval = oldval;
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* __KERNEL__ */
|
|
#endif /* _ASM_ARM_FUTEX_H */
|