futex: clean up fault logic

Impact: cleanup

Older versions of the futex code held the mmap_sem which had to
be dropped in order to call get_user(), so a two-pronged fault
handling mechanism was employed to handle faults of the atomic
operations.  The mmap_sem is no longer held, so get_user()
should be adequate.  This patch greatly simplifies the logic and
improves legibility.

Build and boot tested on a 4 way Intel x86_64 workstation.
Passes basic pthread_mutex and PI tests out of
ltp/testcases/realtime.

Signed-off-by: Darren Hart <dvhltc@us.ibm.com>
Acked-by: Peter Zijlstra <peterz@infradead.org>
Cc: Rusty Russell <rusty@rustcorp.com.au>
LKML-Reference: <20090312075612.9856.48612.stgit@Aeon>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Darren Hart 2009-03-12 00:56:13 -07:00 committed by Ingo Molnar
parent e8f6386c01
commit e4dc5b7a36

View file

@ -298,41 +298,6 @@ static int get_futex_value_locked(u32 *dest, u32 __user *from)
return ret ? -EFAULT : 0; return ret ? -EFAULT : 0;
} }
/*
* Fault handling.
*/
static int futex_handle_fault(unsigned long address, int attempt)
{
struct vm_area_struct * vma;
struct mm_struct *mm = current->mm;
int ret = -EFAULT;
if (attempt > 2)
return ret;
down_read(&mm->mmap_sem);
vma = find_vma(mm, address);
if (vma && address >= vma->vm_start &&
(vma->vm_flags & VM_WRITE)) {
int fault;
fault = handle_mm_fault(mm, vma, address, 1);
if (unlikely((fault & VM_FAULT_ERROR))) {
#if 0
/* XXX: let's do this when we verify it is OK */
if (ret & VM_FAULT_OOM)
ret = -ENOMEM;
#endif
} else {
ret = 0;
if (fault & VM_FAULT_MAJOR)
current->maj_flt++;
else
current->min_flt++;
}
}
up_read(&mm->mmap_sem);
return ret;
}
/* /*
* PI code: * PI code:
@ -760,9 +725,9 @@ futex_wake_op(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
struct futex_hash_bucket *hb1, *hb2; struct futex_hash_bucket *hb1, *hb2;
struct plist_head *head; struct plist_head *head;
struct futex_q *this, *next; struct futex_q *this, *next;
int ret, op_ret, attempt = 0; int ret, op_ret;
retryfull: retry:
ret = get_futex_key(uaddr1, fshared, &key1); ret = get_futex_key(uaddr1, fshared, &key1);
if (unlikely(ret != 0)) if (unlikely(ret != 0))
goto out; goto out;
@ -773,9 +738,8 @@ retryfull:
hb1 = hash_futex(&key1); hb1 = hash_futex(&key1);
hb2 = hash_futex(&key2); hb2 = hash_futex(&key2);
retry:
double_lock_hb(hb1, hb2); double_lock_hb(hb1, hb2);
retry_private:
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)) {
u32 dummy; u32 dummy;
@ -796,28 +760,16 @@ retry:
goto out_put_keys; goto out_put_keys;
} }
/*
* futex_atomic_op_inuser needs to both read and write
* *(int __user *)uaddr2, but we can't modify it
* non-atomically. Therefore, if get_user below is not
* enough, we need to handle the fault ourselves, while
* still holding the mmap_sem.
*/
if (attempt++) {
ret = futex_handle_fault((unsigned long)uaddr2,
attempt);
if (ret)
goto out_put_keys;
goto retry;
}
ret = get_user(dummy, uaddr2); ret = get_user(dummy, uaddr2);
if (ret) if (ret)
goto out_put_keys; goto out_put_keys;
if (!fshared)
goto retry_private;
put_futex_key(fshared, &key2); put_futex_key(fshared, &key2);
put_futex_key(fshared, &key1); put_futex_key(fshared, &key1);
goto retryfull; goto retry;
} }
head = &hb1->chain; head = &hb1->chain;
@ -877,6 +829,7 @@ retry:
hb1 = hash_futex(&key1); hb1 = hash_futex(&key1);
hb2 = hash_futex(&key2); hb2 = hash_futex(&key2);
retry_private:
double_lock_hb(hb1, hb2); double_lock_hb(hb1, hb2);
if (likely(cmpval != NULL)) { if (likely(cmpval != NULL)) {
@ -887,15 +840,16 @@ retry:
if (unlikely(ret)) { if (unlikely(ret)) {
double_unlock_hb(hb1, hb2); double_unlock_hb(hb1, hb2);
ret = get_user(curval, uaddr1);
if (ret)
goto out_put_keys;
if (!fshared)
goto retry_private;
put_futex_key(fshared, &key2); put_futex_key(fshared, &key2);
put_futex_key(fshared, &key1); put_futex_key(fshared, &key1);
goto retry;
ret = get_user(curval, uaddr1);
if (!ret)
goto retry;
goto out_put_keys;
} }
if (curval != *cmpval) { if (curval != *cmpval) {
ret = -EAGAIN; ret = -EAGAIN;
@ -1070,7 +1024,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
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; struct task_struct *oldowner = pi_state->owner;
u32 uval, curval, newval; u32 uval, curval, newval;
int ret, attempt = 0; int ret;
/* Owner died? */ /* Owner died? */
if (!pi_state->owner) if (!pi_state->owner)
@ -1141,7 +1095,7 @@ retry:
handle_fault: handle_fault:
spin_unlock(q->lock_ptr); spin_unlock(q->lock_ptr);
ret = futex_handle_fault((unsigned long)uaddr, attempt++); ret = get_user(uval, uaddr);
spin_lock(q->lock_ptr); spin_lock(q->lock_ptr);
@ -1190,6 +1144,7 @@ retry:
if (unlikely(ret != 0)) if (unlikely(ret != 0))
goto out; goto out;
retry_private:
hb = queue_lock(&q); hb = queue_lock(&q);
/* /*
@ -1216,13 +1171,16 @@ retry:
if (unlikely(ret)) { if (unlikely(ret)) {
queue_unlock(&q, hb); queue_unlock(&q, hb);
put_futex_key(fshared, &q.key);
ret = get_user(uval, uaddr); ret = get_user(uval, uaddr);
if (ret)
goto out_put_key;
if (!ret) if (!fshared)
goto retry; goto retry_private;
goto out;
put_futex_key(fshared, &q.key);
goto retry;
} }
ret = -EWOULDBLOCK; ret = -EWOULDBLOCK;
if (unlikely(uval != val)) { if (unlikely(uval != val)) {
@ -1356,7 +1314,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
struct futex_hash_bucket *hb; struct futex_hash_bucket *hb;
u32 uval, newval, curval; u32 uval, newval, curval;
struct futex_q q; struct futex_q q;
int ret, lock_taken, ownerdied = 0, attempt = 0; int ret, lock_taken, ownerdied = 0;
if (refill_pi_state_cache()) if (refill_pi_state_cache())
return -ENOMEM; return -ENOMEM;
@ -1376,7 +1334,7 @@ retry:
if (unlikely(ret != 0)) if (unlikely(ret != 0))
goto out; goto out;
retry_unlocked: retry_private:
hb = queue_lock(&q); hb = queue_lock(&q);
retry_locked: retry_locked:
@ -1601,18 +1559,15 @@ uaddr_faulted:
*/ */
queue_unlock(&q, hb); queue_unlock(&q, hb);
if (attempt++) {
ret = futex_handle_fault((unsigned long)uaddr, attempt);
if (ret)
goto out_put_key;
goto retry_unlocked;
}
ret = get_user(uval, uaddr); ret = get_user(uval, uaddr);
if (!ret) if (ret)
goto retry_unlocked; goto out_put_key;
goto out_put_key; if (!fshared)
goto retry_private;
put_futex_key(fshared, &q.key);
goto retry;
} }
@ -1628,7 +1583,7 @@ static int futex_unlock_pi(u32 __user *uaddr, int fshared)
u32 uval; u32 uval;
struct plist_head *head; struct plist_head *head;
union futex_key key = FUTEX_KEY_INIT; union futex_key key = FUTEX_KEY_INIT;
int ret, attempt = 0; int ret;
retry: retry:
if (get_user(uval, uaddr)) if (get_user(uval, uaddr))
@ -1644,7 +1599,6 @@ retry:
goto out; goto out;
hb = hash_futex(&key); hb = hash_futex(&key);
retry_unlocked:
spin_lock(&hb->lock); spin_lock(&hb->lock);
/* /*
@ -1709,17 +1663,9 @@ pi_faulted:
* we have to drop the mmap_sem in order to call get_user(). * we have to drop the mmap_sem in order to call get_user().
*/ */
spin_unlock(&hb->lock); spin_unlock(&hb->lock);
put_futex_key(fshared, &key);
if (attempt++) {
ret = futex_handle_fault((unsigned long)uaddr, attempt);
if (ret)
goto out;
uval = 0;
goto retry_unlocked;
}
ret = get_user(uval, uaddr); ret = get_user(uval, uaddr);
put_futex_key(fshared, &key);
if (!ret) if (!ret)
goto retry; goto retry;