Skip to content

Commit 9881051

Browse files
committed
Merge branch 'locking-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull locking fixes from Thomas Gleixner: "A small series of fixes which all address possible missed wakeups: - Document and fix the wakeup ordering of wake_q - Add the missing barrier in rcuwait_wake_up(), which was documented in the comment but missing in the code - Fix the possible missed wakeups in the rwsem and futex code" * 'locking-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: locking/rwsem: Fix (possible) missed wakeup futex: Fix (possible) missed wakeup sched/wake_q: Fix wakeup ordering for wake_q sched/wake_q: Document wake_q_add() sched/wait: Fix rcuwait_wake_up() ordering
2 parents 0d48437 + e158488 commit 9881051

File tree

5 files changed

+39
-12
lines changed

5 files changed

+39
-12
lines changed

include/linux/sched/wake_q.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@
2424
* called near the end of a function. Otherwise, the list can be
2525
* re-initialized for later re-use by wake_q_init().
2626
*
27-
* Note that this can cause spurious wakeups. schedule() callers
27+
* NOTE that this can cause spurious wakeups. schedule() callers
2828
* must ensure the call is done inside a loop, confirming that the
2929
* wakeup condition has in fact occurred.
30+
*
31+
* NOTE that there is no guarantee the wakeup will happen any later than the
32+
* wake_q_add() location. Therefore task must be ready to be woken at the
33+
* location of the wake_q_add().
3034
*/
3135

3236
#include <linux/sched.h>

kernel/exit.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ void rcuwait_wake_up(struct rcuwait *w)
307307
* MB (A) MB (B)
308308
* [L] cond [L] tsk
309309
*/
310-
smp_rmb(); /* (B) */
310+
smp_mb(); /* (B) */
311311

312312
/*
313313
* Avoid using task_rcu_dereference() magic as long as we are careful,

kernel/futex.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,11 +1452,7 @@ static void mark_wake_futex(struct wake_q_head *wake_q, struct futex_q *q)
14521452
if (WARN(q->pi_state || q->rt_waiter, "refusing to wake PI futex\n"))
14531453
return;
14541454

1455-
/*
1456-
* Queue the task for later wakeup for after we've released
1457-
* the hb->lock. wake_q_add() grabs reference to p.
1458-
*/
1459-
wake_q_add(wake_q, p);
1455+
get_task_struct(p);
14601456
__unqueue_futex(q);
14611457
/*
14621458
* The waiting task can free the futex_q as soon as q->lock_ptr = NULL
@@ -1466,6 +1462,13 @@ static void mark_wake_futex(struct wake_q_head *wake_q, struct futex_q *q)
14661462
* plist_del in __unqueue_futex().
14671463
*/
14681464
smp_store_release(&q->lock_ptr, NULL);
1465+
1466+
/*
1467+
* Queue the task for later wakeup for after we've released
1468+
* the hb->lock. wake_q_add() grabs reference to p.
1469+
*/
1470+
wake_q_add(wake_q, p);
1471+
put_task_struct(p);
14691472
}
14701473

14711474
/*

kernel/locking/rwsem-xadd.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,22 @@ static void __rwsem_mark_wake(struct rw_semaphore *sem,
198198
woken++;
199199
tsk = waiter->task;
200200

201-
wake_q_add(wake_q, tsk);
201+
get_task_struct(tsk);
202202
list_del(&waiter->list);
203203
/*
204-
* Ensure that the last operation is setting the reader
204+
* Ensure calling get_task_struct() before setting the reader
205205
* waiter to nil such that rwsem_down_read_failed() cannot
206206
* race with do_exit() by always holding a reference count
207207
* to the task to wakeup.
208208
*/
209209
smp_store_release(&waiter->task, NULL);
210+
/*
211+
* Ensure issuing the wakeup (either by us or someone else)
212+
* after setting the reader waiter to nil.
213+
*/
214+
wake_q_add(wake_q, tsk);
215+
/* wake_q_add() already take the task ref */
216+
put_task_struct(tsk);
210217
}
211218

212219
adjustment = woken * RWSEM_ACTIVE_READ_BIAS - adjustment;

kernel/sched/core.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,18 @@ static bool set_nr_if_polling(struct task_struct *p)
396396
#endif
397397
#endif
398398

399+
/**
400+
* wake_q_add() - queue a wakeup for 'later' waking.
401+
* @head: the wake_q_head to add @task to
402+
* @task: the task to queue for 'later' wakeup
403+
*
404+
* Queue a task for later wakeup, most likely by the wake_up_q() call in the
405+
* same context, _HOWEVER_ this is not guaranteed, the wakeup can come
406+
* instantly.
407+
*
408+
* This function must be used as-if it were wake_up_process(); IOW the task
409+
* must be ready to be woken at this location.
410+
*/
399411
void wake_q_add(struct wake_q_head *head, struct task_struct *task)
400412
{
401413
struct wake_q_node *node = &task->wake_q;
@@ -405,10 +417,11 @@ void wake_q_add(struct wake_q_head *head, struct task_struct *task)
405417
* its already queued (either by us or someone else) and will get the
406418
* wakeup due to that.
407419
*
408-
* This cmpxchg() executes a full barrier, which pairs with the full
409-
* barrier executed by the wakeup in wake_up_q().
420+
* In order to ensure that a pending wakeup will observe our pending
421+
* state, even in the failed case, an explicit smp_mb() must be used.
410422
*/
411-
if (cmpxchg(&node->next, NULL, WAKE_Q_TAIL))
423+
smp_mb__before_atomic();
424+
if (cmpxchg_relaxed(&node->next, NULL, WAKE_Q_TAIL))
412425
return;
413426

414427
get_task_struct(task);

0 commit comments

Comments
 (0)