projects/linux·blob master · refs

231 lines · 4.9 KiB

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) <[email protected]>
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
22static ulong delay = 100;
23static char test_mode[12] = "irq";
24static uint burst_size = 1;
25static int cpu_affinity = -1;
26
27module_param_named(delay, delay, ulong, 0444);
28module_param_string(test_mode, test_mode, 12, 0444);
29module_param_named(burst_size, burst_size, uint, 0444);
30module_param_named(cpu_affinity, cpu_affinity, int, 0444);
31MODULE_PARM_DESC(delay, "Period in microseconds (100 us default)");
32MODULE_PARM_DESC(test_mode, "Mode of the test such as preempt, irq, or alternate (default irq)");
33MODULE_PARM_DESC(burst_size, "The size of a burst (default 1)");
34MODULE_PARM_DESC(cpu_affinity, "Cpu num test is running on");
35
36static struct completion done;
37
38static 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
51static __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
60static __always_inline void preemptoff_test(void)
61{
62 preempt_disable();
63 busy_wait(delay);
64 preempt_enable();
65}
66
67static 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 */
91DECLARE_TESTFN(0)
92DECLARE_TESTFN(1)
93DECLARE_TESTFN(2)
94DECLARE_TESTFN(3)
95DECLARE_TESTFN(4)
96DECLARE_TESTFN(5)
97DECLARE_TESTFN(6)
98DECLARE_TESTFN(7)
99DECLARE_TESTFN(8)
100DECLARE_TESTFN(9)
101
102static 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
117static 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
143out:
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
159static 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
178static 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
189static struct kobj_attribute trigger_attribute =
190 __ATTR(trigger, 0200, NULL, trigger_store);
191
192static struct attribute *attrs[] = {
193 &trigger_attribute.attr,
194 NULL,
195};
196
197static struct attribute_group attr_group = {
198 .attrs = attrs,
199};
200
201static struct kobject *preemptirq_delay_kobj;
202
203static 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
223static void __exit preemptirq_delay_exit(void)
224{
225 kobject_put(preemptirq_delay_kobj);
226}
227
228module_init(preemptirq_delay_init)
229module_exit(preemptirq_delay_exit)
230MODULE_DESCRIPTION("Preempt / IRQ disable delay thread to test latency tracers");
231MODULE_LICENSE("GPL v2");