futex: Update futex_q lock_ptr on requeue proxy lock

futex_requeue() can acquire the lock on behalf of a waiter
early on or during the requeue loop if it is uncontended or in
the event of a lock steal or owner died. On wakeup, the waiter
(in futex_wait_requeue_pi()) cleans up the pi_state owner using
the lock_ptr to protect against concurrent access to the
pi_state. The pi_state is hung off futex_q's on the requeue
target futex hash bucket so the lock_ptr needs to be updated
accordingly.

The problem manifested by triggering the WARN_ON in
lookup_pi_state() about the pid != pi_state->owner->pid. With
this patch, the pi_state is properly guarded against concurrent
access via the requeue target hb lock.

The astute reviewer may notice that there is a window of time
between when futex_requeue() unlocks the hb locks and when
futex_wait_requeue_pi() will acquire hb2->lock. During this
time the pi_state and uval are not in sync with the underlying
rtmutex owner (but the uval does indicate there are waiters, so
no atomic changes will occur in userspace). However, this is
not a problem. Should a contending thread enter
lookup_pi_state() and acquire hb2->lock before the ownership is
fixed up, it will find the pi_state hung off a waiter's
(possibly the pending owner's) futex_q and block on the
rtmutex. Once futex_wait_requeue_pi() fixes up the owner, it
will also move the pi_state from the old owner's
task->pi_state_list to its own.

v3: Fix plist lock name for application to mainline (rather
than -rt) Compile tested against tip/v2.6.31-rc5.

Signed-off-by: Darren Hart <dvhltc@us.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Dinakar Guniguntala <dino@in.ibm.com>
Cc: John Stultz <johnstul@linux.vnet.ibm.com>
LKML-Reference: <4A7F4EFF.6090903@us.ibm.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

authored by Darren Hart and committed by Ingo Molnar beda2c7e f4b9a988

+13 -4
+13 -4
kernel/futex.c
··· 1010 1010 * requeue_pi_wake_futex() - Wake a task that acquired the lock during requeue 1011 1011 * q: the futex_q 1012 1012 * key: the key of the requeue target futex 1013 + * hb: the hash_bucket of the requeue target futex 1013 1014 * 1014 1015 * During futex_requeue, with requeue_pi=1, it is possible to acquire the 1015 1016 * target futex if it is uncontended or via a lock steal. Set the futex_q key 1016 1017 * to the requeue target futex so the waiter can detect the wakeup on the right 1017 1018 * futex, but remove it from the hb and NULL the rt_waiter so it can detect 1018 - * atomic lock acquisition. Must be called with the q->lock_ptr held. 1019 + * atomic lock acquisition. Set the q->lock_ptr to the requeue target hb->lock 1020 + * to protect access to the pi_state to fixup the owner later. Must be called 1021 + * with both q->lock_ptr and hb->lock held. 1019 1022 */ 1020 1023 static inline 1021 - void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key) 1024 + void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key, 1025 + struct futex_hash_bucket *hb) 1022 1026 { 1023 1027 drop_futex_key_refs(&q->key); 1024 1028 get_futex_key_refs(key); ··· 1033 1029 1034 1030 WARN_ON(!q->rt_waiter); 1035 1031 q->rt_waiter = NULL; 1032 + 1033 + q->lock_ptr = &hb->lock; 1034 + #ifdef CONFIG_DEBUG_PI_LIST 1035 + q->list.plist.lock = &hb->lock; 1036 + #endif 1036 1037 1037 1038 wake_up_state(q->task, TASK_NORMAL); 1038 1039 } ··· 1097 1088 ret = futex_lock_pi_atomic(pifutex, hb2, key2, ps, top_waiter->task, 1098 1089 set_waiters); 1099 1090 if (ret == 1) 1100 - requeue_pi_wake_futex(top_waiter, key2); 1091 + requeue_pi_wake_futex(top_waiter, key2, hb2); 1101 1092 1102 1093 return ret; 1103 1094 } ··· 1282 1273 this->task, 1); 1283 1274 if (ret == 1) { 1284 1275 /* We got the lock. */ 1285 - requeue_pi_wake_futex(this, &key2); 1276 + requeue_pi_wake_futex(this, &key2, hb2); 1286 1277 continue; 1287 1278 } else if (ret) { 1288 1279 /* -EDEADLK */