mirror of https://gitee.com/openkylin/linux.git
Merge branch 'core-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'core-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: futexes: fix fault handling in futex_lock_pi
This commit is contained in:
commit
27f4837cbf
|
@ -1096,21 +1096,64 @@ static void unqueue_me_pi(struct futex_q *q)
|
||||||
* 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 *newowner)
|
struct task_struct *newowner,
|
||||||
|
struct rw_semaphore *fshared)
|
||||||
{
|
{
|
||||||
u32 newtid = task_pid_vnr(newowner) | 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;
|
||||||
|
struct task_struct *oldowner = pi_state->owner;
|
||||||
u32 uval, curval, newval;
|
u32 uval, curval, newval;
|
||||||
int ret;
|
int ret, attempt = 0;
|
||||||
|
|
||||||
/* Owner died? */
|
/* Owner died? */
|
||||||
|
if (!pi_state->owner)
|
||||||
|
newtid |= FUTEX_OWNER_DIED;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We are here either because we stole the rtmutex from the
|
||||||
|
* pending owner or we are the pending owner which failed to
|
||||||
|
* get the rtmutex. We have to replace the pending owner TID
|
||||||
|
* in the user space variable. This must be atomic as we have
|
||||||
|
* to preserve the owner died bit here.
|
||||||
|
*
|
||||||
|
* Note: We write the user space value _before_ changing the
|
||||||
|
* pi_state because we can fault here. Imagine swapped out
|
||||||
|
* pages or a fork, which was running right before we acquired
|
||||||
|
* mmap_sem, that marked all the anonymous memory readonly for
|
||||||
|
* cow.
|
||||||
|
*
|
||||||
|
* Modifying pi_state _before_ the user space value would
|
||||||
|
* leave the pi_state in an inconsistent state when we fault
|
||||||
|
* here, because we need to drop the hash bucket lock to
|
||||||
|
* handle the fault. This might be observed in the PID check
|
||||||
|
* in lookup_pi_state.
|
||||||
|
*/
|
||||||
|
retry:
|
||||||
|
if (get_futex_value_locked(&uval, uaddr))
|
||||||
|
goto handle_fault;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
newval = (uval & FUTEX_OWNER_DIED) | newtid;
|
||||||
|
|
||||||
|
curval = cmpxchg_futex_value_locked(uaddr, uval, newval);
|
||||||
|
|
||||||
|
if (curval == -EFAULT)
|
||||||
|
goto handle_fault;
|
||||||
|
if (curval == uval)
|
||||||
|
break;
|
||||||
|
uval = curval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We fixed up user space. Now we need to fix the pi_state
|
||||||
|
* itself.
|
||||||
|
*/
|
||||||
if (pi_state->owner != NULL) {
|
if (pi_state->owner != NULL) {
|
||||||
spin_lock_irq(&pi_state->owner->pi_lock);
|
spin_lock_irq(&pi_state->owner->pi_lock);
|
||||||
WARN_ON(list_empty(&pi_state->list));
|
WARN_ON(list_empty(&pi_state->list));
|
||||||
list_del_init(&pi_state->list);
|
list_del_init(&pi_state->list);
|
||||||
spin_unlock_irq(&pi_state->owner->pi_lock);
|
spin_unlock_irq(&pi_state->owner->pi_lock);
|
||||||
} else
|
}
|
||||||
newtid |= FUTEX_OWNER_DIED;
|
|
||||||
|
|
||||||
pi_state->owner = newowner;
|
pi_state->owner = newowner;
|
||||||
|
|
||||||
|
@ -1118,26 +1161,35 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
|
||||||
WARN_ON(!list_empty(&pi_state->list));
|
WARN_ON(!list_empty(&pi_state->list));
|
||||||
list_add(&pi_state->list, &newowner->pi_state_list);
|
list_add(&pi_state->list, &newowner->pi_state_list);
|
||||||
spin_unlock_irq(&newowner->pi_lock);
|
spin_unlock_irq(&newowner->pi_lock);
|
||||||
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We own it, so we have to replace the pending owner
|
* To handle the page fault we need to drop the hash bucket
|
||||||
* TID. This must be atomic as we have preserve the
|
* lock here. That gives the other task (either the pending
|
||||||
* owner died bit here.
|
* owner 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 hash bucket lock and before trying to
|
||||||
|
* do another fixup. When the fixup has been done already we
|
||||||
|
* simply return.
|
||||||
*/
|
*/
|
||||||
ret = get_futex_value_locked(&uval, uaddr);
|
handle_fault:
|
||||||
|
spin_unlock(q->lock_ptr);
|
||||||
|
|
||||||
while (!ret) {
|
ret = futex_handle_fault((unsigned long)uaddr, fshared, attempt++);
|
||||||
newval = (uval & FUTEX_OWNER_DIED) | newtid;
|
|
||||||
|
|
||||||
curval = cmpxchg_futex_value_locked(uaddr, uval, newval);
|
spin_lock(q->lock_ptr);
|
||||||
|
|
||||||
if (curval == -EFAULT)
|
/*
|
||||||
ret = -EFAULT;
|
* Check if someone else fixed it for us:
|
||||||
if (curval == uval)
|
*/
|
||||||
break;
|
if (pi_state->owner != oldowner)
|
||||||
uval = curval;
|
return 0;
|
||||||
}
|
|
||||||
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1507,7 +1559,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
||||||
* that case:
|
* that case:
|
||||||
*/
|
*/
|
||||||
if (q.pi_state->owner != curr)
|
if (q.pi_state->owner != curr)
|
||||||
ret = fixup_pi_state_owner(uaddr, &q, curr);
|
ret = fixup_pi_state_owner(uaddr, &q, curr, fshared);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Catch the rare case, where the lock was released
|
* Catch the rare case, where the lock was released
|
||||||
|
@ -1539,7 +1591,8 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
owner = rt_mutex_owner(&q.pi_state->pi_mutex);
|
owner = rt_mutex_owner(&q.pi_state->pi_mutex);
|
||||||
res = fixup_pi_state_owner(uaddr, &q, owner);
|
res = fixup_pi_state_owner(uaddr, &q, owner,
|
||||||
|
fshared);
|
||||||
|
|
||||||
/* propagate -EFAULT, if the fixup failed */
|
/* propagate -EFAULT, if the fixup failed */
|
||||||
if (res)
|
if (res)
|
||||||
|
|
Loading…
Reference in New Issue