Commit 9431ac2b authored by Will Deacon's avatar Will Deacon

Merge branch 'for-next/futex' of...

Merge branch 'for-next/futex' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux into for-next/core
parents 61cf61d8 42750351
...@@ -218,5 +218,4 @@ All other architectures should build just fine too - but they won't have ...@@ -218,5 +218,4 @@ All other architectures should build just fine too - but they won't have
the new syscalls yet. the new syscalls yet.
Architectures need to implement the new futex_atomic_cmpxchg_inatomic() Architectures need to implement the new futex_atomic_cmpxchg_inatomic()
inline function before writing up the syscalls (that function returns inline function before writing up the syscalls.
-ENOSYS right now).
...@@ -23,26 +23,34 @@ ...@@ -23,26 +23,34 @@
#include <asm/errno.h> #include <asm/errno.h>
#define FUTEX_MAX_LOOPS 128 /* What's the largest number you can think of? */
#define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg) \ #define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg) \
do { \ do { \
unsigned int loops = FUTEX_MAX_LOOPS; \
\
uaccess_enable(); \ uaccess_enable(); \
asm volatile( \ asm volatile( \
" prfm pstl1strm, %2\n" \ " prfm pstl1strm, %2\n" \
"1: ldxr %w1, %2\n" \ "1: ldxr %w1, %2\n" \
insn "\n" \ insn "\n" \
"2: stlxr %w3, %w0, %2\n" \ "2: stlxr %w0, %w3, %2\n" \
" cbnz %w3, 1b\n" \ " cbz %w0, 3f\n" \
" dmb ish\n" \ " sub %w4, %w4, %w0\n" \
" cbnz %w4, 1b\n" \
" mov %w0, %w7\n" \
"3:\n" \ "3:\n" \
" dmb ish\n" \
" .pushsection .fixup,\"ax\"\n" \ " .pushsection .fixup,\"ax\"\n" \
" .align 2\n" \ " .align 2\n" \
"4: mov %w0, %w5\n" \ "4: mov %w0, %w6\n" \
" b 3b\n" \ " b 3b\n" \
" .popsection\n" \ " .popsection\n" \
_ASM_EXTABLE(1b, 4b) \ _ASM_EXTABLE(1b, 4b) \
_ASM_EXTABLE(2b, 4b) \ _ASM_EXTABLE(2b, 4b) \
: "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp) \ : "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp), \
: "r" (oparg), "Ir" (-EFAULT) \ "+r" (loops) \
: "r" (oparg), "Ir" (-EFAULT), "Ir" (-EAGAIN) \
: "memory"); \ : "memory"); \
uaccess_disable(); \ uaccess_disable(); \
} while (0) } while (0)
...@@ -50,30 +58,30 @@ do { \ ...@@ -50,30 +58,30 @@ do { \
static inline int static inline int
arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *_uaddr) arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *_uaddr)
{ {
int oldval = 0, ret, tmp; int oldval, ret, tmp;
u32 __user *uaddr = __uaccess_mask_ptr(_uaddr); u32 __user *uaddr = __uaccess_mask_ptr(_uaddr);
pagefault_disable(); pagefault_disable();
switch (op) { switch (op) {
case FUTEX_OP_SET: case FUTEX_OP_SET:
__futex_atomic_op("mov %w0, %w4", __futex_atomic_op("mov %w3, %w5",
ret, oldval, uaddr, tmp, oparg); ret, oldval, uaddr, tmp, oparg);
break; break;
case FUTEX_OP_ADD: case FUTEX_OP_ADD:
__futex_atomic_op("add %w0, %w1, %w4", __futex_atomic_op("add %w3, %w1, %w5",
ret, oldval, uaddr, tmp, oparg); ret, oldval, uaddr, tmp, oparg);
break; break;
case FUTEX_OP_OR: case FUTEX_OP_OR:
__futex_atomic_op("orr %w0, %w1, %w4", __futex_atomic_op("orr %w3, %w1, %w5",
ret, oldval, uaddr, tmp, oparg); ret, oldval, uaddr, tmp, oparg);
break; break;
case FUTEX_OP_ANDN: case FUTEX_OP_ANDN:
__futex_atomic_op("and %w0, %w1, %w4", __futex_atomic_op("and %w3, %w1, %w5",
ret, oldval, uaddr, tmp, ~oparg); ret, oldval, uaddr, tmp, ~oparg);
break; break;
case FUTEX_OP_XOR: case FUTEX_OP_XOR:
__futex_atomic_op("eor %w0, %w1, %w4", __futex_atomic_op("eor %w3, %w1, %w5",
ret, oldval, uaddr, tmp, oparg); ret, oldval, uaddr, tmp, oparg);
break; break;
default: default:
...@@ -93,6 +101,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr, ...@@ -93,6 +101,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr,
u32 oldval, u32 newval) u32 oldval, u32 newval)
{ {
int ret = 0; int ret = 0;
unsigned int loops = FUTEX_MAX_LOOPS;
u32 val, tmp; u32 val, tmp;
u32 __user *uaddr; u32 __user *uaddr;
...@@ -104,24 +113,30 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr, ...@@ -104,24 +113,30 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr,
asm volatile("// futex_atomic_cmpxchg_inatomic\n" asm volatile("// futex_atomic_cmpxchg_inatomic\n"
" prfm pstl1strm, %2\n" " prfm pstl1strm, %2\n"
"1: ldxr %w1, %2\n" "1: ldxr %w1, %2\n"
" sub %w3, %w1, %w4\n" " sub %w3, %w1, %w5\n"
" cbnz %w3, 3f\n" " cbnz %w3, 4f\n"
"2: stlxr %w3, %w5, %2\n" "2: stlxr %w3, %w6, %2\n"
" cbnz %w3, 1b\n" " cbz %w3, 3f\n"
" dmb ish\n" " sub %w4, %w4, %w3\n"
" cbnz %w4, 1b\n"
" mov %w0, %w8\n"
"3:\n" "3:\n"
" dmb ish\n"
"4:\n"
" .pushsection .fixup,\"ax\"\n" " .pushsection .fixup,\"ax\"\n"
"4: mov %w0, %w6\n" "5: mov %w0, %w7\n"
" b 3b\n" " b 4b\n"
" .popsection\n" " .popsection\n"
_ASM_EXTABLE(1b, 4b) _ASM_EXTABLE(1b, 5b)
_ASM_EXTABLE(2b, 4b) _ASM_EXTABLE(2b, 5b)
: "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp) : "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp), "+r" (loops)
: "r" (oldval), "r" (newval), "Ir" (-EFAULT) : "r" (oldval), "r" (newval), "Ir" (-EFAULT), "Ir" (-EAGAIN)
: "memory"); : "memory");
uaccess_disable(); uaccess_disable();
if (!ret)
*uval = val; *uval = val;
return ret; return ret;
} }
......
...@@ -23,7 +23,9 @@ ...@@ -23,7 +23,9 @@
* *
* Return: * Return:
* 0 - On success * 0 - On success
* <0 - On error * -EFAULT - User access resulted in a page fault
* -EAGAIN - Atomic operation was unable to complete due to contention
* -ENOSYS - Operation not supported
*/ */
static inline int static inline int
arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr) arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
...@@ -85,7 +87,9 @@ arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr) ...@@ -85,7 +87,9 @@ arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
* *
* Return: * Return:
* 0 - On success * 0 - On success
* <0 - On error * -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 static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
......
...@@ -1311,13 +1311,15 @@ static int lookup_pi_state(u32 __user *uaddr, u32 uval, ...@@ -1311,13 +1311,15 @@ static int lookup_pi_state(u32 __user *uaddr, u32 uval,
static int lock_pi_update_atomic(u32 __user *uaddr, u32 uval, u32 newval) static int lock_pi_update_atomic(u32 __user *uaddr, u32 uval, u32 newval)
{ {
int err;
u32 uninitialized_var(curval); u32 uninitialized_var(curval);
if (unlikely(should_fail_futex(true))) if (unlikely(should_fail_futex(true)))
return -EFAULT; return -EFAULT;
if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))) err = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval);
return -EFAULT; if (unlikely(err))
return err;
/* If user space value changed, let the caller retry */ /* If user space value changed, let the caller retry */
return curval != uval ? -EAGAIN : 0; return curval != uval ? -EAGAIN : 0;
...@@ -1502,10 +1504,8 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_pi_state *pi_ ...@@ -1502,10 +1504,8 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_pi_state *pi_
if (unlikely(should_fail_futex(true))) if (unlikely(should_fail_futex(true)))
ret = -EFAULT; ret = -EFAULT;
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) { ret = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval);
ret = -EFAULT; if (!ret && (curval != uval)) {
} else if (curval != uval) {
/* /*
* If a unconditional UNLOCK_PI operation (user space did not * If a unconditional UNLOCK_PI operation (user space did not
* try the TID->0 transition) raced with a waiter setting the * try the TID->0 transition) raced with a waiter setting the
...@@ -1700,32 +1700,32 @@ futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2, ...@@ -1700,32 +1700,32 @@ futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2,
double_lock_hb(hb1, hb2); double_lock_hb(hb1, hb2);
op_ret = futex_atomic_op_inuser(op, uaddr2); op_ret = futex_atomic_op_inuser(op, uaddr2);
if (unlikely(op_ret < 0)) { if (unlikely(op_ret < 0)) {
double_unlock_hb(hb1, hb2); double_unlock_hb(hb1, hb2);
#ifndef CONFIG_MMU if (!IS_ENABLED(CONFIG_MMU) ||
unlikely(op_ret != -EFAULT && op_ret != -EAGAIN)) {
/* /*
* we don't get EFAULT from MMU faults if we don't have an MMU, * we don't get EFAULT from MMU faults if we don't have
* but we might get them from range checking * an MMU, but we might get them from range checking
*/ */
ret = op_ret; ret = op_ret;
goto out_put_keys; goto out_put_keys;
#endif
if (unlikely(op_ret != -EFAULT)) {
ret = op_ret;
goto out_put_keys;
} }
if (op_ret == -EFAULT) {
ret = fault_in_user_writeable(uaddr2); ret = fault_in_user_writeable(uaddr2);
if (ret) if (ret)
goto out_put_keys; goto out_put_keys;
}
if (!(flags & FLAGS_SHARED)) if (!(flags & FLAGS_SHARED)) {
cond_resched();
goto retry_private; goto retry_private;
}
put_futex_key(&key2); put_futex_key(&key2);
put_futex_key(&key1); put_futex_key(&key1);
cond_resched();
goto retry; goto retry;
} }
...@@ -2350,7 +2350,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, ...@@ -2350,7 +2350,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
u32 uval, uninitialized_var(curval), newval; u32 uval, uninitialized_var(curval), newval;
struct task_struct *oldowner, *newowner; struct task_struct *oldowner, *newowner;
u32 newtid; u32 newtid;
int ret; int ret, err = 0;
lockdep_assert_held(q->lock_ptr); lockdep_assert_held(q->lock_ptr);
...@@ -2421,14 +2421,17 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, ...@@ -2421,14 +2421,17 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
if (!pi_state->owner) if (!pi_state->owner)
newtid |= FUTEX_OWNER_DIED; newtid |= FUTEX_OWNER_DIED;
if (get_futex_value_locked(&uval, uaddr)) err = get_futex_value_locked(&uval, uaddr);
goto handle_fault; if (err)
goto handle_err;
for (;;) { for (;;) {
newval = (uval & FUTEX_OWNER_DIED) | newtid; newval = (uval & FUTEX_OWNER_DIED) | newtid;
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) err = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval);
goto handle_fault; if (err)
goto handle_err;
if (curval == uval) if (curval == uval)
break; break;
uval = curval; uval = curval;
...@@ -2456,23 +2459,37 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, ...@@ -2456,23 +2459,37 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
return 0; return 0;
/* /*
* To handle the page fault we need to drop the locks here. That gives * In order to reschedule or handle a page fault, we need to drop the
* the other task (either the highest priority waiter itself or the * locks here. In the case of a fault, this gives the other task
* task which stole the rtmutex) the chance to try the fixup of the * (either the highest priority waiter itself or the task which stole
* pi_state. So once we are back from handling the fault we need to * the rtmutex) the chance to try the fixup of the pi_state. So once we
* check the pi_state after reacquiring the locks and before trying to * are back from handling the fault we need to check the pi_state after
* do another fixup. When the fixup has been done already we simply * reacquiring the locks and before trying to do another fixup. When
* return. * the fixup has been done already we simply return.
* *
* Note: we hold both hb->lock and pi_mutex->wait_lock. We can safely * Note: we hold both hb->lock and pi_mutex->wait_lock. We can safely
* drop hb->lock since the caller owns the hb -> futex_q relation. * drop hb->lock since the caller owns the hb -> futex_q relation.
* Dropping the pi_mutex->wait_lock requires the state revalidate. * Dropping the pi_mutex->wait_lock requires the state revalidate.
*/ */
handle_fault: handle_err:
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock); raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
spin_unlock(q->lock_ptr); spin_unlock(q->lock_ptr);
switch (err) {
case -EFAULT:
ret = fault_in_user_writeable(uaddr); ret = fault_in_user_writeable(uaddr);
break;
case -EAGAIN:
cond_resched();
ret = 0;
break;
default:
WARN_ON_ONCE(1);
ret = err;
break;
}
spin_lock(q->lock_ptr); spin_lock(q->lock_ptr);
raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock); raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
...@@ -3041,10 +3058,8 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) ...@@ -3041,10 +3058,8 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
* A unconditional UNLOCK_PI op raced against a waiter * A unconditional UNLOCK_PI op raced against a waiter
* setting the FUTEX_WAITERS bit. Try again. * setting the FUTEX_WAITERS bit. Try again.
*/ */
if (ret == -EAGAIN) { if (ret == -EAGAIN)
put_futex_key(&key); goto pi_retry;
goto retry;
}
/* /*
* wake_futex_pi has detected invalid state. Tell user * wake_futex_pi has detected invalid state. Tell user
* space. * space.
...@@ -3059,9 +3074,19 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) ...@@ -3059,9 +3074,19 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
* preserve the WAITERS bit not the OWNER_DIED one. We are the * preserve the WAITERS bit not the OWNER_DIED one. We are the
* owner. * owner.
*/ */
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, 0)) { if ((ret = cmpxchg_futex_value_locked(&curval, uaddr, uval, 0))) {
spin_unlock(&hb->lock); spin_unlock(&hb->lock);
switch (ret) {
case -EFAULT:
goto pi_faulted; goto pi_faulted;
case -EAGAIN:
goto pi_retry;
default:
WARN_ON_ONCE(1);
goto out_putkey;
}
} }
/* /*
...@@ -3075,6 +3100,11 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) ...@@ -3075,6 +3100,11 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
put_futex_key(&key); put_futex_key(&key);
return ret; return ret;
pi_retry:
put_futex_key(&key);
cond_resched();
goto retry;
pi_faulted: pi_faulted:
put_futex_key(&key); put_futex_key(&key);
...@@ -3435,6 +3465,7 @@ SYSCALL_DEFINE3(get_robust_list, int, pid, ...@@ -3435,6 +3465,7 @@ SYSCALL_DEFINE3(get_robust_list, int, pid,
static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi) static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi)
{ {
u32 uval, uninitialized_var(nval), mval; u32 uval, uninitialized_var(nval), mval;
int err;
/* Futex address must be 32bit aligned */ /* Futex address must be 32bit aligned */
if ((((unsigned long)uaddr) % sizeof(*uaddr)) != 0) if ((((unsigned long)uaddr) % sizeof(*uaddr)) != 0)
...@@ -3444,7 +3475,9 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p ...@@ -3444,7 +3475,9 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p
if (get_user(uval, uaddr)) if (get_user(uval, uaddr))
return -1; return -1;
if ((uval & FUTEX_TID_MASK) == task_pid_vnr(curr)) { if ((uval & FUTEX_TID_MASK) != task_pid_vnr(curr))
return 0;
/* /*
* Ok, this dying thread is truly holding a futex * Ok, this dying thread is truly holding a futex
* of interest. Set the OWNER_DIED bit atomically * of interest. Set the OWNER_DIED bit atomically
...@@ -3456,6 +3489,7 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p ...@@ -3456,6 +3489,7 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p
* userspace. * userspace.
*/ */
mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
/* /*
* We are not holding a lock here, but we want to have * We are not holding a lock here, but we want to have
* the pagefault_disable/enable() protection because * the pagefault_disable/enable() protection because
...@@ -3465,11 +3499,23 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p ...@@ -3465,11 +3499,23 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p
* does not guarantee R/W access. If that fails we * does not guarantee R/W access. If that fails we
* give up and leave the futex locked. * give up and leave the futex locked.
*/ */
if (cmpxchg_futex_value_locked(&nval, uaddr, uval, mval)) { if ((err = cmpxchg_futex_value_locked(&nval, uaddr, uval, mval))) {
switch (err) {
case -EFAULT:
if (fault_in_user_writeable(uaddr)) if (fault_in_user_writeable(uaddr))
return -1; return -1;
goto retry; goto retry;
case -EAGAIN:
cond_resched();
goto retry;
default:
WARN_ON_ONCE(1);
return err;
}
} }
if (nval != uval) if (nval != uval)
goto retry; goto retry;
...@@ -3479,7 +3525,7 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p ...@@ -3479,7 +3525,7 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p
*/ */
if (!pi && (uval & FUTEX_WAITERS)) if (!pi && (uval & FUTEX_WAITERS))
futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY); futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY);
}
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment