|
@@ -903,11 +903,27 @@ void exit_pi_state_list(struct task_struct *curr)
|
|
|
*/
|
|
|
raw_spin_lock_irq(&curr->pi_lock);
|
|
|
while (!list_empty(head)) {
|
|
|
-
|
|
|
next = head->next;
|
|
|
pi_state = list_entry(next, struct futex_pi_state, list);
|
|
|
key = pi_state->key;
|
|
|
hb = hash_futex(&key);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We can race against put_pi_state() removing itself from the
|
|
|
+ * list (a waiter going away). put_pi_state() will first
|
|
|
+ * decrement the reference count and then modify the list, so
|
|
|
+ * its possible to see the list entry but fail this reference
|
|
|
+ * acquire.
|
|
|
+ *
|
|
|
+ * In that case; drop the locks to let put_pi_state() make
|
|
|
+ * progress and retry the loop.
|
|
|
+ */
|
|
|
+ if (!atomic_inc_not_zero(&pi_state->refcount)) {
|
|
|
+ raw_spin_unlock_irq(&curr->pi_lock);
|
|
|
+ cpu_relax();
|
|
|
+ raw_spin_lock_irq(&curr->pi_lock);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
raw_spin_unlock_irq(&curr->pi_lock);
|
|
|
|
|
|
spin_lock(&hb->lock);
|
|
@@ -918,8 +934,10 @@ void exit_pi_state_list(struct task_struct *curr)
|
|
|
* task still owns the PI-state:
|
|
|
*/
|
|
|
if (head->next != next) {
|
|
|
+ /* retain curr->pi_lock for the loop invariant */
|
|
|
raw_spin_unlock(&pi_state->pi_mutex.wait_lock);
|
|
|
spin_unlock(&hb->lock);
|
|
|
+ put_pi_state(pi_state);
|
|
|
continue;
|
|
|
}
|
|
|
|
|
@@ -927,9 +945,8 @@ void exit_pi_state_list(struct task_struct *curr)
|
|
|
WARN_ON(list_empty(&pi_state->list));
|
|
|
list_del_init(&pi_state->list);
|
|
|
pi_state->owner = NULL;
|
|
|
- raw_spin_unlock(&curr->pi_lock);
|
|
|
|
|
|
- get_pi_state(pi_state);
|
|
|
+ raw_spin_unlock(&curr->pi_lock);
|
|
|
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
|
|
|
spin_unlock(&hb->lock);
|
|
|
|