mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00

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 #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 #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> [akpm@linux-foundation.org: 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) <mhiramat@kernel.org> Reviewed-by: Waiman Long <longman@redhat.com> Reviewed-by: Lance Yang <ioworker0@gmail.com> Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org> Cc: Anna Schumaker <anna.schumaker@oracle.com> Cc: Boqun Feng <boqun.feng@gmail.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Joel Granados <joel.granados@kernel.org> Cc: Kent Overstreet <kent.overstreet@linux.dev> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Tomasz Figa <tfiga@chromium.org> Cc: Will Deacon <will@kernel.org> Cc: Yongliang Gao <leonylgao@tencent.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
208 lines
5.9 KiB
C
208 lines
5.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Mutexes: blocking mutual exclusion locks
|
|
*
|
|
* started by Ingo Molnar:
|
|
*
|
|
* Copyright (C) 2004, 2005, 2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
|
|
*
|
|
* This file contains the main data structure and API definitions.
|
|
*/
|
|
#ifndef __LINUX_MUTEX_H
|
|
#define __LINUX_MUTEX_H
|
|
|
|
#include <asm/current.h>
|
|
#include <linux/list.h>
|
|
#include <linux/spinlock_types.h>
|
|
#include <linux/lockdep.h>
|
|
#include <linux/atomic.h>
|
|
#include <asm/processor.h>
|
|
#include <linux/osq_lock.h>
|
|
#include <linux/debug_locks.h>
|
|
#include <linux/cleanup.h>
|
|
#include <linux/mutex_types.h>
|
|
|
|
struct device;
|
|
|
|
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
|
# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \
|
|
, .dep_map = { \
|
|
.name = #lockname, \
|
|
.wait_type_inner = LD_WAIT_SLEEP, \
|
|
}
|
|
#else
|
|
# define __DEP_MAP_MUTEX_INITIALIZER(lockname)
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEBUG_MUTEXES
|
|
|
|
# define __DEBUG_MUTEX_INITIALIZER(lockname) \
|
|
, .magic = &lockname
|
|
|
|
extern void mutex_destroy(struct mutex *lock);
|
|
|
|
#else
|
|
|
|
# define __DEBUG_MUTEX_INITIALIZER(lockname)
|
|
|
|
static inline void mutex_destroy(struct mutex *lock) {}
|
|
|
|
#endif
|
|
|
|
/**
|
|
* mutex_init - initialize the mutex
|
|
* @mutex: the mutex to be initialized
|
|
*
|
|
* Initialize the mutex to unlocked state.
|
|
*
|
|
* It is not allowed to initialize an already locked mutex.
|
|
*/
|
|
#define mutex_init(mutex) \
|
|
do { \
|
|
static struct lock_class_key __key; \
|
|
\
|
|
__mutex_init((mutex), #mutex, &__key); \
|
|
} while (0)
|
|
|
|
/**
|
|
* mutex_init_with_key - initialize a mutex with a given lockdep key
|
|
* @mutex: the mutex to be initialized
|
|
* @key: the lockdep key to be associated with the mutex
|
|
*
|
|
* Initialize the mutex to the unlocked state.
|
|
*
|
|
* It is not allowed to initialize an already locked mutex.
|
|
*/
|
|
#define mutex_init_with_key(mutex, key) __mutex_init((mutex), #mutex, (key))
|
|
|
|
#ifndef CONFIG_PREEMPT_RT
|
|
#define __MUTEX_INITIALIZER(lockname) \
|
|
{ .owner = ATOMIC_LONG_INIT(0) \
|
|
, .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(lockname.wait_lock) \
|
|
, .wait_list = LIST_HEAD_INIT(lockname.wait_list) \
|
|
__DEBUG_MUTEX_INITIALIZER(lockname) \
|
|
__DEP_MAP_MUTEX_INITIALIZER(lockname) }
|
|
|
|
#define DEFINE_MUTEX(mutexname) \
|
|
struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
|
|
|
|
extern void __mutex_init(struct mutex *lock, const char *name,
|
|
struct lock_class_key *key);
|
|
|
|
/**
|
|
* mutex_is_locked - is the mutex locked
|
|
* @lock: the mutex to be queried
|
|
*
|
|
* Returns true if the mutex is locked, false if unlocked.
|
|
*/
|
|
extern bool mutex_is_locked(struct mutex *lock);
|
|
|
|
#else /* !CONFIG_PREEMPT_RT */
|
|
/*
|
|
* Preempt-RT variant based on rtmutexes.
|
|
*/
|
|
|
|
#define __MUTEX_INITIALIZER(mutexname) \
|
|
{ \
|
|
.rtmutex = __RT_MUTEX_BASE_INITIALIZER(mutexname.rtmutex) \
|
|
__DEP_MAP_MUTEX_INITIALIZER(mutexname) \
|
|
}
|
|
|
|
#define DEFINE_MUTEX(mutexname) \
|
|
struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
|
|
|
|
extern void __mutex_rt_init(struct mutex *lock, const char *name,
|
|
struct lock_class_key *key);
|
|
|
|
#define mutex_is_locked(l) rt_mutex_base_is_locked(&(l)->rtmutex)
|
|
|
|
#define __mutex_init(mutex, name, key) \
|
|
do { \
|
|
rt_mutex_base_init(&(mutex)->rtmutex); \
|
|
__mutex_rt_init((mutex), name, key); \
|
|
} while (0)
|
|
|
|
#endif /* CONFIG_PREEMPT_RT */
|
|
|
|
#ifdef CONFIG_DEBUG_MUTEXES
|
|
|
|
int __devm_mutex_init(struct device *dev, struct mutex *lock);
|
|
|
|
#else
|
|
|
|
static inline int __devm_mutex_init(struct device *dev, struct mutex *lock)
|
|
{
|
|
/*
|
|
* When CONFIG_DEBUG_MUTEXES is off mutex_destroy() is just a nop so
|
|
* no really need to register it in the devm subsystem.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
#define devm_mutex_init(dev, mutex) \
|
|
({ \
|
|
typeof(mutex) mutex_ = (mutex); \
|
|
\
|
|
mutex_init(mutex_); \
|
|
__devm_mutex_init(dev, mutex_); \
|
|
})
|
|
|
|
/*
|
|
* See kernel/locking/mutex.c for detailed documentation of these APIs.
|
|
* Also see Documentation/locking/mutex-design.rst.
|
|
*/
|
|
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
|
extern void mutex_lock_nested(struct mutex *lock, unsigned int subclass);
|
|
extern void _mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest_lock);
|
|
|
|
extern int __must_check mutex_lock_interruptible_nested(struct mutex *lock,
|
|
unsigned int subclass);
|
|
extern int __must_check mutex_lock_killable_nested(struct mutex *lock,
|
|
unsigned int subclass);
|
|
extern void mutex_lock_io_nested(struct mutex *lock, unsigned int subclass);
|
|
|
|
#define mutex_lock(lock) mutex_lock_nested(lock, 0)
|
|
#define mutex_lock_interruptible(lock) mutex_lock_interruptible_nested(lock, 0)
|
|
#define mutex_lock_killable(lock) mutex_lock_killable_nested(lock, 0)
|
|
#define mutex_lock_io(lock) mutex_lock_io_nested(lock, 0)
|
|
|
|
#define mutex_lock_nest_lock(lock, nest_lock) \
|
|
do { \
|
|
typecheck(struct lockdep_map *, &(nest_lock)->dep_map); \
|
|
_mutex_lock_nest_lock(lock, &(nest_lock)->dep_map); \
|
|
} while (0)
|
|
|
|
#else
|
|
extern void mutex_lock(struct mutex *lock);
|
|
extern int __must_check mutex_lock_interruptible(struct mutex *lock);
|
|
extern int __must_check mutex_lock_killable(struct mutex *lock);
|
|
extern void mutex_lock_io(struct mutex *lock);
|
|
|
|
# define mutex_lock_nested(lock, subclass) mutex_lock(lock)
|
|
# define mutex_lock_interruptible_nested(lock, subclass) mutex_lock_interruptible(lock)
|
|
# define mutex_lock_killable_nested(lock, subclass) mutex_lock_killable(lock)
|
|
# define mutex_lock_nest_lock(lock, nest_lock) mutex_lock(lock)
|
|
# define mutex_lock_io_nested(lock, subclass) mutex_lock_io(lock)
|
|
#endif
|
|
|
|
/*
|
|
* NOTE: mutex_trylock() follows the spin_trylock() convention,
|
|
* not the down_trylock() convention!
|
|
*
|
|
* Returns 1 if the mutex has been acquired successfully, and 0 on contention.
|
|
*/
|
|
extern int mutex_trylock(struct mutex *lock);
|
|
extern void mutex_unlock(struct mutex *lock);
|
|
|
|
extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
|
|
|
|
DEFINE_GUARD(mutex, struct mutex *, mutex_lock(_T), mutex_unlock(_T))
|
|
DEFINE_GUARD_COND(mutex, _try, mutex_trylock(_T))
|
|
DEFINE_GUARD_COND(mutex, _intr, mutex_lock_interruptible(_T) == 0)
|
|
|
|
extern unsigned long mutex_get_owner(struct mutex *lock);
|
|
|
|
#endif /* __LINUX_MUTEX_H */
|