xref: /linux/include/linux/hung_task.h (revision 0f3ad9c6105f32d1755c0bd54a7f98c892f3ceb7)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Detect Hung Task: detecting tasks stuck in D state
4  *
5  * Copyright (C) 2025 Tongcheng Travel (www.ly.com)
6  * Author: Lance Yang <mingzhe.yang@ly.com>
7  */
8 #ifndef __LINUX_HUNG_TASK_H
9 #define __LINUX_HUNG_TASK_H
10 
11 #include <linux/bug.h>
12 #include <linux/sched.h>
13 #include <linux/compiler.h>
14 
15 /*
16  * @blocker: Combines lock address and blocking type.
17  *
18  * Since lock pointers are at least 4-byte aligned(32-bit) or 8-byte
19  * aligned(64-bit). This leaves the 2 least bits (LSBs) of the pointer
20  * always zero. So we can use these bits to encode the specific blocking
21  * type.
22  *
23  * Note that on architectures where this is not guaranteed, or for any
24  * unaligned lock, this tracking mechanism is silently skipped for that
25  * lock.
26  *
27  * Type encoding:
28  * 00 - Blocked on mutex			(BLOCKER_TYPE_MUTEX)
29  * 01 - Blocked on semaphore			(BLOCKER_TYPE_SEM)
30  * 10 - Blocked on rw-semaphore as READER	(BLOCKER_TYPE_RWSEM_READER)
31  * 11 - Blocked on rw-semaphore as WRITER	(BLOCKER_TYPE_RWSEM_WRITER)
32  */
33 #define BLOCKER_TYPE_MUTEX		0x00UL
34 #define BLOCKER_TYPE_SEM		0x01UL
35 #define BLOCKER_TYPE_RWSEM_READER	0x02UL
36 #define BLOCKER_TYPE_RWSEM_WRITER	0x03UL
37 
38 #define BLOCKER_TYPE_MASK		0x03UL
39 
40 #ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
hung_task_set_blocker(void * lock,unsigned long type)41 static inline void hung_task_set_blocker(void *lock, unsigned long type)
42 {
43 	unsigned long lock_ptr = (unsigned long)lock;
44 
45 	WARN_ON_ONCE(!lock_ptr);
46 	WARN_ON_ONCE(READ_ONCE(current->blocker));
47 
48 	/*
49 	 * If the lock pointer matches the BLOCKER_TYPE_MASK, return
50 	 * without writing anything.
51 	 */
52 	if (lock_ptr & BLOCKER_TYPE_MASK)
53 		return;
54 
55 	WRITE_ONCE(current->blocker, lock_ptr | type);
56 }
57 
hung_task_clear_blocker(void)58 static inline void hung_task_clear_blocker(void)
59 {
60 	WRITE_ONCE(current->blocker, 0UL);
61 }
62 
63 /*
64  * hung_task_get_blocker_type - Extracts blocker type from encoded blocker
65  * address.
66  *
67  * @blocker: Blocker pointer with encoded type (via LSB bits)
68  *
69  * Returns: BLOCKER_TYPE_MUTEX, BLOCKER_TYPE_SEM, etc.
70  */
hung_task_get_blocker_type(unsigned long blocker)71 static inline unsigned long hung_task_get_blocker_type(unsigned long blocker)
72 {
73 	WARN_ON_ONCE(!blocker);
74 
75 	return blocker & BLOCKER_TYPE_MASK;
76 }
77 
hung_task_blocker_to_lock(unsigned long blocker)78 static inline void *hung_task_blocker_to_lock(unsigned long blocker)
79 {
80 	WARN_ON_ONCE(!blocker);
81 
82 	return (void *)(blocker & ~BLOCKER_TYPE_MASK);
83 }
84 #else
hung_task_set_blocker(void * lock,unsigned long type)85 static inline void hung_task_set_blocker(void *lock, unsigned long type)
86 {
87 }
hung_task_clear_blocker(void)88 static inline void hung_task_clear_blocker(void)
89 {
90 }
hung_task_get_blocker_type(unsigned long blocker)91 static inline unsigned long hung_task_get_blocker_type(unsigned long blocker)
92 {
93 	return 0UL;
94 }
hung_task_blocker_to_lock(unsigned long blocker)95 static inline void *hung_task_blocker_to_lock(unsigned long blocker)
96 {
97 	return NULL;
98 }
99 #endif
100 
101 #endif /* __LINUX_HUNG_TASK_H */
102