2006-07-03 11:24:41 +04:00
/*
* Stack trace management functions
*
2012-07-20 13:15:04 +04:00
* Copyright IBM Corp . 2006
2006-07-03 11:24:41 +04:00
* Author ( s ) : Heiko Carstens < heiko . carstens @ de . ibm . com >
*/
# include <linux/sched.h>
# include <linux/stacktrace.h>
# include <linux/kallsyms.h>
2008-07-17 19:16:45 +04:00
# include <linux/module.h>
2006-07-03 11:24:41 +04:00
2007-02-05 23:18:53 +03:00
static unsigned long save_context_stack ( struct stack_trace * trace ,
unsigned long sp ,
unsigned long low ,
2008-02-05 18:50:45 +03:00
unsigned long high ,
int savesched )
2006-07-03 11:24:41 +04:00
{
struct stack_frame * sf ;
struct pt_regs * regs ;
unsigned long addr ;
while ( 1 ) {
sp & = PSW_ADDR_INSN ;
if ( sp < low | | sp > high )
return sp ;
sf = ( struct stack_frame * ) sp ;
while ( 1 ) {
addr = sf - > gprs [ 8 ] & PSW_ADDR_INSN ;
2007-07-17 15:36:07 +04:00
if ( ! trace - > skip )
2006-07-03 11:24:41 +04:00
trace - > entries [ trace - > nr_entries + + ] = addr ;
else
2007-07-17 15:36:07 +04:00
trace - > skip - - ;
2006-07-03 11:24:41 +04:00
if ( trace - > nr_entries > = trace - > max_entries )
return sp ;
low = sp ;
sp = sf - > back_chain & PSW_ADDR_INSN ;
if ( ! sp )
break ;
if ( sp < = low | | sp > high - sizeof ( * sf ) )
return sp ;
sf = ( struct stack_frame * ) sp ;
}
/* Zero backchain detected, check for interrupt frame. */
sp = ( unsigned long ) ( sf + 1 ) ;
if ( sp < = low | | sp > high - sizeof ( * regs ) )
return sp ;
regs = ( struct pt_regs * ) sp ;
addr = regs - > psw . addr & PSW_ADDR_INSN ;
2008-02-05 18:50:45 +03:00
if ( savesched | | ! in_sched_functions ( addr ) ) {
if ( ! trace - > skip )
trace - > entries [ trace - > nr_entries + + ] = addr ;
else
trace - > skip - - ;
}
2006-07-03 11:24:41 +04:00
if ( trace - > nr_entries > = trace - > max_entries )
return sp ;
low = sp ;
sp = regs - > gprs [ 15 ] ;
}
}
2007-05-08 11:23:29 +04:00
void save_stack_trace ( struct stack_trace * trace )
2006-07-03 11:24:41 +04:00
{
register unsigned long sp asm ( " 15 " ) ;
2006-10-11 17:31:52 +04:00
unsigned long orig_sp , new_sp ;
2006-07-03 11:24:41 +04:00
2006-10-11 17:31:52 +04:00
orig_sp = sp & PSW_ADDR_INSN ;
2007-07-17 15:36:07 +04:00
new_sp = save_context_stack ( trace , orig_sp ,
S390_lowcore . panic_stack - PAGE_SIZE ,
2008-02-05 18:50:45 +03:00
S390_lowcore . panic_stack , 1 ) ;
2007-05-08 11:23:29 +04:00
if ( new_sp ! = orig_sp )
2006-07-03 11:24:41 +04:00
return ;
2007-07-17 15:36:07 +04:00
new_sp = save_context_stack ( trace , new_sp ,
S390_lowcore . async_stack - ASYNC_SIZE ,
2008-02-05 18:50:45 +03:00
S390_lowcore . async_stack , 1 ) ;
2007-05-08 11:23:29 +04:00
if ( new_sp ! = orig_sp )
2006-07-03 11:24:41 +04:00
return ;
2007-07-17 15:36:07 +04:00
save_context_stack ( trace , new_sp ,
2007-05-08 11:23:29 +04:00
S390_lowcore . thread_info ,
2008-02-05 18:50:45 +03:00
S390_lowcore . thread_info + THREAD_SIZE , 1 ) ;
}
2008-07-03 11:17:55 +04:00
EXPORT_SYMBOL_GPL ( save_stack_trace ) ;
2008-02-05 18:50:45 +03:00
void save_stack_trace_tsk ( struct task_struct * tsk , struct stack_trace * trace )
{
unsigned long sp , low , high ;
sp = tsk - > thread . ksp & PSW_ADDR_INSN ;
low = ( unsigned long ) task_stack_page ( tsk ) ;
high = ( unsigned long ) task_pt_regs ( tsk ) ;
save_context_stack ( trace , sp , low , high , 0 ) ;
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = ULONG_MAX ;
2006-07-03 11:24:41 +04:00
}
2008-07-03 11:17:55 +04:00
EXPORT_SYMBOL_GPL ( save_stack_trace_tsk ) ;