2005-04-17 02:20:36 +04:00
/**
* @ file timer_int . c
*
* @ remark Copyright 2002 OProfile authors
* @ remark Read the file COPYING
*
* @ author John Levon < levon @ movementarian . org >
*/
# include <linux/kernel.h>
# include <linux/notifier.h>
# include <linux/smp.h>
# include <linux/oprofile.h>
# include <linux/profile.h>
# include <linux/init.h>
2010-03-02 18:01:10 +03:00
# include <linux/cpu.h>
# include <linux/hrtimer.h>
# include <asm/irq_regs.h>
2005-04-17 02:20:36 +04:00
# include <asm/ptrace.h>
# include "oprof.h"
2010-03-02 18:01:10 +03:00
static DEFINE_PER_CPU ( struct hrtimer , oprofile_hrtimer ) ;
2010-10-27 19:17:15 +04:00
static int ctr_running ;
2010-03-02 18:01:10 +03:00
static enum hrtimer_restart oprofile_hrtimer_notify ( struct hrtimer * hrtimer )
{
oprofile_add_sample ( get_irq_regs ( ) , 0 ) ;
hrtimer_forward_now ( hrtimer , ns_to_ktime ( TICK_NSEC ) ) ;
return HRTIMER_RESTART ;
}
static void __oprofile_hrtimer_start ( void * unused )
{
2014-08-17 21:30:31 +04:00
struct hrtimer * hrtimer = this_cpu_ptr ( & oprofile_hrtimer ) ;
2010-03-02 18:01:10 +03:00
2010-10-27 19:17:15 +04:00
if ( ! ctr_running )
return ;
2010-03-02 18:01:10 +03:00
hrtimer_init ( hrtimer , CLOCK_MONOTONIC , HRTIMER_MODE_REL ) ;
hrtimer - > function = oprofile_hrtimer_notify ;
hrtimer_start ( hrtimer , ns_to_ktime ( TICK_NSEC ) ,
HRTIMER_MODE_REL_PINNED ) ;
}
static int oprofile_hrtimer_start ( void )
2005-04-17 02:20:36 +04:00
{
2010-10-27 19:17:15 +04:00
get_online_cpus ( ) ;
ctr_running = 1 ;
2010-03-02 18:01:10 +03:00
on_each_cpu ( __oprofile_hrtimer_start , NULL , 1 ) ;
2010-10-27 19:17:15 +04:00
put_online_cpus ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2010-03-02 18:01:10 +03:00
static void __oprofile_hrtimer_stop ( int cpu )
2005-04-17 02:20:36 +04:00
{
2010-03-02 18:01:10 +03:00
struct hrtimer * hrtimer = & per_cpu ( oprofile_hrtimer , cpu ) ;
2010-10-27 19:17:15 +04:00
if ( ! ctr_running )
return ;
2010-03-02 18:01:10 +03:00
hrtimer_cancel ( hrtimer ) ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 18:01:10 +03:00
static void oprofile_hrtimer_stop ( void )
{
int cpu ;
2010-10-27 19:17:15 +04:00
get_online_cpus ( ) ;
2010-03-02 18:01:10 +03:00
for_each_online_cpu ( cpu )
__oprofile_hrtimer_stop ( cpu ) ;
2010-10-27 19:17:15 +04:00
ctr_running = 0 ;
put_online_cpus ( ) ;
2010-03-02 18:01:10 +03:00
}
2005-04-17 02:20:36 +04:00
2016-09-06 20:04:45 +03:00
static int oprofile_timer_online ( unsigned int cpu )
2005-04-17 02:20:36 +04:00
{
2016-09-06 20:04:45 +03:00
local_irq_disable ( ) ;
__oprofile_hrtimer_start ( NULL ) ;
local_irq_enable ( ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2016-09-06 20:04:45 +03:00
static int oprofile_timer_prep_down ( unsigned int cpu )
{
__oprofile_hrtimer_stop ( cpu ) ;
return 0 ;
}
static enum cpuhp_state hp_online ;
2005-04-17 02:20:36 +04:00
2011-10-14 17:46:10 +04:00
static int oprofile_hrtimer_setup ( void )
2005-04-17 02:20:36 +04:00
{
2016-09-06 20:04:45 +03:00
int ret ;
ret = cpuhp_setup_state_nocalls ( CPUHP_AP_ONLINE_DYN ,
" oprofile/timer:online " ,
oprofile_timer_online ,
oprofile_timer_prep_down ) ;
if ( ret < 0 )
return ret ;
hp_online = ret ;
return 0 ;
2010-03-02 18:01:10 +03:00
}
2011-10-14 17:46:10 +04:00
static void oprofile_hrtimer_shutdown ( void )
2010-03-02 18:01:10 +03:00
{
2016-09-06 20:04:45 +03:00
cpuhp_remove_state_nocalls ( hp_online ) ;
2005-04-17 02:20:36 +04:00
}
2011-10-14 17:46:10 +04:00
int oprofile_timer_init ( struct oprofile_operations * ops )
{
ops - > create_files = NULL ;
ops - > setup = oprofile_hrtimer_setup ;
ops - > shutdown = oprofile_hrtimer_shutdown ;
ops - > start = oprofile_hrtimer_start ;
ops - > stop = oprofile_hrtimer_stop ;
ops - > cpu_type = " timer " ;
printk ( KERN_INFO " oprofile: using timer interrupt. \n " ) ;
return 0 ;
}