[MIPS] Make unwind_stack() can dig into interrupted context
If the PC was ret_from_irq or ret_from_exception, there will be no more normal stackframe. Instead of stopping the unwinding, use PC and RA saved by an exception handler to continue unwinding into the interrupted context. This also simplifies the CONFIG_STACKTRACE code. Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
parent
23126692e3
commit
1924600cdb
|
@ -399,7 +399,7 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
|
||||||
#ifdef CONFIG_KALLSYMS
|
#ifdef CONFIG_KALLSYMS
|
||||||
/* used by show_backtrace() */
|
/* used by show_backtrace() */
|
||||||
unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
|
unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
|
||||||
unsigned long pc, unsigned long ra)
|
unsigned long pc, unsigned long *ra)
|
||||||
{
|
{
|
||||||
unsigned long stack_page;
|
unsigned long stack_page;
|
||||||
struct mips_frame_info info;
|
struct mips_frame_info info;
|
||||||
|
@ -407,18 +407,42 @@ unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
|
||||||
char namebuf[KSYM_NAME_LEN + 1];
|
char namebuf[KSYM_NAME_LEN + 1];
|
||||||
unsigned long size, ofs;
|
unsigned long size, ofs;
|
||||||
int leaf;
|
int leaf;
|
||||||
|
extern void ret_from_irq(void);
|
||||||
|
extern void ret_from_exception(void);
|
||||||
|
|
||||||
stack_page = (unsigned long)task_stack_page(task);
|
stack_page = (unsigned long)task_stack_page(task);
|
||||||
if (!stack_page)
|
if (!stack_page)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we reached the bottom of interrupt context,
|
||||||
|
* return saved pc in pt_regs.
|
||||||
|
*/
|
||||||
|
if (pc == (unsigned long)ret_from_irq ||
|
||||||
|
pc == (unsigned long)ret_from_exception) {
|
||||||
|
struct pt_regs *regs;
|
||||||
|
if (*sp >= stack_page &&
|
||||||
|
*sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) {
|
||||||
|
regs = (struct pt_regs *)*sp;
|
||||||
|
pc = regs->cp0_epc;
|
||||||
|
if (__kernel_text_address(pc)) {
|
||||||
|
*sp = regs->regs[29];
|
||||||
|
*ra = regs->regs[31];
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (!kallsyms_lookup(pc, &size, &ofs, &modname, namebuf))
|
if (!kallsyms_lookup(pc, &size, &ofs, &modname, namebuf))
|
||||||
return 0;
|
return 0;
|
||||||
/*
|
/*
|
||||||
* Return ra if an exception occured at the first instruction
|
* Return ra if an exception occured at the first instruction
|
||||||
*/
|
*/
|
||||||
if (unlikely(ofs == 0))
|
if (unlikely(ofs == 0)) {
|
||||||
return ra;
|
pc = *ra;
|
||||||
|
*ra = 0;
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
|
||||||
info.func = (void *)(pc - ofs);
|
info.func = (void *)(pc - ofs);
|
||||||
info.func_size = ofs; /* analyze from start to ofs */
|
info.func_size = ofs; /* analyze from start to ofs */
|
||||||
|
@ -437,11 +461,12 @@ unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
|
||||||
* one. In that cases avoid to return always the
|
* one. In that cases avoid to return always the
|
||||||
* same value.
|
* same value.
|
||||||
*/
|
*/
|
||||||
pc = pc != ra ? ra : 0;
|
pc = pc != *ra ? *ra : 0;
|
||||||
else
|
else
|
||||||
pc = ((unsigned long *)(*sp))[info.pc_offset];
|
pc = ((unsigned long *)(*sp))[info.pc_offset];
|
||||||
|
|
||||||
*sp += info.frame_size;
|
*sp += info.frame_size;
|
||||||
|
*ra = 0;
|
||||||
return __kernel_text_address(pc) ? pc : 0;
|
return __kernel_text_address(pc) ? pc : 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -454,6 +479,7 @@ unsigned long get_wchan(struct task_struct *task)
|
||||||
unsigned long pc = 0;
|
unsigned long pc = 0;
|
||||||
#ifdef CONFIG_KALLSYMS
|
#ifdef CONFIG_KALLSYMS
|
||||||
unsigned long sp;
|
unsigned long sp;
|
||||||
|
unsigned long ra = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!task || task == current || task->state == TASK_RUNNING)
|
if (!task || task == current || task->state == TASK_RUNNING)
|
||||||
|
@ -467,7 +493,7 @@ unsigned long get_wchan(struct task_struct *task)
|
||||||
sp = task->thread.reg29 + schedule_mfi.frame_size;
|
sp = task->thread.reg29 + schedule_mfi.frame_size;
|
||||||
|
|
||||||
while (in_sched_functions(pc))
|
while (in_sched_functions(pc))
|
||||||
pc = unwind_stack(task, &sp, pc, 0);
|
pc = unwind_stack(task, &sp, pc, &ra);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -31,23 +31,21 @@ static void save_raw_context_stack(struct stack_trace *trace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pt_regs * save_context_stack(struct stack_trace *trace,
|
static void save_context_stack(struct stack_trace *trace,
|
||||||
struct task_struct *task, struct pt_regs *regs)
|
struct task_struct *task, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
unsigned long sp = regs->regs[29];
|
unsigned long sp = regs->regs[29];
|
||||||
#ifdef CONFIG_KALLSYMS
|
#ifdef CONFIG_KALLSYMS
|
||||||
unsigned long ra = regs->regs[31];
|
unsigned long ra = regs->regs[31];
|
||||||
unsigned long pc = regs->cp0_epc;
|
unsigned long pc = regs->cp0_epc;
|
||||||
unsigned long stack_page =
|
|
||||||
(unsigned long)task_stack_page(task);
|
|
||||||
extern void ret_from_irq(void);
|
|
||||||
extern void ret_from_exception(void);
|
|
||||||
|
|
||||||
if (raw_show_trace || !__kernel_text_address(pc)) {
|
if (raw_show_trace || !__kernel_text_address(pc)) {
|
||||||
|
unsigned long stack_page =
|
||||||
|
(unsigned long)task_stack_page(task);
|
||||||
if (stack_page && sp >= stack_page &&
|
if (stack_page && sp >= stack_page &&
|
||||||
sp <= stack_page + THREAD_SIZE - 32)
|
sp <= stack_page + THREAD_SIZE - 32)
|
||||||
save_raw_context_stack(trace, sp);
|
save_raw_context_stack(trace, sp);
|
||||||
return NULL;
|
return;
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
if (trace->skip > 0)
|
if (trace->skip > 0)
|
||||||
|
@ -56,25 +54,11 @@ static struct pt_regs * save_context_stack(struct stack_trace *trace,
|
||||||
trace->entries[trace->nr_entries++] = pc;
|
trace->entries[trace->nr_entries++] = pc;
|
||||||
if (trace->nr_entries >= trace->max_entries)
|
if (trace->nr_entries >= trace->max_entries)
|
||||||
break;
|
break;
|
||||||
/*
|
pc = unwind_stack(task, &sp, pc, &ra);
|
||||||
* If we reached the bottom of interrupt context,
|
|
||||||
* return saved pt_regs.
|
|
||||||
*/
|
|
||||||
if (pc == (unsigned long)ret_from_irq ||
|
|
||||||
pc == (unsigned long)ret_from_exception) {
|
|
||||||
if (stack_page && sp >= stack_page &&
|
|
||||||
sp <= stack_page + THREAD_SIZE - 32)
|
|
||||||
return (struct pt_regs *)sp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pc = unwind_stack(task, &sp, pc, ra);
|
|
||||||
ra = 0;
|
|
||||||
} while (pc);
|
} while (pc);
|
||||||
#else
|
#else
|
||||||
save_raw_context_stack(sp);
|
save_raw_context_stack(sp);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -97,9 +81,5 @@ void save_stack_trace(struct stack_trace *trace, struct task_struct *task)
|
||||||
prepare_frametrace(regs);
|
prepare_frametrace(regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
save_context_stack(trace, task, regs);
|
||||||
regs = save_context_stack(trace, task, regs);
|
|
||||||
if (!regs || trace->nr_entries >= trace->max_entries)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,8 +115,7 @@ static void show_backtrace(struct task_struct *task, struct pt_regs *regs)
|
||||||
printk("Call Trace:\n");
|
printk("Call Trace:\n");
|
||||||
do {
|
do {
|
||||||
print_ip_sym(pc);
|
print_ip_sym(pc);
|
||||||
pc = unwind_stack(task, &sp, pc, ra);
|
pc = unwind_stack(task, &sp, pc, &ra);
|
||||||
ra = 0;
|
|
||||||
} while (pc);
|
} while (pc);
|
||||||
printk("\n");
|
printk("\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#ifdef CONFIG_KALLSYMS
|
#ifdef CONFIG_KALLSYMS
|
||||||
extern int raw_show_trace;
|
extern int raw_show_trace;
|
||||||
extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
|
extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
|
||||||
unsigned long pc, unsigned long ra);
|
unsigned long pc, unsigned long *ra);
|
||||||
#else
|
#else
|
||||||
#define raw_show_trace 1
|
#define raw_show_trace 1
|
||||||
#define unwind_stack(task, sp, pc, ra) 0
|
#define unwind_stack(task, sp, pc, ra) 0
|
||||||
|
|
Loading…
Reference in New Issue