Loading Documentation/robust-futexes.txt +1 −2 Original line number Diff line number Diff line Loading @@ -218,5 +218,4 @@ All other architectures should build just fine too - but they won't have the new syscalls yet. Architectures need to implement the new futex_atomic_cmpxchg_inatomic() inline function before writing up the syscalls (that function returns -ENOSYS right now). inline function before writing up the syscalls. arch/arm64/include/asm/futex.h +39 −24 Original line number Diff line number Diff line Loading @@ -23,26 +23,34 @@ #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) \ do { \ unsigned int loops = FUTEX_MAX_LOOPS; \ \ uaccess_enable(); \ asm volatile( \ " prfm pstl1strm, %2\n" \ "1: ldxr %w1, %2\n" \ insn "\n" \ "2: stlxr %w3, %w0, %2\n" \ " cbnz %w3, 1b\n" \ " dmb ish\n" \ "2: stlxr %w0, %w3, %2\n" \ " cbz %w0, 3f\n" \ " sub %w4, %w4, %w0\n" \ " cbnz %w4, 1b\n" \ " mov %w0, %w7\n" \ "3:\n" \ " dmb ish\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ "4: mov %w0, %w5\n" \ "4: mov %w0, %w6\n" \ " b 3b\n" \ " .popsection\n" \ _ASM_EXTABLE(1b, 4b) \ _ASM_EXTABLE(2b, 4b) \ : "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp) \ : "r" (oparg), "Ir" (-EFAULT) \ : "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp), \ "+r" (loops) \ : "r" (oparg), "Ir" (-EFAULT), "Ir" (-EAGAIN) \ : "memory"); \ uaccess_disable(); \ } while (0) Loading @@ -50,30 +58,30 @@ do { \ static inline int 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); pagefault_disable(); switch (op) { case FUTEX_OP_SET: __futex_atomic_op("mov %w0, %w4", __futex_atomic_op("mov %w3, %w5", ret, oldval, uaddr, tmp, oparg); break; case FUTEX_OP_ADD: __futex_atomic_op("add %w0, %w1, %w4", __futex_atomic_op("add %w3, %w1, %w5", ret, oldval, uaddr, tmp, oparg); break; case FUTEX_OP_OR: __futex_atomic_op("orr %w0, %w1, %w4", __futex_atomic_op("orr %w3, %w1, %w5", ret, oldval, uaddr, tmp, oparg); break; case FUTEX_OP_ANDN: __futex_atomic_op("and %w0, %w1, %w4", __futex_atomic_op("and %w3, %w1, %w5", ret, oldval, uaddr, tmp, ~oparg); break; case FUTEX_OP_XOR: __futex_atomic_op("eor %w0, %w1, %w4", __futex_atomic_op("eor %w3, %w1, %w5", ret, oldval, uaddr, tmp, oparg); break; default: Loading @@ -93,6 +101,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr, u32 oldval, u32 newval) { int ret = 0; unsigned int loops = FUTEX_MAX_LOOPS; u32 val, tmp; u32 __user *uaddr; Loading @@ -104,24 +113,30 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr, asm volatile("// futex_atomic_cmpxchg_inatomic\n" " prfm pstl1strm, %2\n" "1: ldxr %w1, %2\n" " sub %w3, %w1, %w4\n" " cbnz %w3, 3f\n" "2: stlxr %w3, %w5, %2\n" " cbnz %w3, 1b\n" " dmb ish\n" " sub %w3, %w1, %w5\n" " cbnz %w3, 4f\n" "2: stlxr %w3, %w6, %2\n" " cbz %w3, 3f\n" " sub %w4, %w4, %w3\n" " cbnz %w4, 1b\n" " mov %w0, %w8\n" "3:\n" " dmb ish\n" "4:\n" " .pushsection .fixup,\"ax\"\n" "4: mov %w0, %w6\n" " b 3b\n" "5: mov %w0, %w7\n" " b 4b\n" " .popsection\n" _ASM_EXTABLE(1b, 4b) _ASM_EXTABLE(2b, 4b) : "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp) : "r" (oldval), "r" (newval), "Ir" (-EFAULT) _ASM_EXTABLE(1b, 5b) _ASM_EXTABLE(2b, 5b) : "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp), "+r" (loops) : "r" (oldval), "r" (newval), "Ir" (-EFAULT), "Ir" (-EAGAIN) : "memory"); uaccess_disable(); if (!ret) *uval = val; return ret; } Loading include/asm-generic/futex.h +6 −2 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ * * Return: * 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 arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr) Loading Loading @@ -85,7 +87,9 @@ arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr) * * Return: * 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 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, Loading kernel/futex.c +117 −71 Original line number Diff line number Diff line Loading @@ -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) { int err; u32 uninitialized_var(curval); if (unlikely(should_fail_futex(true))) return -EFAULT; if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))) return -EFAULT; err = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval); if (unlikely(err)) return err; /* If user space value changed, let the caller retry */ return curval != uval ? -EAGAIN : 0; Loading Loading @@ -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))) ret = -EFAULT; if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) { ret = -EFAULT; } else if (curval != uval) { ret = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval); if (!ret && (curval != uval)) { /* * If a unconditional UNLOCK_PI operation (user space did not * try the TID->0 transition) raced with a waiter setting the Loading Loading @@ -1700,32 +1700,32 @@ futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2, double_lock_hb(hb1, hb2); op_ret = futex_atomic_op_inuser(op, uaddr2); if (unlikely(op_ret < 0)) { 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, * but we might get them from range checking * we don't get EFAULT from MMU faults if we don't have * an MMU, but we might get them from range checking */ ret = op_ret; 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); if (ret) goto out_put_keys; } if (!(flags & FLAGS_SHARED)) if (!(flags & FLAGS_SHARED)) { cond_resched(); goto retry_private; } put_futex_key(&key2); put_futex_key(&key1); cond_resched(); goto retry; } Loading Loading @@ -2350,7 +2350,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, u32 uval, uninitialized_var(curval), newval; struct task_struct *oldowner, *newowner; u32 newtid; int ret; int ret, err = 0; lockdep_assert_held(q->lock_ptr); Loading Loading @@ -2421,14 +2421,17 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, if (!pi_state->owner) newtid |= FUTEX_OWNER_DIED; if (get_futex_value_locked(&uval, uaddr)) goto handle_fault; err = get_futex_value_locked(&uval, uaddr); if (err) goto handle_err; for (;;) { newval = (uval & FUTEX_OWNER_DIED) | newtid; if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) goto handle_fault; err = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval); if (err) goto handle_err; if (curval == uval) break; uval = curval; Loading Loading @@ -2456,23 +2459,37 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, return 0; /* * To handle the page fault we need to drop the locks here. That gives * the other task (either the highest priority waiter itself or the * task which stole the rtmutex) the chance to try the fixup of the * pi_state. So once we are back from handling the fault we need to * check the pi_state after reacquiring the locks and before trying to * do another fixup. When the fixup has been done already we simply * return. * In order to reschedule or handle a page fault, we need to drop the * locks here. In the case of a fault, this gives the other task * (either the highest priority waiter itself or the task which stole * the rtmutex) the chance to try the fixup of the pi_state. So once we * are back from handling the fault we need to check the pi_state after * reacquiring the locks and before trying to do another fixup. When * the fixup has been done already we simply return. * * 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. * Dropping the pi_mutex->wait_lock requires the state revalidate. */ handle_fault: handle_err: raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock); spin_unlock(q->lock_ptr); switch (err) { case -EFAULT: 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); raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock); Loading Loading @@ -3041,10 +3058,8 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) * A unconditional UNLOCK_PI op raced against a waiter * setting the FUTEX_WAITERS bit. Try again. */ if (ret == -EAGAIN) { put_futex_key(&key); goto retry; } if (ret == -EAGAIN) goto pi_retry; /* * wake_futex_pi has detected invalid state. Tell user * space. Loading @@ -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 * owner. */ if (cmpxchg_futex_value_locked(&curval, uaddr, uval, 0)) { if ((ret = cmpxchg_futex_value_locked(&curval, uaddr, uval, 0))) { spin_unlock(&hb->lock); switch (ret) { case -EFAULT: goto pi_faulted; case -EAGAIN: goto pi_retry; default: WARN_ON_ONCE(1); goto out_putkey; } } /* Loading @@ -3075,6 +3100,11 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) put_futex_key(&key); return ret; pi_retry: put_futex_key(&key); cond_resched(); goto retry; pi_faulted: put_futex_key(&key); Loading Loading @@ -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) { u32 uval, uninitialized_var(nval), mval; int err; /* Futex address must be 32bit aligned */ if ((((unsigned long)uaddr) % sizeof(*uaddr)) != 0) Loading @@ -3444,7 +3475,9 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p if (get_user(uval, uaddr)) 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 * of interest. Set the OWNER_DIED bit atomically Loading @@ -3456,6 +3489,7 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p * userspace. */ mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; /* * We are not holding a lock here, but we want to have * the pagefault_disable/enable() protection because Loading @@ -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 * 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)) return -1; goto retry; case -EAGAIN: cond_resched(); goto retry; default: WARN_ON_ONCE(1); return err; } } if (nval != uval) goto retry; Loading @@ -3479,7 +3525,7 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p */ if (!pi && (uval & FUTEX_WAITERS)) futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY); } return 0; } Loading Loading
Documentation/robust-futexes.txt +1 −2 Original line number Diff line number Diff line Loading @@ -218,5 +218,4 @@ All other architectures should build just fine too - but they won't have the new syscalls yet. Architectures need to implement the new futex_atomic_cmpxchg_inatomic() inline function before writing up the syscalls (that function returns -ENOSYS right now). inline function before writing up the syscalls.
arch/arm64/include/asm/futex.h +39 −24 Original line number Diff line number Diff line Loading @@ -23,26 +23,34 @@ #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) \ do { \ unsigned int loops = FUTEX_MAX_LOOPS; \ \ uaccess_enable(); \ asm volatile( \ " prfm pstl1strm, %2\n" \ "1: ldxr %w1, %2\n" \ insn "\n" \ "2: stlxr %w3, %w0, %2\n" \ " cbnz %w3, 1b\n" \ " dmb ish\n" \ "2: stlxr %w0, %w3, %2\n" \ " cbz %w0, 3f\n" \ " sub %w4, %w4, %w0\n" \ " cbnz %w4, 1b\n" \ " mov %w0, %w7\n" \ "3:\n" \ " dmb ish\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ "4: mov %w0, %w5\n" \ "4: mov %w0, %w6\n" \ " b 3b\n" \ " .popsection\n" \ _ASM_EXTABLE(1b, 4b) \ _ASM_EXTABLE(2b, 4b) \ : "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp) \ : "r" (oparg), "Ir" (-EFAULT) \ : "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp), \ "+r" (loops) \ : "r" (oparg), "Ir" (-EFAULT), "Ir" (-EAGAIN) \ : "memory"); \ uaccess_disable(); \ } while (0) Loading @@ -50,30 +58,30 @@ do { \ static inline int 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); pagefault_disable(); switch (op) { case FUTEX_OP_SET: __futex_atomic_op("mov %w0, %w4", __futex_atomic_op("mov %w3, %w5", ret, oldval, uaddr, tmp, oparg); break; case FUTEX_OP_ADD: __futex_atomic_op("add %w0, %w1, %w4", __futex_atomic_op("add %w3, %w1, %w5", ret, oldval, uaddr, tmp, oparg); break; case FUTEX_OP_OR: __futex_atomic_op("orr %w0, %w1, %w4", __futex_atomic_op("orr %w3, %w1, %w5", ret, oldval, uaddr, tmp, oparg); break; case FUTEX_OP_ANDN: __futex_atomic_op("and %w0, %w1, %w4", __futex_atomic_op("and %w3, %w1, %w5", ret, oldval, uaddr, tmp, ~oparg); break; case FUTEX_OP_XOR: __futex_atomic_op("eor %w0, %w1, %w4", __futex_atomic_op("eor %w3, %w1, %w5", ret, oldval, uaddr, tmp, oparg); break; default: Loading @@ -93,6 +101,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr, u32 oldval, u32 newval) { int ret = 0; unsigned int loops = FUTEX_MAX_LOOPS; u32 val, tmp; u32 __user *uaddr; Loading @@ -104,24 +113,30 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr, asm volatile("// futex_atomic_cmpxchg_inatomic\n" " prfm pstl1strm, %2\n" "1: ldxr %w1, %2\n" " sub %w3, %w1, %w4\n" " cbnz %w3, 3f\n" "2: stlxr %w3, %w5, %2\n" " cbnz %w3, 1b\n" " dmb ish\n" " sub %w3, %w1, %w5\n" " cbnz %w3, 4f\n" "2: stlxr %w3, %w6, %2\n" " cbz %w3, 3f\n" " sub %w4, %w4, %w3\n" " cbnz %w4, 1b\n" " mov %w0, %w8\n" "3:\n" " dmb ish\n" "4:\n" " .pushsection .fixup,\"ax\"\n" "4: mov %w0, %w6\n" " b 3b\n" "5: mov %w0, %w7\n" " b 4b\n" " .popsection\n" _ASM_EXTABLE(1b, 4b) _ASM_EXTABLE(2b, 4b) : "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp) : "r" (oldval), "r" (newval), "Ir" (-EFAULT) _ASM_EXTABLE(1b, 5b) _ASM_EXTABLE(2b, 5b) : "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp), "+r" (loops) : "r" (oldval), "r" (newval), "Ir" (-EFAULT), "Ir" (-EAGAIN) : "memory"); uaccess_disable(); if (!ret) *uval = val; return ret; } Loading
include/asm-generic/futex.h +6 −2 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ * * Return: * 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 arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr) Loading Loading @@ -85,7 +87,9 @@ arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr) * * Return: * 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 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, Loading
kernel/futex.c +117 −71 Original line number Diff line number Diff line Loading @@ -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) { int err; u32 uninitialized_var(curval); if (unlikely(should_fail_futex(true))) return -EFAULT; if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))) return -EFAULT; err = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval); if (unlikely(err)) return err; /* If user space value changed, let the caller retry */ return curval != uval ? -EAGAIN : 0; Loading Loading @@ -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))) ret = -EFAULT; if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) { ret = -EFAULT; } else if (curval != uval) { ret = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval); if (!ret && (curval != uval)) { /* * If a unconditional UNLOCK_PI operation (user space did not * try the TID->0 transition) raced with a waiter setting the Loading Loading @@ -1700,32 +1700,32 @@ futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2, double_lock_hb(hb1, hb2); op_ret = futex_atomic_op_inuser(op, uaddr2); if (unlikely(op_ret < 0)) { 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, * but we might get them from range checking * we don't get EFAULT from MMU faults if we don't have * an MMU, but we might get them from range checking */ ret = op_ret; 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); if (ret) goto out_put_keys; } if (!(flags & FLAGS_SHARED)) if (!(flags & FLAGS_SHARED)) { cond_resched(); goto retry_private; } put_futex_key(&key2); put_futex_key(&key1); cond_resched(); goto retry; } Loading Loading @@ -2350,7 +2350,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, u32 uval, uninitialized_var(curval), newval; struct task_struct *oldowner, *newowner; u32 newtid; int ret; int ret, err = 0; lockdep_assert_held(q->lock_ptr); Loading Loading @@ -2421,14 +2421,17 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, if (!pi_state->owner) newtid |= FUTEX_OWNER_DIED; if (get_futex_value_locked(&uval, uaddr)) goto handle_fault; err = get_futex_value_locked(&uval, uaddr); if (err) goto handle_err; for (;;) { newval = (uval & FUTEX_OWNER_DIED) | newtid; if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) goto handle_fault; err = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval); if (err) goto handle_err; if (curval == uval) break; uval = curval; Loading Loading @@ -2456,23 +2459,37 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, return 0; /* * To handle the page fault we need to drop the locks here. That gives * the other task (either the highest priority waiter itself or the * task which stole the rtmutex) the chance to try the fixup of the * pi_state. So once we are back from handling the fault we need to * check the pi_state after reacquiring the locks and before trying to * do another fixup. When the fixup has been done already we simply * return. * In order to reschedule or handle a page fault, we need to drop the * locks here. In the case of a fault, this gives the other task * (either the highest priority waiter itself or the task which stole * the rtmutex) the chance to try the fixup of the pi_state. So once we * are back from handling the fault we need to check the pi_state after * reacquiring the locks and before trying to do another fixup. When * the fixup has been done already we simply return. * * 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. * Dropping the pi_mutex->wait_lock requires the state revalidate. */ handle_fault: handle_err: raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock); spin_unlock(q->lock_ptr); switch (err) { case -EFAULT: 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); raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock); Loading Loading @@ -3041,10 +3058,8 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) * A unconditional UNLOCK_PI op raced against a waiter * setting the FUTEX_WAITERS bit. Try again. */ if (ret == -EAGAIN) { put_futex_key(&key); goto retry; } if (ret == -EAGAIN) goto pi_retry; /* * wake_futex_pi has detected invalid state. Tell user * space. Loading @@ -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 * owner. */ if (cmpxchg_futex_value_locked(&curval, uaddr, uval, 0)) { if ((ret = cmpxchg_futex_value_locked(&curval, uaddr, uval, 0))) { spin_unlock(&hb->lock); switch (ret) { case -EFAULT: goto pi_faulted; case -EAGAIN: goto pi_retry; default: WARN_ON_ONCE(1); goto out_putkey; } } /* Loading @@ -3075,6 +3100,11 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) put_futex_key(&key); return ret; pi_retry: put_futex_key(&key); cond_resched(); goto retry; pi_faulted: put_futex_key(&key); Loading Loading @@ -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) { u32 uval, uninitialized_var(nval), mval; int err; /* Futex address must be 32bit aligned */ if ((((unsigned long)uaddr) % sizeof(*uaddr)) != 0) Loading @@ -3444,7 +3475,9 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p if (get_user(uval, uaddr)) 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 * of interest. Set the OWNER_DIED bit atomically Loading @@ -3456,6 +3489,7 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p * userspace. */ mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; /* * We are not holding a lock here, but we want to have * the pagefault_disable/enable() protection because Loading @@ -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 * 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)) return -1; goto retry; case -EAGAIN: cond_resched(); goto retry; default: WARN_ON_ONCE(1); return err; } } if (nval != uval) goto retry; Loading @@ -3479,7 +3525,7 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int p */ if (!pi && (uval & FUTEX_WAITERS)) futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY); } return 0; } Loading