Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[safe/jmp/linux-2.6] / kernel / futex.c
index e149545..d546b2d 100644 (file)
@@ -193,6 +193,7 @@ static void drop_futex_key_refs(union futex_key *key)
  * @uaddr: virtual address of the futex
  * @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED
  * @key: address where result is stored.
+ * @rw: mapping needs to be read/write (values: VERIFY_READ, VERIFY_WRITE)
  *
  * Returns a negative error code or 0
  * The key words are stored in *key on success.
@@ -203,7 +204,8 @@ static void drop_futex_key_refs(union futex_key *key)
  *
  * lock_page() might sleep, the caller should not hold a spinlock.
  */
-static int get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
+static int
+get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw)
 {
        unsigned long address = (unsigned long)uaddr;
        struct mm_struct *mm = current->mm;
@@ -226,7 +228,7 @@ static int get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
         *        but access_ok() should be faster than find_vma()
         */
        if (!fshared) {
-               if (unlikely(!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))))
+               if (unlikely(!access_ok(rw, uaddr, sizeof(u32))))
                        return -EFAULT;
                key->private.mm = mm;
                key->private.address = address;
@@ -235,7 +237,7 @@ static int get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
        }
 
 again:
-       err = get_user_pages_fast(address, 1, 0, &page);
+       err = get_user_pages_fast(address, 1, rw == VERIFY_WRITE, &page);
        if (err < 0)
                return err;
 
@@ -298,41 +300,6 @@ static int get_futex_value_locked(u32 *dest, u32 __user *from)
        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:
@@ -693,14 +660,9 @@ double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
 static inline void
 double_unlock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
 {
-       if (hb1 <= hb2) {
-               spin_unlock(&hb2->lock);
-               if (hb1 < hb2)
-                       spin_unlock(&hb1->lock);
-       } else { /* hb1 > hb2 */
-               spin_unlock(&hb1->lock);
+       spin_unlock(&hb1->lock);
+       if (hb1 != hb2)
                spin_unlock(&hb2->lock);
-       }
 }
 
 /*
@@ -717,7 +679,7 @@ static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset)
        if (!bitset)
                return -EINVAL;
 
-       ret = get_futex_key(uaddr, fshared, &key);
+       ret = get_futex_key(uaddr, fshared, &key, VERIFY_READ);
        if (unlikely(ret != 0))
                goto out;
 
@@ -760,22 +722,21 @@ futex_wake_op(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
        struct futex_hash_bucket *hb1, *hb2;
        struct plist_head *head;
        struct futex_q *this, *next;
-       int ret, op_ret, attempt = 0;
+       int ret, op_ret;
 
-retryfull:
-       ret = get_futex_key(uaddr1, fshared, &key1);
+retry:
+       ret = get_futex_key(uaddr1, fshared, &key1, VERIFY_READ);
        if (unlikely(ret != 0))
                goto out;
-       ret = get_futex_key(uaddr2, fshared, &key2);
+       ret = get_futex_key(uaddr2, fshared, &key2, VERIFY_WRITE);
        if (unlikely(ret != 0))
                goto out_put_key1;
 
        hb1 = hash_futex(&key1);
        hb2 = hash_futex(&key2);
 
-retry:
        double_lock_hb(hb1, hb2);
-
+retry_private:
        op_ret = futex_atomic_op_inuser(op, uaddr2);
        if (unlikely(op_ret < 0)) {
                u32 dummy;
@@ -796,28 +757,16 @@ retry:
                        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);
                if (ret)
                        goto out_put_keys;
 
+               if (!fshared)
+                       goto retry_private;
+
                put_futex_key(fshared, &key2);
                put_futex_key(fshared, &key1);
-               goto retryfull;
+               goto retry;
        }
 
        head = &hb1->chain;
@@ -867,16 +816,17 @@ static int futex_requeue(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
        int ret, drop_count = 0;
 
 retry:
-       ret = get_futex_key(uaddr1, fshared, &key1);
+       ret = get_futex_key(uaddr1, fshared, &key1, VERIFY_READ);
        if (unlikely(ret != 0))
                goto out;
-       ret = get_futex_key(uaddr2, fshared, &key2);
+       ret = get_futex_key(uaddr2, fshared, &key2, VERIFY_READ);
        if (unlikely(ret != 0))
                goto out_put_key1;
 
        hb1 = hash_futex(&key1);
        hb2 = hash_futex(&key2);
 
+retry_private:
        double_lock_hb(hb1, hb2);
 
        if (likely(cmpval != NULL)) {
@@ -887,15 +837,16 @@ retry:
                if (unlikely(ret)) {
                        double_unlock_hb(hb1, hb2);
 
-                       put_futex_key(fshared, &key2);
-                       put_futex_key(fshared, &key1);
-
                        ret = get_user(curval, uaddr1);
+                       if (ret)
+                               goto out_put_keys;
 
-                       if (!ret)
-                               goto retry;
+                       if (!fshared)
+                               goto retry_private;
 
-                       goto out_put_keys;
+                       put_futex_key(fshared, &key2);
+                       put_futex_key(fshared, &key1);
+                       goto retry;
                }
                if (curval != *cmpval) {
                        ret = -EAGAIN;
@@ -934,7 +885,12 @@ retry:
 out_unlock:
        double_unlock_hb(hb1, hb2);
 
-       /* drop_futex_key_refs() must be called outside the spinlocks. */
+       /*
+        * drop_futex_key_refs() must be called outside the spinlocks. During
+        * the requeue we moved futex_q's from the hash bucket at key1 to the
+        * one at key2 and updated their key pointer.  We no longer need to
+        * hold the references to key1.
+        */
        while (--drop_count >= 0)
                drop_futex_key_refs(&key1);
 
