| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Preempt / IRQ disable delay thread to test latency tracers |
| * |
| * Copyright (C) 2018 Joel Fernandes (Google) <joel@joelfernandes.org> |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/interrupt.h> |
| #include <linux/irq.h> |
| #include <linux/kernel.h> |
| #include <linux/kthread.h> |
| #include <linux/ktime.h> |
| #include <linux/module.h> |
| #include <linux/printk.h> |
| #include <linux/string.h> |
| |
| static ulong delay = 100; |
| static char test_mode[10] = "irq"; |
| |
| module_param_named(delay, delay, ulong, S_IRUGO); |
| module_param_string(test_mode, test_mode, 10, S_IRUGO); |
| MODULE_PARM_DESC(delay, "Period in microseconds (100 uS default)"); |
| MODULE_PARM_DESC(test_mode, "Mode of the test such as preempt or irq (default irq)"); |
| |
| static void busy_wait(ulong time) |
| { |
| ktime_t start, end; |
| start = ktime_get(); |
| do { |
| end = ktime_get(); |
| if (kthread_should_stop()) |
| break; |
| } while (ktime_to_ns(ktime_sub(end, start)) < (time * 1000)); |
| } |
| |
| static int preemptirq_delay_run(void *data) |
| { |
| unsigned long flags; |
| |
| if (!strcmp(test_mode, "irq")) { |
| local_irq_save(flags); |
| busy_wait(delay); |
| local_irq_restore(flags); |
| } else if (!strcmp(test_mode, "preempt")) { |
| preempt_disable(); |
| busy_wait(delay); |
| preempt_enable(); |
| } |
| |
| return 0; |
| } |
| |
| static int __init preemptirq_delay_init(void) |
| { |
| char task_name[50]; |
| struct task_struct *test_task; |
| |
| snprintf(task_name, sizeof(task_name), "%s_test", test_mode); |
| |
| test_task = kthread_run(preemptirq_delay_run, NULL, task_name); |
| return PTR_ERR_OR_ZERO(test_task); |
| } |
| |
| static void __exit preemptirq_delay_exit(void) |
| { |
| return; |
| } |
| |
| module_init(preemptirq_delay_init) |
| module_exit(preemptirq_delay_exit) |
| MODULE_LICENSE("GPL v2"); |