Skip to content

Commit 3cf67d6

Browse files
mhiramatakpm00
authored andcommitted
hung_task: show the blocker task if the task is hung on mutex
Patch series "hung_task: Dump the blocking task stacktrace", v4. The hung_task detector is very useful for detecting the lockup. However, since it only dumps the blocked (uninterruptible sleep) processes, it is not enough to identify the root cause of that lockup. For example, if a process holds a mutex and sleep an event in interruptible state long time, the other processes will wait on the mutex in uninterruptible state. In this case, the waiter processes are dumped, but the blocker process is not shown because it is sleep in interruptible state. This adds a feature to dump the blocker task which holds a mutex when detecting a hung task. e.g. INFO: task cat:115 blocked for more than 122 seconds. Not tainted 6.14.0-rc3-00003-ga8946be3de00 torvalds#156 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:cat state:D stack:13432 pid:115 tgid:115 ppid:106 task_flags:0x400100 flags:0x00000002 Call Trace: <TASK> __schedule+0x731/0x960 ? schedule_preempt_disabled+0x54/0xa0 schedule+0xb7/0x140 ? __mutex_lock+0x51b/0xa60 ? __mutex_lock+0x51b/0xa60 schedule_preempt_disabled+0x54/0xa0 __mutex_lock+0x51b/0xa60 read_dummy+0x23/0x70 full_proxy_read+0x6a/0xc0 vfs_read+0xc2/0x340 ? __pfx_direct_file_splice_eof+0x10/0x10 ? do_sendfile+0x1bd/0x2e0 ksys_read+0x76/0xe0 do_syscall_64+0xe3/0x1c0 ? exc_page_fault+0xa9/0x1d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x4840cd RSP: 002b:00007ffe99071828 EFLAGS: 00000246 ORIG_RAX: 0000000000000000 RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00000000004840cd RDX: 0000000000001000 RSI: 00007ffe99071870 RDI: 0000000000000003 RBP: 00007ffe99071870 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000001000000 R11: 0000000000000246 R12: 0000000000001000 R13: 00000000132fd3a0 R14: 0000000000000001 R15: ffffffffffffffff </TASK> INFO: task cat:115 is blocked on a mutex likely owned by task cat:114. task:cat state:S stack:13432 pid:114 tgid:114 ppid:106 task_flags:0x400100 flags:0x00000002 Call Trace: <TASK> __schedule+0x731/0x960 ? schedule_timeout+0xa8/0x120 schedule+0xb7/0x140 schedule_timeout+0xa8/0x120 ? __pfx_process_timeout+0x10/0x10 msleep_interruptible+0x3e/0x60 read_dummy+0x2d/0x70 full_proxy_read+0x6a/0xc0 vfs_read+0xc2/0x340 ? __pfx_direct_file_splice_eof+0x10/0x10 ? do_sendfile+0x1bd/0x2e0 ksys_read+0x76/0xe0 do_syscall_64+0xe3/0x1c0 ? exc_page_fault+0xa9/0x1d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x4840cd RSP: 002b:00007ffe3e0147b8 EFLAGS: 00000246 ORIG_RAX: 0000000000000000 RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00000000004840cd RDX: 0000000000001000 RSI: 00007ffe3e014800 RDI: 0000000000000003 RBP: 00007ffe3e014800 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000001000000 R11: 0000000000000246 R12: 0000000000001000 R13: 000000001a0a93a0 R14: 0000000000000001 R15: ffffffffffffffff </TASK> TBD: We can extend this feature to cover other locks like rwsem and rt_mutex, but rwsem requires to dump all the tasks which acquire and wait that rwsem. We can follow the waiter link but the output will be a bit different compared with mutex case. This patch (of 2): The "hung_task" shows a long-time uninterruptible slept task, but most often, it's blocked on a mutex acquired by another task. Without dumping such a task, investigating the root cause of the hung task problem is very difficult. This introduce task_struct::blocker_mutex to point the mutex lock which this task is waiting for. Since the mutex has "owner" information, we can find the owner task and dump it with hung tasks. Note: the owner can be changed while dumping the owner task, so this is "likely" the owner of the mutex. With this change, the hung task shows blocker task's info like below; INFO: task cat:115 blocked for more than 122 seconds. Not tainted 6.14.0-rc3-00003-ga8946be3de00 torvalds#156 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:cat state:D stack:13432 pid:115 tgid:115 ppid:106 task_flags:0x400100 flags:0x00000002 Call Trace: <TASK> __schedule+0x731/0x960 ? schedule_preempt_disabled+0x54/0xa0 schedule+0xb7/0x140 ? __mutex_lock+0x51b/0xa60 ? __mutex_lock+0x51b/0xa60 schedule_preempt_disabled+0x54/0xa0 __mutex_lock+0x51b/0xa60 read_dummy+0x23/0x70 full_proxy_read+0x6a/0xc0 vfs_read+0xc2/0x340 ? __pfx_direct_file_splice_eof+0x10/0x10 ? do_sendfile+0x1bd/0x2e0 ksys_read+0x76/0xe0 do_syscall_64+0xe3/0x1c0 ? exc_page_fault+0xa9/0x1d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x4840cd RSP: 002b:00007ffe99071828 EFLAGS: 00000246 ORIG_RAX: 0000000000000000 RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00000000004840cd RDX: 0000000000001000 RSI: 00007ffe99071870 RDI: 0000000000000003 RBP: 00007ffe99071870 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000001000000 R11: 0000000000000246 R12: 0000000000001000 R13: 00000000132fd3a0 R14: 0000000000000001 R15: ffffffffffffffff </TASK> INFO: task cat:115 is blocked on a mutex likely owned by task cat:114. task:cat state:S stack:13432 pid:114 tgid:114 ppid:106 task_flags:0x400100 flags:0x00000002 Call Trace: <TASK> __schedule+0x731/0x960 ? schedule_timeout+0xa8/0x120 schedule+0xb7/0x140 schedule_timeout+0xa8/0x120 ? __pfx_process_timeout+0x10/0x10 msleep_interruptible+0x3e/0x60 read_dummy+0x2d/0x70 full_proxy_read+0x6a/0xc0 vfs_read+0xc2/0x340 ? __pfx_direct_file_splice_eof+0x10/0x10 ? do_sendfile+0x1bd/0x2e0 ksys_read+0x76/0xe0 do_syscall_64+0xe3/0x1c0 ? exc_page_fault+0xa9/0x1d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x4840cd RSP: 002b:00007ffe3e0147b8 EFLAGS: 00000246 ORIG_RAX: 0000000000000000 RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00000000004840cd RDX: 0000000000001000 RSI: 00007ffe3e014800 RDI: 0000000000000003 RBP: 00007ffe3e014800 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000001000000 R11: 0000000000000246 R12: 0000000000001000 R13: 000000001a0a93a0 R14: 0000000000000001 R15: ffffffffffffffff </TASK> [[email protected]: implement debug_show_blocker() in C rather than in CPP] Link: https://lkml.kernel.org/r/174046694331.2194069.15472952050240807469.stgit@mhiramat.tok.corp.google.com Link: https://lkml.kernel.org/r/174046695384.2194069.16796289525958195643.stgit@mhiramat.tok.corp.google.com Signed-off-by: Masami Hiramatsu (Google) <[email protected]> Reviewed-by: Waiman Long <[email protected]> Reviewed-by: Lance Yang <[email protected]> Reviewed-by: Sergey Senozhatsky <[email protected]> Cc: Anna Schumaker <[email protected]> Cc: Boqun Feng <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Joel Granados <[email protected]> Cc: Kent Overstreet <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: Tomasz Figa <[email protected]> Cc: Will Deacon <[email protected]> Cc: Yongliang Gao <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent caeb8ba commit 3cf67d6

