Memory controller: OOM handling
[linux-2.6.git] / kernel / futex.c
index 86b2600..a6baaec 100644 (file)
@@ -109,6 +109,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;
 };
 
 /*
@@ -181,8 +184,8 @@ static inline int match_futex(union futex_key *key1, union futex_key *key2)
  * For other futexes, it points to &current->mm->mmap_sem and
  * caller must have taken the reader lock. but NOT any spinlocks.
  */
-int get_futex_key(u32 __user *uaddr, struct rw_semaphore *fshared,
-                 union futex_key *key)
+static int get_futex_key(u32 __user *uaddr, struct rw_semaphore *fshared,
+                        union futex_key *key)
 {
        unsigned long address = (unsigned long)uaddr;
        struct mm_struct *mm = current->mm;
@@ -268,14 +271,13 @@ int get_futex_key(u32 __user *uaddr, struct rw_semaphore *fshared,
        }
        return err;
 }
-EXPORT_SYMBOL_GPL(get_futex_key);
 
 /*
  * Take a reference to the resource addressed by a key.
  * Can be called while holding spinlocks.
  *
  */
-inline void get_futex_key_refs(union futex_key *key)
+static void get_futex_key_refs(union futex_key *key)
 {
        if (key->both.ptr == 0)
                return;
@@ -288,13 +290,12 @@ inline void get_futex_key_refs(union futex_key *key)
                        break;
        }
 }
-EXPORT_SYMBOL_GPL(get_futex_key_refs);
 
 /*
  * Drop a reference to the resource addressed by a key.
  * The hash bucket spinlock must not be held.
  */
-void drop_futex_key_refs(union futex_key *key)
+static void drop_futex_key_refs(union futex_key *key)
 {
        if (!key->both.ptr)
                return;
@@ -307,7 +308,6 @@ void drop_futex_key_refs(union futex_key *key)
                        break;
        }
 }
-EXPORT_SYMBOL_GPL(drop_futex_key_refs);
 
 static u32 cmpxchg_futex_value_locked(u32 __user *uaddr, u32 uval, u32 newval)
 {
@@ -446,9 +446,7 @@ static struct task_struct * futex_find_get_task(pid_t pid)
        struct task_struct *p;
 
        rcu_read_lock();
-       p = find_task_by_pid_ns(pid,
-                       current->nsproxy->pid_ns);
-
+       p = find_task_by_vpid(pid);
        if (!p || ((current->euid != p->euid) && (current->euid != p->uid)))
                p = ERR_PTR(-ESRCH);
        else
@@ -663,7 +661,7 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this)
 
                if (curval == -EFAULT)
                        ret = -EFAULT;
-               if (curval != uval)
+               else if (curval != uval)
                        ret = -EINVAL;
                if (ret) {
                        spin_unlock(&pi_state->pi_mutex.wait_lock);
@@ -727,7 +725,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;
@@ -735,6 +733,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);
@@ -751,6 +752,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;
@@ -1102,15 +1108,15 @@ static void unqueue_me_pi(struct futex_q *q)
 }
 
 /*
- * Fixup the pi_state owner with current.
+ * Fixup the pi_state owner with the new owner.
  *
  * Must be called with hash bucket lock held and mm->sem held for non
  * private futexes.
  */
 static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
-                               struct task_struct *curr)
+                               struct task_struct *newowner)
 {
-       u32 newtid = task_pid_vnr(curr) | FUTEX_WAITERS;
+       u32 newtid = task_pid_vnr(newowner) | FUTEX_WAITERS;
        struct futex_pi_state *pi_state = q->pi_state;
        u32 uval, curval, newval;
        int ret;
@@ -1124,12 +1130,12 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
        } else
                newtid |= FUTEX_OWNER_DIED;
 
-       pi_state->owner = curr;
+       pi_state->owner = newowner;
 
-       spin_lock_irq(&curr->pi_lock);
+       spin_lock_irq(&newowner->pi_lock);
        WARN_ON(!list_empty(&pi_state->list));
