mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	 adc353c0bf
			
		
	
	
		adc353c0bf
		
	
	
	
	
		
			
			A CPU mask on the stack is broken for large values of CONFIG_NR_CPUS:
kernel/trace/preemptirq_delay_test.c: In function ‘preemptirq_delay_run’:
kernel/trace/preemptirq_delay_test.c:143:1: error: the frame size of 8512 bytes is larger than 1536 bytes [-Werror=frame-larger-than=]
Fall back to dynamic allocation here.
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Song Chen <chensong_2000@189.cn>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Link: https://lore.kernel.org/20250620111215.3365305-1-arnd@kernel.org
Fixes: 4b9091e1c1 ("kernel: trace: preemptirq_delay_test: add cpu affinity")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
		
	
			
		
			
				
	
	
		
			222 lines
		
	
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // 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/trace_clock.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/irq.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/kobject.h>
 | |
| #include <linux/kthread.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/printk.h>
 | |
| #include <linux/string.h>
 | |
| #include <linux/sysfs.h>
 | |
| #include <linux/completion.h>
 | |
| 
 | |
| static ulong delay = 100;
 | |
| static char test_mode[12] = "irq";
 | |
| static uint burst_size = 1;
 | |
| static int  cpu_affinity = -1;
 | |
| 
 | |
| module_param_named(delay, delay, ulong, 0444);
 | |
| module_param_string(test_mode, test_mode, 12, 0444);
 | |
| module_param_named(burst_size, burst_size, uint, 0444);
 | |
| module_param_named(cpu_affinity, cpu_affinity, int, 0444);
 | |
| MODULE_PARM_DESC(delay, "Period in microseconds (100 us default)");
 | |
| MODULE_PARM_DESC(test_mode, "Mode of the test such as preempt, irq, or alternate (default irq)");
 | |
| MODULE_PARM_DESC(burst_size, "The size of a burst (default 1)");
 | |
| MODULE_PARM_DESC(cpu_affinity, "Cpu num test is running on");
 | |
| 
 | |
| static struct completion done;
 | |
| 
 | |
| static void busy_wait(ulong time)
 | |
| {
 | |
| 	u64 start, end;
 | |
| 
 | |
| 	start = trace_clock_local();
 | |
| 
 | |
| 	do {
 | |
| 		end = trace_clock_local();
 | |
| 		if (kthread_should_stop())
 | |
| 			break;
 | |
| 	} while ((end - start) < (time * 1000));
 | |
| }
 | |
| 
 | |
| static __always_inline void irqoff_test(void)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	local_irq_save(flags);
 | |
| 	busy_wait(delay);
 | |
| 	local_irq_restore(flags);
 | |
| }
 | |
| 
 | |
| static __always_inline void preemptoff_test(void)
 | |
| {
 | |
| 	preempt_disable();
 | |
| 	busy_wait(delay);
 | |
| 	preempt_enable();
 | |
| }
 | |
| 
 | |
| static void execute_preemptirqtest(int idx)
 | |
