Skip to content

Commit 77da18d

Browse files
ioworker0akpm00
authored andcommitted
hung_task: extend hung task blocker tracking to rwsems
Inspired by mutex blocker tracking[1], and having already extended it to semaphores, let's now add support for reader-writer semaphores (rwsems). The approach is simple: when a task enters TASK_UNINTERRUPTIBLE while waiting for an rwsem, we just call hung_task_set_blocker(). The hung task detector can then query the rwsem's owner to identify the lock holder. Tracking works reliably for writers, as there can only be a single writer holding the lock, and its task struct is stored in the owner field. The main challenge lies with readers. The owner field points to only one of many concurrent readers, so we might lose track of the blocker if that specific reader unlocks, even while others remain. This is not a significant issue, however. In practice, long-lasting lock contention is almost always caused by a writer. Therefore, reliably tracking the writer is the primary goal of this patch series ;) With this change, the hung task detector can now show blocker task's info like below: [Fri Jun 27 15:21:34 2025] INFO: task cat:28631 blocked for more than 122 seconds. [Fri Jun 27 15:21:34 2025] Tainted: G S 6.16.0-rc3 #8 [Fri Jun 27 15:21:34 2025] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [Fri Jun 27 15:21:34 2025] task:cat state:D stack:0 pid:28631 tgid:28631 ppid:28501 task_flags:0x400000 flags:0x00004000 [Fri Jun 27 15:21:34 2025] Call Trace: [Fri Jun 27 15:21:34 2025] <TASK> [Fri Jun 27 15:21:34 2025] __schedule+0x7c7/0x1930 [Fri Jun 27 15:21:34 2025] ? __pfx___schedule+0x10/0x10 [Fri Jun 27 15:21:34 2025] ? policy_nodemask+0x215/0x340 [Fri Jun 27 15:21:34 2025] ? _raw_spin_lock_irq+0x8a/0xe0 [Fri Jun 27 15:21:34 2025] ? __pfx__raw_spin_lock_irq+0x10/0x10 [Fri Jun 27 15:21:34 2025] schedule+0x6a/0x180 [Fri Jun 27 15:21:34 2025] schedule_preempt_disabled+0x15/0x30 [Fri Jun 27 15:21:34 2025] rwsem_down_read_slowpath+0x55e/0xe10 [Fri Jun 27 15:21:34 2025] ? __pfx_rwsem_down_read_slowpath+0x10/0x10 [Fri Jun 27 15:21:34 2025] ? __pfx___might_resched+0x10/0x10 [Fri Jun 27 15:21:34 2025] down_read+0xc9/0x230 [Fri Jun 27 15:21:34 2025] ? __pfx_down_read+0x10/0x10 [Fri Jun 27 15:21:34 2025] ? __debugfs_file_get+0x14d/0x700 [Fri Jun 27 15:21:34 2025] ? __pfx___debugfs_file_get+0x10/0x10 [Fri Jun 27 15:21:34 2025] ? handle_pte_fault+0x52a/0x710 [Fri Jun 27 15:21:34 2025] ? selinux_file_permission+0x3a9/0x590 [Fri Jun 27 15:21:34 2025] read_dummy_rwsem_read+0x4a/0x90 [Fri Jun 27 15:21:34 2025] full_proxy_read+0xff/0x1c0 [Fri Jun 27 15:21:34 2025] ? rw_verify_area+0x6d/0x410 [Fri Jun 27 15:21:34 2025] vfs_read+0x177/0xa50 [Fri Jun 27 15:21:34 2025] ? __pfx_vfs_read+0x10/0x10 [Fri Jun 27 15:21:34 2025] ? fdget_pos+0x1cf/0x4c0 [Fri Jun 27 15:21:34 2025] ksys_read+0xfc/0x1d0 [Fri Jun 27 15:21:34 2025] ? __pfx_ksys_read+0x10/0x10 [Fri Jun 27 15:21:34 2025] do_syscall_64+0x66/0x2d0 [Fri Jun 27 15:21:34 2025] entry_SYSCALL_64_after_hwframe+0x76/0x7e [Fri Jun 27 15:21:34 2025] RIP: 0033:0x7f3f8faefb40 [Fri Jun 27 15:21:34 2025] RSP: 002b:00007ffdeda5ab98 EFLAGS: 00000246 ORIG_RAX: 0000000000000000 [Fri Jun 27 15:21:34 2025] RAX: ffffffffffffffda RBX: 0000000000010000 RCX: 00007f3f8faefb40 [Fri Jun 27 15:21:34 2025] RDX: 0000000000010000 RSI: 00000000010fa000 RDI: 0000000000000003 [Fri Jun 27 15:21:34 2025] RBP: 00000000010fa000 R08: 0000000000000000 R09: 0000000000010fff [Fri Jun 27 15:21:34 2025] R10: 00007ffdeda59fe0 R11: 0000000000000246 R12: 00000000010fa000 [Fri Jun 27 15:21:34 2025] R13: 0000000000000003 R14: 0000000000000000 R15: 0000000000000fff [Fri Jun 27 15:21:34 2025] </TASK> [Fri Jun 27 15:21:34 2025] INFO: task cat:28631 <reader> blocked on an rw-semaphore likely owned by task cat:28630 <writer> [Fri Jun 27 15:21:34 2025] task:cat state:S stack:0 pid:28630 tgid:28630 ppid:28501 task_flags:0x400000 flags:0x00004000 [Fri Jun 27 15:21:34 2025] Call Trace: [Fri Jun 27 15:21:34 2025] <TASK> [Fri Jun 27 15:21:34 2025] __schedule+0x7c7/0x1930 [Fri Jun 27 15:21:34 2025] ? __pfx___schedule+0x10/0x10 [Fri Jun 27 15:21:34 2025] ? __mod_timer+0x304/0xa80 [Fri Jun 27 15:21:34 2025] schedule+0x6a/0x180 [Fri Jun 27 15:21:34 2025] schedule_timeout+0xfb/0x230 [Fri Jun 27 15:21:34 2025] ? __pfx_schedule_timeout+0x10/0x10 [Fri Jun 27 15:21:34 2025] ? __pfx_process_timeout+0x10/0x10 [Fri Jun 27 15:21:34 2025] ? down_write+0xc4/0x140 [Fri Jun 27 15:21:34 2025] msleep_interruptible+0xbe/0x150 [Fri Jun 27 15:21:34 2025] read_dummy_rwsem_write+0x54/0x90 [Fri Jun 27 15:21:34 2025] full_proxy_read+0xff/0x1c0 [Fri Jun 27 15:21:34 2025] ? rw_verify_area+0x6d/0x410 [Fri Jun 27 15:21:34 2025] vfs_read+0x177/0xa50 [Fri Jun 27 15:21:34 2025] ? __pfx_vfs_read+0x10/0x10 [Fri Jun 27 15:21:34 2025] ? fdget_pos+0x1cf/0x4c0 [Fri Jun 27 15:21:34 2025] ksys_read+0xfc/0x1d0 [Fri Jun 27 15:21:34 2025] ? __pfx_ksys_read+0x10/0x10 [Fri Jun 27 15:21:34 2025] do_syscall_64+0x66/0x2d0 [Fri Jun 27 15:21:34 2025] entry_SYSCALL_64_after_hwframe+0x76/0x7e [Fri Jun 27 15:21:34 2025] RIP: 0033:0x7f8f288efb40 [Fri Jun 27 15:21:34 2025] RSP: 002b:00007ffffb631038 EFLAGS: 00000246 ORIG_RAX: 0000000000000000 [Fri Jun 27 15:21:34 2025] RAX: ffffffffffffffda RBX: 0000000000010000 RCX: 00007f8f288efb40 [Fri Jun 27 15:21:34 2025] RDX: 0000000000010000 RSI: 000000002a4b5000 RDI: 0000000000000003 [Fri Jun 27 15:21:34 2025] RBP: 000000002a4b5000 R08: 0000000000000000 R09: 0000000000010fff [Fri Jun 27 15:21:34 2025] R10: 00007ffffb630460 R11: 0000000000000246 R12: 000000002a4b5000 [Fri Jun 27 15:21:34 2025] R13: 0000000000000003 R14: 0000000000000000 R15: 0000000000000fff [Fri Jun 27 15:21:34 2025] </TASK> [1] https://lore.kernel.org/all/174046694331.2194069.15472952050240807469.stgit@mhiramat.tok.corp.google.com/ Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Lance Yang <[email protected]> Suggested-by: Masami Hiramatsu (Google) <[email protected]> Reviewed-by: Masami Hiramatsu (Google) <[email protected]> Cc: Anna Schumaker <[email protected]> Cc: Boqun Feng <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Joel Granados <[email protected]> Cc: John Stultz <[email protected]> Cc: Kent Overstreet <[email protected]> Cc: Mingzhe Yang <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Sergey Senozhatsky <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: Tomasz Figa <[email protected]> Cc: Waiman Long <[email protected]> Cc: Will Deacon <[email protected]> Cc: Yongliang Gao <[email protected]> Cc: Zi Li <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent ae2da51 commit 77da18d

