|
@@ -1097,15 +1097,15 @@ static void unqueue_me_pi(struct futex_q *q)
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Fixup the pi_state owner with current.
|
|
|
|
|
|
+ * Fixup the pi_state owner with the new owner.
|
|
*
|
|
*
|
|
* Must be called with hash bucket lock held and mm->sem held for non
|
|
* Must be called with hash bucket lock held and mm->sem held for non
|
|
* private futexes.
|
|
* private futexes.
|
|
*/
|
|
*/
|
|
static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
|
|
static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
|
|
- struct task_struct *curr)
|
|
|
|
|
|
+ struct task_struct *newowner)
|
|
{
|
|
{
|
|
- u32 newtid = task_pid_vnr(curr) | FUTEX_WAITERS;
|
|
|
|
|
|
+ u32 newtid = task_pid_vnr(newowner) | FUTEX_WAITERS;
|
|
struct futex_pi_state *pi_state = q->pi_state;
|
|
struct futex_pi_state *pi_state = q->pi_state;
|
|
u32 uval, curval, newval;
|
|
u32 uval, curval, newval;
|
|
int ret;
|
|
int ret;
|
|
@@ -1119,12 +1119,12 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
|
|
} else
|
|
} else
|
|
newtid |= FUTEX_OWNER_DIED;
|
|
newtid |= FUTEX_OWNER_DIED;
|
|
|
|
|
|
- pi_state->owner = curr;
|
|
|
|
|
|
+ pi_state->owner = newowner;
|
|
|
|
|
|
- spin_lock_irq(&curr->pi_lock);
|
|
|
|
|
|
+ spin_lock_irq(&newowner->pi_lock);
|
|
WARN_ON(!list_empty(&pi_state->list));
|
|
WARN_ON(!list_empty(&pi_state->list));
|
|
- list_add(&pi_state->list, &curr->pi_state_list);
|
|
|
|
- spin_unlock_irq(&curr->pi_lock);
|
|
|
|
|
|
+ list_add(&pi_state->list, &newowner->pi_state_list);
|
|
|
|
+ spin_unlock_irq(&newowner->pi_lock);
|
|
|
|
|
|
/*
|
|
/*
|
|
* We own it, so we have to replace the pending owner
|
|
* We own it, so we have to replace the pending owner
|
|
@@ -1508,9 +1508,40 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
|
* when we were on the way back before we locked the
|
|
* when we were on the way back before we locked the
|
|
* hash bucket.
|
|
* hash bucket.
|
|
*/
|
|
*/
|
|
- if (q.pi_state->owner == curr &&
|
|
|
|
- rt_mutex_trylock(&q.pi_state->pi_mutex)) {
|
|
|
|
- ret = 0;
|
|
|
|
|
|
+ if (q.pi_state->owner == curr) {
|
|
|
|
+ /*
|
|
|
|
+ * Try to get the rt_mutex now. This might
|
|
|
|
+ * fail as some other task acquired the
|
|
|
|
+ * rt_mutex after we removed ourself from the
|
|
|
|
+ * rt_mutex waiters list.
|
|
|
|
+ */
|
|
|
|
+ if (rt_mutex_trylock(&q.pi_state->pi_mutex))
|
|
|
|
+ ret = 0;
|
|
|
|
+ else {
|
|
|
|
+ /*
|
|
|
|
+ * pi_state is incorrect, some other
|
|
|
|
+ * task did a lock steal and we
|
|
|
|
+ * returned due to timeout or signal
|
|
|
|
+ * without taking the rt_mutex. Too
|
|
|
|
+ * late. We can access the
|
|
|
|
+ * rt_mutex_owner without locking, as
|
|
|
|
+ * the other task is now blocked on
|
|
|
|
+ * the hash bucket lock. Fix the state
|
|
|
|
+ * up.
|
|
|
|
+ */
|
|
|
|
+ struct task_struct *owner;
|
|
|
|
+ int res;
|
|
|
|
+
|
|
|
|
+ owner = rt_mutex_owner(&q.pi_state->pi_mutex);
|
|
|
|
+ res = fixup_pi_state_owner(uaddr, &q, owner);
|
|
|
|
+
|
|
|
|
+ WARN_ON(rt_mutex_owner(&q.pi_state->pi_mutex) !=
|
|
|
|
+ owner);
|
|
|
|
+
|
|
|
|
+ /* propagate -EFAULT, if the fixup failed */
|
|
|
|
+ if (res)
|
|
|
|
+ ret = res;
|
|
|
|
+ }
|
|
} else {
|
|
} else {
|
|
/*
|
|
/*
|
|
* Paranoia check. If we did not take the lock
|
|
* Paranoia check. If we did not take the lock
|