mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 11:18:45 +07:00
946c191161
With frame pointers, when a task is interrupted, its stack is no longer completely reliable because the function could have been interrupted before it had a chance to save the previous frame pointer on the stack. So the caller of the interrupted function could get skipped by a stack trace. This is problematic for live patching, which needs to know whether a stack trace of a sleeping task can be relied upon. There's currently no way to detect if a sleeping task was interrupted by a page fault exception or preemption before it went to sleep. Another issue is that when dumping the stack of an interrupted task, the unwinder has no way of knowing where the saved pt_regs registers are, so it can't print them. This solves those issues by encoding the pt_regs pointer in the frame pointer on entry from an interrupt or an exception. This patch also updates the unwinder to be able to decode it, because otherwise the unwinder would be broken by this change. Note that this causes a change in the behavior of the unwinder: each instance of a pt_regs on the stack is now considered a "frame". So callers of unwind_get_return_address() will now get an occasional 'regs->ip' address that would have previously been skipped over. Suggested-by: Andy Lutomirski <luto@amacapital.net> Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/8b9f84a21e39d249049e0547b559ff8da0df0988.1476973742.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
78 lines
1.6 KiB
C
78 lines
1.6 KiB
C
#ifndef _ASM_X86_UNWIND_H
|
|
#define _ASM_X86_UNWIND_H
|
|
|
|
#include <linux/sched.h>
|
|
#include <linux/ftrace.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/stacktrace.h>
|
|
|
|
struct unwind_state {
|
|
struct stack_info stack_info;
|
|
unsigned long stack_mask;
|
|
struct task_struct *task;
|
|
int graph_idx;
|
|
#ifdef CONFIG_FRAME_POINTER
|
|
unsigned long *bp;
|
|
struct pt_regs *regs;
|
|
#else
|
|
unsigned long *sp;
|
|
#endif
|
|
};
|
|
|
|
void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
|
struct pt_regs *regs, unsigned long *first_frame);
|
|
|
|
bool unwind_next_frame(struct unwind_state *state);
|
|
|
|
unsigned long unwind_get_return_address(struct unwind_state *state);
|
|
|
|
static inline bool unwind_done(struct unwind_state *state)
|
|
{
|
|
return state->stack_info.type == STACK_TYPE_UNKNOWN;
|
|
}
|
|
|
|
static inline
|
|
void unwind_start(struct unwind_state *state, struct task_struct *task,
|
|
struct pt_regs *regs, unsigned long *first_frame)
|
|
{
|
|
first_frame = first_frame ? : get_stack_pointer(task, regs);
|
|
|
|
__unwind_start(state, task, regs, first_frame);
|
|
}
|
|
|
|
#ifdef CONFIG_FRAME_POINTER
|
|
|
|
static inline
|
|
unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
|
|
{
|
|
if (unwind_done(state))
|
|
return NULL;
|
|
|
|
return state->regs ? &state->regs->ip : state->bp + 1;
|
|
}
|
|
|
|
static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
|
|
{
|
|
if (unwind_done(state))
|
|
return NULL;
|
|
|
|
return state->regs;
|
|
}
|
|
|
|
#else /* !CONFIG_FRAME_POINTER */
|
|
|
|
static inline
|
|
unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* CONFIG_FRAME_POINTER */
|
|
|
|
#endif /* _ASM_X86_UNWIND_H */
|