Skip to content

Commit b04b8f3

Browse files
Sebastian Andrzej SiewiorPeter Zijlstra
authored andcommitted
futex: Introduce futex_q_lockptr_lock()
futex_lock_pi() and __fixup_pi_state_owner() acquire the futex_q::lock_ptr without holding a reference assuming the previously obtained hash bucket and the assigned lock_ptr are still valid. This isn't the case once the private hash can be resized and becomes invalid after the reference drop. Introduce futex_q_lockptr_lock() to lock the hash bucket recorded in futex_q::lock_ptr. The lock pointer is read in a RCU section to ensure that it does not go away if the hash bucket has been replaced and the old pointer has been observed. After locking the pointer needs to be compared to check if it changed. If so then the hash bucket has been replaced and the user has been moved to the new one and lock_ptr has been updated. The lock operation needs to be redone in this case. The locked hash bucket is not returned. A special case is an early return in futex_lock_pi() (due to signal or timeout) and a successful futex_wait_requeue_pi(). In both cases a valid futex_q::lock_ptr is expected (and its matching hash bucket) but since the waiter has been removed from the hash this can no longer be guaranteed. Therefore before the waiter is removed and a reference is acquired which is later dropped by the waiter to avoid a resize. Add futex_q_lockptr_lock() and use it. Acquire an additional reference in requeue_pi_wake_futex() and futex_unlock_pi() while the futex_q is removed, denote this extra reference in futex_q::drop_hb_ref and let the waiter drop the reference in this case. Signed-off-by: Sebastian Andrzej Siewior <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent fe00e88 commit b04b8f3

File tree

4 files changed

+53
-6
lines changed

4 files changed

+53
-6
lines changed

kernel/futex/core.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ struct futex_hash_bucket *futex_hash(union futex_key *key)
134134
return &futex_queues[hash & futex_hashmask];
135135
}
136136

137+
/**
138+
* futex_hash_get - Get an additional reference for the local hash.
139+
* @hb: ptr to the private local hash.
140+
*
141+
* Obtain an additional reference for the already obtained hash bucket. The
142+
* caller must already own an reference.
143+
*/
137144
void futex_hash_get(struct futex_hash_bucket *hb) { }
138145
void futex_hash_put(struct futex_hash_bucket *hb) { }
139146

@@ -615,6 +622,24 @@ int futex_unqueue(struct futex_q *q)
615622
return ret;
616623
}
617624

625+
void futex_q_lockptr_lock(struct futex_q *q)
626+
{
627+
spinlock_t *lock_ptr;
628+
629+
/*
630+
* See futex_unqueue() why lock_ptr can change.
631+
*/
632+
guard(rcu)();
633+
retry:
634+
lock_ptr = READ_ONCE(q->lock_ptr);
635+
spin_lock(lock_ptr);
636+
637+
if (unlikely(lock_ptr != q->lock_ptr)) {
638+
spin_unlock(lock_ptr);
639+
goto retry;
640+
}
641+
}
642+
618643
/*
619644
* PI futexes can not be requeued and must remove themselves from the hash
620645
* bucket. The hash bucket lock (i.e. lock_ptr) is held.

kernel/futex/futex.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ struct futex_q {
183183
union futex_key *requeue_pi_key;
184184
u32 bitset;
185185
atomic_t requeue_state;
186+
bool drop_hb_ref;
186187
#ifdef CONFIG_PREEMPT_RT
187188
struct rcuwait requeue_wait;
188189
#endif
@@ -197,7 +198,7 @@ enum futex_access {
197198

198199
extern int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
199200
enum futex_access rw);
200-
201+
extern void futex_q_lockptr_lock(struct futex_q *q);
201202
extern struct hrtimer_sleeper *
202203
futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
203204
int flags, u64 range_ns);

kernel/futex/pi.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ static int __fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
806806
break;
807807
}
808808

809-
spin_lock(q->lock_ptr);
809+
futex_q_lockptr_lock(q);
810810
raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
811811

812812
/*
@@ -1072,7 +1072,7 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl
10721072
* spinlock/rtlock (which might enqueue its own rt_waiter) and fix up
10731073
* the
10741074
*/
1075-
spin_lock(q.lock_ptr);
1075+
futex_q_lockptr_lock(&q);
10761076
/*
10771077
* Waiter is unqueued.
10781078
*/
@@ -1092,6 +1092,11 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl
10921092

10931093
futex_unqueue_pi(&q);
10941094
spin_unlock(q.lock_ptr);
1095+
if (q.drop_hb_ref) {
1096+
CLASS(hb, hb)(&q.key);
1097+
/* Additional reference from futex_unlock_pi() */
1098+
futex_hash_put(hb);
1099+
}
10951100
goto out;
10961101

10971102
out_unlock_put_key:
@@ -1200,6 +1205,12 @@ int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
12001205
*/
12011206
rt_waiter = rt_mutex_top_waiter(&pi_state->pi_mutex);
12021207
if (!rt_waiter) {
1208+
/*
1209+
* Acquire a reference for the leaving waiter to ensure
1210+
* valid futex_q::lock_ptr.
1211+
*/
1212+
futex_hash_get(hb);
1213+
top_waiter->drop_hb_ref = true;
12031214
__futex_unqueue(top_waiter);
12041215
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
12051216
goto retry_hb;

kernel/futex/requeue.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,12 @@ void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key,
231231

232232
WARN_ON(!q->rt_waiter);
233233
q->rt_waiter = NULL;
234-
234+
/*
235+
* Acquire a reference for the waiter to ensure valid
236+
* futex_q::lock_ptr.
237+
*/
238+
futex_hash_get(hb);
239+
q->drop_hb_ref = true;
235240
q->lock_ptr = &hb->lock;
236241

237242
/* Signal locked state to the waiter */
@@ -826,7 +831,7 @@ int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
826831
case Q_REQUEUE_PI_LOCKED:
827832
/* The requeue acquired the lock */
828833
if (q.pi_state && (q.pi_state->owner != current)) {
829-
spin_lock(q.lock_ptr);
834+
futex_q_lockptr_lock(&q);
830835
ret = fixup_pi_owner(uaddr2, &q, true);
831836
/*
832837
* Drop the reference to the pi state which the
@@ -853,7 +858,7 @@ int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
853858
if (ret && !rt_mutex_cleanup_proxy_lock(pi_mutex, &rt_waiter))
854859
ret = 0;
855860

856-
spin_lock(q.lock_ptr);
861+
futex_q_lockptr_lock(&q);
857862
debug_rt_mutex_free_waiter(&rt_waiter);
858863
/*
859864
* Fixup the pi_state owner and possibly acquire the lock if we
@@ -885,6 +890,11 @@ int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
885890
default:
886891
BUG();
887892
}
893+
if (q.drop_hb_ref) {
894+
CLASS(hb, hb)(&q.key);
895+
/* Additional reference from requeue_pi_wake_futex() */
896+
futex_hash_put(hb);
897+
}
888898

889899
out:
890900
if (to) {

0 commit comments

Comments
 (0)