1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * hung_task_tests.c - Sample code for testing hung tasks with mutex, 4 * semaphore, etc. 5 * 6 * Usage: Load this module and read `<debugfs>/hung_task/mutex`, 7 * `<debugfs>/hung_task/semaphore`, `<debugfs>/hung_task/rw_semaphore_read`, 8 * `<debugfs>/hung_task/rw_semaphore_write`, etc., with 2 or more processes. 9 * 10 * This is for testing kernel hung_task error messages with various locking 11 * mechanisms (e.g., mutex, semaphore, rw_semaphore_read, rw_semaphore_write, etc.). 12 * Note that this may freeze your system or cause a panic. Use only for testing purposes. 13 */ 14 15 #include <linux/debugfs.h> 16 #include <linux/delay.h> 17 #include <linux/fs.h> 18 #include <linux/module.h> 19 #include <linux/mutex.h> 20 #include <linux/semaphore.h> 21 #include <linux/rwsem.h> 22 23 #define HUNG_TASK_DIR "hung_task" 24 #define HUNG_TASK_MUTEX_FILE "mutex" 25 #define HUNG_TASK_SEM_FILE "semaphore" 26 #define HUNG_TASK_RWSEM_READ_FILE "rw_semaphore_read" 27 #define HUNG_TASK_RWSEM_WRITE_FILE "rw_semaphore_write" 28 #define SLEEP_SECOND 256 29 30 static const char dummy_string[] = "This is a dummy string."; 31 static DEFINE_MUTEX(dummy_mutex); 32 static DEFINE_SEMAPHORE(dummy_sem, 1); 33 static DECLARE_RWSEM(dummy_rwsem); 34 static struct dentry *hung_task_dir; 35 36 /* Mutex-based read function */ 37 static ssize_t read_dummy_mutex(struct file *file, char __user *user_buf, 38 size_t count, loff_t *ppos) 39 { 40 /* Check if data is already read */ 41 if (*ppos >= sizeof(dummy_string)) 42 return 0; 43 44 /* Second task waits on mutex, entering uninterruptible sleep */ 45 guard(mutex)(&dummy_mutex); 46 47 /* First task sleeps here, interruptible */ 48 msleep_interruptible(SLEEP_SECOND * 1000); 49 50 return simple_read_from_buffer(user_buf, count, ppos, dummy_string, 51 sizeof(dummy_string)); 52 } 53 54 /* Semaphore-based read function */ 55 static ssize_t read_dummy_semaphore(struct file *file, char __user *user_buf, 56 size_t count, loff_t *ppos) 57 { 58 /* Check if data is already read */ 59 if (*ppos >= sizeof(dummy_string)) 60 return 0; 61 62 /* Second task waits on semaphore, entering uninterruptible sleep */ 63 down(&dummy_sem); 64 65 /* First task sleeps here, interruptible */ 66 msleep_interruptible(SLEEP_SECOND * 1000); 67 68 up(&dummy_sem); 69 70 return simple_read_from_buffer(user_buf, count, ppos, dummy_string, 71 sizeof(dummy_string)); 72 } 73 74 /* Read-write semaphore read function */ 75 static ssize_t read_dummy_rwsem_read(struct file *file, char __user *user_buf, 76 size_t count, loff_t *ppos) 77 { 78 /* Check if data is already read */ 79 if (*ppos >= sizeof(dummy_string)) 80 return 0; 81 82 /* Acquires read lock, allowing concurrent readers but blocks if write lock is held */ 83 down_read(&dummy_rwsem); 84 85 /* Sleeps here, potentially triggering hung task detection if lock is held too long */ 86 msleep_interruptible(SLEEP_SECOND * 1000); 87 88 up_read(&dummy_rwsem); 89 90 return simple_read_from_buffer(user_buf, count, ppos, dummy_string, 91 sizeof(dummy_string)); 92 } 93 94 /* Read-write semaphore write function */ 95 static ssize_t read_dummy_rwsem_write(struct file *file, char __user *user_buf, 96 size_t count, loff_t *ppos) 97 { 98 /* Check if data is already read */ 99 if (*ppos >= sizeof(dummy_string)) 100 return 0; 101 102 /* Acquires exclusive write lock, blocking all other readers and writers */ 103 down_write(&dummy_rwsem); 104 105 /* Sleeps here, potentially triggering hung task detection if lock is held too long */ 106 msleep_interruptible(SLEEP_SECOND * 1000); 107 108 up_write(&dummy_rwsem); 109 110 return simple_read_from_buffer(user_buf, count, ppos, dummy_string, 111 sizeof(dummy_string)); 112 } 113 114 /* File operations for mutex */ 115 static const struct file_operations hung_task_mutex_fops = { 116 .read = read_dummy_mutex, 117 }; 118 119 /* File operations for semaphore */ 120 static const struct file_operations hung_task_sem_fops = { 121 .read = read_dummy_semaphore, 122 }; 123 124 /* File operations for rw_semaphore read */ 125 static const struct file_operations hung_task_rwsem_read_fops = { 126 .read = read_dummy_rwsem_read, 127 }; 128 129 /* File operations for rw_semaphore write */ 130 static const struct file_operations hung_task_rwsem_write_fops = { 131 .read = read_dummy_rwsem_write, 132 }; 133 134 static int __init hung_task_tests_init(void) 135 { 136 hung_task_dir = debugfs_create_dir(HUNG_TASK_DIR, NULL); 137 if (IS_ERR(hung_task_dir)) 138 return PTR_ERR(hung_task_dir); 139 140 /* Create debugfs files for mutex and semaphore tests */ 141 debugfs_create_file(HUNG_TASK_MUTEX_FILE, 0400, hung_task_dir, NULL, 142 &hung_task_mutex_fops); 143 debugfs_create_file(HUNG_TASK_SEM_FILE, 0400, hung_task_dir, NULL, 144 &hung_task_sem_fops); 145 debugfs_create_file(HUNG_TASK_RWSEM_READ_FILE, 0400, hung_task_dir, NULL, 146 &hung_task_rwsem_read_fops); 147 debugfs_create_file(HUNG_TASK_RWSEM_WRITE_FILE, 0400, hung_task_dir, NULL, 148 &hung_task_rwsem_write_fops); 149 150 return 0; 151 } 152 153 static void __exit hung_task_tests_exit(void) 154 { 155 debugfs_remove_recursive(hung_task_dir); 156 } 157 158 module_init(hung_task_tests_init); 159 module_exit(hung_task_tests_exit); 160 161 MODULE_LICENSE("GPL"); 162 MODULE_AUTHOR("Masami Hiramatsu <mhiramat@kernel.org>"); 163 MODULE_AUTHOR("Zi Li <amaindex@outlook.com>"); 164 MODULE_DESCRIPTION("Simple sleep under lock files for testing hung task"); 165