Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sched: avoid false lockdep splat in put_task_struct()

In put_task_struct(), a spin_lock is indirectly acquired under the kernel
stock. When running the kernel in real-time (RT) configuration, the
operation is dispatched to a preemptible context call to ensure
guaranteed preemption. However, if PROVE_RAW_LOCK_NESTING is enabled
and __put_task_struct() is called while holding a raw_spinlock, lockdep
incorrectly reports an "Invalid lock context" in the stock kernel.

This false splat occurs because lockdep is unaware of the different
route taken under RT. To address this issue, override the inner wait
type to prevent the false lockdep splat.

Suggested-by: Oleg Nesterov <oleg@redhat.com>
Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Wander Lairson Costa <wander@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20230614122323.37957-3-wander@redhat.com

authored by

Wander Lairson Costa and committed by
Peter Zijlstra
893cdaaa d243b344

+14 -4
+14 -4
include/linux/sched/task.h
··· 126 126 return; 127 127 128 128 /* 129 + * In !RT, it is always safe to call __put_task_struct(). 130 + * Under RT, we can only call it in preemptible context. 131 + */ 132 + if (!IS_ENABLED(CONFIG_PREEMPT_RT) || preemptible()) { 133 + static DEFINE_WAIT_OVERRIDE_MAP(put_task_map, LD_WAIT_SLEEP); 134 + 135 + lock_map_acquire_try(&put_task_map); 136 + __put_task_struct(t); 137 + lock_map_release(&put_task_map); 138 + return; 139 + } 140 + 141 + /* 129 142 * under PREEMPT_RT, we can't call put_task_struct 130 143 * in atomic context because it will indirectly 131 144 * acquire sleeping locks. ··· 158 145 * when it fails to fork a process. Therefore, there is no 159 146 * way it can conflict with put_task_struct(). 160 147 */ 161 - if (IS_ENABLED(CONFIG_PREEMPT_RT) && !preemptible()) 162 - call_rcu(&t->rcu, __put_task_struct_rcu_cb); 163 - else 164 - __put_task_struct(t); 148 + call_rcu(&t->rcu, __put_task_struct_rcu_cb); 165 149 } 166 150 167 151 DEFINE_FREE(put_task, struct task_struct *, if (_T) put_task_struct(_T))