@@ -1070,7 +1026,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
        struct futex_pi_state *pi_state = q->pi_state;
        struct task_struct *oldowner = pi_state->owner;
        u32 uval, curval, newval;
-       int ret, attempt = 0;
+       int ret;
 
        /* Owner died? */
        if (!pi_state->owner)
@@ -1141,7 +1097,7 @@ retry:
 handle_fault:
        spin_unlock(q->lock_ptr);
 
-       ret = futex_handle_fault((unsigned long)uaddr, attempt++);
+       ret = get_user(uval, uaddr);
 
        spin_lock(q->lock_ptr);
 
@@ -1186,10 +1142,11 @@ static int futex_wait(u32 __user *uaddr, int fshared,
        q.bitset = bitset;
 retry:
        q.key = FUTEX_KEY_INIT;
-       ret = get_futex_key(uaddr, fshared, &q.key);
+       ret = get_futex_key(uaddr, fshared, &q.key, VERIFY_READ);
        if (unlikely(ret != 0))
                goto out;
 
+retry_private:
        hb = queue_lock(&q);
 
        /*
@@ -1216,13 +1173,16 @@ retry:
 
        if (unlikely(ret)) {
                queue_unlock(&q, hb);
-               put_futex_key(fshared, &q.key);
 
                ret = get_user(uval, uaddr);
+               if (ret)
+                       goto out_put_key;
 
-               if (!ret)
-                       goto retry;
-               goto out;
+               if (!fshared)
+                       goto retry_private;
+
+               put_futex_key(fshared, &q.key);
+               goto retry;
        }
        ret = -EWOULDBLOCK;
        if (unlikely(uval != val)) {
@@ -1253,16 +1213,13 @@ retry:
                if (!abs_time)
                        schedule();
                else {
-                       unsigned long slack;
-                       slack = current->timer_slack_ns;
-                       if (rt_task(current))
-                               slack = 0;
                        hrtimer_init_on_stack(&t.timer,
                                              clockrt ? CLOCK_REALTIME :
                                              CLOCK_MONOTONIC,
                                              HRTIMER_MODE_ABS);
                        hrtimer_init_sleeper(&t, current);
-                       hrtimer_set_expires_range_ns(&t.timer, *abs_time, slack);
+                       hrtimer_set_expires_range_ns(&t.timer, *abs_time,
+                                                    current->timer_slack_ns);
 
                        hrtimer_start_expires(&t.timer, HRTIMER_MODE_ABS);
                        if (!hrtimer_active(&t.timer))
@@ -1359,7 +1316,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
        struct futex_hash_bucket *hb;
        u32 uval, newval, curval;
        struct futex_q q;
-       int ret, lock_taken, ownerdied = 0, attempt = 0;
+       int ret, lock_taken, ownerdied = 0;
 
        if (refill_pi_state_cache())
                return -ENOMEM;
@@ -1375,11 +1332,11 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
        q.pi_state = NULL;
 retry:
        q.key = FUTEX_KEY_INIT;
-       ret = get_futex_key(uaddr, fshared, &q.key);
+       ret = get_futex_key(uaddr, fshared, &q.key, VERIFY_WRITE);
        if (unlikely(ret != 0))
                goto out;
 
-retry_unlocked:
+retry_private:
        hb = queue_lock(&q);
 
 retry_locked:
@@ -1570,6 +1527,13 @@ retry_locked:
                }
        }
 
+       /*
+        * If fixup_pi_state_owner() faulted and was unable to handle the
+        * fault, unlock it and return the fault to userspace.
+        */
+       if (ret && (rt_mutex_owner(&q.pi_state->pi_mutex) == current))
+               rt_mutex_unlock(&q.pi_state->pi_mutex);
+
        /* Unqueue and drop the lock */
        unqueue_me_pi(&q);
 
@@ -1597,18 +1561,15 @@ uaddr_faulted:
         */
        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);
-       if (!ret)
-               goto retry_unlocked;
+       if (ret)
+               goto out_put_key;
+
+       if (!fshared)
+               goto retry_private;
 
-       goto out_put_key;
+       put_futex_key(fshared, &q.key);
+       goto retry;
 }
 
 
@@ -1624,7 +1585,7 @@ static int futex_unlock_pi(u32 __user *uaddr, int fshared)
        u32 uval;
        struct plist_head *head;
        union futex_key key = FUTEX_KEY_INIT;
-       int ret, attempt = 0;
+       int ret;
 
 retry:
        if (get_user(uval, uaddr))
@@ -1635,12 +1596,11 @@ retry:
        if ((uval & FUTEX_TID_MASK) != task_pid_vnr(current))
                return -EPERM;
 
-       ret = get_futex_key(uaddr, fshared, &key);
+       ret = get_futex_key(uaddr, fshared, &key, VERIFY_WRITE);
        if (unlikely(ret != 0))
                goto out;
 
        hb = hash_futex(&key);
-retry_unlocked:
        spin_lock(&hb->lock);
 
        /*
@@ -1705,17 +1665,9 @@ pi_faulted:
         * we have to drop the mmap_sem in order to call get_user().
         */
        spin_unlock(&hb->lock);
-
-       if (attempt++) {
-               ret = futex_handle_fault((unsigned long)uaddr, attempt);
-               if (ret)
-                       goto out;
-               uval = 0;
-               goto retry_unlocked;
-       }
+       put_futex_key(fshared, &key);
 
        ret = get_user(uval, uaddr);
-       put_futex_key(fshared, &key);
        if (!ret)
                goto retry;