<linux/seccomp.h> needs to include <linux/errno.h>.
[safe/jmp/linux-2.6] / kernel / futex.c
index 7c6cbab..eef8cd2 100644 (file)
@@ -114,7 +114,9 @@ struct futex_q {
 };
 
 /*
- * Split the global futex_lock into every hash list lock.
+ * Hash buckets are shared by all the futex_keys that hash to the same
+ * location.  Each key may have multiple futex_q structures, one for each task
+ * waiting on a futex.
  */
 struct futex_hash_bucket {
        spinlock_t lock;
@@ -170,8 +172,11 @@ static void get_futex_key_refs(union futex_key *key)
  */
 static void drop_futex_key_refs(union futex_key *key)
 {
-       if (!key->both.ptr)
+       if (!key->both.ptr) {
+               /* If we're here then we tried to put a key we failed to get */
+               WARN_ON_ONCE(1);
                return;
+       }
 
        switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) {
        case FUT_OFF_INODE:
@@ -186,8 +191,7 @@ static void drop_futex_key_refs(union futex_key *key)
 /**
  * get_futex_key - Get parameters which are the keys for a futex.
  * @uaddr: virtual address of the futex
- * @shared: NULL for a PROCESS_PRIVATE futex,
- *     &current->mm->mmap_sem for a PROCESS_SHARED futex
+ * @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED
  * @key: address where result is stored.
  *
  * Returns a negative error code or 0
@@ -197,9 +201,7 @@ static void drop_futex_key_refs(union futex_key *key)
  * offset_within_page).  For private mappings, it's (uaddr, current->mm).
  * We can usually work out the index without swapping in the page.
  *
- * fshared is NULL for PROCESS_PRIVATE futexes
- * For other futexes, it points to &current->mm->mmap_sem and
- * caller must have taken the reader lock. but NOT any spinlocks.
+ * 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)
 {
@@ -296,41 +298,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:
@@ -586,10 +553,9 @@ static void wake_futex(struct futex_q *q)
         * The waiting task can free the futex_q as soon as this is written,
         * without taking any locks.  This must come last.
         *
-        * A memory barrier is required here to prevent the following store
-        * to lock_ptr from getting ahead of the wakeup. Clearing the lock
-        * at the end of wake_up_all() does not prevent this store from
-        * moving.
+        * A memory barrier is required here to prevent the following store to
+        * lock_ptr from getting ahead of the wakeup. Clearing the lock at the
+        * end of wake_up() does not prevent this store from moving.
         */
        smp_wmb();
        q->lock_ptr = NULL;
@@ -689,9 +655,16 @@ 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)
+{
+       spin_unlock(&hb1->lock);
+       if (hb1 != hb2)
+               spin_unlock(&hb2->lock);
+}
+
 /*
- * Wake up all waiters hashed on the physical page that is mapped
- * to this virtual address:
+ * Wake up waiters matching bitset queued on this futex (uaddr).
  */
 static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset)
 {
@@ -730,8 +703,8 @@ static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset)
        }
 
        spin_unlock(&hb->lock);
-out:
        put_futex_key(fshared, &key);
+out:
        return ret;
 }
 
@@ -747,29 +720,26 @@ 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:
+retry:
        ret = get_futex_key(uaddr1, fshared, &key1);
        if (unlikely(ret != 0))
                goto out;
        ret = get_futex_key(uaddr2, fshared, &key2);
        if (unlikely(ret != 0))
-               goto out;
+               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;
 
-               spin_unlock(&hb1->lock);
-               if (hb1 != hb2)
-                       spin_unlock(&hb2->lock);
+               double_unlock_hb(hb1, hb2);
 
 #ifndef CONFIG_MMU
                /*
@@ -777,34 +747,24 @@ retry:
                 * but we might get them from range checking
                 */
                ret = op_ret;