| {
 | |
| 	if (!strcmp(test_mode, "irq"))
 | |
| 		irqoff_test();
 | |
| 	else if (!strcmp(test_mode, "preempt"))
 | |
| 		preemptoff_test();
 | |
| 	else if (!strcmp(test_mode, "alternate")) {
 | |
| 		if (idx % 2 == 0)
 | |
| 			irqoff_test();
 | |
| 		else
 | |
| 			preemptoff_test();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #define DECLARE_TESTFN(POSTFIX)				\
 | |
| 	static void preemptirqtest_##POSTFIX(int idx)	\
 | |
| 	{						\
 | |
| 		execute_preemptirqtest(idx);		\
 | |
| 	}						\
 | |
| 
 | |
| /*
 | |
|  * We create 10 different functions, so that we can get 10 different
 | |
|  * backtraces.
 | |
|  */
 | |
| DECLARE_TESTFN(0)
 | |
| DECLARE_TESTFN(1)
 | |
| DECLARE_TESTFN(2)
 | |
| DECLARE_TESTFN(3)
 | |
| DECLARE_TESTFN(4)
 | |
| DECLARE_TESTFN(5)
 | |
| DECLARE_TESTFN(6)
 | |
| DECLARE_TESTFN(7)
 | |
| DECLARE_TESTFN(8)
 | |
| DECLARE_TESTFN(9)
 | |
| 
 | |
| static void (*testfuncs[])(int)  = {
 | |
| 	preemptirqtest_0,
 | |
| 	preemptirqtest_1,
 | |
| 	preemptirqtest_2,
 | |
| 	preemptirqtest_3,
 | |
| 	preemptirqtest_4,
 | |
| 	preemptirqtest_5,
 | |
| 	preemptirqtest_6,
 | |
| 	preemptirqtest_7,
 | |
| 	preemptirqtest_8,
 | |
| 	preemptirqtest_9,
 | |
| };
 | |
| 
 | |
| #define NR_TEST_FUNCS ARRAY_SIZE(testfuncs)
 | |
| 
 | |
| static int preemptirq_delay_run(void *data)
 | |
| {
 | |
| 	int i;
 | |
| 	int s = MIN(burst_size, NR_TEST_FUNCS);
 | |
| 	cpumask_var_t cpu_mask;
 | |
| 
 | |
| 	if (!alloc_cpumask_var(&cpu_mask, GFP_KERNEL))
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	if (cpu_affinity > -1) {
 | |
| 		cpumask_clear(cpu_mask);
 | |
| 		cpumask_set_cpu(cpu_affinity, cpu_mask);
 | |
| 		if (set_cpus_allowed_ptr(current, cpu_mask))
 | |
| 			pr_err("cpu_affinity:%d, failed\n", cpu_affinity);
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < s; i++)
 | |
| 		(testfuncs[i])(i);
 | |
| 
 | |
| 	complete(&done);
 | |
| 
 | |
| 	set_current_state(TASK_INTERRUPTIBLE);
 | |
| 	while (!kthread_should_stop()) {
 | |
| 		schedule();
 | |
| 		set_current_state(TASK_INTERRUPTIBLE);
 | |
| 	}
 | |
| 
 | |
| 	__set_current_state(TASK_RUNNING);
 | |
| 
 | |
| 	free_cpumask_var(cpu_mask);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int preemptirq_run_test(void)
 | |
| {
 | |
| 	struct task_struct *task;
 | |
| 	char task_name[50];
 | |
| 
 | |
| 	init_completion(&done);
 | |
| 
 | |
| 	snprintf(task_name, sizeof(task_name), "%s_test", test_mode);
 | |
| 	task =  kthread_run(preemptirq_delay_run, NULL, task_name);
 | |
| 	if (IS_ERR(task))
 | |
| 		return PTR_ERR(task);
 | |
| 	if (task) {
 | |
| 		wait_for_completion(&done);
 | |
| 		kthread_stop(task);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| static ssize_t trigger_store(struct kobject *kobj, struct kobj_attribute *attr,
 | |
| 			 const char *buf, size_t count)
 | |
| {
 | |
| 	ssize_t ret;
 | |
| 
 | |
| 	ret = preemptirq_run_test();
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	return count;
 | |
| }
 | |
| 
 | |
| static struct kobj_attribute trigger_attribute =
 | |
| 	__ATTR(trigger, 0200, NULL, trigger_store);
 | |
| 
 | |
| static struct attribute *attrs[] = {
 | |
| 	&trigger_attribute.attr,
 | |
| 	NULL,
 | |
| };
 | |
| 
 | |
| static struct attribute_group attr_group = {
 | |
| 	.attrs = attrs,
 | |
| };
 | |
| 
 | |
| static struct kobject *preemptirq_delay_kobj;
 | |
| 
 | |
| static int __init preemptirq_delay_init(void)
 | |
| {
 | |
| 	int retval;
 | |
| 
 | |
| 	retval = preemptirq_run_test();
 | |
| 	if (retval != 0)
 | |
| 		return retval;
 | |
| 
 | |
| 	preemptirq_delay_kobj = kobject_create_and_add("preemptirq_delay_test",
 | |
| 						       kernel_kobj);
 | |
| 	if (!preemptirq_delay_kobj)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	retval = sysfs_create_group(preemptirq_delay_kobj, &attr_group);
 | |
| 	if (retval)
 | |
| 		kobject_put(preemptirq_delay_kobj);
 | |
| 
 | |
| 	return retval;
 | |
| }
 | |
| 
 | |
| static void __exit preemptirq_delay_exit(void)
 | |
| {
 | |
| 	kobject_put(preemptirq_delay_kobj);
 | |
| }
 | |
| 
 | |
| module_init(preemptirq_delay_init)
 | |
| module_exit(preemptirq_delay_exit)
 | |
| MODULE_DESCRIPTION("Preempt / IRQ disable delay thread to test latency tracers");
 | |
| MODULE_LICENSE("GPL v2");
 |