aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips
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
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')
-rw-r--r--arch/mips/kernel/process.c36
-rw-r--r--arch/mips/kernel/stacktrace.c32
-rw-r--r--arch/mips/kernel/traps.c3
3 files changed, 38 insertions, 33 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:
diff --git a/arch/mips/kernel/stacktrace.c b/arch/mips/kernel/stacktrace.c
index 676e6f69d24b..4aabe526a68e 100644
--- a/arch/mips/kernel/stacktrace.c
+++ b/arch/mips/kernel/stacktrace.c
@@ -31,23 +31,21 @@ static void save_raw_context_stack(struct stack_trace *trace,
31 } 31 }
32} 32}
33 33
34static struct pt_regs * save_context_stack(struct stack_trace *trace, 34static void save_context_stack(struct stack_trace *trace,
35 struct task_struct *task, struct pt_regs *regs) 35 struct task_struct *task, struct pt_regs *regs)
36{ 36{
37 unsigned long sp = regs->regs[29]; 37 unsigned long sp = regs->regs[29];
38#ifdef CONFIG_KALLSYMS 38#ifdef CONFIG_KALLSYMS
39 unsigned long ra = regs->regs[31]; 39 unsigned long ra = regs->regs[31];
40 unsigned long pc = regs->cp0_epc; 40 unsigned long pc = regs->cp0_epc;
41 unsigned long stack_page =
42 (unsigned long)task_stack_page(task);
43 extern void ret_from_irq(void);
44 extern void ret_from_exception(void);
45 41
46 if (raw_show_trace || !__kernel_text_address(pc)) { 42 if (raw_show_trace || !__kernel_text_address(pc)) {
43 unsigned long stack_page =
44 (unsigned long)task_stack_page(task);
47 if (stack_page && sp >= stack_page && 45 if (stack_page && sp >= stack_page &&
48 sp <= stack_page + THREAD_SIZE - 32) 46 sp <= stack_page + THREAD_SIZE - 32)
49 save_raw_context_stack(trace, sp); 47 save_raw_context_stack(trace, sp);
50 return NULL; 48 return;
51 } 49 }
52 do { 50 do {
53 if (trace->skip > 0) 51 if (trace->skip > 0)
@@ -56,25 +54,11 @@ static struct pt_regs * save_context_stack(struct stack_trace *trace,
56 trace->entries[trace->nr_entries++] = pc; 54 trace->entries[trace->nr_entries++] = pc;
57 if (trace->nr_entries >= trace->max_entries) 55 if (trace->nr_entries >= trace->max_entries)
58 break; 56 break;
59 /* 57 pc = unwind_stack(task, &sp, pc, &ra);
60 * If we reached the bottom of interrupt context,
61 * return saved pt_regs.
62 */
63 if (pc == (unsigned long)ret_from_irq ||
64 pc == (unsigned long)ret_from_exception) {
65 if (stack_page && sp >= stack_page &&
66 sp <= stack_page + THREAD_SIZE - 32)
67 return (struct pt_regs *)sp;
68 break;
69 }
70 pc = unwind_stack(task, &sp, pc, ra);
71 ra = 0;
72 } while (pc); 58 } while (pc);
73#else 59#else
74 save_raw_context_stack(sp); 60 save_raw_context_stack(sp);
75#endif 61#endif
76
77 return NULL;
78} 62}
79 63
80/* 64/*
@@ -97,9 +81,5 @@ void save_stack_trace(struct stack_trace *trace, struct task_struct *task)
97 prepare_frametrace(regs); 81 prepare_frametrace(regs);
98 } 82 }
99 83
100 while (1) { 84 save_context_stack(trace, task, regs);
101 regs = save_context_stack(trace, task, regs);
102 if (!regs || trace->nr_entries >= trace->max_entries)
103 break;
104 }
105} 85}
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 440b8651fd8b..b7292a56d4cd 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -115,8 +115,7 @@ static void show_backtrace(struct task_struct *task, struct pt_regs *regs)
115 printk("Call Trace:\n"); 115 printk("Call Trace:\n");
116 do { 116 do {
117 print_ip_sym(pc); 117 print_ip_sym(pc);
118 pc = unwind_stack(task, &sp, pc, ra); 118 pc = unwind_stack(task, &sp, pc, &ra);
119 ra = 0;
120 } while (pc); 119 } while (pc);
121 printk("\n"); 120 printk("\n");
122} 121}