Merge branch 'for-linus' of git://ftp.arm.linux.org.uk/~rmk/linux-arm
[firefly-linux-kernel-4.4.55.git] / kernel / futex.c
index ea6ca0bca52570b8cd88a9c428016cd54cf55a0a..c4a182f5357eb4372685f9524cf18b0bedfaf7ca 100644 (file)
@@ -1117,11 +1117,14 @@ static void mark_wake_futex(struct wake_q_head *wake_q, struct futex_q *q)
        q->lock_ptr = NULL;
 }
 
-static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this)
+static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this,
+                        struct futex_hash_bucket *hb)
 {
        struct task_struct *new_owner;
        struct futex_pi_state *pi_state = this->pi_state;
        u32 uninitialized_var(curval), newval;
+       WAKE_Q(wake_q);
+       bool deboost;
        int ret = 0;
 
        if (!pi_state)
@@ -1173,7 +1176,19 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this)
        raw_spin_unlock_irq(&new_owner->pi_lock);
 
        raw_spin_unlock(&pi_state->pi_mutex.wait_lock);
-       rt_mutex_unlock(&pi_state->pi_mutex);
+
+       deboost = rt_mutex_futex_unlock(&pi_state->pi_mutex, &wake_q);
+
+       /*
+        * First unlock HB so the waiter does not spin on it once he got woken
+        * up. Second wake up the waiter before the priority is adjusted. If we
+        * deboost first (and lose our higher priority), then the task might get
+        * scheduled away before the wake up can take place.
+        */
+       spin_unlock(&hb->lock);
+       wake_up_q(&wake_q);
+       if (deboost)
+               rt_mutex_adjust_prio(current);
 
        return 0;
 }
@@ -2410,13 +2425,23 @@ retry:
         */
        match = futex_top_waiter(hb, &key);
        if (match) {
-               ret = wake_futex_pi(uaddr, uval, match);
+               ret = wake_futex_pi(uaddr, uval, match, hb);
+               /*
+                * In case of success wake_futex_pi dropped the hash
+                * bucket lock.
+                */
+               if (!ret)
+                       goto out_putkey;
                /*
                 * The atomic access to the futex value generated a
                 * pagefault, so retry the user-access and the wakeup:
                 */
                if (ret == -EFAULT)
                        goto pi_faulted;
+               /*
+                * wake_futex_pi has detected invalid state. Tell user
+                * space.
+                */
                goto out_unlock;
        }
 
@@ -2437,6 +2462,7 @@ retry:
 
 out_unlock:
        spin_unlock(&hb->lock);
+out_putkey:
        put_futex_key(&key);
        return ret;