2008-05-13 22:06:59 -07:00
# include <linux/spinlock.h>
# include <linux/hardirq.h>
# include <linux/ftrace.h>
# include <linux/percpu.h>
# include <linux/init.h>
# include <linux/list.h>
2009-12-11 00:44:47 -08:00
# include <trace/syscall.h>
2008-05-13 22:06:59 -07:00
2008-06-21 23:47:27 +05:30
# include <asm/ftrace.h>
2009-06-13 01:03:24 -07:00
# ifdef CONFIG_DYNAMIC_FTRACE
2008-05-13 22:06:59 -07:00
static const u32 ftrace_nop = 0x01000000 ;
2009-06-13 01:03:24 -07:00
static u32 ftrace_call_replace ( unsigned long ip , unsigned long addr )
2008-05-13 22:06:59 -07:00
{
2010-04-06 17:32:08 -07:00
u32 call ;
2008-05-13 22:06:59 -07:00
s32 off ;
off = ( ( s32 ) addr - ( s32 ) ip ) ;
call = 0x40000000 | ( ( u32 ) off > > 2 ) ;
2009-06-13 01:03:24 -07:00
return call ;
2008-05-13 22:06:59 -07:00
}
2009-06-13 01:03:24 -07:00
static int ftrace_modify_code ( unsigned long ip , u32 old , u32 new )
2008-05-13 22:06:59 -07:00
{
u32 replaced ;
int faulted ;
__asm__ __volatile__ (
" 1: cas [%[ip]], %[old], %[new] \n "
" flush %[ip] \n "
" mov 0, %[faulted] \n "
" 2: \n "
" .section .fixup,#alloc,#execinstr \n "
" .align 4 \n "
" 3: sethi %%hi(2b), %[faulted] \n "
" jmpl %[faulted] + %%lo(2b), %%g0 \n "
" mov 1, %[faulted] \n "
" .previous \n "
" .section __ex_table, \" a \" \n "
" .align 4 \n "
" .word 1b, 3b \n "
" .previous \n "
: " =r " ( replaced ) , [ faulted ] " =r " ( faulted )
: [ new ] " 0 " ( new ) , [ old ] " r " ( old ) , [ ip ] " r " ( ip )
: " memory " ) ;
if ( replaced ! = old & & replaced ! = new )
faulted = 2 ;
return faulted ;
}
2009-06-13 01:03:24 -07:00
int ftrace_make_nop ( struct module * mod , struct dyn_ftrace * rec , unsigned long addr )
{
unsigned long ip = rec - > ip ;
u32 old , new ;
old = ftrace_call_replace ( ip , addr ) ;
new = ftrace_nop ;
return ftrace_modify_code ( ip , old , new ) ;
}
int ftrace_make_call ( struct dyn_ftrace * rec , unsigned long addr )
{
unsigned long ip = rec - > ip ;
u32 old , new ;
old = ftrace_nop ;
new = ftrace_call_replace ( ip , addr ) ;
return ftrace_modify_code ( ip , old , new ) ;
}
2008-10-23 09:33:08 -04:00
int ftrace_update_ftrace_func ( ftrace_func_t func )
2008-05-13 22:06:59 -07:00
{
unsigned long ip = ( unsigned long ) ( & ftrace_call ) ;
2009-06-13 01:03:24 -07:00
u32 old , new ;
2008-05-13 22:06:59 -07:00
2009-06-13 01:03:24 -07:00
old = * ( u32 * ) & ftrace_call ;
2008-05-13 22:06:59 -07:00
new = ftrace_call_replace ( ip , ( unsigned long ) func ) ;
return ftrace_modify_code ( ip , old , new ) ;
}
int __init ftrace_dyn_arch_init ( void * data )
{
2009-06-13 01:03:24 -07:00
unsigned long * p = data ;
* p = 0 ;
2008-05-13 22:06:59 -07:00
return 0 ;
}
2009-06-13 01:03:24 -07:00
# endif
2010-04-07 04:41:33 -07:00
# ifdef CONFIG_FUNCTION_GRAPH_TRACER
# ifdef CONFIG_DYNAMIC_FTRACE
extern void ftrace_graph_call ( void ) ;
int ftrace_enable_ftrace_graph_caller ( void )
{
unsigned long ip = ( unsigned long ) ( & ftrace_graph_call ) ;
u32 old , new ;
old = * ( u32 * ) & ftrace_graph_call ;
new = ftrace_call_replace ( ip , ( unsigned long ) & ftrace_graph_caller ) ;
return ftrace_modify_code ( ip , old , new ) ;
}
int ftrace_disable_ftrace_graph_caller ( void )
{
unsigned long ip = ( unsigned long ) ( & ftrace_graph_call ) ;
u32 old , new ;
old = * ( u32 * ) & ftrace_graph_call ;
new = ftrace_call_replace ( ip , ( unsigned long ) & ftrace_stub ) ;
return ftrace_modify_code ( ip , old , new ) ;
}
# endif /* !CONFIG_DYNAMIC_FTRACE */
/*
* Hook the return address and push it in the stack of return addrs
* in current thread info .
*/
unsigned long prepare_ftrace_return ( unsigned long parent ,
unsigned long self_addr ,
unsigned long frame_pointer )
{
unsigned long return_hooker = ( unsigned long ) & return_to_handler ;
struct ftrace_graph_ent trace ;
if ( unlikely ( atomic_read ( & current - > tracing_graph_pause ) ) )
return parent + 8UL ;
if ( ftrace_push_return_trace ( parent , self_addr , & trace . depth ,
frame_pointer ) = = - EBUSY )
return parent + 8UL ;
trace . func = self_addr ;
/* Only trace if the calling function expects to */
if ( ! ftrace_graph_entry ( & trace ) ) {
current - > curr_ret_stack - - ;
return parent + 8UL ;
}
return return_hooker ;
}
# endif /* CONFIG_FUNCTION_GRAPH_TRACER */