File tree

3 files changed

+50
-14
lines changed

3 files changed

+50
-14
lines changed

include/linux/hung_task.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@
2121
* type.
2222
*
2323
* Type encoding:
24-
* 00 - Blocked on mutex (BLOCKER_TYPE_MUTEX)
25-
* 01 - Blocked on semaphore (BLOCKER_TYPE_SEM)
26-
* 10 - Blocked on rt-mutex (BLOCKER_TYPE_RTMUTEX)
27-
* 11 - Blocked on rw-semaphore (BLOCKER_TYPE_RWSEM)
24+
* 00 - Blocked on mutex (BLOCKER_TYPE_MUTEX)
25+
* 01 - Blocked on semaphore (BLOCKER_TYPE_SEM)
26+
* 10 - Blocked on rw-semaphore as READER (BLOCKER_TYPE_RWSEM_READER)
27+
* 11 - Blocked on rw-semaphore as WRITER (BLOCKER_TYPE_RWSEM_WRITER)
2828
*/
29-
#define BLOCKER_TYPE_MUTEX 0x00UL
30-
#define BLOCKER_TYPE_SEM 0x01UL
31-
#define BLOCKER_TYPE_RTMUTEX 0x02UL
32-
#define BLOCKER_TYPE_RWSEM 0x03UL
29+
#define BLOCKER_TYPE_MUTEX 0x00UL
30+
#define BLOCKER_TYPE_SEM 0x01UL
31+
#define BLOCKER_TYPE_RWSEM_READER 0x02UL
32+
#define BLOCKER_TYPE_RWSEM_WRITER 0x03UL
3333