-               goto out;
+               goto out_put_keys;
 #endif
 
                if (unlikely(op_ret != -EFAULT)) {
                        ret = op_ret;
-                       goto out;
-               }
-
-               /*
-                * 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;
-                       goto retry;
+                       goto out_put_keys;
                }
 
                ret = get_user(dummy, uaddr2);
                if (ret)
-                       return ret;
+                       goto out_put_keys;
+
+               if (!fshared)
+                       goto retry_private;
 
-               goto retryfull;
+               put_futex_key(fshared, &key2);
+               put_futex_key(fshared, &key1);
+               goto retry;
        }
 
        head = &hb1->chain;
@@ -831,13 +791,12 @@ retry:
                ret += op_ret;
        }
 
-       spin_unlock(&hb1->lock);
-       if (hb1 != hb2)
-               spin_unlock(&hb2->lock);
-out:
+       double_unlock_hb(hb1, hb2);
+out_put_keys:
        put_futex_key(fshared, &key2);
+out_put_key1:
        put_futex_key(fshared, &key1);
-
+out:
        return ret;
 }
 
@@ -854,17 +813,18 @@ static int futex_requeue(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
        struct futex_q *this, *next;
        int ret, drop_count = 0;
 
- retry:
+retry:
        ret = get_futex_key(uaddr1, fshared, &key1);
        if (unlikely(ret != 0))
                goto out;
        ret = get_futex_key(uaddr2, fshared, &key2);
        if (unlikely(ret != 0))
-               goto out;
+               goto out_put_key1;
 
        hb1 = hash_futex(&key1);
        hb2 = hash_futex(&key2);
 
+retry_private:
        double_lock_hb(hb1, hb2);
 
        if (likely(cmpval != NULL)) {
@@ -873,16 +833,18 @@ static int futex_requeue(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
                ret = get_futex_value_locked(&curval, uaddr1);
 
                if (unlikely(ret)) {
-                       spin_unlock(&hb1->lock);
-                       if (hb1 != hb2)
-                               spin_unlock(&hb2->lock);
+                       double_unlock_hb(hb1, hb2);
 
                        ret = get_user(curval, uaddr1);
+                       if (ret)
+                               goto out_put_keys;
 
-                       if (!ret)
-                               goto retry;
+                       if (!fshared)
+                               goto retry_private;
 
-                       return ret;
+                       put_futex_key(fshared, &key2);
+                       put_futex_key(fshared, &key1);
+                       goto retry;
                }
                if (curval != *cmpval) {
                        ret = -EAGAIN;
@@ -919,17 +881,22 @@ static int futex_requeue(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
        }
 
 out_unlock:
-       spin_unlock(&hb1->lock);
-       if (hb1 != hb2)
-               spin_unlock(&hb2->lock);
+       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);
 
-out:
+out_put_keys:
        put_futex_key(fshared, &key2);
+out_put_key1:
        put_futex_key(fshared, &key1);
+out:
        return ret;
 }
 
@@ -990,7 +957,7 @@ static int unqueue_me(struct futex_q *q)
        int ret = 0;
 
        /* In the common case we don't take the spinlock, which is nice. */
- retry:
+retry:
        lock_ptr = q->lock_ptr;
        barrier();
        if (lock_ptr != NULL) {
@@ -1057,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 task_struct *oldowner = pi_state->owner;
        u32 uval, curval, newval;
-       int ret, attempt = 0;
+       int ret;
 
        /* Owner died? */
        if (!pi_state->owner)
@@ -1070,11 +1037,9 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
         * 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.
+        * Note: We write the user space value _before_ changing the pi_state
+        * because we can fault here. Imagine swapped out pages or a fork
+        * 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
@@ -1130,7 +1095,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);
 
