From: Josh Poimboeuf <jpoim...@kernel.org> Now that the sframe infrastructure is fully in place, make it work by hooking it up to the unwind_user interface.
Signed-off-by: Josh Poimboeuf <jpoim...@kernel.org> Signed-off-by: Steven Rostedt (Google) <rost...@goodmis.org> --- Changes since v9: https://lore.kernel.org/20250717012936.619600...@kernel.org - Update the changes to unwind/user.c to handle passing a const unwind_user_frame pointer. arch/Kconfig | 1 + include/linux/unwind_user_types.h | 4 ++- kernel/unwind/user.c | 41 +++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index 69fcabf53088..277b87af949f 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -453,6 +453,7 @@ config HAVE_UNWIND_USER_FP config HAVE_UNWIND_USER_SFRAME bool + select UNWIND_USER config HAVE_PERF_REGS bool diff --git a/include/linux/unwind_user_types.h b/include/linux/unwind_user_types.h index a449f15be890..d30e8495eaa9 100644 --- a/include/linux/unwind_user_types.h +++ b/include/linux/unwind_user_types.h @@ -9,7 +9,8 @@ * available. */ enum unwind_user_type_bits { - UNWIND_USER_TYPE_FP_BIT = 0, + UNWIND_USER_TYPE_SFRAME_BIT = 0, + UNWIND_USER_TYPE_FP_BIT = 1, NR_UNWIND_USER_TYPE_BITS, }; @@ -17,6 +18,7 @@ enum unwind_user_type_bits { enum unwind_user_type { /* Type "none" for the start of stack walk iteration. */ UNWIND_USER_TYPE_NONE = 0, + UNWIND_USER_TYPE_SFRAME = BIT(UNWIND_USER_TYPE_SFRAME_BIT), UNWIND_USER_TYPE_FP = BIT(UNWIND_USER_TYPE_FP_BIT), }; diff --git a/kernel/unwind/user.c b/kernel/unwind/user.c index 97a8415e3216..9d34c7659f90 100644 --- a/kernel/unwind/user.c +++ b/kernel/unwind/user.c @@ -7,17 +7,24 @@ #include <linux/sched/task_stack.h> #include <linux/unwind_user.h> #include <linux/uaccess.h> +#include <linux/sframe.h> static const struct unwind_user_frame fp_frame = { ARCH_INIT_USER_FP_FRAME }; +static const struct unwind_user_frame *get_fp_frame(struct pt_regs *regs) +{ + return &fp_frame; +} + #define for_each_user_frame(state) \ for (unwind_user_start(state); !(state)->done; unwind_user_next(state)) -static int unwind_user_next_fp(struct unwind_user_state *state) +static int unwind_user_next_common(struct unwind_user_state *state, + const struct unwind_user_frame *frame, + struct pt_regs *regs) { - const struct unwind_user_frame *frame = &fp_frame; unsigned long cfa, fp, ra; unsigned int shift; @@ -55,6 +62,24 @@ static int unwind_user_next_fp(struct unwind_user_state *state) return 0; } +static int unwind_user_next_sframe(struct unwind_user_state *state) +{ + struct unwind_user_frame _frame, *frame; + + /* sframe expects the frame to be local storage */ + frame = &_frame; + if (sframe_find(state->ip, frame)) + return -ENOENT; + return unwind_user_next_common(state, frame, task_pt_regs(current)); +} + +static int unwind_user_next_fp(struct unwind_user_state *state) +{ + struct pt_regs *regs = task_pt_regs(current); + + return unwind_user_next_common(state, get_fp_frame(regs), regs); +} + static int unwind_user_next(struct unwind_user_state *state) { unsigned long iter_mask = state->available_types; @@ -68,6 +93,16 @@ static int unwind_user_next(struct unwind_user_state *state) state->current_type = type; switch (type) { + case UNWIND_USER_TYPE_SFRAME: + switch (unwind_user_next_sframe(state)) { + case 0: + return 0; + case -ENOENT: + continue; /* Try next method. */ + default: + state->done = true; + } + break; case UNWIND_USER_TYPE_FP: if (!unwind_user_next_fp(state)) return 0; @@ -96,6 +131,8 @@ static int unwind_user_start(struct unwind_user_state *state) return -EINVAL; } + if (current_has_sframe()) + state->available_types |= UNWIND_USER_TYPE_SFRAME; if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP)) state->available_types |= UNWIND_USER_TYPE_FP; -- 2.50.1