diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /arch/x86/kernel/dumpstack_64.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'arch/x86/kernel/dumpstack_64.c')
-rw-r--r-- | arch/x86/kernel/dumpstack_64.c | 99 |
1 files changed, 69 insertions, 30 deletions
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c index a071e6be177e..272c9f1f05f3 100644 --- a/arch/x86/kernel/dumpstack_64.c +++ b/arch/x86/kernel/dumpstack_64.c | |||
@@ -10,34 +10,31 @@ | |||
10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/ptrace.h> | 11 | #include <linux/ptrace.h> |
12 | #include <linux/kexec.h> | 12 | #include <linux/kexec.h> |
13 | #include <linux/sysfs.h> | ||
13 | #include <linux/bug.h> | 14 | #include <linux/bug.h> |
14 | #include <linux/nmi.h> | 15 | #include <linux/nmi.h> |
15 | #include <linux/sysfs.h> | ||
16 | 16 | ||
17 | #include <asm/stacktrace.h> | 17 | #include <asm/stacktrace.h> |
18 | 18 | ||
19 | #include "dumpstack.h" | 19 | #include "dumpstack.h" |
20 | 20 | ||
21 | #define N_EXCEPTION_STACKS_END \ | ||
22 | (N_EXCEPTION_STACKS + DEBUG_STKSZ/EXCEPTION_STKSZ - 2) | ||
21 | 23 | ||
22 | static char x86_stack_ids[][8] = { | 24 | static char x86_stack_ids[][8] = { |
23 | [DEBUG_STACK - 1] = "#DB", | 25 | [ DEBUG_STACK-1 ] = "#DB", |
24 | [NMI_STACK - 1] = "NMI", | 26 | [ NMI_STACK-1 ] = "NMI", |
25 | [DOUBLEFAULT_STACK - 1] = "#DF", | 27 | [ DOUBLEFAULT_STACK-1 ] = "#DF", |
26 | [STACKFAULT_STACK - 1] = "#SS", | 28 | [ STACKFAULT_STACK-1 ] = "#SS", |
27 | [MCE_STACK - 1] = "#MC", | 29 | [ MCE_STACK-1 ] = "#MC", |
28 | #if DEBUG_STKSZ > EXCEPTION_STKSZ | 30 | #if DEBUG_STKSZ > EXCEPTION_STKSZ |
29 | [N_EXCEPTION_STACKS ... | 31 | [ N_EXCEPTION_STACKS ... |
30 | N_EXCEPTION_STACKS + DEBUG_STKSZ / EXCEPTION_STKSZ - 2] = "#DB[?]" | 32 | N_EXCEPTION_STACKS_END ] = "#DB[?]" |
31 | #endif | 33 | #endif |
32 | }; | 34 | }; |
33 | |||
34 | int x86_is_stack_id(int id, char *name) | ||
35 | { | ||
36 | return x86_stack_ids[id - 1] == name; | ||
37 | } | ||
38 | 35 | ||
39 | static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, | 36 | static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, |
40 | unsigned *usedp, char **idp) | 37 | unsigned *usedp, char **idp) |
41 | { | 38 | { |
42 | unsigned k; | 39 | unsigned k; |
43 | 40 | ||
@@ -101,6 +98,41 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, | |||
101 | return NULL; | 98 | return NULL; |
102 | } | 99 | } |
103 | 100 | ||
101 | static inline int | ||
102 | in_irq_stack(unsigned long *stack, unsigned long *irq_stack, | ||
103 | unsigned long *irq_stack_end) | ||
104 | { | ||
105 | return (stack >= irq_stack && stack < irq_stack_end); | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * We are returning from the irq stack and go to the previous one. | ||
110 | * If the previous stack is also in the irq stack, then bp in the first | ||
111 | * frame of the irq stack points to the previous, interrupted one. | ||
112 | * Otherwise we have another level of indirection: We first save | ||
113 | * the bp of the previous stack, then we switch the stack to the irq one | ||
114 | * and save a new bp that links to the previous one. | ||
115 | * (See save_args()) | ||
116 | */ | ||
117 | static inline unsigned long | ||
118 | fixup_bp_irq_link(unsigned long bp, unsigned long *stack, | ||
119 | unsigned long *irq_stack, unsigned long *irq_stack_end) | ||
120 | { | ||
121 | #ifdef CONFIG_FRAME_POINTER | ||
122 | struct stack_frame *frame = (struct stack_frame *)bp; | ||
123 | unsigned long next; | ||
124 | |||
125 | if (!in_irq_stack(stack, irq_stack, irq_stack_end)) { | ||
126 | if (!probe_kernel_address(&frame->next_frame, next)) | ||
127 | return next; | ||
128 | else | ||
129 | WARN_ONCE(1, "Perf: bad frame pointer = %p in " | ||
130 | "callchain\n", &frame->next_frame); | ||
131 | } | ||
132 | #endif | ||
133 | return bp; | ||
134 | } | ||
135 | |||
104 | /* | 136 | /* |
105 | * x86-64 can have up to three kernel stacks: | 137 | * x86-64 can have up to three kernel stacks: |
106 | * process stack | 138 | * process stack |
@@ -157,8 +189,8 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, | |||
157 | if (ops->stack(data, id) < 0) | 189 | if (ops->stack(data, id) < 0) |
158 | break; | 190 | break; |
159 | 191 | ||
160 | bp = print_context_stack(tinfo, stack, bp, ops, | 192 | bp = ops->walk_stack(tinfo, stack, bp, ops, |
161 | data, estack_end, &graph); | 193 | data, estack_end, &graph); |
162 | ops->stack(data, "<EOE>"); | 194 | ops->stack(data, "<EOE>"); |
163 | /* | 195 | /* |
164 | * We link to the next stack via the | 196 | * We link to the next stack via the |
@@ -173,10 +205,10 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, | |||
173 | irq_stack = irq_stack_end - | 205 | irq_stack = irq_stack_end - |
174 | (IRQ_STACK_SIZE - 64) / sizeof(*irq_stack); | 206 | (IRQ_STACK_SIZE - 64) / sizeof(*irq_stack); |
175 | 207 | ||
176 | if (stack >= irq_stack && stack < irq_stack_end) { | 208 | if (in_irq_stack(stack, irq_stack, irq_stack_end)) { |
177 | if (ops->stack(data, "IRQ") < 0) | 209 | if (ops->stack(data, "IRQ") < 0) |
178 | break; | 210 | break; |
179 | bp = print_context_stack(tinfo, stack, bp, | 211 | bp = ops->walk_stack(tinfo, stack, bp, |
180 | ops, data, irq_stack_end, &graph); | 212 | ops, data, irq_stack_end, &graph); |
181 | /* | 213 | /* |
182 | * We link to the next stack (which would be | 214 | * We link to the next stack (which would be |
@@ -184,6 +216,8 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, | |||
184 | * pointer (index -1 to end) in the IRQ stack: | 216 | * pointer (index -1 to end) in the IRQ stack: |
185 | */ | 217 | */ |
186 | stack = (unsigned long *) (irq_stack_end[-1]); | 218 | stack = (unsigned long *) (irq_stack_end[-1]); |
219 | bp = fixup_bp_irq_link(bp, stack, irq_stack, | ||
220 | irq_stack_end); | ||
187 | irq_stack_end = NULL; | 221 | irq_stack_end = NULL; |
188 | ops->stack(data, "EOI"); | 222 | ops->stack(data, "EOI"); |
189 | continue; | 223 | continue; |
@@ -195,28 +229,31 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, | |||
195 | /* | 229 | /* |
196 | * This handles the process stack: | 230 | * This handles the process stack: |
197 | */ | 231 | */ |
198 | bp = print_context_stack(tinfo, stack, bp, ops, data, NULL, &graph); | 232 | bp = ops->walk_stack(tinfo, stack, bp, ops, data, NULL, &graph); |
199 | put_cpu(); | 233 | put_cpu(); |
200 | } | 234 | } |
201 | EXPORT_SYMBOL(dump_trace); | 235 | EXPORT_SYMBOL(dump_trace); |
202 | 236 | ||
203 | void | 237 | void |
204 | show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, | 238 | show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, |
205 | unsigned long *sp, unsigned long bp, char *log_lvl) | 239 | unsigned long *sp, unsigned long bp, char *log_lvl) |
206 | { | 240 | { |
241 | unsigned long *irq_stack_end; | ||
242 | unsigned long *irq_stack; | ||
207 | unsigned long *stack; | 243 | unsigned long *stack; |
244 | int cpu; | ||
208 | int i; | 245 | int i; |
209 | const int cpu = smp_processor_id(); | 246 | |
210 | unsigned long *irq_stack_end = | 247 | preempt_disable(); |
211 | (unsigned long *)(per_cpu(irq_stack_ptr, cpu)); | 248 | cpu = smp_processor_id(); |
212 | unsigned long *irq_stack = | 249 | |
213 | (unsigned long *)(per_cpu(irq_stack_ptr, cpu) - IRQ_STACK_SIZE); | 250 | irq_stack_end = (unsigned long *)(per_cpu(irq_stack_ptr, cpu)); |
251 | irq_stack = (unsigned long *)(per_cpu(irq_stack_ptr, cpu) - IRQ_STACK_SIZE); | ||
214 | 252 | ||
215 | /* | 253 | /* |
216 | * debugging aid: "show_stack(NULL, NULL);" prints the | 254 | * Debugging aid: "show_stack(NULL, NULL);" prints the |
217 | * back trace for this cpu. | 255 | * back trace for this cpu: |
218 | */ | 256 | */ |
219 | |||
220 | if (sp == NULL) { | 257 | if (sp == NULL) { |
221 | if (task) | 258 | if (task) |
222 | sp = (unsigned long *)task->thread.sp; | 259 | sp = (unsigned long *)task->thread.sp; |
@@ -240,6 +277,8 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, | |||
240 | printk(" %016lx", *stack++); | 277 | printk(" %016lx", *stack++); |
241 | touch_nmi_watchdog(); | 278 | touch_nmi_watchdog(); |
242 | } | 279 | } |
280 | preempt_enable(); | ||
281 | |||
243 | printk("\n"); | 282 | printk("\n"); |
244 | show_trace_log_lvl(task, regs, sp, bp, log_lvl); | 283 | show_trace_log_lvl(task, regs, sp, bp, log_lvl); |
245 | } | 284 | } |
@@ -253,6 +292,7 @@ void show_registers(struct pt_regs *regs) | |||
253 | 292 | ||
254 | sp = regs->sp; | 293 | sp = regs->sp; |
255 | printk("CPU %d ", cpu); | 294 | printk("CPU %d ", cpu); |
295 | print_modules(); | ||
256 | __show_regs(regs, 1); | 296 | __show_regs(regs, 1); |
257 | printk("Process %s (pid: %d, threadinfo %p, task %p)\n", | 297 | printk("Process %s (pid: %d, threadinfo %p, task %p)\n", |
258 | cur->comm, cur->pid, task_thread_info(cur), cur); | 298 | cur->comm, cur->pid, task_thread_info(cur), cur); |
@@ -303,4 +343,3 @@ int is_valid_bugaddr(unsigned long ip) | |||
303 | 343 | ||
304 | return ud2 == 0x0b0f; | 344 | return ud2 == 0x0b0f; |
305 | } | 345 | } |
306 | |||