@@ -1159,6 +1124,7 @@ static int futex_wait(u32 __user *uaddr, int fshared,
                      u32 val, ktime_t *abs_time, u32 bitset, int clockrt)
 {
        struct task_struct *curr = current;
+       struct restart_block *restart;
        DECLARE_WAITQUEUE(wait, curr);
        struct futex_hash_bucket *hb;
        struct futex_q q;
@@ -1172,16 +1138,17 @@ static int futex_wait(u32 __user *uaddr, int fshared,
 
        q.pi_state = NULL;
        q.bitset = bitset;
- retry:
+retry:
        q.key = FUTEX_KEY_INIT;
        ret = get_futex_key(uaddr, fshared, &q.key);
        if (unlikely(ret != 0))
-               goto out_release_sem;
+               goto out;
 
+retry_private:
        hb = queue_lock(&q);
 
        /*
-        * Access the page AFTER the futex is queued.
+        * Access the page AFTER the hash-bucket is locked.
         * Order is important:
         *
         *   Userspace waiter: val = var; if (cond(val)) futex_wait(&var, val);
@@ -1197,7 +1164,7 @@ static int futex_wait(u32 __user *uaddr, int fshared,
         * a wakeup when *uaddr != val on entry to the syscall.  This is
         * rare, but normal.
         *
-        * for shared futexes, we hold the mmap semaphore, so the mapping
+        * For shared futexes, we hold the mmap semaphore, so the mapping
         * cannot have changed since we looked it up in get_futex_key.
         */
        ret = get_futex_value_locked(&uval, uaddr);
@@ -1206,14 +1173,20 @@ static int futex_wait(u32 __user *uaddr, int fshared,
                queue_unlock(&q, hb);
 
                ret = get_user(uval, uaddr);
+               if (ret)
+                       goto out_put_key;
 
-               if (!ret)
-                       goto retry;
-               return ret;
+               if (!fshared)
+                       goto retry_private;
+
+               put_futex_key(fshared, &q.key);
+               goto retry;
        }
        ret = -EWOULDBLOCK;
-       if (uval != val)
-               goto out_unlock_release_sem;
+       if (unlikely(uval != val)) {
+               queue_unlock(&q, hb);
+               goto out_put_key;
+       }
 
        /* Only actually queue if *uaddr contained val.  */
        queue_me(&q, hb);
@@ -1238,16 +1211,13 @@ static int futex_wait(u32 __user *uaddr, int fshared,
                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))
@@ -1277,39 +1247,39 @@ static int futex_wait(u32 __user *uaddr, int fshared,
         */
 
        /* If we were woken (and unqueued), we succeeded, whatever. */
+       ret = 0;
        if (!unqueue_me(&q))
-               return 0;
+               goto out_put_key;
+       ret = -ETIMEDOUT;
        if (rem)
-               return -ETIMEDOUT;
+               goto out_put_key;
 
        /*
         * We expect signal_pending(current), but another thread may
         * have handled it for us already.
         */
+       ret = -ERESTARTSYS;
        if (!abs_time)
-               return -ERESTARTSYS;
-       else {
-               struct restart_block *restart;
-               restart = &current_thread_info()->restart_block;
-               restart->fn = futex_wait_restart;
-               restart->futex.uaddr = (u32 *)uaddr;
-               restart->futex.val = val;
-               restart->futex.time = abs_time->tv64;
-               restart->futex.bitset = bitset;
-               restart->futex.flags = 0;
-
-               if (fshared)
-                       restart->futex.flags |= FLAGS_SHARED;
-               if (clockrt)
-                       restart->futex.flags |= FLAGS_CLOCKRT;
-               return -ERESTART_RESTARTBLOCK;
-       }
+               goto out_put_key;
 
- out_unlock_release_sem:
-       queue_unlock(&q, hb);
+       restart = &current_thread_info()->restart_block;
+       restart->fn = futex_wait_restart;
+       restart->futex.uaddr = (u32 *)uaddr;
+       restart->futex.val = val;
+       restart->futex.time = abs_time->tv64;
+       restart->futex.bitset = bitset;
+       restart->futex.flags = 0;
+
+       if (fshared)
+               restart->futex.flags |= FLAGS_SHARED;
+       if (clockrt)
+               restart->futex.flags |= FLAGS_CLOCKRT;
 
- out_release_sem:
+       ret = -ERESTART_RESTARTBLOCK;
+
+out_put_key:
        put_futex_key(fshared, &q.key);
+out:
        return ret;
 }
 
@@ -1344,7 +1314,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;
@@ -1358,16 +1328,16 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
        }
 
        q.pi_state = NULL;
- retry:
+retry:
        q.key = FUTEX_KEY_INIT;
        ret = get_futex_key(uaddr, fshared, &q.key);
        if (unlikely(ret != 0))
-               goto out_release_sem;
+               goto out;
 
- retry_unlocked:
+retry_private:
        hb = queue_lock(&q);
 
- retry_locked:
+retry_locked:
        ret = lock_taken = 0;
 
        /*
@@ -1388,14 +1358,14 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
         */
        if (unlikely((curval & FUTEX_TID_MASK) == task_pid_vnr(current))) {
                ret = -EDEADLK;
-               goto out_unlock_release_sem;
+               goto out_unlock_put_key;
        }
 
        /*
         * Surprise - we got the lock. Just return to userspace:
         */
        if (unlikely(!curval))
