Commit 1df0f0ff7e56f6dcb1351b9490d55ebf91ff4bd8
Committed by
Ralf Baechle
1 parent
eae6c0da9d
Exists in
master
and in
4 other branches
[MIPS] lockdep: Add STACKTRACE_SUPPORT and enable LOCKDEP_SUPPORT
Implement stacktrace interface by using unwind_stack() and enable lockdep support in Kconfig. Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Showing 6 changed files with 168 additions and 34 deletions Side-by-side Diff
arch/mips/Kconfig
... | ... | @@ -1841,6 +1841,14 @@ |
1841 | 1841 | bool |
1842 | 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 | 1852 | source "init/Kconfig" |
1845 | 1853 | |
1846 | 1854 | menu "Bus options (PCI, PCMCIA, EISA, ISA, TC)" |
arch/mips/kernel/Makefile
arch/mips/kernel/process.c
arch/mips/kernel/stacktrace.c
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 | +} |
arch/mips/kernel/traps.c
... | ... | @@ -41,6 +41,7 @@ |
41 | 41 | #include <asm/mmu_context.h> |
42 | 42 | #include <asm/watch.h> |
43 | 43 | #include <asm/types.h> |
44 | +#include <asm/stacktrace.h> | |
44 | 45 | |
45 | 46 | extern asmlinkage void handle_int(void); |
46 | 47 | extern asmlinkage void handle_tlbm(void); |
47 | 48 | |
48 | 49 | |
... | ... | @@ -92,17 +93,15 @@ |
92 | 93 | } |
93 | 94 | |
94 | 95 | #ifdef CONFIG_KALLSYMS |
95 | -static int raw_show_trace; | |
96 | +int raw_show_trace; | |
96 | 97 | static int __init set_raw_show_trace(char *str) |
97 | 98 | { |
98 | 99 | raw_show_trace = 1; |
99 | 100 | return 1; |
100 | 101 | } |
101 | 102 | __setup("raw_show_trace", set_raw_show_trace); |
103 | +#endif | |
102 | 104 | |
103 | -extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, | |
104 | - unsigned long pc, unsigned long ra); | |
105 | - | |
106 | 105 | static void show_backtrace(struct task_struct *task, struct pt_regs *regs) |
107 | 106 | { |
108 | 107 | unsigned long sp = regs->regs[29]; |
... | ... | @@ -121,9 +120,6 @@ |
121 | 120 | } while (pc); |
122 | 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 | 125 | * This routine abuses get_user()/put_user() to reference pointers |
... | ... | @@ -158,28 +154,6 @@ |
158 | 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 | 157 | void show_stack(struct task_struct *task, unsigned long *sp) |
184 | 158 | { |
185 | 159 | struct pt_regs regs; |
... | ... | @@ -206,11 +180,6 @@ |
206 | 180 | { |
207 | 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 | 183 | prepare_frametrace(®s); |
215 | 184 | show_backtrace(current, ®s); |
216 | 185 | } |
include/asm-mips/stacktrace.h
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 */ |