34-
#define BLOCKER_TYPE_MASK 0x03UL
34+
#define BLOCKER_TYPE_MASK 0x03UL
3535

3636
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
3737
static inline void hung_task_set_blocker(void *lock, unsigned long type)

kernel/hung_task.c

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/sched/debug.h>
2424
#include <linux/sched/sysctl.h>
2525
#include <linux/hung_task.h>
26+
#include <linux/rwsem.h>
2627

2728
#include <trace/events/sched.h>
2829

@@ -100,6 +101,7 @@ static void debug_show_blocker(struct task_struct *task)
100101
{
101102
struct task_struct *g, *t;
102103
unsigned long owner, blocker, blocker_type;
104+
const char *rwsem_blocked_by, *rwsem_blocked_as;
103105

104106
RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "No rcu lock held");
105107

@@ -111,12 +113,20 @@ static void debug_show_blocker(struct task_struct *task)
111113

112114
switch (blocker_type) {
113115
case BLOCKER_TYPE_MUTEX:
114-
owner = mutex_get_owner(
115-
(struct mutex *)hung_task_blocker_to_lock(blocker));
116+
owner = mutex_get_owner(hung_task_blocker_to_lock(blocker));
116117
break;
117118
case BLOCKER_TYPE_SEM:
118-
owner = sem_last_holder(
119-
(struct semaphore *)hung_task_blocker_to_lock(blocker));
119+
owner = sem_last_holder(hung_task_blocker_to_lock(blocker));
120+
break;
121+
case BLOCKER_TYPE_RWSEM_READER:
122+
case BLOCKER_TYPE_RWSEM_WRITER:
123+
owner = (unsigned long)rwsem_owner(
124+
hung_task_blocker_to_lock(blocker));
125+
rwsem_blocked_as = (blocker_type == BLOCKER_TYPE_RWSEM_READER) ?
126+
"reader" : "writer";
127+
rwsem_blocked_by = is_rwsem_reader_owned(
128+
hung_task_blocker_to_lock(blocker)) ?
129+
"reader" : "writer";
120130
break;
121131
default:
122132
WARN_ON_ONCE(1);
@@ -134,6 +144,11 @@ static void debug_show_blocker(struct task_struct *task)
134144
pr_err("INFO: task %s:%d is blocked on a semaphore, but the last holder is not found.\n",
135145
task->comm, task->pid);
136146
break;
147+
case BLOCKER_TYPE_RWSEM_READER:
148+
case BLOCKER_TYPE_RWSEM_WRITER:
149+
pr_err("INFO: task %s:%d is blocked on an rw-semaphore, but the owner is not found.\n",
150+
task->comm, task->pid);
151+
break;
137152
}
138153
return;
139154
}
@@ -152,6 +167,12 @@ static void debug_show_blocker(struct task_struct *task)
152167
pr_err("INFO: task %s:%d blocked on a semaphore likely last held by task %s:%d\n",
153168
task->comm, task->pid, t->comm, t->pid);
154169
break;
170+
case BLOCKER_TYPE_RWSEM_READER:
171+
case BLOCKER_TYPE_RWSEM_WRITER:
172+
pr_err("INFO: task %s:%d <%s> blocked on an rw-semaphore likely owned by task %s:%d <%s>\n",
173+
task->comm, task->pid, rwsem_blocked_as, t->comm,
174+
t->pid, rwsem_blocked_by);
175+
break;
155176
}
156177
sched_show_task(t);
157178
return;

