mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-26 23:45:19 +07:00
9f416319f4
do_task_stat() calls get_wchan(), which further does unwind_frame().
unwind_frame() restores frame->pc to original value in case function
graph tracer has modified a return address (LR) in a stack frame to hook
a function return. However, if function graph tracer has hit a filtered
function, then we can't unwind it as ftrace_push_return_trace() has
biased the index(frame->graph) with a 'huge negative'
offset(-FTRACE_NOTRACE_DEPTH).
Moreover, arm64 stack walker defines index(frame->graph) as unsigned
int, which can not compare a -ve number.
Similar problem we can have with calling of walk_stackframe() from
save_stack_trace_tsk() or dump_backtrace().
This patch fixes unwind_frame() to test the index for -ve value and
restore index accordingly before we can restore frame->pc.
Reproducer:
cd /sys/kernel/debug/tracing/
echo schedule > set_graph_notrace
echo 1 > options/display-graph
echo wakeup > current_tracer
ps -ef | grep -i agent
Above commands result in:
Unable to handle kernel paging request at virtual address ffff801bd3d1e000
pgd = ffff8003cbe97c00
[ffff801bd3d1e000] *pgd=0000000000000000, *pud=0000000000000000
Internal error: Oops: 96000006 [#1] SMP
[...]
CPU: 5 PID: 11696 Comm: ps Not tainted 4.11.0+ #33
[...]
task: ffff8003c21ba000 task.stack: ffff8003cc6c0000
PC is at unwind_frame+0x12c/0x180
LR is at get_wchan+0xd4/0x134
pc : [<ffff00000808892c>] lr : [<ffff0000080860b8>] pstate: 60000145
sp : ffff8003cc6c3ab0
x29: ffff8003cc6c3ab0 x28: 0000000000000001
x27: 0000000000000026 x26: 0000000000000026
x25: 00000000000012d8 x24: 0000000000000000
x23: ffff8003c1c04000 x22: ffff000008c83000
x21: ffff8003c1c00000 x20: 000000000000000f
x19: ffff8003c1bc0000 x18: 0000fffffc593690
x17: 0000000000000000 x16: 0000000000000001
x15: 0000b855670e2b60 x14: 0003e97f22cf1d0f
x13: 0000000000000001 x12: 0000000000000000
x11: 00000000e8f4883e x10: 0000000154f47ec8
x9 : 0000000070f367c0 x8 : 0000000000000000
x7 : 00008003f7290000 x6 : 0000000000000018
x5 : 0000000000000000 x4 : ffff8003c1c03cb0
x3 : ffff8003c1c03ca0 x2 : 00000017ffe80000
x1 : ffff8003cc6c3af8 x0 : ffff8003d3e9e000
Process ps (pid: 11696, stack limit = 0xffff8003cc6c0000)
Stack: (0xffff8003cc6c3ab0 to 0xffff8003cc6c4000)
[...]
[<ffff00000808892c>] unwind_frame+0x12c/0x180
[<ffff000008305008>] do_task_stat+0x864/0x870
[<ffff000008305c44>] proc_tgid_stat+0x3c/0x48
[<ffff0000082fde0c>] proc_single_show+0x5c/0xb8
[<ffff0000082b27e0>] seq_read+0x160/0x414
[<ffff000008289e6c>] __vfs_read+0x58/0x164
[<ffff00000828b164>] vfs_read+0x88/0x144
[<ffff00000828c2e8>] SyS_read+0x60/0xc0
[<ffff0000080834a0>] __sys_trace_return+0x0/0x4
Fixes: 20380bb390
(arm64: ftrace: fix a stack tracer's output under function graph tracer)
Signed-off-by: Pratyush Anand <panand@redhat.com>
Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
[catalin.marinas@arm.com: replace WARN_ON with WARN_ON_ONCE]
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
96 lines
2.5 KiB
C
96 lines
2.5 KiB
C
/*
|
|
* Copyright (C) 2012 ARM Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef __ASM_STACKTRACE_H
|
|
#define __ASM_STACKTRACE_H
|
|
|
|
#include <linux/percpu.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/task_stack.h>
|
|
|
|
#include <asm/memory.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/sdei.h>
|
|
|
|
struct stackframe {
|
|
unsigned long fp;
|
|
unsigned long pc;
|
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
|
int graph;
|
|
#endif
|
|
};
|
|
|
|
extern int unwind_frame(struct task_struct *tsk, struct stackframe *frame);
|
|
extern void walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
|
|
int (*fn)(struct stackframe *, void *), void *data);
|
|
extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk);
|
|
|
|
DECLARE_PER_CPU(unsigned long *, irq_stack_ptr);
|
|
|
|
static inline bool on_irq_stack(unsigned long sp)
|
|
{
|
|
unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr);
|
|
unsigned long high = low + IRQ_STACK_SIZE;
|
|
|
|
if (!low)
|
|
return false;
|
|
|
|
return (low <= sp && sp < high);
|
|
}
|
|
|
|
static inline bool on_task_stack(struct task_struct *tsk, unsigned long sp)
|
|
{
|
|
unsigned long low = (unsigned long)task_stack_page(tsk);
|
|
unsigned long high = low + THREAD_SIZE;
|
|
|
|
return (low <= sp && sp < high);
|
|
}
|
|
|
|
#ifdef CONFIG_VMAP_STACK
|
|
DECLARE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack);
|
|
|
|
static inline bool on_overflow_stack(unsigned long sp)
|
|
{
|
|
unsigned long low = (unsigned long)raw_cpu_ptr(overflow_stack);
|
|
unsigned long high = low + OVERFLOW_STACK_SIZE;
|
|
|
|
return (low <= sp && sp < high);
|
|
}
|
|
#else
|
|
static inline bool on_overflow_stack(unsigned long sp) { return false; }
|
|
#endif
|
|
|
|
/*
|
|
* We can only safely access per-cpu stacks from current in a non-preemptible
|
|
* context.
|
|
*/
|
|
static inline bool on_accessible_stack(struct task_struct *tsk, unsigned long sp)
|
|
{
|
|
if (on_task_stack(tsk, sp))
|
|
return true;
|
|
if (tsk != current || preemptible())
|
|
return false;
|
|
if (on_irq_stack(sp))
|
|
return true;
|
|
if (on_overflow_stack(sp))
|
|
return true;
|
|
if (on_sdei_stack(sp))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
#endif /* __ASM_STACKTRACE_H */
|