Skip to content

Commit 44e4e02

Browse files
author
Peter Zijlstra
committed
locking/mutex: Rework task_struct::blocked_on
Track the blocked-on relation for mutexes, to allow following this relation at schedule time. task | blocked-on v mutex | owner v task This all will be used for tracking blocked-task/mutex chains with the prox-execution patch in a similar fashion to how priority inheritance is done with rt_mutexes. For serialization, blocked-on is only set by the task itself (current). And both when setting or clearing (potentially by others), is done while holding the mutex::wait_lock. [minor changes while rebasing] [jstultz: Fix blocked_on tracking in __mutex_lock_common in error paths] Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Signed-off-by: Juri Lelli <[email protected]> Signed-off-by: Connor O'Brien <[email protected]> Signed-off-by: John Stultz <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Tested-by: K Prateek Nayak <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 25c411f commit 44e4e02

File tree

5 files changed

+45
-12
lines changed

5 files changed

+45
-12
lines changed

include/linux/sched.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,10 +1230,7 @@ struct task_struct {
12301230
struct rt_mutex_waiter *pi_blocked_on;
12311231
#endif
12321232

1233-
#ifdef CONFIG_DEBUG_MUTEXES
1234-
/* Mutex deadlock detection: */
1235-
struct mutex_waiter *blocked_on;
1236-
#endif
1233+
struct mutex *blocked_on; /* lock we're blocked on */
12371234

12381235
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
12391236
/*

kernel/fork.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2123,9 +2123,8 @@ __latent_entropy struct task_struct *copy_process(
21232123
lockdep_init_task(p);
21242124
#endif
21252125

2126-
#ifdef CONFIG_DEBUG_MUTEXES
21272126
p->blocked_on = NULL; /* not blocked yet */
2128-
#endif
2127+
21292128
#ifdef CONFIG_BCACHE
21302129
p->sequential_io = 0;
21312130
p->sequential_io_avg = 0;

kernel/locking/mutex-debug.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,18 @@ void debug_mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter,
5353
{
5454
lockdep_assert_held(&lock->wait_lock);
5555

56-
/* Mark the current thread as blocked on the lock: */
57-
task->blocked_on = waiter;
56+
/* Current thread can't be already blocked (since it's executing!) */
57+
DEBUG_LOCKS_WARN_ON(task->blocked_on);
5858
}
5959

6060
void debug_mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter,
6161
struct task_struct *task)
6262
{
63+
struct mutex *blocked_on = READ_ONCE(task->blocked_on);
64+
6365
DEBUG_LOCKS_WARN_ON(list_empty(&waiter->list));
6466
DEBUG_LOCKS_WARN_ON(waiter->task != task);
65-
DEBUG_LOCKS_WARN_ON(task->blocked_on != waiter);
66-
task->blocked_on = NULL;
67+
DEBUG_LOCKS_WARN_ON(blocked_on && blocked_on != lock);
6768

6869
INIT_LIST_HEAD(&waiter->list);
6970
waiter->task = NULL;

kernel/locking/mutex.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,8 @@ __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclas
644644
goto err_early_kill;
645645
}
646646

647+
WARN_ON(current->blocked_on);
648+
current->blocked_on = lock;
647649
set_current_state(state);
648650
trace_contention_begin(lock, LCB_F_MUTEX);
649651
for (;;) {
@@ -680,6 +682,12 @@ __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclas
680682

681683
first = __mutex_waiter_is_first(lock, &waiter);
682684

685+
/*
686+
* As we likely have been woken up by task
687+
* that has cleared our blocked_on state, re-set
688+
* it to the lock we are trying to aquire.
689+
*/
690+
current->blocked_on = lock;
683691
set_current_state(state);
684692
/*
685693
* Here we order against unlock; we must either see it change
@@ -691,15 +699,19 @@ __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclas
691699

692700
if (first) {
693701
trace_contention_begin(lock, LCB_F_MUTEX | LCB_F_SPIN);
702+
/* clear blocked_on as mutex_optimistic_spin may schedule() */
703+
current->blocked_on = NULL;
694704
if (mutex_optimistic_spin(lock, ww_ctx, &waiter))
695705
break;
706+
current->blocked_on = lock;
696707
trace_contention_begin(lock, LCB_F_MUTEX);
697708
}
698709

699710
raw_spin_lock_irqsave(&lock->wait_lock, flags);
700711
}
701712
raw_spin_lock_irqsave(&lock->wait_lock, flags);
702713
acquired:
714+
current->blocked_on = NULL;
703715
__set_current_state(TASK_RUNNING);
704716

705717
if (ww_ctx) {
@@ -729,9 +741,11 @@ __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclas
729741
return 0;
730742

731743
err:
744+
current->blocked_on = NULL;
732745
__set_current_state(TASK_RUNNING);
733746
__mutex_remove_waiter(lock, &waiter);
734747
err_early_kill:
748+
WARN_ON(current->blocked_on);
735749
trace_contention_end(lock, ret);
736750
raw_spin_unlock_irqrestore_wake(&lock->wait_lock, flags, &wake_q);
737751
debug_mutex_free_waiter(&waiter);
@@ -942,6 +956,14 @@ static noinline void __sched __mutex_unlock_slowpath(struct mutex *lock, unsigne
942956
next = waiter->task;
943957

944958
debug_mutex_wake_waiter(lock, waiter);
959+
/*
960+
* Unlock wakeups can be happening in parallel
961+
* (when optimistic spinners steal and release
962+
* the lock), so blocked_on may already be
963+
* cleared here.
964+
*/
965+
WARN_ON(next->blocked_on && next->blocked_on != lock);
966+
next->blocked_on = NULL;
945967
wake_q_add(&wake_q, next);
946968
}
947969

kernel/locking/ww_mutex.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,15 @@ __ww_mutex_die(struct MUTEX *lock, struct MUTEX_WAITER *waiter,
283283
if (waiter->ww_ctx->acquired > 0 && __ww_ctx_less(waiter->ww_ctx, ww_ctx)) {
284284
#ifndef WW_RT
285285
debug_mutex_wake_waiter(lock, waiter);
286+
/*
287+
* When waking up the task to die, be sure to clear the
288+
* blocked_on pointer. Otherwise we can see circular
289+
* blocked_on relationships that can't resolve.
290+
*/
291+
WARN_ON(waiter->task->blocked_on &&
292+
waiter->task->blocked_on != lock);
286293
#endif
294+
waiter->task->blocked_on = NULL;
287295
wake_q_add(wake_q, waiter->task);
288296
}
289297

@@ -331,9 +339,15 @@ static bool __ww_mutex_wound(struct MUTEX *lock,
331339
* it's wounded in __ww_mutex_check_kill() or has a
332340
* wakeup pending to re-read the wounded state.
333341
*/
334-
if (owner != current)
342+
if (owner != current) {
343+
/*
344+
* When waking up the task to wound, be sure to clear the
345+
* blocked_on pointer. Otherwise we can see circular
346+
* blocked_on relationships that can't resolve.
347+
*/
348+
owner->blocked_on = NULL;
335349
wake_q_add(wake_q, owner);
336-
350+
}
337351
return true;
338352
}
339353

0 commit comments

Comments
 (0)