kernel/locking/rwsem.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/export.h>
2828
#include <linux/rwsem.h>
2929
#include <linux/atomic.h>
30+
#include <linux/hung_task.h>
3031
#include <trace/events/lock.h>
3132

3233
#ifndef CONFIG_PREEMPT_RT
@@ -1065,10 +1066,13 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, long count, unsigned int stat
10651066
wake_up_q(&wake_q);
10661067

10671068
trace_contention_begin(sem, LCB_F_READ);
1069+
set_current_state(state);
1070+
1071+
if (state == TASK_UNINTERRUPTIBLE)
1072+
hung_task_set_blocker(sem, BLOCKER_TYPE_RWSEM_READER);
10681073

10691074
/* wait to be given the lock */
10701075
for (;;) {
1071-
set_current_state(state);
10721076
if (!smp_load_acquire(&waiter.task)) {
10731077
/* Matches rwsem_mark_wake()'s smp_store_release(). */
10741078
break;
@@ -1083,8 +1087,12 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, long count, unsigned int stat
10831087
}
10841088
schedule_preempt_disabled();
10851089
lockevent_inc(rwsem_sleep_reader);
1090+
set_current_state(state);
10861091
}
10871092

1093+
if (state == TASK_UNINTERRUPTIBLE)
1094+
hung_task_clear_blocker();
1095+
10881096
__set_current_state(TASK_RUNNING);
10891097
lockevent_inc(rwsem_rlock);
10901098
trace_contention_end(sem, 0);
@@ -1146,6 +1154,9 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
11461154
set_current_state(state);
11471155
trace_contention_begin(sem, LCB_F_WRITE);
11481156

1157+
if (state == TASK_UNINTERRUPTIBLE)
1158+
hung_task_set_blocker(sem, BLOCKER_TYPE_RWSEM_WRITER);
1159+
11491160
for (;;) {
11501161
if (rwsem_try_write_lock(sem, &waiter)) {
11511162
/* rwsem_try_write_lock() implies ACQUIRE on success */
@@ -1179,6 +1190,10 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
11791190
trylock_again:
11801191
raw_spin_lock_irq(&sem->wait_lock);
11811192
}
1193+
1194+
if (state == TASK_UNINTERRUPTIBLE)
1195+
hung_task_clear_blocker();
1196+
11821197
__set_current_state(TASK_RUNNING);
11831198
raw_spin_unlock_irq(&sem->wait_lock);
11841199
lockevent_inc(rwsem_wlock);

0 commit comments

Comments
 (0)