11abf729eSZi Li // SPDX-License-Identifier: GPL-2.0-or-later
21abf729eSZi Li /*
31abf729eSZi Li * hung_task_tests.c - Sample code for testing hung tasks with mutex,
41abf729eSZi Li * semaphore, etc.
51abf729eSZi Li *
61abf729eSZi Li * Usage: Load this module and read `<debugfs>/hung_task/mutex`,
7*4efec6c0SZi Li * `<debugfs>/hung_task/semaphore`, `<debugfs>/hung_task/rw_semaphore_read`,
8*4efec6c0SZi Li * `<debugfs>/hung_task/rw_semaphore_write`, etc., with 2 or more processes.
91abf729eSZi Li *
101abf729eSZi Li * This is for testing kernel hung_task error messages with various locking
11*4efec6c0SZi Li * mechanisms (e.g., mutex, semaphore, rw_semaphore_read, rw_semaphore_write, etc.).
12*4efec6c0SZi Li * Note that this may freeze your system or cause a panic. Use only for testing purposes.
131abf729eSZi Li */
141abf729eSZi Li
151abf729eSZi Li #include <linux/debugfs.h>
161abf729eSZi Li #include <linux/delay.h>
171abf729eSZi Li #include <linux/fs.h>
181abf729eSZi Li #include <linux/module.h>
191abf729eSZi Li #include <linux/mutex.h>
201abf729eSZi Li #include <linux/semaphore.h>
21*4efec6c0SZi Li #include <linux/rwsem.h>
221abf729eSZi Li
231abf729eSZi Li #define HUNG_TASK_DIR "hung_task"
241abf729eSZi Li #define HUNG_TASK_MUTEX_FILE "mutex"
251abf729eSZi Li #define HUNG_TASK_SEM_FILE "semaphore"
26*4efec6c0SZi Li #define HUNG_TASK_RWSEM_READ_FILE "rw_semaphore_read"
27*4efec6c0SZi Li #define HUNG_TASK_RWSEM_WRITE_FILE "rw_semaphore_write"
281abf729eSZi Li #define SLEEP_SECOND 256
291abf729eSZi Li
301abf729eSZi Li static const char dummy_string[] = "This is a dummy string.";
311abf729eSZi Li static DEFINE_MUTEX(dummy_mutex);
321abf729eSZi Li static DEFINE_SEMAPHORE(dummy_sem, 1);
33*4efec6c0SZi Li static DECLARE_RWSEM(dummy_rwsem);
341abf729eSZi Li static struct dentry *hung_task_dir;
351abf729eSZi Li
361abf729eSZi Li /* Mutex-based read function */
read_dummy_mutex(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)371abf729eSZi Li static ssize_t read_dummy_mutex(struct file *file, char __user *user_buf,
381abf729eSZi Li size_t count, loff_t *ppos)
391abf729eSZi Li {
40*4efec6c0SZi Li /* Check if data is already read */
41*4efec6c0SZi Li if (*ppos >= sizeof(dummy_string))
42*4efec6c0SZi Li return 0;
43*4efec6c0SZi Li
441abf729eSZi Li /* Second task waits on mutex, entering uninterruptible sleep */
451abf729eSZi Li guard(mutex)(&dummy_mutex);
461abf729eSZi Li
471abf729eSZi Li /* First task sleeps here, interruptible */
481abf729eSZi Li msleep_interruptible(SLEEP_SECOND * 1000);
491abf729eSZi Li
501abf729eSZi Li return simple_read_from_buffer(user_buf, count, ppos, dummy_string,
511abf729eSZi Li sizeof(dummy_string));
521abf729eSZi Li }
531abf729eSZi Li
541abf729eSZi Li /* Semaphore-based read function */
read_dummy_semaphore(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)551abf729eSZi Li static ssize_t read_dummy_semaphore(struct file *file, char __user *user_buf,
561abf729eSZi Li size_t count, loff_t *ppos)
571abf729eSZi Li {
58*4efec6c0SZi Li /* Check if data is already read */
59*4efec6c0SZi Li if (*ppos >= sizeof(dummy_string))
60*4efec6c0SZi Li return 0;
61*4efec6c0SZi Li
621abf729eSZi Li /* Second task waits on semaphore, entering uninterruptible sleep */
631abf729eSZi Li down(&dummy_sem);
641abf729eSZi Li
651abf729eSZi Li /* First task sleeps here, interruptible */
661abf729eSZi Li msleep_interruptible(SLEEP_SECOND * 1000);
671abf729eSZi Li
681abf729eSZi Li up(&dummy_sem);
691abf729eSZi Li
701abf729eSZi Li return simple_read_from_buffer(user_buf, count, ppos, dummy_string,
711abf729eSZi Li sizeof(dummy_string));
721abf729eSZi Li }
731abf729eSZi Li
74*4efec6c0SZi Li /* Read-write semaphore read function */
read_dummy_rwsem_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)75*4efec6c0SZi Li static ssize_t read_dummy_rwsem_read(struct file *file, char __user *user_buf,
76*4efec6c0SZi Li size_t count, loff_t *ppos)
77*4efec6c0SZi Li {
78*4efec6c0SZi Li /* Check if data is already read */
79*4efec6c0SZi Li if (*ppos >= sizeof(dummy_string))
80*4efec6c0SZi Li return 0;
81*4efec6c0SZi Li
82*4efec6c0SZi Li /* Acquires read lock, allowing concurrent readers but blocks if write lock is held */
83*4efec6c0SZi Li down_read(&dummy_rwsem);
84*4efec6c0SZi Li
85*4efec6c0SZi Li /* Sleeps here, potentially triggering hung task detection if lock is held too long */
86*4efec6c0SZi Li msleep_interruptible(SLEEP_SECOND * 1000);
87*4efec6c0SZi Li
88*4efec6c0SZi Li up_read(&dummy_rwsem);
89*4efec6c0SZi Li
90*4efec6c0SZi Li return simple_read_from_buffer(user_buf, count, ppos, dummy_string,
91*4efec6c0SZi Li sizeof(dummy_string));
92*4efec6c0SZi Li }
93*4efec6c0SZi Li
94*4efec6c0SZi Li /* Read-write semaphore write function */
read_dummy_rwsem_write(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)95*4efec6c0SZi Li static ssize_t read_dummy_rwsem_write(struct file *file, char __user *user_buf,
96*4efec6c0SZi Li size_t count, loff_t *ppos)
97*4efec6c0SZi Li {
98*4efec6c0SZi Li /* Check if data is already read */
99*4efec6c0SZi Li if (*ppos >= sizeof(dummy_string))
100*4efec6c0SZi Li return 0;
101*4efec6c0SZi Li
102*4efec6c0SZi Li /* Acquires exclusive write lock, blocking all other readers and writers */
103*4efec6c0SZi Li down_write(&dummy_rwsem);
104*4efec6c0SZi Li
105*4efec6c0SZi Li /* Sleeps here, potentially triggering hung task detection if lock is held too long */
106*4efec6c0SZi Li msleep_interruptible(SLEEP_SECOND * 1000);
107*4efec6c0SZi Li
108*4efec6c0SZi Li up_write(&dummy_rwsem);
109*4efec6c0SZi Li
110*4efec6c0SZi Li return simple_read_from_buffer(user_buf, count, ppos, dummy_string,
111*4efec6c0SZi Li sizeof(dummy_string));
112*4efec6c0SZi Li }
113*4efec6c0SZi Li
1141abf729eSZi Li /* File operations for mutex */
1151abf729eSZi Li static const struct file_operations hung_task_mutex_fops = {
1161abf729eSZi Li .read = read_dummy_mutex,
1171abf729eSZi Li };
1181abf729eSZi Li
1191abf729eSZi Li /* File operations for semaphore */
1201abf729eSZi Li static const struct file_operations hung_task_sem_fops = {
1211abf729eSZi Li .read = read_dummy_semaphore,
1221abf729eSZi Li };
1231abf729eSZi Li
124*4efec6c0SZi Li /* File operations for rw_semaphore read */
125*4efec6c0SZi Li static const struct file_operations hung_task_rwsem_read_fops = {
126*4efec6c0SZi Li .read = read_dummy_rwsem_read,
127*4efec6c0SZi Li };
128*4efec6c0SZi Li
129*4efec6c0SZi Li /* File operations for rw_semaphore write */
130*4efec6c0SZi Li static const struct file_operations hung_task_rwsem_write_fops = {
131*4efec6c0SZi Li .read = read_dummy_rwsem_write,
132*4efec6c0SZi Li };
133*4efec6c0SZi Li
hung_task_tests_init(void)1341abf729eSZi Li static int __init hung_task_tests_init(void)
1351abf729eSZi Li {
1361abf729eSZi Li hung_task_dir = debugfs_create_dir(HUNG_TASK_DIR, NULL);
1371abf729eSZi Li if (IS_ERR(hung_task_dir))
1381abf729eSZi Li return PTR_ERR(hung_task_dir);
1391abf729eSZi Li
1401abf729eSZi Li /* Create debugfs files for mutex and semaphore tests */
1411abf729eSZi Li debugfs_create_file(HUNG_TASK_MUTEX_FILE, 0400, hung_task_dir, NULL,
1421abf729eSZi Li &hung_task_mutex_fops);
1431abf729eSZi Li debugfs_create_file(HUNG_TASK_SEM_FILE, 0400, hung_task_dir, NULL,
1441abf729eSZi Li &hung_task_sem_fops);
145*4efec6c0SZi Li debugfs_create_file(HUNG_TASK_RWSEM_READ_FILE, 0400, hung_task_dir, NULL,
146*4efec6c0SZi Li &hung_task_rwsem_read_fops);
147*4efec6c0SZi Li debugfs_create_file(HUNG_TASK_RWSEM_WRITE_FILE, 0400, hung_task_dir, NULL,
148*4efec6c0SZi Li &hung_task_rwsem_write_fops);
1491abf729eSZi Li
1501abf729eSZi Li return 0;
1511abf729eSZi Li }
1521abf729eSZi Li
hung_task_tests_exit(void)1531abf729eSZi Li static void __exit hung_task_tests_exit(void)
1541abf729eSZi Li {
1551abf729eSZi Li debugfs_remove_recursive(hung_task_dir);
1561abf729eSZi Li }
1571abf729eSZi Li
1581abf729eSZi Li module_init(hung_task_tests_init);
1591abf729eSZi Li module_exit(hung_task_tests_exit);
1601abf729eSZi Li
1611abf729eSZi Li MODULE_LICENSE("GPL");
1621abf729eSZi Li MODULE_AUTHOR("Masami Hiramatsu <mhiramat@kernel.org>");
1631abf729eSZi Li MODULE_AUTHOR("Zi Li <amaindex@outlook.com>");
1641abf729eSZi Li MODULE_DESCRIPTION("Simple sleep under lock files for testing hung task");
165