-               goto out_unlock_release_sem;
+               goto out_unlock_put_key;
 
        uval = curval;
 
@@ -1431,7 +1401,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
         * We took the lock due to owner died take over.
         */
        if (unlikely(lock_taken))
-               goto out_unlock_release_sem;
+               goto out_unlock_put_key;
 
        /*
         * We dont have the lock. Look up the PI state (or create it if
@@ -1448,6 +1418,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
                         * exit to complete.
                         */
                        queue_unlock(&q, hb);
+                       put_futex_key(fshared, &q.key);
                        cond_resched();
                        goto retry;
 
@@ -1470,7 +1441,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
                                goto retry_locked;
                        }
                default:
-                       goto out_unlock_release_sem;
+                       goto out_unlock_put_key;
                }
        }
 
@@ -1554,6 +1525,13 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
                }
        }
 
+       /*
+        * 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);
 
@@ -1561,16 +1539,17 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
                destroy_hrtimer_on_stack(&to->timer);
        return ret != -EINTR ? ret : -ERESTARTNOINTR;
 
- out_unlock_release_sem:
+out_unlock_put_key:
        queue_unlock(&q, hb);
 
- out_release_sem:
+out_put_key:
        put_futex_key(fshared, &q.key);
+out:
        if (to)
                destroy_hrtimer_on_stack(&to->timer);
        return ret;
 
- uaddr_faulted:
+uaddr_faulted:
        /*
         * We have to r/w  *(int __user *)uaddr, and we have to modify it
         * atomically.  Therefore, if we continue to fault after get_user()
@@ -1580,22 +1559,18 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
         */
        queue_unlock(&q, hb);
 
-       if (attempt++) {
-               ret = futex_handle_fault((unsigned long)uaddr, attempt);
-               if (ret)
-                       goto out_release_sem;
-               goto retry_unlocked;
-       }
-
        ret = get_user(uval, uaddr);
-       if (!ret)
-               goto retry;
+       if (ret)
+               goto out_put_key;
 
-       if (to)
-               destroy_hrtimer_on_stack(&to->timer);
-       return ret;
+       if (!fshared)
+               goto retry_private;
+
+       put_futex_key(fshared, &q.key);
+       goto retry;
 }
 
+
 /*
  * Userspace attempted a TID -> 0 atomic transition, and failed.
  * This is the in-kernel slowpath: we look up the PI state (if any),
@@ -1608,7 +1583,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))
@@ -1624,7 +1599,6 @@ retry:
                goto out;
 
        hb = hash_futex(&key);
-retry_unlocked:
        spin_lock(&hb->lock);
 
        /*
@@ -1675,9 +1649,9 @@ retry_unlocked:
 
 out_unlock:
        spin_unlock(&hb->lock);
-out:
        put_futex_key(fshared, &key);
 
+out:
        return ret;
 
 pi_faulted:
@@ -1689,14 +1663,7 @@ 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);
        if (!ret)
@@ -1725,9 +1692,8 @@ pi_faulted:
  * @head: pointer to the list-head
  * @len: length of the list-head, as userspace expects
  */
-asmlinkage long
-sys_set_robust_list(struct robust_list_head __user *head,
-                   size_t len)
+SYSCALL_DEFINE2(set_robust_list, struct robust_list_head __user *, head,
+               size_t, len)
 {
        if (!futex_cmpxchg_enabled)
                return -ENOSYS;
@@ -1748,9 +1714,9 @@ sys_set_robust_list(struct robust_list_head __user *head,
  * @head_ptr: pointer to a list-head pointer, the kernel fills it in
  * @len_ptr: pointer to a length field, the kernel fills in the header size
  */
-asmlinkage long
-sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
-                   size_t __user *len_ptr)
+SYSCALL_DEFINE3(get_robust_list, int, pid,
+               struct robust_list_head __user * __user *, head_ptr,
+               size_t __user *, len_ptr)
 {
        struct robust_list_head __user *head;
        unsigned long ret;
@@ -1970,9 +1936,9 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
 }
 
 
-asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val,
-                         struct timespec __user *utime, u32 __user *uaddr2,
-                         u32 val3)
+SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
+               struct timespec __user *, utime, u32 __user *, uaddr2,
+               u32, val3)
 {
        struct timespec ts;
        ktime_t t, *tp = NULL;