diff options
| -rw-r--r-- | arch/mips/kernel/process.c | 36 | ||||
| -rw-r--r-- | arch/mips/kernel/stacktrace.c | 32 | ||||
| -rw-r--r-- | arch/mips/kernel/traps.c | 3 | ||||
| -rw-r--r-- | include/asm-mips/stacktrace.h | 2 |
4 files changed, 39 insertions, 34 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() */ |
| 401 | unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, | 401 | unsigned 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 | ||
| 473 | out: | 499 | out: |
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 | ||
| 34 | static struct pt_regs * save_context_stack(struct stack_trace *trace, | 34 | static 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 | } |
diff --git a/include/asm-mips/stacktrace.h b/include/asm-mips/stacktrace.h index 231f6f897a61..07f873351a86 100644 --- a/include/asm-mips/stacktrace.h +++ b/include/asm-mips/stacktrace.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #ifdef CONFIG_KALLSYMS | 6 | #ifdef CONFIG_KALLSYMS |
| 7 | extern int raw_show_trace; | 7 | extern int raw_show_trace; |
| 8 | extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, | 8 | extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, |
| 9 | unsigned long pc, unsigned long ra); | 9 | unsigned long pc, unsigned long *ra); |
| 10 | #else | 10 | #else |
| 11 | #define raw_show_trace 1 | 11 | #define raw_show_trace 1 |
| 12 | #define unwind_stack(task, sp, pc, ra) 0 | 12 | #define unwind_stack(task, sp, pc, ra) 0 |