File tree

5 files changed

+69
-0
lines changed

5 files changed

+69
-0
lines changed

include/linux/mutex.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,6 @@ DEFINE_GUARD(mutex, struct mutex *, mutex_lock(_T), mutex_unlock(_T))
202202
DEFINE_GUARD_COND(mutex, _try, mutex_trylock(_T))
203203
DEFINE_GUARD_COND(mutex, _intr, mutex_lock_interruptible(_T) == 0)
204204

205+
extern unsigned long mutex_get_owner(struct mutex *lock);
206+
205207
#endif /* __LINUX_MUTEX_H */

include/linux/sched.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,10 @@ struct task_struct {
12171217
struct mutex_waiter *blocked_on;
12181218
#endif
12191219

1220+
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
1221+
struct mutex *blocker_mutex;
1222+
#endif
1223+
12201224
#ifdef CONFIG_DEBUG_ATOMIC_SLEEP
12211225
int non_block_count;
12221226
#endif

kernel/hung_task.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,43 @@ static struct notifier_block panic_block = {
9393
.notifier_call = hung_task_panic,
9494
};
9595

96+
97+
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
98+
static void debug_show_blocker(struct task_struct *task)
99+
{
100+
struct task_struct *g, *t;
101+
unsigned long owner;
102+
struct mutex *lock;
103+
104+
RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "No rcu lock held");
105+
106+
lock = READ_ONCE(task->blocker_mutex);
107+
if (!lock)
108+
return;
109+
110+
owner = mutex_get_owner(lock);
111+
if (unlikely(!owner)) {
112+
pr_err("INFO: task %s:%d is blocked on a mutex, but the owner is not found.\n",
113+
task->comm, task->pid);
114+
return;
115+
}
116+
117+
/* Ensure the owner information is correct. */
118+
for_each_process_thread(g, t) {
119+
if ((unsigned long)t == owner) {
120+
pr_err("INFO: task %s:%d is blocked on a mutex likely owned by task %s:%d.\n",
121+
task->comm, task->pid, t->comm, t->pid);
122+
sched_show_task(t);
123+
return;
124+
}
125+
}
126+
}
127+
#else
128+
static inline void debug_show_blocker(struct task_struct *task)
129+
{
130+
}
131+
#endif
132+
96133
static void check_hung_task(struct task_struct *t, unsigned long timeout)
97134
{
98135
unsigned long switch_count = t->nvcsw + t->nivcsw;
@@ -152,6 +189,7 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout)
152189
pr_err("\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\""
153190
" disables this message.\n");
154191
sched_show_task(t);
192+
debug_show_blocker(t);
155193
hung_task_show_lock = true;
156194