-       list_add(&pi_state->list, &curr->pi_state_list);
-       spin_unlock_irq(&curr->pi_lock);
+       list_add(&pi_state->list, &newowner->pi_state_list);
+       spin_unlock_irq(&newowner->pi_lock);
 
        /*
         * We own it, so we have to replace the pending owner
@@ -1154,14 +1160,14 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
 
 /*
  * In case we must use restart_block to restart a futex_wait,
- * we encode in the 'arg3' shared capability
+ * we encode in the 'flags' shared capability
  */
-#define ARG3_SHARED  1
+#define FLAGS_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)
+                     u32 val, ktime_t *abs_time, u32 bitset)
 {
        struct task_struct *curr = current;
        DECLARE_WAITQUEUE(wait, curr);
@@ -1172,7 +1178,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);
 
@@ -1257,6 +1267,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
@@ -1295,12 +1307,14 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
                struct restart_block *restart;
                restart = &current_thread_info()->restart_block;
                restart->fn = futex_wait_restart;
-               restart->arg0 = (unsigned long)uaddr;
-               restart->arg1 = (unsigned long)val;
-               restart->arg2 = (unsigned long)abs_time;
-               restart->arg3 = 0;
+               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->arg3 |= ARG3_SHARED;
+                       restart->futex.flags |= FLAGS_SHARED;
                return -ERESTART_RESTARTBLOCK;
        }
 
@@ -1315,15 +1329,16 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
 
 static long futex_wait_restart(struct restart_block *restart)
 {
-       u32 __user *uaddr = (u32 __user *)restart->arg0;
-       u32 val = (u32)restart->arg1;
-       ktime_t *abs_time = (ktime_t *)restart->arg2;
+       u32 __user *uaddr = (u32 __user *)restart->futex.uaddr;
        struct rw_semaphore *fshared = NULL;
+       ktime_t t;
 
+       t.tv64 = restart->futex.time;
        restart->fn = do_no_restart_syscall;
-       if (restart->arg3 & ARG3_SHARED)
+       if (restart->futex.flags & FLAGS_SHARED)
                fshared = &current->mm->mmap_sem;
-       return (long)futex_wait(uaddr, fshared, val, abs_time);
+       return (long)futex_wait(uaddr, fshared, restart->futex.val, &t,
+                               restart->futex.bitset);
 }
 
 
@@ -1512,9 +1527,37 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
                 * when we were on the way back before we locked the
                 * hash bucket.
                 */
-               if (q.pi_state->owner == curr &&
-                   rt_mutex_trylock(&q.pi_state->pi_mutex)) {
-                       ret = 0;
+               if (q.pi_state->owner == curr) {
+                       /*
+                        * Try to get the rt_mutex now. This might
+                        * fail as some other task acquired the
+                        * rt_mutex after we removed ourself from the
+                        * rt_mutex waiters list.
+                        */
+                       if (rt_mutex_trylock(&q.pi_state->pi_mutex))
+                               ret = 0;
+                       else {
+                               /*
+                                * pi_state is incorrect, some other
+                                * task did a lock steal and we
+                                * returned due to timeout or signal
+                                * without taking the rt_mutex. Too
+                                * late. We can access the
+                                * rt_mutex_owner without locking, as
+                                * the other task is now blocked on
+                                * the hash bucket lock. Fix the state
+                                * up.
+                                */
+                               struct task_struct *owner;
+                               int res;
+
+                               owner = rt_mutex_owner(&q.pi_state->pi_mutex);
+                               res = fixup_pi_state_owner(uaddr, &q, owner);
+
+                               /* propagate -EFAULT, if the fixup failed */
+                               if (res)
+                                       ret = res;
+                       }
                } else {
                        /*
                         * Paranoia check. If we did not take the lock
@@ -1858,8 +1901,7 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
 
                ret = -ESRCH;
                rcu_read_lock();
-               p = find_task_by_pid_ns(pid,
-                               current->nsproxy->pid_ns);
+               p = find_task_by_vpid(pid);
                if (!p)
                        goto err_unlock;
                ret = -EPERM;
@@ -1917,7 +1959,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;
 }
@@ -2017,10 +2060,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) */
@@ -2060,7 +2107,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))