Give futex init a proper name
[sfrench/cifs-2.6.git] / kernel / futex.c
index db9824de8bf061323b734d88377a6fa606fb9a86..87a6428cb5b67363a23252628c6a03d8b2a789c8 100644 (file)
@@ -60,6 +60,8 @@
 
 #include "rtmutex_common.h"
 
+int __read_mostly futex_cmpxchg_enabled;
+
 #define FUTEX_HASHBITS (CONFIG_BASE_SMALL ? 4 : 8)
 
 /*
@@ -109,6 +111,9 @@ struct futex_q {
        /* Optional priority inheritance state: */
        struct futex_pi_state *pi_state;
        struct task_struct *task;
+
+       /* Bitset for the optional bitmasked wakeup */
+       u32 bitset;
 };
 
 /*
@@ -466,6 +471,8 @@ void exit_pi_state_list(struct task_struct *curr)
        struct futex_hash_bucket *hb;
        union futex_key key;
 
+       if (!futex_cmpxchg_enabled)
+               return;
        /*
         * We are a ZOMBIE and nobody can enqueue itself on
         * pi_state_list anymore, but we have to be careful
@@ -722,7 +729,7 @@ double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
  * to this virtual address:
  */
 static int futex_wake(u32 __user *uaddr, struct rw_semaphore *fshared,
-                     int nr_wake)
+                     int nr_wake, u32 bitset)
 {
        struct futex_hash_bucket *hb;
        struct futex_q *this, *next;
@@ -730,6 +737,9 @@ static int futex_wake(u32 __user *uaddr, struct rw_semaphore *fshared,
        union futex_key key;
        int ret;
 
+       if (!bitset)
+               return -EINVAL;
+
        futex_lock_mm(fshared);
 
        ret = get_futex_key(uaddr, fshared, &key);
@@ -746,6 +756,11 @@ static int futex_wake(u32 __user *uaddr, struct rw_semaphore *fshared,
                                ret = -EINVAL;
                                break;
                        }
+
+                       /* Check if one of the bits is set in both bitsets */
+                       if (!(this->bitset & bitset))
+                               continue;
+
                        wake_futex(this);
                        if (++ret >= nr_wake)
                                break;
@@ -1156,7 +1171,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
 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)
+                     u32 val, ktime_t *abs_time, u32 bitset)
 {
        struct task_struct *curr = current;
        DECLARE_WAITQUEUE(wait, curr);
@@ -1167,7 +1182,11 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
        struct hrtimer_sleeper t;
        int rem = 0;
 
+       if (!bitset)
+               return -EINVAL;
+
        q.pi_state = NULL;
+       q.bitset = bitset;
  retry:
        futex_lock_mm(fshared);
 
@@ -1252,6 +1271,8 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
                        t.timer.expires = *abs_time;
 
                        hrtimer_start(&t.timer, t.timer.expires, HRTIMER_MODE_ABS);
+                       if (!hrtimer_active(&t.timer))
+                               t.task = NULL;
 
                        /*
                         * the timer could have already expired, in which
@@ -1293,6 +1314,7 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
                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)
@@ -1319,7 +1341,8 @@ static long futex_wait_restart(struct restart_block *restart)
        restart->fn = do_no_restart_syscall;
        if (restart->futex.flags & FLAGS_SHARED)
                fshared = &current->mm->mmap_sem;
-       return (long)futex_wait(uaddr, fshared, restart->futex.val, &t);
+       return (long)futex_wait(uaddr, fshared, restart->futex.val, &t,
+                               restart->futex.bitset);
 }
 
 
@@ -1535,9 +1558,6 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
                                owner = rt_mutex_owner(&q.pi_state->pi_mutex);
                                res = fixup_pi_state_owner(uaddr, &q, owner);
 
-                               WARN_ON(rt_mutex_owner(&q.pi_state->pi_mutex) !=
-                                       owner);
-
                                /* propagate -EFAULT, if the fixup failed */
                                if (res)
                                        ret = res;
@@ -1854,6 +1874,8 @@ asmlinkage long
 sys_set_robust_list(struct robust_list_head __user *head,
                    size_t len)
 {
+       if (!futex_cmpxchg_enabled)
+               return -ENOSYS;
        /*
         * The kernel knows only one size for now:
         */
@@ -1878,6 +1900,9 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
        struct robust_list_head __user *head;
        unsigned long ret;
 
+       if (!futex_cmpxchg_enabled)
+               return -ENOSYS;
+
        if (!pid)
                head = current->robust_list;
        else {
@@ -1943,7 +1968,8 @@ retry:
                 * PI futexes happens in exit_pi_state():
                 */
                if (!pi && (uval & FUTEX_WAITERS))
-                               futex_wake(uaddr, &curr->mm->mmap_sem, 1);
+                       futex_wake(uaddr, &curr->mm->mmap_sem, 1,
+                                  FUTEX_BITSET_MATCH_ANY);
        }
        return 0;
 }
@@ -1980,6 +2006,9 @@ void exit_robust_list(struct task_struct *curr)
        unsigned long futex_offset;
        int rc;
 
+       if (!futex_cmpxchg_enabled)
+               return;
+
        /*
         * Fetch the list head (which was registered earlier, via
         * sys_set_robust_list()):
@@ -2034,7 +2063,7 @@ void exit_robust_list(struct task_struct *curr)
 long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
                u32 __user *uaddr2, u32 val2, u32 val3)
 {
-       int ret;
+       int ret = -ENOSYS;
        int cmd = op & FUTEX_CMD_MASK;
        struct rw_semaphore *fshared = NULL;
 
@@ -2043,10 +2072,14 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
 
        switch (cmd) {
        case FUTEX_WAIT:
-               ret = futex_wait(uaddr, fshared, val, timeout);
+               val3 = FUTEX_BITSET_MATCH_ANY;
+       case FUTEX_WAIT_BITSET:
+               ret = futex_wait(uaddr, fshared, val, timeout, val3);
                break;
        case FUTEX_WAKE:
-               ret = futex_wake(uaddr, fshared, val);
+               val3 = FUTEX_BITSET_MATCH_ANY;
+       case FUTEX_WAKE_BITSET:
+               ret = futex_wake(uaddr, fshared, val, val3);
                break;
        case FUTEX_FD:
                /* non-zero val means F_SETOWN(getpid()) & F_SETSIG(val) */
@@ -2062,13 +2095,16 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
                ret = futex_wake_op(uaddr, fshared, uaddr2, val, val2, val3);
                break;
        case FUTEX_LOCK_PI:
-               ret = futex_lock_pi(uaddr, fshared, val, timeout, 0);
+               if (futex_cmpxchg_enabled)
+                       ret = futex_lock_pi(uaddr, fshared, val, timeout, 0);
                break;
        case FUTEX_UNLOCK_PI:
-               ret = futex_unlock_pi(uaddr, fshared);
+               if (futex_cmpxchg_enabled)
+                       ret = futex_unlock_pi(uaddr, fshared);
                break;
        case FUTEX_TRYLOCK_PI:
-               ret = futex_lock_pi(uaddr, fshared, 0, timeout, 1);
+               if (futex_cmpxchg_enabled)
+                       ret = futex_lock_pi(uaddr, fshared, 0, timeout, 1);
                break;
        default:
                ret = -ENOSYS;
@@ -2086,7 +2122,8 @@ asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val,
        u32 val2 = 0;
        int cmd = op & FUTEX_CMD_MASK;
 
-       if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI)) {
+       if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
+                     cmd == FUTEX_WAIT_BITSET)) {
                if (copy_from_user(&ts, utime, sizeof(ts)) != 0)
                        return -EFAULT;
                if (!timespec_valid(&ts))
@@ -2094,7 +2131,7 @@ asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val,
 
                t = timespec_to_ktime(ts);
                if (cmd == FUTEX_WAIT)
-                       t = ktime_add(ktime_get(), t);
+                       t = ktime_add_safe(ktime_get(), t);
                tp = &t;
        }
        /*
@@ -2121,10 +2158,31 @@ static struct file_system_type futex_fs_type = {
        .kill_sb        = kill_anon_super,
 };
 
-static int __init init(void)
+static int __init futex_init(void)
 {
-       int i = register_filesystem(&futex_fs_type);
+       u32 curval;
+       int i;
+
+       /*
+        * This will fail and we want it. Some arch implementations do
+        * runtime detection of the futex_atomic_cmpxchg_inatomic()
+        * functionality. We want to know that before we call in any
+        * of the complex code paths. Also we want to prevent
+        * registration of robust lists in that case. NULL is
+        * guaranteed to fault and we get -EFAULT on functional
+        * implementation, the non functional ones will return
+        * -ENOSYS.
+        */
+       curval = cmpxchg_futex_value_locked(NULL, 0, 0);
+       if (curval == -EFAULT)
+               futex_cmpxchg_enabled = 1;
 
+       for (i = 0; i < ARRAY_SIZE(futex_queues); i++) {
+               plist_head_init(&futex_queues[i].chain, &futex_queues[i].lock);
+               spin_lock_init(&futex_queues[i].lock);
+       }
+
+       i = register_filesystem(&futex_fs_type);
        if (i)
                return i;
 
@@ -2134,10 +2192,6 @@ static int __init init(void)
                return PTR_ERR(futex_mnt);
        }
 
-       for (i = 0; i < ARRAY_SIZE(futex_queues); i++) {
-               plist_head_init(&futex_queues[i].chain, &futex_queues[i].lock);
-               spin_lock_init(&futex_queues[i].lock);
-       }
        return 0;
 }
-__initcall(init);
+__initcall(futex_init);