]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - kernel/futex.c
[IPG]: add IP1000A driver to kernel tree
[linux-2.6-omap-h63xx.git] / kernel / futex.c
index 45490bec5831d5a806c1703251f82dec3e599d63..fcc94e7b40864afcb72170f19d832f8117e517fc 100644 (file)
@@ -120,6 +120,24 @@ static struct futex_hash_bucket futex_queues[1<<FUTEX_HASHBITS];
 /* Futex-fs vfsmount entry: */
 static struct vfsmount *futex_mnt;
 
+/*
+ * Take mm->mmap_sem, when futex is shared
+ */
+static inline void futex_lock_mm(struct rw_semaphore *fshared)
+{
+       if (fshared)
+               down_read(fshared);
+}
+
+/*
+ * Release mm->mmap_sem, when the futex is shared
+ */
+static inline void futex_unlock_mm(struct rw_semaphore *fshared)
+{
+       if (fshared)
+               up_read(fshared);
+}
+
 /*
  * We hash on the keys returned from get_futex_key (see below).
  */
@@ -287,7 +305,18 @@ void drop_futex_key_refs(union futex_key *key)
 }
 EXPORT_SYMBOL_GPL(drop_futex_key_refs);
 
-static inline int get_futex_value_locked(u32 *dest, u32 __user *from)
+static u32 cmpxchg_futex_value_locked(u32 __user *uaddr, u32 uval, u32 newval)
+{
+       u32 curval;
+
+       pagefault_disable();
+       curval = futex_atomic_cmpxchg_inatomic(uaddr, uval, newval);
+       pagefault_enable();
+
+       return curval;
+}
+
+static int get_futex_value_locked(u32 *dest, u32 __user *from)
 {
        int ret;
 
@@ -317,15 +346,20 @@ static int futex_handle_fault(unsigned long address,
        vma = find_vma(mm, address);
        if (vma && address >= vma->vm_start &&
            (vma->vm_flags & VM_WRITE)) {
-               switch (handle_mm_fault(mm, vma, address, 1)) {
-               case VM_FAULT_MINOR:
-                       ret = 0;
-                       current->min_flt++;
-                       break;
-               case VM_FAULT_MAJOR:
+               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;
-                       current->maj_flt++;
-                       break;
+                       if (fault & VM_FAULT_MAJOR)
+                               current->maj_flt++;
+                       else
+                               current->min_flt++;
                }
        }
        if (!fshared)
@@ -620,9 +654,7 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this)
 
                newval = FUTEX_WAITERS | new_owner->pid;
 
-               pagefault_disable();
-               curval = futex_atomic_cmpxchg_inatomic(uaddr, uval, newval);
-               pagefault_enable();
+               curval = cmpxchg_futex_value_locked(uaddr, uval, newval);
 
                if (curval == -EFAULT)
                        ret = -EFAULT;
@@ -659,9 +691,7 @@ static int unlock_futex_pi(u32 __user *uaddr, u32 uval)
         * There is no waiter, so we unlock the futex. The owner died
         * bit has not to be preserved here. We are the owner:
         */
-       pagefault_disable();
-       oldval = futex_atomic_cmpxchg_inatomic(uaddr, uval, 0);
-       pagefault_enable();
+       oldval = cmpxchg_futex_value_locked(uaddr, uval, 0);
 
        if (oldval == -EFAULT)
                return oldval;
@@ -700,8 +730,7 @@ static int futex_wake(u32 __user *uaddr, struct rw_semaphore *fshared,
        union futex_key key;
        int ret;
 
-       if (fshared)
-               down_read(fshared);
+       futex_lock_mm(fshared);
 
        ret = get_futex_key(uaddr, fshared, &key);
        if (unlikely(ret != 0))
@@ -725,8 +754,7 @@ static int futex_wake(u32 __user *uaddr, struct rw_semaphore *fshared,
 
        spin_unlock(&hb->lock);
 out:
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
        return ret;
 }
 
@@ -746,8 +774,7 @@ futex_wake_op(u32 __user *uaddr1, struct rw_semaphore *fshared,
        int ret, op_ret, attempt = 0;
 
 retryfull:
-       if (fshared)
-               down_read(fshared);
+       futex_lock_mm(fshared);
 
        ret = get_futex_key(uaddr1, fshared, &key1);
        if (unlikely(ret != 0))
@@ -793,7 +820,7 @@ retry:
                 */
                if (attempt++) {
                        ret = futex_handle_fault((unsigned long)uaddr2,
-                                               fshared, attempt);
+                                                fshared, attempt);
                        if (ret)
                                goto out;
                        goto retry;
@@ -803,8 +830,7 @@ retry:
                 * If we would have faulted, release mmap_sem,
                 * fault it in and start all over again.
                 */
-               if (fshared)
-                       up_read(fshared);
+               futex_unlock_mm(fshared);
 
                ret = get_user(dummy, uaddr2);
                if (ret)
@@ -841,8 +867,8 @@ retry:
        if (hb1 != hb2)
                spin_unlock(&hb2->lock);
 out:
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
+
        return ret;
 }
 
@@ -861,8 +887,7 @@ static int futex_requeue(u32 __user *uaddr1, struct rw_semaphore *fshared,
        int ret, drop_count = 0;
 
  retry:
-       if (fshared)
-               down_read(fshared);
+       futex_lock_mm(fshared);
 
        ret = get_futex_key(uaddr1, fshared, &key1);
        if (unlikely(ret != 0))
@@ -890,8 +915,7 @@ static int futex_requeue(u32 __user *uaddr1, struct rw_semaphore *fshared,
                         * If we would have faulted, release mmap_sem, fault
                         * it in and start all over again.
                         */
-                       if (fshared)
-                               up_read(fshared);
+                       futex_unlock_mm(fshared);
 
                        ret = get_user(curval, uaddr1);
 
@@ -944,8 +968,7 @@ out_unlock:
                drop_futex_key_refs(&key1);
 
 out:
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
        return ret;
 }
 
@@ -1113,10 +1136,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
        while (!ret) {
                newval = (uval & FUTEX_OWNER_DIED) | newtid;
 
-               pagefault_disable();
-               curval = futex_atomic_cmpxchg_inatomic(uaddr,
-                                                      uval, newval);
-               pagefault_enable();
+               curval = cmpxchg_futex_value_locked(uaddr, uval, newval);
 
                if (curval == -EFAULT)
                        ret = -EFAULT;
@@ -1134,6 +1154,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
 #define ARG3_SHARED  1
 
 static long futex_wait_restart(struct restart_block *restart);
+
 static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
                      u32 val, ktime_t *abs_time)
 {
@@ -1148,8 +1169,7 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
 
        q.pi_state = NULL;
  retry:
-       if (fshared)
-               down_read(fshared);
+       futex_lock_mm(fshared);
 
        ret = get_futex_key(uaddr, fshared, &q.key);
        if (unlikely(ret != 0))
@@ -1186,8 +1206,7 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
                 * If we would have faulted, release mmap_sem, fault it in and
                 * start all over again.
                 */
-               if (fshared)
-                       up_read(fshared);
+               futex_unlock_mm(fshared);
 
                ret = get_user(uval, uaddr);
 
@@ -1206,8 +1225,7 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
         * Now the futex is queued and we have checked the data, we
         * don't want to hold mmap_sem while we sleep.
         */
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
 
        /*
         * There might have been scheduling since the queue_me(), as we
@@ -1285,8 +1303,7 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
        queue_unlock(&q, hb);
 
  out_release_sem:
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
        return ret;
 }
 
@@ -1333,8 +1350,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
 
        q.pi_state = NULL;
  retry:
-       if (fshared)
-               down_read(fshared);
+       futex_lock_mm(fshared);
 
        ret = get_futex_key(uaddr, fshared, &q.key);
        if (unlikely(ret != 0))
@@ -1353,9 +1369,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
         */
        newval = current->pid;
 
-       pagefault_disable();
-       curval = futex_atomic_cmpxchg_inatomic(uaddr, 0, newval);
-       pagefault_enable();
+       curval = cmpxchg_futex_value_locked(uaddr, 0, newval);
 
        if (unlikely(curval == -EFAULT))
                goto uaddr_faulted;
@@ -1398,9 +1412,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
                lock_taken = 1;
        }
 
-       pagefault_disable();
-       curval = futex_atomic_cmpxchg_inatomic(uaddr, uval, newval);
-       pagefault_enable();
+       curval = cmpxchg_futex_value_locked(uaddr, uval, newval);
 
        if (unlikely(curval == -EFAULT))
                goto uaddr_faulted;
@@ -1428,8 +1440,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
                         * exit to complete.
                         */
                        queue_unlock(&q, hb);
-                       if (fshared)
-                               up_read(fshared);
+                       futex_unlock_mm(fshared);
                        cond_resched();
                        goto retry;
 
@@ -1465,8 +1476,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
         * Now the futex is queued and we have checked the data, we
         * don't want to hold mmap_sem while we sleep.
         */
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
 
        WARN_ON(!q.pi_state);
        /*
@@ -1480,8 +1490,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
                ret = ret ? 0 : -EWOULDBLOCK;
        }
 
-       if (fshared)
-               down_read(fshared);
+       futex_lock_mm(fshared);
        spin_lock(q.lock_ptr);
 
        if (!ret) {
@@ -1518,8 +1527,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
 
        /* Unqueue and drop the lock */
        unqueue_me_pi(&q);
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
 
        return ret != -EINTR ? ret : -ERESTARTNOINTR;
 
@@ -1527,8 +1535,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
        queue_unlock(&q, hb);
 
  out_release_sem:
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
        return ret;
 
  uaddr_faulted:
@@ -1550,8 +1557,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
                goto retry_unlocked;
        }
 
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
 
        ret = get_user(uval, uaddr);
        if (!ret && (uval != -EFAULT))
@@ -1585,8 +1591,7 @@ retry:
        /*
         * First take all the futex related locks:
         */
-       if (fshared)
-               down_read(fshared);
+       futex_lock_mm(fshared);
 
        ret = get_futex_key(uaddr, fshared, &key);
        if (unlikely(ret != 0))
@@ -1601,11 +1606,9 @@ retry_unlocked:
         * again. If it succeeds then we can return without waking
         * anyone else up:
         */
-       if (!(uval & FUTEX_OWNER_DIED)) {
-               pagefault_disable();
-               uval = futex_atomic_cmpxchg_inatomic(uaddr, current->pid, 0);
-               pagefault_enable();
-       }
+       if (!(uval & FUTEX_OWNER_DIED))
+               uval = cmpxchg_futex_value_locked(uaddr, current->pid, 0);
+
 
        if (unlikely(uval == -EFAULT))
                goto pi_faulted;
@@ -1647,8 +1650,7 @@ retry_unlocked:
 out_unlock:
        spin_unlock(&hb->lock);
 out:
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
 
        return ret;
 
@@ -1668,11 +1670,11 @@ pi_faulted:
                                         attempt);
                if (ret)
                        goto out;
+               uval = 0;
                goto retry_unlocked;
        }
 
