Skip to content

Commit

Permalink
arm64: Make all stack walking functions use arch_stack_walk()
Browse files Browse the repository at this point in the history
Currently, there are multiple functions in ARM64 code that walk the
stack using start_backtrace() and unwind_frame(). Convert all of
them to use arch_stack_walk(). This makes maintenance easier.

Here is the list of functions:

	perf_callchain_kernel()
	get_wchan()
	return_address()
	dump_backtrace()
	profile_pc()

Signed-off-by: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
  • Loading branch information
madvenka786 authored and intel-lab-lkp committed Aug 12, 2021
1 parent 36a21d5 commit 560d91e
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 70 deletions.
3 changes: 0 additions & 3 deletions arch/arm64/include/asm/stacktrace.h
Expand Up @@ -61,9 +61,6 @@ struct stackframe {
#endif
};

extern int unwind_frame(struct task_struct *tsk, struct stackframe *frame);
extern void walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
bool (*fn)(void *, unsigned long), void *data);
extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
const char *loglvl);

Expand Down
5 changes: 1 addition & 4 deletions arch/arm64/kernel/perf_callchain.c
Expand Up @@ -147,15 +147,12 @@ static bool callchain_trace(void *data, unsigned long pc)
void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
struct pt_regs *regs)
{
struct stackframe frame;

if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
/* We don't support guest os callchain now */
return;
}

start_backtrace(&frame, regs->regs[29], regs->pc);
walk_stackframe(current, &frame, callchain_trace, entry);
arch_stack_walk(callchain_trace, entry, current, regs);
}

unsigned long perf_instruction_pointer(struct pt_regs *regs)
Expand Down
39 changes: 24 additions & 15 deletions arch/arm64/kernel/process.c
Expand Up @@ -544,32 +544,41 @@ __notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,
return last;
}

struct wchan_info {
unsigned long pc;
int count;
};

static bool get_wchan_cb(void *arg, unsigned long pc)
{
struct wchan_info *wchan_info = arg;

if (!in_sched_functions(pc)) {
wchan_info->pc = pc;
return false;
}
wchan_info->count--;
return !!wchan_info->count;
}

unsigned long get_wchan(struct task_struct *p)
{
struct stackframe frame;
unsigned long stack_page, ret = 0;
int count = 0;
unsigned long stack_page;
struct wchan_info wchan_info;

if (!p || p == current || task_is_running(p))
return 0;

stack_page = (unsigned long)try_get_task_stack(p);
if (!stack_page)
return 0;

start_backtrace(&frame, thread_saved_fp(p), thread_saved_pc(p));
wchan_info.pc = 0;
wchan_info.count = 16;
arch_stack_walk(get_wchan_cb, &wchan_info, p, NULL);

do {
if (unwind_frame(p, &frame))
goto out;
if (!in_sched_functions(frame.pc)) {
ret = frame.pc;
goto out;
}
} while (count++ < 16);

out:
put_task_stack(p);
return ret;
return wchan_info.pc;
}

unsigned long arch_align_stack(unsigned long sp)
Expand Down
6 changes: 1 addition & 5 deletions arch/arm64/kernel/return_address.c
Expand Up @@ -35,15 +35,11 @@ NOKPROBE_SYMBOL(save_return_addr);
void *return_address(unsigned int level)
{
struct return_address_data data;
struct stackframe frame;

data.level = level + 2;
data.addr = NULL;

start_backtrace(&frame,
(unsigned long)__builtin_frame_address(0),
(unsigned long)return_address);
walk_stackframe(current, &frame, save_return_addr, &data);
arch_stack_walk(save_return_addr, &data, current, NULL);

if (!data.level)
return data.addr;
Expand Down
38 changes: 4 additions & 34 deletions arch/arm64/kernel/stacktrace.c
Expand Up @@ -151,23 +151,21 @@ void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
}
NOKPROBE_SYMBOL(walk_stackframe);

static void dump_backtrace_entry(unsigned long where, const char *loglvl)
static bool dump_backtrace_entry(void *arg, unsigned long where)
{
char *loglvl = arg;
printk("%s %pSb\n", loglvl, (void *)where);
return true;
}

void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
const char *loglvl)
{
struct stackframe frame;
int skip = 0;

pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);

if (regs) {
if (user_mode(regs))
return;
skip = 1;
}

if (!tsk)
Expand All @@ -176,36 +174,8 @@ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
if (!try_get_task_stack(tsk))
return;

if (tsk == current) {
start_backtrace(&frame,
(unsigned long)__builtin_frame_address(0),
(unsigned long)dump_backtrace);
} else {
/*
* task blocked in __switch_to
*/
start_backtrace(&frame,
thread_saved_fp(tsk),
thread_saved_pc(tsk));
}

printk("%sCall trace:\n", loglvl);
do {
/* skip until specified stack frame */
if (!skip) {
dump_backtrace_entry(frame.pc, loglvl);
} else if (frame.fp == regs->regs[29]) {
skip = 0;
/*
* Mostly, this is the case where this function is
* called in panic/abort. As exception handler's
* stack frame does not contain the corresponding pc
* at which an exception has taken place, use regs->pc
* instead.
*/
dump_backtrace_entry(regs->pc, loglvl);
}
} while (!unwind_frame(tsk, &frame));
arch_stack_walk(dump_backtrace_entry, (void *)loglvl, tsk, regs);

put_task_stack(tsk);
}
Expand Down
22 changes: 13 additions & 9 deletions arch/arm64/kernel/time.c
Expand Up @@ -32,22 +32,26 @@
#include <asm/stacktrace.h>
#include <asm/paravirt.h>

static bool profile_pc_cb(void *arg, unsigned long pc)
{
unsigned long *prof_pc = arg;

if (in_lock_functions(pc))
return true;
*prof_pc = pc;
return false;
}

unsigned long profile_pc(struct pt_regs *regs)
{
struct stackframe frame;
unsigned long prof_pc = 0;

if (!in_lock_functions(regs->pc))
return regs->pc;

start_backtrace(&frame, regs->regs[29], regs->pc);

do {
int ret = unwind_frame(NULL, &frame);
if (ret < 0)
return 0;
} while (in_lock_functions(frame.pc));
arch_stack_walk(profile_pc_cb, &prof_pc, current, regs);

return frame.pc;
return prof_pc;
}
EXPORT_SYMBOL(profile_pc);

Expand Down

0 comments on commit 560d91e

Please sign in to comment.