diff options
-rw-r--r-- | arch/mips/Kconfig | 8 | ||||
-rw-r--r-- | arch/mips/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/mips/kernel/process.c | 1 | ||||
-rw-r--r-- | arch/mips/kernel/stacktrace.c | 111 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 37 | ||||
-rw-r--r-- | include/asm-mips/stacktrace.h | 44 |
6 files changed, 168 insertions, 34 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 30750c54bdf5..ba3a317ea42c 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig | |||
@@ -1841,6 +1841,14 @@ config RWSEM_GENERIC_SPINLOCK | |||
1841 | bool | 1841 | bool |
1842 | default y | 1842 | default y |
1843 | 1843 | ||
1844 | config LOCKDEP_SUPPORT | ||
1845 | bool | ||
1846 | default y | ||
1847 | |||
1848 | config STACKTRACE_SUPPORT | ||
1849 | bool | ||
1850 | default y | ||
1851 | |||
1844 | source "init/Kconfig" | 1852 | source "init/Kconfig" |
1845 | 1853 | ||
1846 | menu "Bus options (PCI, PCMCIA, EISA, ISA, TC)" | 1854 | menu "Bus options (PCI, PCMCIA, EISA, ISA, TC)" |
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 881c467c6982..cd9cec9e39e9 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile | |||
@@ -11,6 +11,7 @@ obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \ | |||
11 | binfmt_irix-objs := irixelf.o irixinv.o irixioctl.o irixsig.o \ | 11 | binfmt_irix-objs := irixelf.o irixinv.o irixioctl.o irixsig.o \ |
12 | irix5sys.o sysirix.o | 12 | irix5sys.o sysirix.o |
13 | 13 | ||
14 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | ||
14 | obj-$(CONFIG_MODULES) += mips_ksyms.o module.o | 15 | obj-$(CONFIG_MODULES) += mips_ksyms.o module.o |
15 | 16 | ||
16 | obj-$(CONFIG_APM) += apm.o | 17 | obj-$(CONFIG_APM) += apm.o |
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 2613a0dd4b82..695538031c69 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <asm/elf.h> | 40 | #include <asm/elf.h> |
41 | #include <asm/isadep.h> | 41 | #include <asm/isadep.h> |
42 | #include <asm/inst.h> | 42 | #include <asm/inst.h> |
43 | #include <asm/stacktrace.h> | ||
43 | #ifdef CONFIG_MIPS_MT_SMTC | 44 | #ifdef CONFIG_MIPS_MT_SMTC |
44 | #include <asm/mipsmtregs.h> | 45 | #include <asm/mipsmtregs.h> |
45 | extern void smtc_idle_loop_hook(void); | 46 | extern void smtc_idle_loop_hook(void); |
diff --git a/arch/mips/kernel/stacktrace.c b/arch/mips/kernel/stacktrace.c new file mode 100644 index 000000000000..f851d0cc245f --- /dev/null +++ b/arch/mips/kernel/stacktrace.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * arch/mips/kernel/stacktrace.c | ||
3 | * | ||
4 | * Stack trace management functions | ||
5 | * | ||
6 | * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp> | ||
7 | */ | ||
8 | #include <linux/sched.h> | ||
9 | #include <linux/stacktrace.h> | ||
10 | #include <asm/stacktrace.h> | ||
11 | |||
12 | /* | ||
13 | * Save stack-backtrace addresses into a stack_trace buffer: | ||
14 | */ | ||
15 | static void save_raw_context_stack(struct stack_trace *trace, | ||
16 | unsigned int skip, unsigned long reg29) | ||
17 | { | ||
18 | unsigned long *sp = (unsigned long *)reg29; | ||
19 | unsigned long addr; | ||
20 | |||
21 | while (!kstack_end(sp)) { | ||
22 | addr = *sp++; | ||
23 | if (__kernel_text_address(addr)) { | ||
24 | if (!skip) | ||
25 | trace->entries[trace->nr_entries++] = addr; | ||
26 | else | ||
27 | skip--; | ||
28 | if (trace->nr_entries >= trace->max_entries) | ||
29 | break; | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
34 | static struct pt_regs * save_context_stack(struct stack_trace *trace, | ||
35 | unsigned int skip, struct task_struct *task, struct pt_regs *regs) | ||
36 | { | ||
37 | unsigned long sp = regs->regs[29]; | ||
38 | #ifdef CONFIG_KALLSYMS | ||
39 | unsigned long ra = regs->regs[31]; | ||
40 | unsigned long pc = regs->cp0_epc; | ||
41 | extern void ret_from_irq(void); | ||
42 | |||
43 | if (raw_show_trace || !__kernel_text_address(pc)) { | ||
44 | save_raw_context_stack(trace, skip, sp); | ||
45 | return NULL; | ||
46 | } | ||
47 | do { | ||
48 | if (!skip) | ||
49 | trace->entries[trace->nr_entries++] = pc; | ||
50 | else | ||
51 | skip--; | ||
52 | if (trace->nr_entries >= trace->max_entries) | ||
53 | break; | ||
54 | /* | ||
55 | * If we reached the bottom of interrupt context, | ||
56 | * return saved pt_regs. | ||
57 | */ | ||
58 | if (pc == (unsigned long)ret_from_irq) { | ||
59 | unsigned long stack_page = | ||
60 | (unsigned long)task_stack_page(task); | ||
61 | if (!stack_page || | ||
62 | sp < stack_page || | ||
63 | sp > stack_page + THREAD_SIZE - 32) | ||
64 | break; | ||
65 | return (struct pt_regs *)sp; | ||
66 | } | ||
67 | pc = unwind_stack(task, &sp, pc, ra); | ||
68 | ra = 0; | ||
69 | } while (pc); | ||
70 | #else | ||
71 | save_raw_context_stack(sp); | ||
72 | #endif | ||
73 | |||
74 | return NULL; | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * Save stack-backtrace addresses into a stack_trace buffer. | ||
79 | * If all_contexts is set, all contexts (hardirq, softirq and process) | ||
80 | * are saved. If not set then only the current context is saved. | ||
81 | */ | ||
82 | void save_stack_trace(struct stack_trace *trace, | ||
83 | struct task_struct *task, int all_contexts, | ||
84 | unsigned int skip) | ||
85 | { | ||
86 | struct pt_regs dummyregs; | ||
87 | struct pt_regs *regs = &dummyregs; | ||
88 | |||
89 | WARN_ON(trace->nr_entries || !trace->max_entries); | ||
90 | |||
91 | if (task && task != current) { | ||
92 | regs->regs[29] = task->thread.reg29; | ||
93 | regs->regs[31] = 0; | ||
94 | regs->cp0_epc = task->thread.reg31; | ||
95 | } else { | ||
96 | if (!task) | ||
97 | task = current; | ||
98 | prepare_frametrace(regs); | ||
99 | } | ||
100 | |||
101 | while (1) { | ||
102 | regs = save_context_stack(trace, skip, task, regs); | ||
103 | if (!all_contexts || !regs || | ||
104 | trace->nr_entries >= trace->max_entries) | ||
105 | break; | ||
106 | trace->entries[trace->nr_entries++] = ULONG_MAX; | ||
107 | if (trace->nr_entries >= trace->max_entries) | ||
108 | break; | ||
109 | skip = 0; | ||
110 | } | ||
111 | } | ||
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index e51d8fd9a152..440b8651fd8b 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <asm/mmu_context.h> | 41 | #include <asm/mmu_context.h> |
42 | #include <asm/watch.h> | 42 | #include <asm/watch.h> |
43 | #include <asm/types.h> | 43 | #include <asm/types.h> |
44 | #include <asm/stacktrace.h> | ||
44 | 45 | ||
45 | extern asmlinkage void handle_int(void); | 46 | extern asmlinkage void handle_int(void); |
46 | extern asmlinkage void handle_tlbm(void); | 47 | extern asmlinkage void handle_tlbm(void); |
@@ -92,16 +93,14 @@ static void show_raw_backtrace(unsigned long reg29) | |||
92 | } | 93 | } |
93 | 94 | ||
94 | #ifdef CONFIG_KALLSYMS | 95 | #ifdef CONFIG_KALLSYMS |
95 | static int raw_show_trace; | 96 | int raw_show_trace; |
96 | static int __init set_raw_show_trace(char *str) | 97 | static int __init set_raw_show_trace(char *str) |
97 | { | 98 | { |
98 | raw_show_trace = 1; | 99 | raw_show_trace = 1; |
99 | return 1; | 100 | return 1; |
100 | } | 101 | } |
101 | __setup("raw_show_trace", set_raw_show_trace); | 102 | __setup("raw_show_trace", set_raw_show_trace); |
102 | 103 | #endif | |
103 | extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, | ||
104 | unsigned long pc, unsigned long ra); | ||
105 | 104 | ||
106 | static void show_backtrace(struct task_struct *task, struct pt_regs *regs) | 105 | static void show_backtrace(struct task_struct *task, struct pt_regs *regs) |
107 | { | 106 | { |
@@ -121,9 +120,6 @@ static void show_backtrace(struct task_struct *task, struct pt_regs *regs) | |||
121 | } while (pc); | 120 | } while (pc); |
122 | printk("\n"); | 121 | printk("\n"); |
123 | } | 122 | } |
124 | #else | ||
125 | #define show_backtrace(task, r) show_raw_backtrace((r)->regs[29]); | ||
126 | #endif | ||
127 | 123 | ||
128 | /* | 124 | /* |
129 | * This routine abuses get_user()/put_user() to reference pointers | 125 | * This routine abuses get_user()/put_user() to reference pointers |
@@ -158,28 +154,6 @@ static void show_stacktrace(struct task_struct *task, struct pt_regs *regs) | |||
158 | show_backtrace(task, regs); | 154 | show_backtrace(task, regs); |
159 | } | 155 | } |
160 | 156 | ||
161 | static __always_inline void prepare_frametrace(struct pt_regs *regs) | ||
162 | { | ||
163 | __asm__ __volatile__( | ||
164 | ".set push\n\t" | ||
165 | ".set noat\n\t" | ||
166 | #ifdef CONFIG_64BIT | ||
167 | "1: dla $1, 1b\n\t" | ||
168 | "sd $1, %0\n\t" | ||
169 | "sd $29, %1\n\t" | ||
170 | "sd $31, %2\n\t" | ||
171 | #else | ||
172 | "1: la $1, 1b\n\t" | ||
173 | "sw $1, %0\n\t" | ||
174 | "sw $29, %1\n\t" | ||
175 | "sw $31, %2\n\t" | ||
176 | #endif | ||
177 | ".set pop\n\t" | ||
178 | : "=m" (regs->cp0_epc), | ||
179 | "=m" (regs->regs[29]), "=m" (regs->regs[31]) | ||
180 | : : "memory"); | ||
181 | } | ||
182 | |||
183 | void show_stack(struct task_struct *task, unsigned long *sp) | 157 | void show_stack(struct task_struct *task, unsigned long *sp) |
184 | { | 158 | { |
185 | struct pt_regs regs; | 159 | struct pt_regs regs; |
@@ -206,11 +180,6 @@ void dump_stack(void) | |||
206 | { | 180 | { |
207 | struct pt_regs regs; | 181 | struct pt_regs regs; |
208 | 182 | ||
209 | /* | ||
210 | * Remove any garbage that may be in regs (specially func | ||
211 | * addresses) to avoid show_raw_backtrace() to report them | ||
212 | */ | ||
213 | memset(®s, 0, sizeof(regs)); | ||
214 | prepare_frametrace(®s); | 183 | prepare_frametrace(®s); |
215 | show_backtrace(current, ®s); | 184 | show_backtrace(current, ®s); |
216 | } | 185 | } |
diff --git a/include/asm-mips/stacktrace.h b/include/asm-mips/stacktrace.h new file mode 100644 index 000000000000..231f6f897a61 --- /dev/null +++ b/include/asm-mips/stacktrace.h | |||
@@ -0,0 +1,44 @@ | |||
1 | #ifndef _ASM_STACKTRACE_H | ||
2 | #define _ASM_STACKTRACE_H | ||
3 | |||
4 | #include <asm/ptrace.h> | ||
5 | |||
6 | #ifdef CONFIG_KALLSYMS | ||
7 | extern int raw_show_trace; | ||
8 | extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, | ||
9 | unsigned long pc, unsigned long ra); | ||
10 | #else | ||
11 | #define raw_show_trace 1 | ||
12 | #define unwind_stack(task, sp, pc, ra) 0 | ||
13 | #endif | ||
14 | |||
15 | static __always_inline void prepare_frametrace(struct pt_regs *regs) | ||
16 | { | ||
17 | #ifndef CONFIG_KALLSYMS | ||
18 | /* | ||
19 | * Remove any garbage that may be in regs (specially func | ||
20 | * addresses) to avoid show_raw_backtrace() to report them | ||
21 | */ | ||
22 | memset(regs, 0, sizeof(*regs)); | ||
23 | #endif | ||
24 | __asm__ __volatile__( | ||
25 | ".set push\n\t" | ||
26 | ".set noat\n\t" | ||
27 | #ifdef CONFIG_64BIT | ||
28 | "1: dla $1, 1b\n\t" | ||
29 | "sd $1, %0\n\t" | ||
30 | "sd $29, %1\n\t" | ||
31 | "sd $31, %2\n\t" | ||
32 | #else | ||
33 | "1: la $1, 1b\n\t" | ||
34 | "sw $1, %0\n\t" | ||
35 | "sw $29, %1\n\t" | ||
36 | "sw $31, %2\n\t" | ||
37 | #endif | ||
38 | ".set pop\n\t" | ||
39 | : "=m" (regs->cp0_epc), | ||
40 | "=m" (regs->regs[29]), "=m" (regs->regs[31]) | ||
41 | : : "memory"); | ||
42 | } | ||
43 | |||
44 | #endif /* _ASM_STACKTRACE_H */ | ||