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

Configure Feed

Select the types of activity you want to include in your feed.

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/20250627072924.36567-3-lance.yang@linux.dev
Signed-off-by: Lance Yang <lance.yang@linux.dev>
Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.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: John Stultz <jstultz@google.com>
Cc: Kent Overstreet <kent.overstreet@linux.dev>
Cc: Mingzhe Yang <mingzhe.yang@ly.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Tomasz Figa <tfiga@chromium.org>
Cc: Waiman Long <longman@redhat.com>
Cc: Will Deacon <will@kernel.org>
Cc: Yongliang Gao <leonylgao@tencent.com>
Cc: Zi Li <zi.li@linux.dev>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Lance Yang and committed by
Andrew Morton
77da18de ae2da51d

+50 -14
+9 -9
include/linux/hung_task.h
··· 21 21 * type. 22 22 * 23 23 * 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) 28 28 */ 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 33 33 34 - #define BLOCKER_TYPE_MASK 0x03UL 34 + #define BLOCKER_TYPE_MASK 0x03UL 35 35 36 36 #ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER 37 37 static inline void hung_task_set_blocker(void *lock, unsigned long type)
+25 -4
kernel/hung_task.c
··· 23 23 #include <linux/sched/debug.h> 24 24 #include <linux/sched/sysctl.h> 25 25 #include <linux/hung_task.h> 26 + #include <linux/rwsem.h> 26 27 27 28 #include <trace/events/sched.h> 28 29 ··· 101 100 { 102 101 struct task_struct *g, *t; 103 102 unsigned long owner, blocker, blocker_type; 103 + const char *rwsem_blocked_by, *rwsem_blocked_as; 104 104 105 105 RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "No rcu lock held"); 106 106 ··· 113 111 114 112 switch (blocker_type) { 115 113 case BLOCKER_TYPE_MUTEX: 116 - owner = mutex_get_owner( 117 - (struct mutex *)hung_task_blocker_to_lock(blocker)); 114 + owner = mutex_get_owner(hung_task_blocker_to_lock(blocker)); 118 115 break; 119 116 case BLOCKER_TYPE_SEM: 120 - owner = sem_last_holder( 121 - (struct semaphore *)hung_task_blocker_to_lock(blocker)); 117 + owner = sem_last_holder(hung_task_blocker_to_lock(blocker)); 118 + break; 119 + case BLOCKER_TYPE_RWSEM_READER: 120 + case BLOCKER_TYPE_RWSEM_WRITER: 121 + owner = (unsigned long)rwsem_owner( 122 + hung_task_blocker_to_lock(blocker)); 123 + rwsem_blocked_as = (blocker_type == BLOCKER_TYPE_RWSEM_READER) ? 124 + "reader" : "writer"; 125 + rwsem_blocked_by = is_rwsem_reader_owned( 126 + hung_task_blocker_to_lock(blocker)) ? 127 + "reader" : "writer"; 122 128 break; 123 129 default: 124 130 WARN_ON_ONCE(1); ··· 142 132 break; 143 133 case BLOCKER_TYPE_SEM: 144 134 pr_err("INFO: task %s:%d is blocked on a semaphore, but the last holder is not found.\n", 135 + task->comm, task->pid); 136 + break; 137 + case BLOCKER_TYPE_RWSEM_READER: 138 + case BLOCKER_TYPE_RWSEM_WRITER: 139 + pr_err("INFO: task %s:%d is blocked on an rw-semaphore, but the owner is not found.\n", 145 140 task->comm, task->pid); 146 141 break; 147 142 } ··· 166 151 case BLOCKER_TYPE_SEM: 167 152 pr_err("INFO: task %s:%d blocked on a semaphore likely last held by task %s:%d\n", 168 153 task->comm, task->pid, t->comm, t->pid); 154 + break; 155 + case BLOCKER_TYPE_RWSEM_READER: 156 + case BLOCKER_TYPE_RWSEM_WRITER: 157 + pr_err("INFO: task %s:%d <%s> blocked on an rw-semaphore likely owned by task %s:%d <%s>\n", 158 + task->comm, task->pid, rwsem_blocked_as, t->comm, 159 + t->pid, rwsem_blocked_by); 169 160 break; 170 161 } 171 162 sched_show_task(t);
+16 -1
kernel/locking/rwsem.c
··· 27 27 #include <linux/export.h> 28 28 #include <linux/rwsem.h> 29 29 #include <linux/atomic.h> 30 + #include <linux/hung_task.h> 30 31 #include <trace/events/lock.h> 31 32 32 33 #ifndef CONFIG_PREEMPT_RT ··· 1066 1065 wake_up_q(&wake_q); 1067 1066 1068 1067 trace_contention_begin(sem, LCB_F_READ); 1068 + set_current_state(state); 1069 + 1070 + if (state == TASK_UNINTERRUPTIBLE) 1071 + hung_task_set_blocker(sem, BLOCKER_TYPE_RWSEM_READER); 1069 1072 1070 1073 /* wait to be given the lock */ 1071 1074 for (;;) { 1072 - set_current_state(state); 1073 1075 if (!smp_load_acquire(&waiter.task)) { 1074 1076 /* Matches rwsem_mark_wake()'s smp_store_release(). */ 1075 1077 break; ··· 1087 1083 } 1088 1084 schedule_preempt_disabled(); 1089 1085 lockevent_inc(rwsem_sleep_reader); 1086 + set_current_state(state); 1090 1087 } 1088 + 1089 + if (state == TASK_UNINTERRUPTIBLE) 1090 + hung_task_clear_blocker(); 1091 1091 1092 1092 __set_current_state(TASK_RUNNING); 1093 1093 lockevent_inc(rwsem_rlock); ··· 1154 1146 set_current_state(state); 1155 1147 trace_contention_begin(sem, LCB_F_WRITE); 1156 1148 1149 + if (state == TASK_UNINTERRUPTIBLE) 1150 + hung_task_set_blocker(sem, BLOCKER_TYPE_RWSEM_WRITER); 1151 + 1157 1152 for (;;) { 1158 1153 if (rwsem_try_write_lock(sem, &waiter)) { 1159 1154 /* rwsem_try_write_lock() implies ACQUIRE on success */ ··· 1190 1179 trylock_again: 1191 1180 raw_spin_lock_irq(&sem->wait_lock); 1192 1181 } 1182 + 1183 + if (state == TASK_UNINTERRUPTIBLE) 1184 + hung_task_clear_blocker(); 1185 + 1193 1186 __set_current_state(TASK_RUNNING); 1194 1187 raw_spin_unlock_irq(&sem->wait_lock); 1195 1188 lockevent_inc(rwsem_wlock);