9900c48c46
git commitdc7ee00d47
("s390: lowcore stack pointer offsets") introduced a regression in regard to save_stack_trace(). The stack pointer for the asynchronous and the panic stack in the lowcore now have an additional offset applied to them. This offset needs to be taken into account in the calculation for the low and high address for the stacks. This bug was already partially fixed with9cc5c206d9
("s390/dumpstack: fix address ranges for asynchronous and panic stack"). This patch fixes it also for the stacktrace code. Fixes:dc7ee00d47
("s390: lowcore stack pointer offsets") Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Tested-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
101 lines
2.5 KiB
C
101 lines
2.5 KiB
C
/*
|
|
* Stack trace management functions
|
|
*
|
|
* Copyright IBM Corp. 2006
|
|
* Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
|
|
*/
|
|
|
|
#include <linux/sched.h>
|
|
#include <linux/stacktrace.h>
|
|
#include <linux/kallsyms.h>
|
|
#include <linux/module.h>
|
|
|
|
static unsigned long save_context_stack(struct stack_trace *trace,
|
|
unsigned long sp,
|
|
unsigned long low,
|
|
unsigned long high,
|
|
int savesched)
|
|
{
|
|
struct stack_frame *sf;
|
|
struct pt_regs *regs;
|
|
unsigned long addr;
|
|
|
|
while(1) {
|
|
if (sp < low || sp > high)
|
|
return sp;
|
|
sf = (struct stack_frame *)sp;
|
|
while(1) {
|
|
addr = sf->gprs[8];
|
|
if (!trace->skip)
|
|
trace->entries[trace->nr_entries++] = addr;
|
|
else
|
|
trace->skip--;
|
|
if (trace->nr_entries >= trace->max_entries)
|
|
return sp;
|
|
low = sp;
|
|
sp = sf->back_chain;
|
|
if (!sp)
|
|
break;
|
|
if (sp <= low || sp > high - sizeof(*sf))
|
|
return sp;
|
|
sf = (struct stack_frame *)sp;
|
|
}
|
|
/* Zero backchain detected, check for interrupt frame. */
|
|
sp = (unsigned long)(sf + 1);
|
|
if (sp <= low || sp > high - sizeof(*regs))
|
|
return sp;
|
|
regs = (struct pt_regs *)sp;
|
|
addr = regs->psw.addr;
|
|
if (savesched || !in_sched_functions(addr)) {
|
|
if (!trace->skip)
|
|
trace->entries[trace->nr_entries++] = addr;
|
|
else
|
|
trace->skip--;
|
|
}
|
|
if (trace->nr_entries >= trace->max_entries)
|
|
return sp;
|
|
low = sp;
|
|
sp = regs->gprs[15];
|
|
}
|
|
}
|
|
|
|
void save_stack_trace(struct stack_trace *trace)
|
|
{
|
|
register unsigned long sp asm ("15");
|
|
unsigned long orig_sp, new_sp, frame_size;
|
|
|
|
frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
|
|
orig_sp = sp;
|
|
new_sp = save_context_stack(trace, orig_sp,
|
|
S390_lowcore.panic_stack + frame_size - PAGE_SIZE,
|
|
S390_lowcore.panic_stack + frame_size, 1);
|
|
if (new_sp != orig_sp)
|
|
return;
|
|
new_sp = save_context_stack(trace, new_sp,
|
|
S390_lowcore.async_stack + frame_size - ASYNC_SIZE,
|
|
S390_lowcore.async_stack + frame_size, 1);
|
|
if (new_sp != orig_sp)
|
|
return;
|
|
save_context_stack(trace, new_sp,
|
|
S390_lowcore.thread_info,
|
|
S390_lowcore.thread_info + THREAD_SIZE, 1);
|
|
}
|
|
EXPORT_SYMBOL_GPL(save_stack_trace);
|
|
|
|
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
|
{
|
|
unsigned long sp, low, high;
|
|
|
|
sp = tsk->thread.ksp;
|
|
if (tsk == current) {
|
|
/* Get current stack pointer. */
|
|
asm volatile("la %0,0(15)" : "=a" (sp));
|
|
}
|
|
low = (unsigned long) task_stack_page(tsk);
|
|
high = (unsigned long) task_pt_regs(tsk);
|
|
save_context_stack(trace, sp, low, high, 0);
|
|
if (trace->nr_entries < trace->max_entries)
|
|
trace->entries[trace->nr_entries++] = ULONG_MAX;
|
|
}
|
|
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
|