Skip to content

Commit d3487dd

Browse files
mhiramatintel-lab-lkp
authored andcommitted
hung_task: Show the blocker task if the task is hung on mutex
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> Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
1 parent 337369f commit d3487dd

File tree

5 files changed

+67
-0
lines changed

5 files changed

+67
-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: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,41 @@ 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+
#define debug_show_blocker(t) do {} while (0)
129+
#endif
130+
96131
static void check_hung_task(struct task_struct *t, unsigned long timeout)
97132
{
98133
unsigned long switch_count = t->nvcsw + t->nivcsw;
@@ -152,6 +187,7 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout)
152187
pr_err("\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\""
153188
" disables this message.\n");
154189
sched_show_task(t);
190+
debug_show_blocker(t);
155191
hung_task_show_lock = true;
156192

157193
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
*/
@@ -182,6 +190,9 @@ static void
182190
__mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter,
183191
struct list_head *list)
184192
{
193+
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
194+
WRITE_ONCE(current->blocker_mutex, lock);
195+
#endif
185196
debug_mutex_add_waiter(lock, waiter, current);
186197

187198
list_add_tail(&waiter->list, list);
@@ -197,6 +208,9 @@ __mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter)
197208
__mutex_clear_flag(lock, MUTEX_FLAGS);
198209

199210
debug_mutex_remove_waiter(lock, waiter, current);
211+
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
212+
WRITE_ONCE(current->blocker_mutex, NULL);
213+
#endif
200214
}
201215

202216
/*

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)