arch/riscv/Kconfig | 1 + arch/riscv/kernel/perf_callchain.c | 46 ++---------------------------- arch/riscv/kernel/stacktrace.c | 43 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 43 deletions(-)
Currently, userstacktrace is unsupported for riscv. So use the
perf_callchain_user() code as blueprint to implement the
arch_stack_walk_user() which add userstacktrace support on riscv.
Meanwhile, we can use arch_stack_walk_user() to simplify the implementation
of perf_callchain_user().
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202406012109.PDAjXm2i-lkp@intel.com/
---
v2:
- Fix the cocci warning, !A || A && B is equivalent to !A || B.
---
arch/riscv/Kconfig | 1 +
arch/riscv/kernel/perf_callchain.c | 46 ++----------------------------
arch/riscv/kernel/stacktrace.c | 43 ++++++++++++++++++++++++++++
3 files changed, 47 insertions(+), 43 deletions(-)
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 0525ee2d63c7..6ed96d935b8f 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -193,6 +193,7 @@ config RISCV
select THREAD_INFO_IN_TASK
select TRACE_IRQFLAGS_SUPPORT
select UACCESS_MEMCPY if !MMU
+ select USER_STACKTRACE_SUPPORT
select ZONE_DMA32 if 64BIT
config CLANG_SUPPORTS_DYNAMIC_FTRACE
diff --git a/arch/riscv/kernel/perf_callchain.c b/arch/riscv/kernel/perf_callchain.c
index 3348a61de7d9..c7468af77c66 100644
--- a/arch/riscv/kernel/perf_callchain.c
+++ b/arch/riscv/kernel/perf_callchain.c
@@ -6,37 +6,9 @@
#include <asm/stacktrace.h>
-/*
- * Get the return address for a single stackframe and return a pointer to the
- * next frame tail.
- */
-static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
- unsigned long fp, unsigned long reg_ra)
+static bool fill_callchain(void *entry, unsigned long pc)
{
- struct stackframe buftail;
- unsigned long ra = 0;
- unsigned long __user *user_frame_tail =
- (unsigned long __user *)(fp - sizeof(struct stackframe));
-
- /* Check accessibility of one struct frame_tail beyond */
- if (!access_ok(user_frame_tail, sizeof(buftail)))
- return 0;
- if (__copy_from_user_inatomic(&buftail, user_frame_tail,
- sizeof(buftail)))
- return 0;
-
- if (reg_ra != 0)
- ra = reg_ra;
- else
- ra = buftail.ra;
-
- fp = buftail.fp;
- if (ra != 0)
- perf_callchain_store(entry, ra);
- else
- return 0;
-
- return fp;
+ return perf_callchain_store(entry, pc) == 0;
}
/*
@@ -56,19 +28,7 @@ static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
struct pt_regs *regs)
{
- unsigned long fp = 0;
-
- fp = regs->s0;
- perf_callchain_store(entry, regs->epc);
-
- fp = user_backtrace(entry, fp, regs->ra);
- while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
- fp = user_backtrace(entry, fp, 0);
-}
-
-static bool fill_callchain(void *entry, unsigned long pc)
-{
- return perf_callchain_store(entry, pc) == 0;
+ arch_stack_walk_user(fill_callchain, entry, regs);
}
void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
index 528ec7cc9a62..9685a2baa5d9 100644
--- a/arch/riscv/kernel/stacktrace.c
+++ b/arch/riscv/kernel/stacktrace.c
@@ -161,3 +161,46 @@ noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie
{
walk_stackframe(task, regs, consume_entry, cookie);
}
+
+/*
+ * Get the return address for a single stackframe and return a pointer to the
+ * next frame tail.
+ */
+static unsigned long unwind_user_frame(stack_trace_consume_fn consume_entry,
+ void *cookie, unsigned long fp,
+ unsigned long reg_ra)
+{
+ struct stackframe buftail;
+ unsigned long ra = 0;
+ unsigned long __user *user_frame_tail =
+ (unsigned long __user *)(fp - sizeof(struct stackframe));
+
+ /* Check accessibility of one struct frame_tail beyond */
+ if (!access_ok(user_frame_tail, sizeof(buftail)))
+ return 0;
+ if (__copy_from_user_inatomic(&buftail, user_frame_tail,
+ sizeof(buftail)))
+ return 0;
+
+ ra = reg_ra ? : buftail.ra;
+
+ fp = buftail.fp;
+ if (!ra || !consume_entry(cookie, ra))
+ return 0;
+
+ return fp;
+}
+
+void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
+ const struct pt_regs *regs)
+{
+ unsigned long fp = 0;
+
+ fp = regs->s0;
+ if (!consume_entry(cookie, regs->epc))
+ return;
+
+ fp = unwind_user_frame(consume_entry, cookie, fp, regs->ra);
+ while (fp && !(fp & 0x3))
+ fp = unwind_user_frame(consume_entry, cookie, fp, 0);
+}
--
2.34.1
Jinjie Ruan <ruanjinjie@huawei.com> writes:
> Currently, userstacktrace is unsupported for riscv. So use the
> perf_callchain_user() code as blueprint to implement the
> arch_stack_walk_user() which add userstacktrace support on riscv.
> Meanwhile, we can use arch_stack_walk_user() to simplify the implementation
> of perf_callchain_user().
>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/oe-kbuild-all/202406012109.PDAjXm2i-lkp@intel.com/
For future reference: The LTP tags shouldn't be used when you're
spinning a new version.
> ---
> v2:
> - Fix the cocci warning, !A || A && B is equivalent to !A || B.
> ---
> arch/riscv/Kconfig | 1 +
> arch/riscv/kernel/perf_callchain.c | 46 ++----------------------------
> arch/riscv/kernel/stacktrace.c | 43 ++++++++++++++++++++++++++++
> 3 files changed, 47 insertions(+), 43 deletions(-)
[...]
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 0525ee2d63c7..6ed96d935b8f 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -193,6 +193,7 @@ config RISCV
> select THREAD_INFO_IN_TASK
> select TRACE_IRQFLAGS_SUPPORT
> select UACCESS_MEMCPY if !MMU
> + select USER_STACKTRACE_SUPPORT
> select ZONE_DMA32 if 64BIT
>
> config CLANG_SUPPORTS_DYNAMIC_FTRACE
> diff --git a/arch/riscv/kernel/perf_callchain.c b/arch/riscv/kernel/perf_callchain.c
> index 3348a61de7d9..c7468af77c66 100644
> --- a/arch/riscv/kernel/perf_callchain.c
> +++ b/arch/riscv/kernel/perf_callchain.c
> @@ -6,37 +6,9 @@
>
> #include <asm/stacktrace.h>
>
> -/*
> - * Get the return address for a single stackframe and return a pointer to the
> - * next frame tail.
> - */
> -static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
> - unsigned long fp, unsigned long reg_ra)
> +static bool fill_callchain(void *entry, unsigned long pc)
> {
> - struct stackframe buftail;
> - unsigned long ra = 0;
> - unsigned long __user *user_frame_tail =
> - (unsigned long __user *)(fp - sizeof(struct stackframe));
> -
> - /* Check accessibility of one struct frame_tail beyond */
> - if (!access_ok(user_frame_tail, sizeof(buftail)))
> - return 0;
> - if (__copy_from_user_inatomic(&buftail, user_frame_tail,
> - sizeof(buftail)))
> - return 0;
> -
> - if (reg_ra != 0)
> - ra = reg_ra;
> - else
> - ra = buftail.ra;
> -
> - fp = buftail.fp;
> - if (ra != 0)
> - perf_callchain_store(entry, ra);
> - else
> - return 0;
> -
> - return fp;
> + return perf_callchain_store(entry, pc) == 0;
> }
>
> /*
> @@ -56,19 +28,7 @@ static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
> void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
> struct pt_regs *regs)
> {
> - unsigned long fp = 0;
> -
> - fp = regs->s0;
> - perf_callchain_store(entry, regs->epc);
> -
> - fp = user_backtrace(entry, fp, regs->ra);
> - while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
> - fp = user_backtrace(entry, fp, 0);
> -}
> -
> -static bool fill_callchain(void *entry, unsigned long pc)
> -{
> - return perf_callchain_store(entry, pc) == 0;
> + arch_stack_walk_user(fill_callchain, entry, regs);
> }
>
> void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
> diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
> index 528ec7cc9a62..9685a2baa5d9 100644
> --- a/arch/riscv/kernel/stacktrace.c
> +++ b/arch/riscv/kernel/stacktrace.c
> @@ -161,3 +161,46 @@ noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie
> {
> walk_stackframe(task, regs, consume_entry, cookie);
> }
> +
> +/*
> + * Get the return address for a single stackframe and return a pointer to the
> + * next frame tail.
> + */
> +static unsigned long unwind_user_frame(stack_trace_consume_fn consume_entry,
> + void *cookie, unsigned long fp,
> + unsigned long reg_ra)
> +{
> + struct stackframe buftail;
> + unsigned long ra = 0;
> + unsigned long __user *user_frame_tail =
> + (unsigned long __user *)(fp - sizeof(struct stackframe));
> +
> + /* Check accessibility of one struct frame_tail beyond */
> + if (!access_ok(user_frame_tail, sizeof(buftail)))
> + return 0;
> + if (__copy_from_user_inatomic(&buftail, user_frame_tail,
> + sizeof(buftail)))
> + return 0;
> +
> + ra = reg_ra ? : buftail.ra;
> +
> + fp = buftail.fp;
> + if (!ra || !consume_entry(cookie, ra))
> + return 0;
> +
> + return fp;
> +}
> +
> +void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
> + const struct pt_regs *regs)
> +{
> + unsigned long fp = 0;
> +
> + fp = regs->s0;
> + if (!consume_entry(cookie, regs->epc))
> + return;
> +
> + fp = unwind_user_frame(consume_entry, cookie, fp, regs->ra);
> + while (fp && !(fp & 0x3))
Just an observation that the "entry->nr < entry->max_stack" check was
removed in this generalization, but that's OK since it's checked in
perf_callchain_store().
Not really part of your change, but shouldn't the check be 0x7 (checking
for 16B sp/fp alignment), rather than 0x3?
Björn
On 2024/7/3 22:36, Björn Töpel wrote:
> Jinjie Ruan <ruanjinjie@huawei.com> writes:
>
>> Currently, userstacktrace is unsupported for riscv. So use the
>> perf_callchain_user() code as blueprint to implement the
>> arch_stack_walk_user() which add userstacktrace support on riscv.
>> Meanwhile, we can use arch_stack_walk_user() to simplify the implementation
>> of perf_callchain_user().
>>
>> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
>> Reported-by: kernel test robot <lkp@intel.com>
>> Closes: https://lore.kernel.org/oe-kbuild-all/202406012109.PDAjXm2i-lkp@intel.com/
>
> For future reference: The LTP tags shouldn't be used when you're
> spinning a new version.
Thank you! I'll remove it.
>
>> ---
>> v2:
>> - Fix the cocci warning, !A || A && B is equivalent to !A || B.
>> ---
>> arch/riscv/Kconfig | 1 +
>> arch/riscv/kernel/perf_callchain.c | 46 ++----------------------------
>> arch/riscv/kernel/stacktrace.c | 43 ++++++++++++++++++++++++++++
>> 3 files changed, 47 insertions(+), 43 deletions(-)
>
> [...]
>
>> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
>> index 0525ee2d63c7..6ed96d935b8f 100644
>> --- a/arch/riscv/Kconfig
>> +++ b/arch/riscv/Kconfig
>> @@ -193,6 +193,7 @@ config RISCV
>> select THREAD_INFO_IN_TASK
>> select TRACE_IRQFLAGS_SUPPORT
>> select UACCESS_MEMCPY if !MMU
>> + select USER_STACKTRACE_SUPPORT
>> select ZONE_DMA32 if 64BIT
>>
>> config CLANG_SUPPORTS_DYNAMIC_FTRACE
>> diff --git a/arch/riscv/kernel/perf_callchain.c b/arch/riscv/kernel/perf_callchain.c
>> index 3348a61de7d9..c7468af77c66 100644
>> --- a/arch/riscv/kernel/perf_callchain.c
>> +++ b/arch/riscv/kernel/perf_callchain.c
>> @@ -6,37 +6,9 @@
>>
>> #include <asm/stacktrace.h>
>>
>> -/*
>> - * Get the return address for a single stackframe and return a pointer to the
>> - * next frame tail.
>> - */
>> -static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
>> - unsigned long fp, unsigned long reg_ra)
>> +static bool fill_callchain(void *entry, unsigned long pc)
>> {
>> - struct stackframe buftail;
>> - unsigned long ra = 0;
>> - unsigned long __user *user_frame_tail =
>> - (unsigned long __user *)(fp - sizeof(struct stackframe));
>> -
>> - /* Check accessibility of one struct frame_tail beyond */
>> - if (!access_ok(user_frame_tail, sizeof(buftail)))
>> - return 0;
>> - if (__copy_from_user_inatomic(&buftail, user_frame_tail,
>> - sizeof(buftail)))
>> - return 0;
>> -
>> - if (reg_ra != 0)
>> - ra = reg_ra;
>> - else
>> - ra = buftail.ra;
>> -
>> - fp = buftail.fp;
>> - if (ra != 0)
>> - perf_callchain_store(entry, ra);
>> - else
>> - return 0;
>> -
>> - return fp;
>> + return perf_callchain_store(entry, pc) == 0;
>> }
>>
>> /*
>> @@ -56,19 +28,7 @@ static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
>> void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
>> struct pt_regs *regs)
>> {
>> - unsigned long fp = 0;
>> -
>> - fp = regs->s0;
>> - perf_callchain_store(entry, regs->epc);
>> -
>> - fp = user_backtrace(entry, fp, regs->ra);
>> - while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
>> - fp = user_backtrace(entry, fp, 0);
>> -}
>> -
>> -static bool fill_callchain(void *entry, unsigned long pc)
>> -{
>> - return perf_callchain_store(entry, pc) == 0;
>> + arch_stack_walk_user(fill_callchain, entry, regs);
>> }
>>
>> void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
>> diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
>> index 528ec7cc9a62..9685a2baa5d9 100644
>> --- a/arch/riscv/kernel/stacktrace.c
>> +++ b/arch/riscv/kernel/stacktrace.c
>> @@ -161,3 +161,46 @@ noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie
>> {
>> walk_stackframe(task, regs, consume_entry, cookie);
>> }
>> +
>> +/*
>> + * Get the return address for a single stackframe and return a pointer to the
>> + * next frame tail.
>> + */
>> +static unsigned long unwind_user_frame(stack_trace_consume_fn consume_entry,
>> + void *cookie, unsigned long fp,
>> + unsigned long reg_ra)
>> +{
>> + struct stackframe buftail;
>> + unsigned long ra = 0;
>> + unsigned long __user *user_frame_tail =
>> + (unsigned long __user *)(fp - sizeof(struct stackframe));
>> +
>> + /* Check accessibility of one struct frame_tail beyond */
>> + if (!access_ok(user_frame_tail, sizeof(buftail)))
>> + return 0;
>> + if (__copy_from_user_inatomic(&buftail, user_frame_tail,
>> + sizeof(buftail)))
>> + return 0;
>> +
>> + ra = reg_ra ? : buftail.ra;
>> +
>> + fp = buftail.fp;
>> + if (!ra || !consume_entry(cookie, ra))
>> + return 0;
>> +
>> + return fp;
>> +}
>> +
>> +void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
>> + const struct pt_regs *regs)
>> +{
>> + unsigned long fp = 0;
>> +
>> + fp = regs->s0;
>> + if (!consume_entry(cookie, regs->epc))
>> + return;
>> +
>> + fp = unwind_user_frame(consume_entry, cookie, fp, regs->ra);
>> + while (fp && !(fp & 0x3))
>
> Just an observation that the "entry->nr < entry->max_stack" check was
> removed in this generalization, but that's OK since it's checked in
> perf_callchain_store().
Yes, the checked is not necessary, because perf_callchain_store() check
"entry->nr < entry->max_stack"
Also the caller function stack_trace_save_user() registers the
consume_entry callback "stack_trace_consume_entry" for saving user stack
traces into a storage array, in which entry->nr against entry->max_stack
is put.
>
> Not really part of your change, but shouldn't the check be 0x7 (checking
> for 16B sp/fp alignment), rather than 0x3?
You are right! From the below "RISCV Calling Convention" :
"In the standard RISC-V calling convention, the stack grows downward
and the stack pointer is always kept 16-byte aligned"
So the sp should be 16-byte aligned, and the "PT_SIZE_ON_STACK" is also
16-byte aligned, so I think the fp is also 16-byte aligned.
Link: https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
>
>
> Björn
>
>
Gentle ping.
On 2024/6/4 11:47, Jinjie Ruan wrote:
> Currently, userstacktrace is unsupported for riscv. So use the
> perf_callchain_user() code as blueprint to implement the
> arch_stack_walk_user() which add userstacktrace support on riscv.
> Meanwhile, we can use arch_stack_walk_user() to simplify the implementation
> of perf_callchain_user().
>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/oe-kbuild-all/202406012109.PDAjXm2i-lkp@intel.com/
> ---
> v2:
> - Fix the cocci warning, !A || A && B is equivalent to !A || B.
> ---
> arch/riscv/Kconfig | 1 +
> arch/riscv/kernel/perf_callchain.c | 46 ++----------------------------
> arch/riscv/kernel/stacktrace.c | 43 ++++++++++++++++++++++++++++
> 3 files changed, 47 insertions(+), 43 deletions(-)
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 0525ee2d63c7..6ed96d935b8f 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -193,6 +193,7 @@ config RISCV
> select THREAD_INFO_IN_TASK
> select TRACE_IRQFLAGS_SUPPORT
> select UACCESS_MEMCPY if !MMU
> + select USER_STACKTRACE_SUPPORT
> select ZONE_DMA32 if 64BIT
>
> config CLANG_SUPPORTS_DYNAMIC_FTRACE
> diff --git a/arch/riscv/kernel/perf_callchain.c b/arch/riscv/kernel/perf_callchain.c
> index 3348a61de7d9..c7468af77c66 100644
> --- a/arch/riscv/kernel/perf_callchain.c
> +++ b/arch/riscv/kernel/perf_callchain.c
> @@ -6,37 +6,9 @@
>
> #include <asm/stacktrace.h>
>
> -/*
> - * Get the return address for a single stackframe and return a pointer to the
> - * next frame tail.
> - */
> -static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
> - unsigned long fp, unsigned long reg_ra)
> +static bool fill_callchain(void *entry, unsigned long pc)
> {
> - struct stackframe buftail;
> - unsigned long ra = 0;
> - unsigned long __user *user_frame_tail =
> - (unsigned long __user *)(fp - sizeof(struct stackframe));
> -
> - /* Check accessibility of one struct frame_tail beyond */
> - if (!access_ok(user_frame_tail, sizeof(buftail)))
> - return 0;
> - if (__copy_from_user_inatomic(&buftail, user_frame_tail,
> - sizeof(buftail)))
> - return 0;
> -
> - if (reg_ra != 0)
> - ra = reg_ra;
> - else
> - ra = buftail.ra;
> -
> - fp = buftail.fp;
> - if (ra != 0)
> - perf_callchain_store(entry, ra);
> - else
> - return 0;
> -
> - return fp;
> + return perf_callchain_store(entry, pc) == 0;
> }
>
> /*
> @@ -56,19 +28,7 @@ static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
> void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
> struct pt_regs *regs)
> {
> - unsigned long fp = 0;
> -
> - fp = regs->s0;
> - perf_callchain_store(entry, regs->epc);
> -
> - fp = user_backtrace(entry, fp, regs->ra);
> - while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
> - fp = user_backtrace(entry, fp, 0);
> -}
> -
> -static bool fill_callchain(void *entry, unsigned long pc)
> -{
> - return perf_callchain_store(entry, pc) == 0;
> + arch_stack_walk_user(fill_callchain, entry, regs);
> }
>
> void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
> diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
> index 528ec7cc9a62..9685a2baa5d9 100644
> --- a/arch/riscv/kernel/stacktrace.c
> +++ b/arch/riscv/kernel/stacktrace.c
> @@ -161,3 +161,46 @@ noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie
> {
> walk_stackframe(task, regs, consume_entry, cookie);
> }
> +
> +/*
> + * Get the return address for a single stackframe and return a pointer to the
> + * next frame tail.
> + */
> +static unsigned long unwind_user_frame(stack_trace_consume_fn consume_entry,
> + void *cookie, unsigned long fp,
> + unsigned long reg_ra)
> +{
> + struct stackframe buftail;
> + unsigned long ra = 0;
> + unsigned long __user *user_frame_tail =
> + (unsigned long __user *)(fp - sizeof(struct stackframe));
> +
> + /* Check accessibility of one struct frame_tail beyond */
> + if (!access_ok(user_frame_tail, sizeof(buftail)))
> + return 0;
> + if (__copy_from_user_inatomic(&buftail, user_frame_tail,
> + sizeof(buftail)))
> + return 0;
> +
> + ra = reg_ra ? : buftail.ra;
> +
> + fp = buftail.fp;
> + if (!ra || !consume_entry(cookie, ra))
> + return 0;
> +
> + return fp;
> +}
> +
> +void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
> + const struct pt_regs *regs)
> +{
> + unsigned long fp = 0;
> +
> + fp = regs->s0;
> + if (!consume_entry(cookie, regs->epc))
> + return;
> +
> + fp = unwind_user_frame(consume_entry, cookie, fp, regs->ra);
> + while (fp && !(fp & 0x3))
> + fp = unwind_user_frame(consume_entry, cookie, fp, 0);
> +}
© 2016 - 2026 Red Hat, Inc.