1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Preempt / IRQ disable delay thread to test latency tracers 4 * 5 * Copyright (C) 2018 Joel Fernandes (Google) <joel@joelfernandes.org> 6 */ 7 8 #include <linux/trace_clock.h> 9 #include <linux/cpumask.h> 10 #include <linux/delay.h> 11 #include <linux/interrupt.h> 12 #include <linux/irq.h> 13 #include <linux/kernel.h> 14 #include <linux/kobject.h> 15 #include <linux/kthread.h> 16 #include <linux/module.h> 17 #include <linux/printk.h> 18 #include <linux/string.h> 19 #include <linux/sysfs.h> 20 #include <linux/completion.h> 21 22 static ulong delay = 100; 23 static char test_mode[12] = "irq"; 24 static uint burst_size = 1; 25 static int cpu_affinity = -1; 26 27 module_param_named(delay, delay, ulong, 0444); 28 module_param_string(test_mode, test_mode, 12, 0444); 29 module_param_named(burst_size, burst_size, uint, 0444); 30 module_param_named(cpu_affinity, cpu_affinity, int, 0444); 31 MODULE_PARM_DESC(delay, "Period in microseconds (100 us default)"); 32 MODULE_PARM_DESC(test_mode, "Mode of the test such as preempt, irq, or alternate (default irq)"); 33 MODULE_PARM_DESC(burst_size, "The size of a burst (default 1)"); 34 MODULE_PARM_DESC(cpu_affinity, "Cpu num test is running on"); 35 36 static struct completion done; 37 38 static void busy_wait(ulong time) 39 { 40 u64 start, end; 41 42 start = trace_clock_local(); 43 44 do { 45 end = trace_clock_local(); 46 if (kthread_should_stop()) 47 break; 48 } while ((end - start) < (time * 1000)); 49 } 50 51 static __always_inline void irqoff_test(void) 52 { 53 unsigned long flags; 54 55 local_irq_save(flags); 56 busy_wait(delay); 57 local_irq_restore(flags); 58 } 59 60 static __always_inline void preemptoff_test(void) 61 { 62 preempt_disable(); 63 busy_wait(delay); 64 preempt_enable(); 65 } 66 67 static void execute_preemptirqtest(int idx) 68 { 69 if (!strcmp(test_mode, "irq")) 70 irqoff_test(); 71 else if (!strcmp(test_mode, "preempt")) 72 preemptoff_test(); 73 else if (!strcmp(test_mode, "alternate")) { 74 if (idx % 2 == 0) 75 irqoff_test(); 76 else 77 preemptoff_test(); 78 } 79 } 80 81 #define DECLARE_TESTFN(POSTFIX) \ 82 static void preemptirqtest_##POSTFIX(int idx) \ 83 { \ 84 execute_preemptirqtest(idx); \ 85 } \ 86 87 /* 88 * We create 10 different functions, so that we can get 10 different 89 * backtraces. 90 */ 91 DECLARE_TESTFN(0) 92 DECLARE_TESTFN(1) 93 DECLARE_TESTFN(2) 94 DECLARE_TESTFN(3) 95 DECLARE_TESTFN(4) 96 DECLARE_TESTFN(5) 97 DECLARE_TESTFN(6) 98 DECLARE_TESTFN(7) 99 DECLARE_TESTFN(8) 100 DECLARE_TESTFN(9) 101 102 static void (*testfuncs[])(int) = { 103 preemptirqtest_0, 104 preemptirqtest_1, 105 preemptirqtest_2, 106 preemptirqtest_3, 107 preemptirqtest_4, 108 preemptirqtest_5, 109 preemptirqtest_6, 110 preemptirqtest_7, 111 preemptirqtest_8, 112 preemptirqtest_9, 113 }; 114 115 #define NR_TEST_FUNCS ARRAY_SIZE(testfuncs) 116 117 static int preemptirq_delay_run(void *data) 118 { 119 int i; 120 int s = MIN(burst_size, NR_TEST_FUNCS); 121 cpumask_var_t cpu_mask; 122 123 if (!alloc_cpumask_var(&cpu_mask, GFP_KERNEL)) 124 return -ENOMEM; 125 126 if (cpu_affinity > -1) { 127 unsigned int cpu = cpu_affinity; 128 129 if (cpu >= nr_cpu_ids || !cpu_possible(cpu)) { 130 pr_err("cpu_affinity:%d, invalid CPU\n", cpu_affinity); 131 goto out; 132 } 133 134 cpumask_clear(cpu_mask); 135 cpumask_set_cpu(cpu_affinity, cpu_mask); 136 if (set_cpus_allowed_ptr(current, cpu_mask)) 137 pr_err("cpu_affinity:%d, failed\n", cpu_affinity); 138 } 139 140 for (i = 0; i < s; i++) 141 (testfuncs[i])(i); 142 143 out: 144 complete(&done); 145 146 set_current_state(TASK_INTERRUPTIBLE); 147 while (!kthread_should_stop()) { 148 schedule(); 149 set_current_state(TASK_INTERRUPTIBLE); 150 } 151 152 __set_current_state(TASK_RUNNING); 153 154 free_cpumask_var(cpu_mask); 155 156 return 0; 157 } 158 159 static int preemptirq_run_test(void) 160 { 161 struct task_struct *task; 162 char task_name[50]; 163 164 init_completion(&done); 165 166 snprintf(task_name, sizeof(task_name), "%s_test", test_mode); 167 task = kthread_run(preemptirq_delay_run, NULL, task_name); 168 if (IS_ERR(task)) 169 return PTR_ERR(task); 170 if (task) { 171 wait_for_completion(&done); 172 kthread_stop(task); 173 } 174 return 0; 175 } 176 177 178 static ssize_t trigger_store(struct kobject *kobj, struct kobj_attribute *attr, 179 const char *buf, size_t count) 180 { 181 ssize_t ret; 182 183 ret = preemptirq_run_test(); 184 if (ret) 185 return ret; 186 return count; 187 } 188 189 static struct kobj_attribute trigger_attribute = 190 __ATTR(trigger, 0200, NULL, trigger_store); 191 192 static struct attribute *attrs[] = { 193 &trigger_attribute.attr, 194 NULL, 195 }; 196 197 static struct attribute_group attr_group = { 198 .attrs = attrs, 199 }; 200 201 static struct kobject *preemptirq_delay_kobj; 202 203 static int __init preemptirq_delay_init(void) 204 { 205 int retval; 206 207 retval = preemptirq_run_test(); 208 if (retval != 0) 209 return retval; 210 211 preemptirq_delay_kobj = kobject_create_and_add("preemptirq_delay_test", 212 kernel_kobj); 213 if (!preemptirq_delay_kobj) 214 return -ENOMEM; 215 216 retval = sysfs_create_group(preemptirq_delay_kobj, &attr_group); 217 if (retval) 218 kobject_put(preemptirq_delay_kobj); 219 220 return retval; 221 } 222 223 static void __exit preemptirq_delay_exit(void) 224 { 225 kobject_put(preemptirq_delay_kobj); 226 } 227 228 module_init(preemptirq_delay_init) 229 module_exit(preemptirq_delay_exit) 230 MODULE_DESCRIPTION("Preempt / IRQ disable delay thread to test latency tracers"); 231 MODULE_LICENSE("GPL v2"); 232