aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/process.c
diff options
context:
space:
mode:
authorAtsushi Nemoto <anemo@mba.ocn.ne.jp>2006-09-29 05:02:51 -0400
committerRalf Baechle <ralf@linux-mips.org>2006-10-01 18:16:59 -0400
commit1924600cdb3143cdcc32b6fa43325739503659b9 (patch)
tree002a03fe8e97db3f3a2b07d1d0d27381f80357b9 /arch/mips/kernel/process.c
parent23126692e30ec22760e0ef932c3c2fff00d440bb (diff)
[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>
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r--arch/mips/kernel/process.c36
1 files changed, 31 insertions, 5 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 695538031c69..045d987bc683 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -399,7 +399,7 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
399#ifdef CONFIG_KALLSYMS 399#ifdef CONFIG_KALLSYMS
400/* used by show_backtrace() */ 400/* used by show_backtrace() */
401unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, 401unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
402 unsigned long pc, unsigned long ra) 402 unsigned long pc, unsigned long *ra)
403{ 403{
404 unsigned long stack_page; 404 unsigned long stack_page;
405 struct mips_frame_info info; 405 struct mips_frame_info info;
@@ -407,18 +407,42 @@ unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
407 char namebuf[KSYM_NAME_LEN + 1]; 407 char namebuf[KSYM_NAME_LEN + 1];
408 unsigned long size, ofs; 408 unsigned long size, ofs;
409 int leaf; 409 int leaf;
410 extern void ret_from_irq(void);
411 extern void ret_from_exception(void);
410 412
411 stack_page = (unsigned long)task_stack_page(task); 413 stack_page = (unsigned long)task_stack_page(task);
412 if (!stack_page) 414 if (!stack_page)
413 return 0; 415 return 0;
414 416
417 /*
418 * If we reached the bottom of interrupt context,
419 * return saved pc in pt_regs.
420 */
421 if (pc == (unsigned long)ret_from_irq ||
422 pc == (unsigned long)ret_from_exception) {
423 struct pt_regs *regs;
424 if (*sp >= stack_page &&
425 *sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) {
426 regs = (struct pt_regs *)*sp;
427 pc = regs->cp0_epc;
428 if (__kernel_text_address(pc)) {
429 *sp = regs->regs[29];
430 *ra = regs->regs[31];
431 return pc;
432 }
433 }
434 return 0;
435 }
415 if (!kallsyms_lookup(pc, &size, &ofs, &modname, namebuf)) 436 if (!kallsyms_lookup(pc, &size, &ofs, &modname, namebuf))
416 return 0; 437 return 0;
417 /* 438 /*
418 * Return ra if an exception occured at the first instruction 439 * Return ra if an exception occured at the first instruction
419 */ 440 */
420 if (unlikely(ofs == 0)) 441 if (unlikely(ofs == 0)) {
421 return ra; 442 pc = *ra;
443 *ra = 0;
444 return pc;
445 }
422 446
423 info.func = (void *)(pc - ofs); 447 info.func = (void *)(pc - ofs);
424 info.func_size = ofs; /* analyze from start to ofs */ 448 info.func_size = ofs; /* analyze from start to ofs */
@@ -437,11 +461,12 @@ unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
437 * one. In that cases avoid to return always the 461 * one. In that cases avoid to return always the
438 * same value. 462 * same value.
439 */ 463 */
440 pc = pc != ra ? ra : 0; 464 pc = pc != *ra ? *ra : 0;
441 else 465 else
442 pc = ((unsigned long *)(*sp))[info.pc_offset]; 466 pc = ((unsigned long *)(*sp))[info.pc_offset];
443 467
444 *sp += info.frame_size; 468 *sp += info.frame_size;
469 *ra = 0;
445 return __kernel_text_address(pc) ? pc : 0; 470 return __kernel_text_address(pc) ? pc : 0;
446} 471}
447#endif 472#endif
@@ -454,6 +479,7 @@ unsigned long get_wchan(struct task_struct *task)
454 unsigned long pc = 0; 479 unsigned long pc = 0;
455#ifdef CONFIG_KALLSYMS 480#ifdef CONFIG_KALLSYMS
456 unsigned long sp; 481 unsigned long sp;
482 unsigned long ra = 0;
457#endif 483#endif
458 484
459 if (!task || task == current || task->state == TASK_RUNNING) 485 if (!task || task == current || task->state == TASK_RUNNING)
@@ -467,7 +493,7 @@ unsigned long get_wchan(struct task_struct *task)
467 sp = task->thread.reg29 + schedule_mfi.frame_size; 493 sp = task->thread.reg29 + schedule_mfi.frame_size;
468 494
469 while (in_sched_functions(pc)) 495 while (in_sched_functions(pc))
470 pc = unwind_stack(task, &sp, pc, 0); 496 pc = unwind_stack(task, &sp, pc, &ra);
471#endif 497#endif
472 498
473out: 499out: