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