s390/ftrace: Avoid calling unwinder in ftrace_return_address()
authorVasily Gorbik <gor@linux.ibm.com>
Sat, 24 Aug 2024 00:14:04 +0000 (02:14 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Tue, 27 Aug 2024 18:16:48 +0000 (20:16 +0200)
ftrace_return_address() is called extremely often from
performance-critical code paths when debugging features like
CONFIG_TRACE_IRQFLAGS are enabled. For example, with debug_defconfig,
ftrace selftests on my LPAR currently execute ftrace_return_address()
as follows:

ftrace_return_address(0) - 0 times (common code uses __builtin_return_address(0) instead)
ftrace_return_address(1) - 2,986,805,401 times (with this patch applied)
ftrace_return_address(2) - 140 times
ftrace_return_address(>2) - 0 times

The use of __builtin_return_address(n) was replaced by return_address()
with an unwinder call by commit cae74ba8c295 ("s390/ftrace:
Use unwinder instead of __builtin_return_address()") because
__builtin_return_address(n) simply walks the stack backchain and doesn't
check for reaching the stack top. For shallow stacks with fewer than
"n" frames, this results in reads at low addresses and random
memory accesses.

While calling the fully functional unwinder "works", it is very slow
for this purpose. Moreover, potentially following stack switches and
walking past IRQ context is simply wrong thing to do for
ftrace_return_address().

Reimplement return_address() to essentially be __builtin_return_address(n)
with checks for reaching the stack top. Since the ftrace_return_address(n)
argument is always a constant, keep the implementation in the header,
allowing both GCC and Clang to unroll the loop and optimize it to the
bare minimum.

Fixes: cae74ba8c295 ("s390/ftrace: Use unwinder instead of __builtin_return_address()")
Cc: stable@vger.kernel.org
Reported-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Acked-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/include/asm/ftrace.h
arch/s390/kernel/stacktrace.c

index fbadca6..4067466 100644 (file)
@@ -6,8 +6,23 @@
 #define MCOUNT_INSN_SIZE       6
 
 #ifndef __ASSEMBLY__
+#include <asm/stacktrace.h>
 
-unsigned long return_address(unsigned int n);
+static __always_inline unsigned long return_address(unsigned int n)
+{
+       struct stack_frame *sf;
+
+       if (!n)
+               return (unsigned long)__builtin_return_address(0);
+
+       sf = (struct stack_frame *)current_frame_address();
+       do {
+               sf = (struct stack_frame *)sf->back_chain;
+               if (!sf)
+                       return 0;
+       } while (--n);
+       return sf->gprs[8];
+}
 #define ftrace_return_address(n) return_address(n)
 
 void ftrace_caller(void);
index 640363b..9f59837 100644 (file)
@@ -162,22 +162,3 @@ void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
 {
        arch_stack_walk_user_common(consume_entry, cookie, NULL, regs, false);
 }
-
-unsigned long return_address(unsigned int n)
-{
-       struct unwind_state state;
-       unsigned long addr;
-
-       /* Increment to skip current stack entry */
-       n++;
-
-       unwind_for_each_frame(&state, NULL, NULL, 0) {
-               addr = unwind_get_return_address(&state);
-               if (!addr)
-                       break;
-               if (!n--)
-                       return addr;
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(return_address);