aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2016-02-09 06:58:54 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2016-02-23 02:56:20 -0500
commit758d39ebd3d5666edb3b1c339f7f138c349ff8bf (patch)
tree9315a925208d986101d59a10d3fd2754e99f3c0f
parent3c2c126a675bd8a95d5ed186fe9cd788f438bff1 (diff)
s390/dumpstack: merge all four stack tracers
We have four different stack tracers of which three had bugs. So it's time to merge them to a single stack tracer which allows to specify a call back function which will be called for each step. This patch changes behavior a bit: - the "nosched" and "in_sched_functions" check within save_stack_trace_tsk did work only for the last stack frame within a context. Now it considers the check for each stack frame like it should. - both the oprofile variant and the perf_events variant did save a return address twice if a zero back chain was detected, which indicates an interrupt frame. The new dump_trace function will call the oprofile and perf_events backends with the psw address that is contained within the corresponding pt_regs structure instead. - the original show_trace and save_context_stack functions did already use the psw address of the pt_regs structure if a zero back chain was detected. However now we ignore the psw address if it is a user space address. After all we trace the kernel stack and not the user space stack. This way we also get rid of the garbage user space address in case of warnings and / or panic call traces. So this should make life easier since now there is only one stack tracer left which we can break. Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--arch/s390/include/asm/processor.h4
-rw-r--r--arch/s390/kernel/dumpstack.c63
-rw-r--r--arch/s390/kernel/perf_event.c56
-rw-r--r--arch/s390/kernel/stacktrace.c84
-rw-r--r--arch/s390/oprofile/Makefile2
-rw-r--r--arch/s390/oprofile/backtrace.c78
-rw-r--r--arch/s390/oprofile/init.c20
7 files changed, 89 insertions, 218 deletions
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index 073e18bc1676..d6fd22ea270d 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -184,6 +184,10 @@ struct task_struct;
184struct mm_struct; 184struct mm_struct;
185struct seq_file; 185struct seq_file;
186 186
187typedef int (*dump_trace_func_t)(void *data, unsigned long address);
188void dump_trace(dump_trace_func_t func, void *data,
189 struct task_struct *task, unsigned long sp);
190
187void show_cacheinfo(struct seq_file *m); 191void show_cacheinfo(struct seq_file *m);
188 192
189/* Free all resources held by a thread. */ 193/* Free all resources held by a thread. */
diff --git a/arch/s390/kernel/dumpstack.c b/arch/s390/kernel/dumpstack.c
index e1c786d23989..2150b0139a0b 100644
--- a/arch/s390/kernel/dumpstack.c
+++ b/arch/s390/kernel/dumpstack.c
@@ -19,28 +19,28 @@
19#include <asm/ipl.h> 19#include <asm/ipl.h>
20 20
21/* 21/*
22 * For show_trace we have tree different stack to consider: 22 * For dump_trace we have tree different stack to consider:
23 * - the panic stack which is used if the kernel stack has overflown 23 * - the panic stack which is used if the kernel stack has overflown
24 * - the asynchronous interrupt stack (cpu related) 24 * - the asynchronous interrupt stack (cpu related)
25 * - the synchronous kernel stack (process related) 25 * - the synchronous kernel stack (process related)
26 * The stack trace can start at any of the three stack and can potentially 26 * The stack trace can start at any of the three stacks and can potentially
27 * touch all of them. The order is: panic stack, async stack, sync stack. 27 * touch all of them. The order is: panic stack, async stack, sync stack.
28 */ 28 */
29static unsigned long 29static unsigned long
30__show_trace(unsigned long sp, unsigned long low, unsigned long high) 30__dump_trace(dump_trace_func_t func, void *data, unsigned long sp,
31 unsigned long low, unsigned long high)
31{ 32{
32 struct stack_frame *sf; 33 struct stack_frame *sf;
33 struct pt_regs *regs; 34 struct pt_regs *regs;
34 unsigned long addr;
35 35
36 while (1) { 36 while (1) {
37 if (sp < low || sp > high - sizeof(*sf)) 37 if (sp < low || sp > high - sizeof(*sf))
38 return sp; 38 return sp;
39 sf = (struct stack_frame *) sp; 39 sf = (struct stack_frame *) sp;
40 addr = sf->gprs[8];
41 printk("([<%016lx>] %pSR)\n", addr, (void *)addr);
42 /* Follow the backchain. */ 40 /* Follow the backchain. */
43 while (1) { 41 while (1) {
42 if (func(data, sf->gprs[8]))
43 return sp;
44 low = sp; 44 low = sp;
45 sp = sf->back_chain; 45 sp = sf->back_chain;
46 if (!sp) 46 if (!sp)
@@ -48,45 +48,58 @@ __show_trace(unsigned long sp, unsigned long low, unsigned long high)
48 if (sp <= low || sp > high - sizeof(*sf)) 48 if (sp <= low || sp > high - sizeof(*sf))
49 return sp; 49 return sp;
50 sf = (struct stack_frame *) sp; 50 sf = (struct stack_frame *) sp;
51 addr = sf->gprs[8];
52 printk(" [<%016lx>] %pSR\n", addr, (void *)addr);
53 } 51 }
54 /* Zero backchain detected, check for interrupt frame. */ 52 /* Zero backchain detected, check for interrupt frame. */
55 sp = (unsigned long) (sf + 1); 53 sp = (unsigned long) (sf + 1);
56 if (sp <= low || sp > high - sizeof(*regs)) 54 if (sp <= low || sp > high - sizeof(*regs))
57 return sp; 55 return sp;
58 regs = (struct pt_regs *) sp; 56 regs = (struct pt_regs *) sp;
59 addr = regs->psw.addr; 57 if (!user_mode(regs)) {
60 printk(" [<%016lx>] %pSR\n", addr, (void *)addr); 58 if (func(data, regs->psw.addr))
59 return sp;
60 }
61 low = sp; 61 low = sp;
62 sp = regs->gprs[15]; 62 sp = regs->gprs[15];
63 } 63 }
64} 64}
65 65
66static void show_trace(struct task_struct *task, unsigned long *stack) 66void dump_trace(dump_trace_func_t func, void *data, struct task_struct *task,
67 unsigned long sp)
67{ 68{
68 const unsigned long frame_size = 69 unsigned long frame_size;
69 STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
70 unsigned long sp;
71 70
72 sp = (unsigned long) stack; 71 frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
73 if (!sp)
74 sp = task ? task->thread.ksp : current_stack_pointer();
75 printk("Call Trace:\n");
76#ifdef CONFIG_CHECK_STACK 72#ifdef CONFIG_CHECK_STACK
77 sp = __show_trace(sp, 73 sp = __dump_trace(func, data, sp,
78 S390_lowcore.panic_stack + frame_size - 4096, 74 S390_lowcore.panic_stack + frame_size - 4096,
79 S390_lowcore.panic_stack + frame_size); 75 S390_lowcore.panic_stack + frame_size);
80#endif 76#endif
81 sp = __show_trace(sp, 77 sp = __dump_trace(func, data, sp,
82 S390_lowcore.async_stack + frame_size - ASYNC_SIZE, 78 S390_lowcore.async_stack + frame_size - ASYNC_SIZE,
83 S390_lowcore.async_stack + frame_size); 79 S390_lowcore.async_stack + frame_size);
84 if (task) 80 if (task)
85 __show_trace(sp, (unsigned long) task_stack_page(task), 81 __dump_trace(func, data, sp,
86 (unsigned long) task_stack_page(task) + THREAD_SIZE); 82 (unsigned long)task_stack_page(task),
83 (unsigned long)task_stack_page(task) + THREAD_SIZE);
87 else 84 else
88 __show_trace(sp, S390_lowcore.thread_info, 85 __dump_trace(func, data, sp,
86 S390_lowcore.thread_info,
89 S390_lowcore.thread_info + THREAD_SIZE); 87 S390_lowcore.thread_info + THREAD_SIZE);
88}
89EXPORT_SYMBOL_GPL(dump_trace);
90
91static int show_address(void *data, unsigned long address)
92{
93 printk("([<%016lx>] %pSR)\n", address, (void *)address);
94 return 0;
95}
96
97static void show_trace(struct task_struct *task, unsigned long sp)
98{
99 if (!sp)
100 sp = task ? task->thread.ksp : current_stack_pointer();
101 printk("Call Trace:\n");
102 dump_trace(show_address, NULL, task, sp);
90 if (!task) 103 if (!task)
91 task = current; 104 task = current;
92 debug_show_held_locks(task); 105 debug_show_held_locks(task);
@@ -112,7 +125,7 @@ void show_stack(struct task_struct *task, unsigned long *sp)
112 printk("%016lx ", *stack++); 125 printk("%016lx ", *stack++);
113 } 126 }
114 printk("\n"); 127 printk("\n");
115 show_trace(task, sp); 128 show_trace(task, (unsigned long)sp);
116} 129}
117 130
118static void show_last_breaking_event(struct pt_regs *regs) 131static void show_last_breaking_event(struct pt_regs *regs)
@@ -152,7 +165,7 @@ void show_regs(struct pt_regs *regs)
152 show_registers(regs); 165 show_registers(regs);
153 /* Show stack backtrace if pt_regs is from kernel mode */ 166 /* Show stack backtrace if pt_regs is from kernel mode */
154 if (!user_mode(regs)) 167 if (!user_mode(regs))
155 show_trace(NULL, (unsigned long *) regs->gprs[15]); 168 show_trace(NULL, regs->gprs[15]);
156 show_last_breaking_event(regs); 169 show_last_breaking_event(regs);
157} 170}
158 171
diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c
index 0943b11a2f6e..a1b708643a2c 100644
--- a/arch/s390/kernel/perf_event.c
+++ b/arch/s390/kernel/perf_event.c
@@ -222,64 +222,20 @@ static int __init service_level_perf_register(void)
222} 222}
223arch_initcall(service_level_perf_register); 223arch_initcall(service_level_perf_register);
224 224
225/* See also arch/s390/kernel/traps.c */ 225static int __perf_callchain_kernel(void *data, unsigned long address)
226static unsigned long __store_trace(struct perf_callchain_entry *entry,
227 unsigned long sp,
228 unsigned long low, unsigned long high)
229{ 226{
230 struct stack_frame *sf; 227 struct perf_callchain_entry *entry = data;
231 struct pt_regs *regs; 228
232 229 perf_callchain_store(entry, address);
233 while (1) { 230 return 0;
234 if (sp < low || sp > high - sizeof(*sf))
235 return sp;
236 sf = (struct stack_frame *) sp;
237 perf_callchain_store(entry, sf->gprs[8]);
238 /* Follow the backchain. */
239 while (1) {
240 low = sp;
241 sp = sf->back_chain;
242 if (!sp)
243 break;
244 if (sp <= low || sp > high - sizeof(*sf))
245 return sp;
246 sf = (struct stack_frame *) sp;
247 perf_callchain_store(entry, sf->gprs[8]);
248 }
249 /* Zero backchain detected, check for interrupt frame. */
250 sp = (unsigned long) (sf + 1);
251 if (sp <= low || sp > high - sizeof(*regs))
252 return sp;
253 regs = (struct pt_regs *) sp;
254 perf_callchain_store(entry, sf->gprs[8]);
255 low = sp;
256 sp = regs->gprs[15];
257 }
258} 231}
259 232
260void perf_callchain_kernel(struct perf_callchain_entry *entry, 233void perf_callchain_kernel(struct perf_callchain_entry *entry,
261 struct pt_regs *regs) 234 struct pt_regs *regs)
262{ 235{
263 unsigned long head, frame_size;
264 struct stack_frame *head_sf;
265
266 if (user_mode(regs)) 236 if (user_mode(regs))
267 return; 237 return;
268 238 dump_trace(__perf_callchain_kernel, entry, NULL, regs->gprs[15]);
269 frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
270 head = regs->gprs[15];
271 head_sf = (struct stack_frame *) head;
272
273 if (!head_sf || !head_sf->back_chain)
274 return;
275
276 head = head_sf->back_chain;
277 head = __store_trace(entry, head,
278 S390_lowcore.async_stack + frame_size - ASYNC_SIZE,
279 S390_lowcore.async_stack + frame_size);
280
281 __store_trace(entry, head, S390_lowcore.thread_info,
282 S390_lowcore.thread_info + THREAD_SIZE);
283} 239}
284 240
285/* Perf defintions for PMU event attributes in sysfs */ 241/* Perf defintions for PMU event attributes in sysfs */
diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c
index 7dd8360b1c39..44f84b23d4e5 100644
--- a/arch/s390/kernel/stacktrace.c
+++ b/arch/s390/kernel/stacktrace.c
@@ -10,69 +10,31 @@
10#include <linux/kallsyms.h> 10#include <linux/kallsyms.h>
11#include <linux/module.h> 11#include <linux/module.h>
12 12
13static unsigned long save_context_stack(struct stack_trace *trace, 13static int __save_address(void *data, unsigned long address, int nosched)
14 unsigned long sp,
15 unsigned long low,
16 unsigned long high,
17 int nosched)
18{ 14{
19 struct stack_frame *sf; 15 struct stack_trace *trace = data;
20 struct pt_regs *regs;
21 unsigned long addr;
22 16
23 while(1) { 17 if (nosched && in_sched_functions(address))
24 if (sp < low || sp > high) 18 return 0;
25 return sp; 19 if (trace->skip > 0) {
26 sf = (struct stack_frame *)sp; 20 trace->skip--;
27 while(1) { 21 return 0;
28 addr = sf->gprs[8];
29 if (!trace->skip)
30 trace->entries[trace->nr_entries++] = addr;
31 else
32 trace->skip--;
33 if (trace->nr_entries >= trace->max_entries)
34 return sp;
35 low = sp;
36 sp = sf->back_chain;
37 if (!sp)
38 break;
39 if (sp <= low || sp > high - sizeof(*sf))
40 return sp;
41 sf = (struct stack_frame *)sp;
42 }
43 /* Zero backchain detected, check for interrupt frame. */
44 sp = (unsigned long)(sf + 1);
45 if (sp <= low || sp > high - sizeof(*regs))
46 return sp;
47 regs = (struct pt_regs *)sp;
48 addr = regs->psw.addr;
49 if (!nosched || !in_sched_functions(addr)) {
50 if (!trace->skip)
51 trace->entries[trace->nr_entries++] = addr;
52 else
53 trace->skip--;
54 }
55 if (trace->nr_entries >= trace->max_entries)
56 return sp;
57 low = sp;
58 sp = regs->gprs[15];
59 } 22 }
23 if (trace->nr_entries < trace->max_entries) {
24 trace->entries[trace->nr_entries++] = address;
25 return 0;
26 }
27 return 1;
60} 28}
61 29
62static void __save_stack_trace(struct stack_trace *trace, unsigned long sp) 30static int save_address(void *data, unsigned long address)
63{ 31{
64 unsigned long new_sp, frame_size; 32 return __save_address(data, address, 0);
33}
65 34
66 frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs); 35static int save_address_nosched(void *data, unsigned long address)
67 new_sp = save_context_stack(trace, sp, 36{
68 S390_lowcore.panic_stack + frame_size - PAGE_SIZE, 37 return __save_address(data, address, 1);
69 S390_lowcore.panic_stack + frame_size, 0);
70 new_sp = save_context_stack(trace, new_sp,
71 S390_lowcore.async_stack + frame_size - ASYNC_SIZE,
72 S390_lowcore.async_stack + frame_size, 0);
73 save_context_stack(trace, new_sp,
74 S390_lowcore.thread_info,
75 S390_lowcore.thread_info + THREAD_SIZE, 0);
76} 38}
77 39
78void save_stack_trace(struct stack_trace *trace) 40void save_stack_trace(struct stack_trace *trace)
@@ -80,7 +42,7 @@ void save_stack_trace(struct stack_trace *trace)
80 unsigned long sp; 42 unsigned long sp;
81 43
82 sp = current_stack_pointer(); 44 sp = current_stack_pointer();
83 __save_stack_trace(trace, sp); 45 dump_trace(save_address, trace, NULL, sp);
84 if (trace->nr_entries < trace->max_entries) 46 if (trace->nr_entries < trace->max_entries)
85 trace->entries[trace->nr_entries++] = ULONG_MAX; 47 trace->entries[trace->nr_entries++] = ULONG_MAX;
86} 48}
@@ -88,14 +50,12 @@ EXPORT_SYMBOL_GPL(save_stack_trace);
88 50
89void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 51void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
90{ 52{
91 unsigned long sp, low, high; 53 unsigned long sp;
92 54
93 sp = tsk->thread.ksp; 55 sp = tsk->thread.ksp;
94 if (tsk == current) 56 if (tsk == current)
95 sp = current_stack_pointer(); 57 sp = current_stack_pointer();
96 low = (unsigned long) task_stack_page(tsk); 58 dump_trace(save_address_nosched, trace, tsk, sp);
97 high = (unsigned long) task_pt_regs(tsk);
98 save_context_stack(trace, sp, low, high, 1);
99 if (trace->nr_entries < trace->max_entries) 59 if (trace->nr_entries < trace->max_entries)
100 trace->entries[trace->nr_entries++] = ULONG_MAX; 60 trace->entries[trace->nr_entries++] = ULONG_MAX;
101} 61}
@@ -106,7 +66,7 @@ void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
106 unsigned long sp; 66 unsigned long sp;
107 67
108 sp = kernel_stack_pointer(regs); 68 sp = kernel_stack_pointer(regs);
109 __save_stack_trace(trace, sp); 69 dump_trace(save_address, trace, NULL, sp);
110 if (trace->nr_entries < trace->max_entries) 70 if (trace->nr_entries < trace->max_entries)
111 trace->entries[trace->nr_entries++] = ULONG_MAX; 71 trace->entries[trace->nr_entries++] = ULONG_MAX;
112} 72}
diff --git a/arch/s390/oprofile/Makefile b/arch/s390/oprofile/Makefile
index 1bd23017191e..496e4a7ee00e 100644
--- a/arch/s390/oprofile/Makefile
+++ b/arch/s390/oprofile/Makefile
@@ -6,5 +6,5 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
6 oprofilefs.o oprofile_stats.o \ 6 oprofilefs.o oprofile_stats.o \
7 timer_int.o ) 7 timer_int.o )
8 8
9oprofile-y := $(DRIVER_OBJS) init.o backtrace.o 9oprofile-y := $(DRIVER_OBJS) init.o
10oprofile-y += hwsampler.o 10oprofile-y += hwsampler.o
diff --git a/arch/s390/oprofile/backtrace.c b/arch/s390/oprofile/backtrace.c
deleted file mode 100644
index 1884e1759529..000000000000
--- a/arch/s390/oprofile/backtrace.c
+++ /dev/null
@@ -1,78 +0,0 @@
1/*
2 * S390 Version
3 * Copyright IBM Corp. 2005
4 * Author(s): Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
5 */
6
7#include <linux/oprofile.h>
8
9#include <asm/processor.h> /* for struct stack_frame */
10
11static unsigned long
12__show_trace(unsigned int *depth, unsigned long sp,
13 unsigned long low, unsigned long high)
14{
15 struct stack_frame *sf;
16 struct pt_regs *regs;
17
18 while (*depth) {
19 if (sp < low || sp > high - sizeof(*sf))
20 return sp;
21 sf = (struct stack_frame *) sp;
22 (*depth)--;
23 oprofile_add_trace(sf->gprs[8]);
24
25 /* Follow the backchain. */
26 while (*depth) {
27 low = sp;
28 sp = sf->back_chain;
29 if (!sp)
30 break;
31 if (sp <= low || sp > high - sizeof(*sf))
32 return sp;
33 sf = (struct stack_frame *) sp;
34 (*depth)--;
35 oprofile_add_trace(sf->gprs[8]);
36
37 }
38
39 if (*depth == 0)
40 break;
41
42 /* Zero backchain detected, check for interrupt frame. */
43 sp = (unsigned long) (sf + 1);
44 if (sp <= low || sp > high - sizeof(*regs))
45 return sp;
46 regs = (struct pt_regs *) sp;
47 (*depth)--;
48 oprofile_add_trace(sf->gprs[8]);
49 low = sp;
50 sp = regs->gprs[15];
51 }
52 return sp;
53}
54
55void s390_backtrace(struct pt_regs * const regs, unsigned int depth)
56{
57 unsigned long head, frame_size;
58 struct stack_frame* head_sf;
59
60 if (user_mode(regs))
61 return;
62
63 frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
64 head = regs->gprs[15];
65 head_sf = (struct stack_frame*)head;
66
67 if (!head_sf->back_chain)
68 return;
69
70 head = head_sf->back_chain;
71
72 head = __show_trace(&depth, head,
73 S390_lowcore.async_stack + frame_size - ASYNC_SIZE,
74 S390_lowcore.async_stack + frame_size);
75
76 __show_trace(&depth, head, S390_lowcore.thread_info,
77 S390_lowcore.thread_info + THREAD_SIZE);
78}
diff --git a/arch/s390/oprofile/init.c b/arch/s390/oprofile/init.c
index 9cfa2ffaa9d6..d85d1e6b1988 100644
--- a/arch/s390/oprofile/init.c
+++ b/arch/s390/oprofile/init.c
@@ -20,8 +20,6 @@
20 20
21#include "../../../drivers/oprofile/oprof.h" 21#include "../../../drivers/oprofile/oprof.h"
22 22
23extern void s390_backtrace(struct pt_regs * const regs, unsigned int depth);
24
25#include "hwsampler.h" 23#include "hwsampler.h"
26#include "op_counter.h" 24#include "op_counter.h"
27 25
@@ -494,6 +492,24 @@ static void oprofile_hwsampler_exit(void)
494 hwsampler_shutdown(); 492 hwsampler_shutdown();
495} 493}
496 494
495static int __s390_backtrace(void *data, unsigned long address)
496{
497 unsigned int *depth = data;
498
499 if (*depth == 0)
500 return 1;
501 (*depth)--;
502 oprofile_add_trace(address);
503 return 0;
504}
505
506static void s390_backtrace(struct pt_regs *regs, unsigned int depth)
507{
508 if (user_mode(regs))
509 return;
510 dump_trace(__s390_backtrace, &depth, NULL, regs->gprs[15]);
511}
512
497int __init oprofile_arch_init(struct oprofile_operations *ops) 513int __init oprofile_arch_init(struct oprofile_operations *ops)
498{ 514{
499 ops->backtrace = s390_backtrace; 515 ops->backtrace = s390_backtrace;