157195
if (sysctl_hung_task_all_cpu_backtrace)

kernel/locking/mutex.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ static inline unsigned long __owner_flags(unsigned long owner)
7272
return owner & MUTEX_FLAGS;
7373
}
7474

75+
/* Do not use the return value as a pointer directly. */
76+
unsigned long mutex_get_owner(struct mutex *lock)
77+
{
78+
unsigned long owner = atomic_long_read(&lock->owner);
79+
80+
return (unsigned long)__owner_task(owner);
81+
}
82+
7583
/*
7684
* Returns: __mutex_owner(lock) on failure or NULL on success.
7785
*/
@@ -180,6 +188,9 @@ static void
180188
__mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter,
181189
struct list_head *list)
182190
{
191+
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
192+
WRITE_ONCE(current->blocker_mutex, lock);
193+
#endif
183194
debug_mutex_add_waiter(lock, waiter, current);
184195

185196
list_add_tail(&waiter->list, list);
@@ -195,6 +206,9 @@ __mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter)
195206
__mutex_clear_flag(lock, MUTEX_FLAGS);
196207

197208
debug_mutex_remove_waiter(lock, waiter, current);
209+
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
210+
WRITE_ONCE(current->blocker_mutex, NULL);
211+
#endif
198212
}
199213

200214
/*

lib/Kconfig.debug

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,6 +1260,17 @@ config BOOTPARAM_HUNG_TASK_PANIC
12601260

12611261
Say N if unsure.
12621262

1263+
config DETECT_HUNG_TASK_BLOCKER
1264+
bool "Dump Hung Tasks Blocker"
1265+
depends on DETECT_HUNG_TASK
1266+
depends on !PREEMPT_RT
1267+
default y
1268+
help
1269+
Say Y here to show the blocker task's stacktrace who acquires
1270+
the mutex lock which "hung tasks" are waiting.
1271+
This will add overhead a bit but shows suspicious tasks and
1272+
call trace if it comes from waiting a mutex.
1273+
12631274
config WQ_WATCHDOG
12641275
bool "Detect Workqueue Stalls"
12651276
depends on DEBUG_KERNEL

0 commit comments

Comments
 (0)