-       if (fshared)
-               up_read(fshared);
+       futex_unlock_mm(fshared);
 
        ret = get_user(uval, uaddr);
        if (!ret && (uval != -EFAULT))
@@ -1729,8 +1731,8 @@ static int futex_fd(u32 __user *uaddr, int signal)
 
        if (printk_timed_ratelimit(&printk_interval, 60 * 60 * 1000)) {
                printk(KERN_WARNING "Process `%s' used FUTEX_FD, which "
-                       "will be removed from the kernel in June 2007\n",
-                       current->comm);
+                      "will be removed from the kernel in June 2007\n",
+                      current->comm);
        }
 
        ret = -EINVAL;
@@ -1908,10 +1910,8 @@ retry:
                 * Wake robust non-PI futexes here. The wakeup of
                 * PI futexes happens in exit_pi_state():
                 */
-               if (!pi) {
-                       if (uval & FUTEX_WAITERS)
+               if (!pi && (uval & FUTEX_WAITERS))
                                futex_wake(uaddr, &curr->mm->mmap_sem, 1);
-               }
        }
        return 0;
 }
@@ -1943,9 +1943,10 @@ static inline int fetch_robust_entry(struct robust_list __user **entry,
 void exit_robust_list(struct task_struct *curr)
 {
        struct robust_list_head __user *head = curr->robust_list;
-       struct robust_list __user *entry, *pending;
-       unsigned int limit = ROBUST_LIST_LIMIT, pi, pip;
+       struct robust_list __user *entry, *next_entry, *pending;
+       unsigned int limit = ROBUST_LIST_LIMIT, pi, next_pi, pip;
        unsigned long futex_offset;
+       int rc;
 
        /*
         * Fetch the list head (which was registered earlier, via
@@ -1965,11 +1966,13 @@ void exit_robust_list(struct task_struct *curr)
        if (fetch_robust_entry(&pending, &head->list_op_pending, &pip))
                return;
 
-       if (pending)
-               handle_futex_death((void __user *)pending + futex_offset,
-                                  curr, pip);
-
+       next_entry = NULL;      /* avoid warning with gcc */
        while (entry != &head->list) {
+               /*
+                * Fetch the next entry in the list before calling
+                * handle_futex_death:
+                */
+               rc = fetch_robust_entry(&next_entry, &entry->next, &next_pi);
                /*
                 * A pending lock might already be on the list, so
                 * don't process it twice:
@@ -1978,11 +1981,10 @@ void exit_robust_list(struct task_struct *curr)
                        if (handle_futex_death((void __user *)entry + futex_offset,
                                                curr, pi))
                                return;
-               /*
-                * Fetch the next entry in the list:
-                */
-               if (fetch_robust_entry(&entry, &entry->next, &pi))
+               if (rc)
                        return;
+               entry = next_entry;
+               pi = next_pi;
                /*
                 * Avoid excessively long or circular lists:
                 */
@@ -1991,6 +1993,10 @@ void exit_robust_list(struct task_struct *curr)
 
                cond_resched();
        }
+
+       if (pending)
+               handle_futex_death((void __user *)pending + futex_offset,
+                                  curr, pip);
 }
 
 long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
@@ -2061,8 +2067,10 @@ asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val,
        }
        /*
         * requeue parameter in 'utime' if cmd == FUTEX_REQUEUE.
+        * number of waiters to wake in 'utime' if cmd == FUTEX_WAKE_OP.
         */
-       if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE)
+       if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
+           cmd == FUTEX_WAKE_OP)
                val2 = (u32) (unsigned long) utime;
 
        return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);