2018-07-13 00:36:11 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Preempt / IRQ disable delay thread to test latency tracers
*
* Copyright ( C ) 2018 Joel Fernandes ( Google ) < joel @ joelfernandes . org >
*/
2018-10-16 06:31:42 +03:00
# include <linux/trace_clock.h>
2018-07-13 00:36:11 +03:00
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/kernel.h>
2019-10-09 01:08:22 +03:00
# include <linux/kobject.h>
2018-07-13 00:36:11 +03:00
# include <linux/kthread.h>
# include <linux/module.h>
# include <linux/printk.h>
# include <linux/string.h>
2019-10-09 01:08:22 +03:00
# include <linux/sysfs.h>
2020-05-10 18:35:10 +03:00
# include <linux/completion.h>
2018-07-13 00:36:11 +03:00
static ulong delay = 100 ;
2019-10-09 01:08:22 +03:00
static char test_mode [ 12 ] = " irq " ;
static uint burst_size = 1 ;
2018-07-13 00:36:11 +03:00
2019-10-09 01:08:22 +03:00
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_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) " ) ;
2020-05-10 18:35:10 +03:00
static struct completion done ;
2019-10-09 01:08:22 +03:00
# define MIN(x, y) ((x) < (y) ? (x) : (y))
2018-07-13 00:36:11 +03:00
static void busy_wait ( ulong time )
{
2018-10-16 06:31:42 +03:00
u64 start , end ;
start = trace_clock_local ( ) ;
2018-07-13 00:36:11 +03:00
do {
2018-10-16 06:31:42 +03:00
end = trace_clock_local ( ) ;
2018-07-13 00:36:11 +03:00
if ( kthread_should_stop ( ) )
break ;
2018-10-16 06:31:42 +03:00
} while ( ( end - start ) < ( time * 1000 ) ) ;
2018-07-13 00:36:11 +03:00
}
2019-10-09 01:08:22 +03:00
static __always_inline void irqoff_test ( void )
2018-07-13 00:36:11 +03:00
{
unsigned long flags ;
2019-10-09 01:08:22 +03:00
local_irq_save ( flags ) ;
busy_wait ( delay ) ;
local_irq_restore ( flags ) ;
}
2018-07-13 00:36:11 +03:00
2019-10-09 01:08:22 +03:00
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 ( ) ;
2018-07-13 00:36:11 +03:00
}
2019-10-09 01:08:22 +03:00
}
# define DECLARE_TESTFN(POSTFIX) \
static void preemptirqtest_ # # POSTFIX ( int idx ) \
{ \
execute_preemptirqtest ( idx ) ; \
} \
2018-07-13 00:36:11 +03:00
2019-10-09 01:08:22 +03:00
/*
* 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 ) ;
for ( i = 0 ; i < s ; i + + )
( testfuncs [ i ] ) ( i ) ;
2020-05-06 17:20:10 +03:00
2020-05-10 18:35:10 +03:00
complete ( & done ) ;
2020-05-06 17:20:10 +03:00
set_current_state ( TASK_INTERRUPTIBLE ) ;
while ( ! kthread_should_stop ( ) ) {
schedule ( ) ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
}
__set_current_state ( TASK_RUNNING ) ;
2018-07-13 00:36:11 +03:00
return 0 ;
}
2020-05-06 17:20:10 +03:00
static int preemptirq_run_test ( void )
2018-07-13 00:36:11 +03:00
{
2020-05-06 17:20:10 +03:00
struct task_struct * task ;
2018-07-13 00:36:11 +03:00
char task_name [ 50 ] ;
2020-05-10 18:35:10 +03:00
init_completion ( & done ) ;
2018-07-13 00:36:11 +03:00
snprintf ( task_name , sizeof ( task_name ) , " %s_test " , test_mode ) ;
2020-05-06 17:20:10 +03:00
task = kthread_run ( preemptirq_delay_run , NULL , task_name ) ;
if ( IS_ERR ( task ) )
return PTR_ERR ( task ) ;
2020-05-10 18:35:10 +03:00
if ( task ) {
wait_for_completion ( & done ) ;
2020-05-06 17:20:10 +03:00
kthread_stop ( task ) ;
2020-05-10 18:35:10 +03:00
}
2020-05-06 17:20:10 +03:00
return 0 ;
2019-10-09 01:08:22 +03:00
}
static ssize_t trigger_store ( struct kobject * kobj , struct kobj_attribute * attr ,
const char * buf , size_t count )
{
2020-05-06 17:20:10 +03:00
ssize_t ret ;
ret = preemptirq_run_test ( ) ;
if ( ret )
return ret ;
2019-10-09 01:08:22 +03:00
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 ;
2020-05-06 17:20:10 +03:00
retval = preemptirq_run_test ( ) ;
2019-10-09 01:08:22 +03:00
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 ) ;
2018-07-13 00:36:11 +03:00
2019-10-09 01:08:22 +03:00
return retval ;
2018-07-13 00:36:11 +03:00
}
static void __exit preemptirq_delay_exit ( void )
{
2019-10-09 01:08:22 +03:00
kobject_put ( preemptirq_delay_kobj ) ;
2018-07-13 00:36:11 +03:00
}
module_init ( preemptirq_delay_init )
module_exit ( preemptirq_delay_exit )
MODULE_LICENSE ( " GPL v2 " ) ;