linux-trace-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph
@ 2024-09-12 15:08 Masami Hiramatsu (Google)
  2024-09-12 15:08 ` [PATCH v14 01/19] tracing: Add a comment about ftrace_regs definition Masami Hiramatsu (Google)
                   ` (18 more replies)
  0 siblings, 19 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:08 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

Hi,

Here is the 14th version of the series to re-implement the fprobe on
function-graph tracer. The previous version is;

https://lore.kernel.org/all/172398527264.293426.2050093948411376857.stgit@devnote2/

This version is based on trace-v6.11-rc6.

I dropped already committed bugfix patch, also add RISC-V support of
ftrace_regs APIs(ftrace_regs_get_return_address() and ftrace_partial_regs()).

Overview
--------
This series rewrites the fprobe on this function-graph.
The purposes of this change are;

 1) Remove dependency of the rethook from fprobe so that we can reduce
   the return hook code and shadow stack.

 2) Make 'ftrace_regs' the common trace interface for the function
   boundary.

1) Currently we have 2(or 3) different function return hook codes,
 the function-graph tracer and rethook (and legacy kretprobe).
 But since this  is redundant and needs double maintenance cost,
 I would like to unify those. From the user's viewpoint, function-
 graph tracer is very useful to grasp the execution path. For this
 purpose, it is hard to use the rethook in the function-graph
 tracer, but the opposite is possible. (Strictly speaking, kretprobe
 can not use it because it requires 'pt_regs' for historical reasons.)

2) Now the fprobe provides the 'pt_regs' for its handler, but that is
 wrong for the function entry and exit. Moreover, depending on the
 architecture, there is no way to accurately reproduce 'pt_regs'
 outside of interrupt or exception handlers. This means fprobe should
 not use 'pt_regs' because it does not use such exceptions.
 (Conversely, kprobe should use 'pt_regs' because it is an abstract
  interface of the software breakpoint exception.)

This series changes fprobe to use function-graph tracer for tracing
function entry and exit, instead of mixture of ftrace and rethook.
Unlike the rethook which is a per-task list of system-wide allocated
nodes, the function graph's ret_stack is a per-task shadow stack.
Thus it does not need to set 'nr_maxactive' (which is the number of
pre-allocated nodes).
Also the handlers will get the 'ftrace_regs' instead of 'pt_regs'.
Since eBPF mulit_kprobe/multi_kretprobe events still use 'pt_regs' as
their register interface, this changes it to convert 'ftrace_regs' to
'pt_regs'. Of course this conversion makes an incomplete 'pt_regs',
so users must access only registers for function parameters or
return value. 

Design
------
Instead of using ftrace's function entry hook directly, the new fprobe
is built on top of the function-graph's entry and return callbacks
with 'ftrace_regs'.

Since the fprobe requires access to 'ftrace_regs', the architecture
must support CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS and
CONFIG_HAVE_FTRACE_GRAPH_FUNC, which enables to call function-graph
entry callback with 'ftrace_regs', and also
CONFIG_HAVE_FUNCTION_GRAPH_FREGS, which passes the ftrace_regs to
return_to_handler.

All fprobes share a single function-graph ops (means shares a common
ftrace filter) similar to the kprobe-on-ftrace. This needs another
layer to find corresponding fprobe in the common function-graph
callbacks, but has much better scalability, since the number of
registered function-graph ops is limited.

In the entry callback, the fprobe runs its entry_handler and saves the
address of 'fprobe' on the function-graph's shadow stack as data. The
return callback decodes the data to get the 'fprobe' address, and runs
the exit_handler.

The fprobe introduces two hash-tables, one is for entry callback which
searches fprobes related to the given function address passed by entry
callback. The other is for a return callback which checks if the given
'fprobe' data structure pointer is still valid. Note that it is
possible to unregister fprobe before the return callback runs. Thus
the address validation must be done before using it in the return
callback.

Download
--------
This series can be applied against the ftrace/for-next branch in
linux-trace tree.

This series can also be found below branch.

https://git.kernel.org/pub/scm/linux/kernel/git/mhiramat/linux.git/log/?h=topic/fprobe-on-fgraph

Thank you,

---

Masami Hiramatsu (Google) (19):
      tracing: Add a comment about ftrace_regs definition
      tracing: Rename ftrace_regs_return_value to ftrace_regs_get_return_value
      function_graph: Pass ftrace_regs to entryfunc
      function_graph: Replace fgraph_ret_regs with ftrace_regs
      function_graph: Pass ftrace_regs to retfunc
      fprobe: Use ftrace_regs in fprobe entry handler
      fprobe: Use ftrace_regs in fprobe exit handler
      tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs
      tracing: Add ftrace_fill_perf_regs() for perf event
      tracing/fprobe: Enable fprobe events with CONFIG_DYNAMIC_FTRACE_WITH_ARGS
      bpf: Enable kprobe_multi feature if CONFIG_FPROBE is enabled
      ftrace: Add CONFIG_HAVE_FTRACE_GRAPH_FUNC
      fprobe: Rewrite fprobe on function-graph tracer
      tracing: Fix function timing profiler to initialize hashtable
      tracing/fprobe: Remove nr_maxactive from fprobe
      selftests: ftrace: Remove obsolate maxactive syntax check
      selftests/ftrace: Add a test case for repeating register/unregister fprobe
      Documentation: probes: Update fprobe on function-graph tracer
      fgraph: Skip recording calltime/rettime if it is not nneeded


 Documentation/trace/fprobe.rst                     |   42 +
 arch/arm64/Kconfig                                 |    2 
 arch/arm64/include/asm/ftrace.h                    |   47 +
 arch/arm64/kernel/asm-offsets.c                    |   12 
 arch/arm64/kernel/entry-ftrace.S                   |   32 +
 arch/arm64/kernel/ftrace.c                         |   20 +
 arch/loongarch/Kconfig                             |    4 
 arch/loongarch/include/asm/ftrace.h                |   32 -
 arch/loongarch/kernel/asm-offsets.c                |   12 
 arch/loongarch/kernel/ftrace_dyn.c                 |   10 
 arch/loongarch/kernel/mcount.S                     |   17 -
 arch/loongarch/kernel/mcount_dyn.S                 |   14 
 arch/powerpc/Kconfig                               |    1 
 arch/powerpc/include/asm/ftrace.h                  |   15 
 arch/powerpc/kernel/trace/ftrace.c                 |    2 
 arch/powerpc/kernel/trace/ftrace_64_pg.c           |   10 
 arch/riscv/Kconfig                                 |    3 
 arch/riscv/include/asm/ftrace.h                    |   43 +
 arch/riscv/kernel/ftrace.c                         |   17 +
 arch/riscv/kernel/mcount.S                         |   24 -
 arch/s390/Kconfig                                  |    3 
 arch/s390/include/asm/ftrace.h                     |   39 +
 arch/s390/kernel/asm-offsets.c                     |    6 
 arch/s390/kernel/mcount.S                          |    9 
 arch/x86/Kconfig                                   |    4 
 arch/x86/include/asm/ftrace.h                      |   37 +
 arch/x86/kernel/ftrace.c                           |   50 +-
 arch/x86/kernel/ftrace_32.S                        |   15 
 arch/x86/kernel/ftrace_64.S                        |   17 -
 include/linux/fprobe.h                             |   57 +-
 include/linux/ftrace.h                             |  136 ++++
 kernel/trace/Kconfig                               |   23 +
 kernel/trace/bpf_trace.c                           |   19 -
 kernel/trace/fgraph.c                              |   96 ++-
 kernel/trace/fprobe.c                              |  638 ++++++++++++++------
 kernel/trace/ftrace.c                              |   10 
 kernel/trace/trace.h                               |    6 
 kernel/trace/trace_fprobe.c                        |  147 ++---
 kernel/trace/trace_functions_graph.c               |   10 
 kernel/trace/trace_irqsoff.c                       |    6 
 kernel/trace/trace_probe_tmpl.h                    |    2 
 kernel/trace/trace_sched_wakeup.c                  |    6 
 kernel/trace/trace_selftest.c                      |   11 
 lib/test_fprobe.c                                  |   51 --
 samples/fprobe/fprobe_example.c                    |    4 
 .../test.d/dynevent/add_remove_fprobe_repeat.tc    |   19 +
 .../ftrace/test.d/dynevent/fprobe_syntax_errors.tc |    4 
 47 files changed, 1167 insertions(+), 617 deletions(-)
 create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_repeat.tc

--
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v14 01/19] tracing: Add a comment about ftrace_regs definition
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
@ 2024-09-12 15:08 ` Masami Hiramatsu (Google)
  2024-09-12 15:08 ` [PATCH v14 02/19] tracing: Rename ftrace_regs_return_value to ftrace_regs_get_return_value Masami Hiramatsu (Google)
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:08 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

To clarify what will be expected on ftrace_regs, add a comment to the
architecture independent definition of the ftrace_regs.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
---
 Changes in v8:
  - Update that the saved registers depends on the context.
 Changes in v3:
  - Add instruction pointer
 Changes in v2:
  - newly added.
---
 include/linux/ftrace.h |   26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index fd5e84d0ec47..42106b3de396 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -117,6 +117,32 @@ extern int ftrace_enabled;
 
 #ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
 
+/**
+ * ftrace_regs - ftrace partial/optimal register set
+ *
+ * ftrace_regs represents a group of registers which is used at the
+ * function entry and exit. There are three types of registers.
+ *
+ * - Registers for passing the parameters to callee, including the stack
+ *   pointer. (e.g. rcx, rdx, rdi, rsi, r8, r9 and rsp on x86_64)
+ * - Registers for passing the return values to caller.
+ *   (e.g. rax and rdx on x86_64)
+ * - Registers for hooking the function call and return including the
+ *   frame pointer (the frame pointer is architecture/config dependent)
+ *   (e.g. rip, rbp and rsp for x86_64)
+ *
+ * Also, architecture dependent fields can be used for internal process.
+ * (e.g. orig_ax on x86_64)
+ *
+ * On the function entry, those registers will be restored except for
+ * the stack pointer, so that user can change the function parameters
+ * and instruction pointer (e.g. live patching.)
+ * On the function exit, only registers which is used for return values
+ * are restored.
+ *
+ * NOTE: user *must not* access regs directly, only do it via APIs, because
+ * the member can be changed according to the architecture.
+ */
 struct ftrace_regs {
 	struct pt_regs		regs;
 };


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 02/19] tracing: Rename ftrace_regs_return_value to ftrace_regs_get_return_value
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
  2024-09-12 15:08 ` [PATCH v14 01/19] tracing: Add a comment about ftrace_regs definition Masami Hiramatsu (Google)
@ 2024-09-12 15:08 ` Masami Hiramatsu (Google)
  2024-09-12 15:08 ` [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc Masami Hiramatsu (Google)
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:08 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Rename ftrace_regs_return_value to ftrace_regs_get_return_value as same as
other ftrace_regs_get/set_* APIs.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
---
 Changes in v6:
  - Moved to top of the series.
 Changes in v3:
  - Newly added.
---
 arch/loongarch/include/asm/ftrace.h |    2 +-
 arch/powerpc/include/asm/ftrace.h   |    2 +-
 arch/s390/include/asm/ftrace.h      |    2 +-
 arch/x86/include/asm/ftrace.h       |    2 +-
 include/linux/ftrace.h              |    2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
index c0a682808e07..6f8517d59954 100644
--- a/arch/loongarch/include/asm/ftrace.h
+++ b/arch/loongarch/include/asm/ftrace.h
@@ -69,7 +69,7 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
 	regs_get_kernel_argument(&(fregs)->regs, n)
 #define ftrace_regs_get_stack_pointer(fregs) \
 	kernel_stack_pointer(&(fregs)->regs)
-#define ftrace_regs_return_value(fregs) \
+#define ftrace_regs_get_return_value(fregs) \
 	regs_return_value(&(fregs)->regs)
 #define ftrace_regs_set_return_value(fregs, ret) \
 	regs_set_return_value(&(fregs)->regs, ret)
diff --git a/arch/powerpc/include/asm/ftrace.h b/arch/powerpc/include/asm/ftrace.h
index 559560286e6d..23d26f3afae4 100644
--- a/arch/powerpc/include/asm/ftrace.h
+++ b/arch/powerpc/include/asm/ftrace.h
@@ -59,7 +59,7 @@ ftrace_regs_get_instruction_pointer(struct ftrace_regs *fregs)
 	regs_get_kernel_argument(&(fregs)->regs, n)
 #define ftrace_regs_get_stack_pointer(fregs) \
 	kernel_stack_pointer(&(fregs)->regs)
-#define ftrace_regs_return_value(fregs) \
+#define ftrace_regs_get_return_value(fregs) \
 	regs_return_value(&(fregs)->regs)
 #define ftrace_regs_set_return_value(fregs, ret) \
 	regs_set_return_value(&(fregs)->regs, ret)
diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
index fbadca645af7..de76c21eb4a3 100644
--- a/arch/s390/include/asm/ftrace.h
+++ b/arch/s390/include/asm/ftrace.h
@@ -83,7 +83,7 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
 	regs_get_kernel_argument(&(fregs)->regs, n)
 #define ftrace_regs_get_stack_pointer(fregs) \
 	kernel_stack_pointer(&(fregs)->regs)
-#define ftrace_regs_return_value(fregs) \
+#define ftrace_regs_get_return_value(fregs) \
 	regs_return_value(&(fregs)->regs)
 #define ftrace_regs_set_return_value(fregs, ret) \
 	regs_set_return_value(&(fregs)->regs, ret)
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 0152a81d9b4a..78f6a200e15b 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -56,7 +56,7 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
 	regs_get_kernel_argument(&(fregs)->regs, n)
 #define ftrace_regs_get_stack_pointer(fregs) \
 	kernel_stack_pointer(&(fregs)->regs)
-#define ftrace_regs_return_value(fregs) \
+#define ftrace_regs_get_return_value(fregs) \
 	regs_return_value(&(fregs)->regs)
 #define ftrace_regs_set_return_value(fregs, ret) \
 	regs_set_return_value(&(fregs)->regs, ret)
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 42106b3de396..f84fb9635fb0 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -183,7 +183,7 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs)
 	regs_get_kernel_argument(ftrace_get_regs(fregs), n)
 #define ftrace_regs_get_stack_pointer(fregs) \
 	kernel_stack_pointer(ftrace_get_regs(fregs))
-#define ftrace_regs_return_value(fregs) \
+#define ftrace_regs_get_return_value(fregs) \
 	regs_return_value(ftrace_get_regs(fregs))
 #define ftrace_regs_set_return_value(fregs, ret) \
 	regs_set_return_value(ftrace_get_regs(fregs), ret)


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
  2024-09-12 15:08 ` [PATCH v14 01/19] tracing: Add a comment about ftrace_regs definition Masami Hiramatsu (Google)
  2024-09-12 15:08 ` [PATCH v14 02/19] tracing: Rename ftrace_regs_return_value to ftrace_regs_get_return_value Masami Hiramatsu (Google)
@ 2024-09-12 15:08 ` Masami Hiramatsu (Google)
  2024-09-15  8:46   ` Steven Rostedt
                     ` (4 more replies)
  2024-09-12 15:08 ` [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs Masami Hiramatsu (Google)
                   ` (15 subsequent siblings)
  18 siblings, 5 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:08 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Pass ftrace_regs to the fgraph_ops::entryfunc(). If ftrace_regs is not
available, it passes a NULL instead. User callback function can access
some registers (including return address) via this ftrace_regs.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 Changes in v11:
  - Update for the latest for-next branch.
 Changes in v8:
  - Just pass ftrace_regs to the handler instead of adding a new
    entryregfunc.
  - Update riscv ftrace_graph_func().
 Changes in v3:
  - Update for new multiple fgraph.
---
 arch/arm64/kernel/ftrace.c               |   20 +++++++++++-
 arch/loongarch/kernel/ftrace_dyn.c       |   10 +++++-
 arch/powerpc/kernel/trace/ftrace.c       |    2 +
 arch/powerpc/kernel/trace/ftrace_64_pg.c |   10 ++++--
 arch/riscv/kernel/ftrace.c               |   17 ++++++++++
 arch/x86/kernel/ftrace.c                 |   50 +++++++++++++++++++++---------
 include/linux/ftrace.h                   |   18 ++++++++---
 kernel/trace/fgraph.c                    |   23 ++++++++------
 kernel/trace/ftrace.c                    |    3 +-
 kernel/trace/trace.h                     |    3 +-
 kernel/trace/trace_functions_graph.c     |    3 +-
 kernel/trace/trace_irqsoff.c             |    3 +-
 kernel/trace/trace_sched_wakeup.c        |    3 +-
 kernel/trace/trace_selftest.c            |    8 +++--
 14 files changed, 128 insertions(+), 45 deletions(-)

diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
index a650f5e11fc5..bc647b725e6a 100644
--- a/arch/arm64/kernel/ftrace.c
+++ b/arch/arm64/kernel/ftrace.c
@@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
 void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
 		       struct ftrace_ops *op, struct ftrace_regs *fregs)
 {
-	prepare_ftrace_return(ip, &fregs->lr, fregs->fp);
+	unsigned long return_hooker = (unsigned long)&return_to_handler;
+	unsigned long frame_pointer = fregs->fp;
+	unsigned long *parent = &fregs->lr;
+	unsigned long old;
+
+	if (unlikely(atomic_read(&current->tracing_graph_pause)))
+		return;
+
+	/*
+	 * Note:
+	 * No protection against faulting at *parent, which may be seen
+	 * on other archs. It's unlikely on AArch64.
+	 */
+	old = *parent;
+
+	if (!function_graph_enter_regs(old, ip, frame_pointer,
+				       (void *)frame_pointer, fregs)) {
+		*parent = return_hooker;
+	}
 }
 #else
 /*
diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
index bff058317062..966e0f7f7aca 100644
--- a/arch/loongarch/kernel/ftrace_dyn.c
+++ b/arch/loongarch/kernel/ftrace_dyn.c
@@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
 {
 	struct pt_regs *regs = &fregs->regs;
 	unsigned long *parent = (unsigned long *)&regs->regs[1];
+	unsigned long return_hooker = (unsigned long)&return_to_handler;
+	unsigned long old;
+
+	if (unlikely(atomic_read(&current->tracing_graph_pause)))
+		return;
+
+	old = *parent;
 
-	prepare_ftrace_return(ip, (unsigned long *)parent);
+	if (!function_graph_enter_regs(old, ip, 0, parent, fregs))
+		*parent = return_hooker;
 }
 #else
 static int ftrace_modify_graph_caller(bool enable)
diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c
index d8d6b4fd9a14..a1a0e0b57662 100644
--- a/arch/powerpc/kernel/trace/ftrace.c
+++ b/arch/powerpc/kernel/trace/ftrace.c
@@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
 	if (bit < 0)
 		goto out;
 
-	if (!function_graph_enter(parent_ip, ip, 0, (unsigned long *)sp))
+	if (!function_graph_enter_regs(parent_ip, ip, 0, (unsigned long *)sp, fregs))
 		parent_ip = ppc_function_entry(return_to_handler);
 
 	ftrace_test_recursion_unlock(bit);
diff --git a/arch/powerpc/kernel/trace/ftrace_64_pg.c b/arch/powerpc/kernel/trace/ftrace_64_pg.c
index 12fab1803bcf..4ae9eeb1c8f1 100644
--- a/arch/powerpc/kernel/trace/ftrace_64_pg.c
+++ b/arch/powerpc/kernel/trace/ftrace_64_pg.c
@@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)
  * in current thread info. Return the address we want to divert to.
  */
 static unsigned long
-__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp)
+__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp,
+			struct ftrace_regs *fregs)
 {
 	unsigned long return_hooker;
 	int bit;
@@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
 
 	return_hooker = ppc_function_entry(return_to_handler);
 
-	if (!function_graph_enter(parent, ip, 0, (unsigned long *)sp))
+	if (!function_graph_enter_regs(parent, ip, 0, (unsigned long *)sp, fregs))
 		parent = return_hooker;
 
 	ftrace_test_recursion_unlock(bit);
@@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
 void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
 		       struct ftrace_ops *op, struct ftrace_regs *fregs)
 {
-	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip, fregs->regs.gpr[1]);
+	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip,
+						   fregs->regs.gpr[1], fregs);
 }
 #else
 unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip,
 				    unsigned long sp)
 {
-	return __prepare_ftrace_return(parent, ip, sp);
+	return __prepare_ftrace_return(parent, ip, sp, NULL);
 }
 #endif
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
index 4b95c574fd04..f5c3b5a752b0 100644
--- a/arch/riscv/kernel/ftrace.c
+++ b/arch/riscv/kernel/ftrace.c
@@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
 void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
 		       struct ftrace_ops *op, struct ftrace_regs *fregs)
 {
-	prepare_ftrace_return(&fregs->ra, ip, fregs->s0);
+	unsigned long return_hooker = (unsigned long)&return_to_handler;
+	unsigned long frame_pointer = fregs->s0;
+	unsigned long *parent = &fregs->ra;
+	unsigned long old;
+
+	if (unlikely(atomic_read(&current->tracing_graph_pause)))
+		return;
+
+	/*
+	 * We don't suffer access faults, so no extra fault-recovery assembly
+	 * is needed here.
+	 */
+	old = *parent;
+
+	if (!function_graph_enter_regs(old, ip, frame_pointer, parent, fregs))
+		*parent = return_hooker;
 }
 #else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */
 extern void ftrace_graph_call(void);
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 8da0e66ca22d..decf4c11dcf3 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void)
 }
 #endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
 
-/*
- * Hook the return address and push it in the stack of return addrs
- * in current thread info.
- */
-void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
-			   unsigned long frame_pointer)
+static inline bool skip_ftrace_return(void)
 {
-	unsigned long return_hooker = (unsigned long)&return_to_handler;
-	int bit;
-
 	/*
 	 * When resuming from suspend-to-ram, this function can be indirectly
 	 * called from early CPU startup code while the CPU is in real mode,
@@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
 	 * This check isn't as accurate as virt_addr_valid(), but it should be
 	 * good enough for this purpose, and it's fast.
 	 */
-	if (unlikely((long)__builtin_frame_address(0) >= 0))
-		return;
+	if ((long)__builtin_frame_address(0) >= 0)
+		return true;
 
-	if (unlikely(ftrace_graph_is_dead()))
-		return;
+	if (ftrace_graph_is_dead())
+		return true;
+
+	if (atomic_read(&current->tracing_graph_pause))
+		return true;
+	return false;
+}
+
+/*
+ * Hook the return address and push it in the stack of return addrs
+ * in current thread info.
+ */
+void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
+			   unsigned long frame_pointer)
+{
+	unsigned long return_hooker = (unsigned long)&return_to_handler;
+	int bit;
 
-	if (unlikely(atomic_read(&current->tracing_graph_pause)))
+	if (unlikely(skip_ftrace_return()))
 		return;
 
 	bit = ftrace_test_recursion_trylock(ip, *parent);
@@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
 {
 	struct pt_regs *regs = &fregs->regs;
 	unsigned long *stack = (unsigned long *)kernel_stack_pointer(regs);
+	unsigned long return_hooker = (unsigned long)&return_to_handler;
+	unsigned long *parent = (unsigned long *)stack;
+	int bit;
 
-	prepare_ftrace_return(ip, (unsigned long *)stack, 0);
+	if (unlikely(skip_ftrace_return()))
+		return;
+
+	bit = ftrace_test_recursion_trylock(ip, *parent);
+	if (bit < 0)
+		return;
+
+	if (!function_graph_enter_regs(*parent, ip, 0, parent, fregs))
+		*parent = return_hooker;
+
+	ftrace_test_recursion_unlock(bit);
 }
 #endif
 
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index f84fb9635fb0..1fe49a28de2d 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -1063,9 +1063,12 @@ struct fgraph_ops;
 typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
 				       struct fgraph_ops *); /* return */
 typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
-				      struct fgraph_ops *); /* entry */
+				      struct fgraph_ops *,
+				      struct ftrace_regs *); /* entry */
 
-extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
+extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
+				   struct fgraph_ops *gops,
+				   struct ftrace_regs *fregs);
 bool ftrace_pids_enabled(struct ftrace_ops *ops);
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
@@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {
 extern void return_to_handler(void);
 
 extern int
-function_graph_enter(unsigned long ret, unsigned long func,
-		     unsigned long frame_pointer, unsigned long *retp);
+function_graph_enter_regs(unsigned long ret, unsigned long func,
+			  unsigned long frame_pointer, unsigned long *retp,
+			  struct ftrace_regs *fregs);
+
+static inline int function_graph_enter(unsigned long ret, unsigned long func,
+				       unsigned long fp, unsigned long *retp)
+{
+	return function_graph_enter_regs(ret, func, fp, retp, NULL);
+}
 
 struct ftrace_ret_stack *
 ftrace_graph_get_ret_stack(struct task_struct *task, int skip);
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index d7d4fb403f6f..0322c5723748 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset)
 }
 
 /* ftrace_graph_entry set to this to tell some archs to run function graph */
-static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops)
+static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
+		     struct ftrace_regs *fregs)
 {
 	return 0;
 }
@@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)
 #endif
 
 int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
-			    struct fgraph_ops *gops)
+			    struct fgraph_ops *gops, struct ftrace_regs *fregs)
 {
 	return 0;
 }
@@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,
 #endif
 
 /* If the caller does not use ftrace, call this function. */
-int function_graph_enter(unsigned long ret, unsigned long func,
-			 unsigned long frame_pointer, unsigned long *retp)
+int function_graph_enter_regs(unsigned long ret, unsigned long func,
+			      unsigned long frame_pointer, unsigned long *retp,
+			      struct ftrace_regs *fregs)
 {
 	struct ftrace_graph_ent trace;
 	unsigned long bitmap = 0;
@@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
 	if (static_branch_likely(&fgraph_do_direct)) {
 		int save_curr_ret_stack = current->curr_ret_stack;
 
-		if (static_call(fgraph_func)(&trace, fgraph_direct_gops))
+		if (static_call(fgraph_func)(&trace, fgraph_direct_gops, fregs))
 			bitmap |= BIT(fgraph_direct_gops->idx);
 		else
 			/* Clear out any saved storage */
@@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
 
 			save_curr_ret_stack = current->curr_ret_stack;
 			if (ftrace_ops_test(&gops->ops, func, NULL) &&
-			    gops->entryfunc(&trace, gops))
+			    gops->entryfunc(&trace, gops, fregs))
 				bitmap |= BIT(i);
 			else
 				/* Clear out any saved storage */
@@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
 
 static struct ftrace_ops graph_ops = {
 	.func			= ftrace_graph_func,
-	.flags			= FTRACE_OPS_GRAPH_STUB,
+	.flags			= FTRACE_OPS_GRAPH_STUB | FTRACE_OPS_FL_SAVE_ARGS,
 #ifdef FTRACE_GRAPH_TRAMP_ADDR
 	.trampoline		= FTRACE_GRAPH_TRAMP_ADDR,
 	/* trampoline_size is only needed for dynamically allocated tramps */
@@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {
 void fgraph_init_ops(struct ftrace_ops *dst_ops,
 		     struct ftrace_ops *src_ops)
 {
-	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB;
+	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB |
+			 FTRACE_OPS_FL_SAVE_ARGS;
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 	if (src_ops) {
@@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 static int fgraph_pid_func(struct ftrace_graph_ent *trace,
-			   struct fgraph_ops *gops)
+			   struct fgraph_ops *gops, struct ftrace_regs *fregs)
 {
 	struct trace_array *tr = gops->ops.private;
 	int pid;
@@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,
 			return 0;
 	}
 
-	return gops->saved_func(trace, gops);
+	return gops->saved_func(trace, gops, fregs);
 }
 
 void fgraph_update_pid_func(void)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 4c28dd177ca6..775040a9f541 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable)
 }
 
 static int profile_graph_entry(struct ftrace_graph_ent *trace,
-			       struct fgraph_ops *gops)
+			       struct fgraph_ops *gops,
+			       struct ftrace_regs *fregs)
 {
 	struct ftrace_ret_stack *ret_stack;
 
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index bd3e3069300e..28d8ad5e31e6 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);
 void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
 
 void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
-int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
+int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
+		      struct ftrace_regs *fregs);
 
 void tracing_start_cmdline_record(void);
 void tracing_stop_cmdline_record(void);
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 13d0387ac6a6..b9785fc919c9 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void)
 }
 
 int trace_graph_entry(struct ftrace_graph_ent *trace,
-		      struct fgraph_ops *gops)
+		      struct fgraph_ops *gops,
+		      struct ftrace_regs *fregs)
 {
 	unsigned long *task_var = fgraph_get_task_var(gops);
 	struct trace_array *tr = gops->private;
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index fce064e20570..ad739d76fc86 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set)
 }
 
 static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
-			       struct fgraph_ops *gops)
+			       struct fgraph_ops *gops,
+			       struct ftrace_regs *fregs)
 {
 	struct trace_array *tr = irqsoff_trace;
 	struct trace_array_cpu *data;
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
index 130ca7e7787e..23360a2700de 100644
--- a/kernel/trace/trace_sched_wakeup.c
+++ b/kernel/trace/trace_sched_wakeup.c
@@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set)
 }
 
 static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
-			      struct fgraph_ops *gops)
+			      struct fgraph_ops *gops,
+			      struct ftrace_regs *fregs)
 {
 	struct trace_array *tr = wakeup_trace;
 	struct trace_array_cpu *data;
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
index c4ad7cd7e778..89067f02094a 100644
--- a/kernel/trace/trace_selftest.c
+++ b/kernel/trace/trace_selftest.c
@@ -773,7 +773,8 @@ struct fgraph_fixture {
 };
 
 static __init int store_entry(struct ftrace_graph_ent *trace,
-			      struct fgraph_ops *gops)
+			      struct fgraph_ops *gops,
+			      struct ftrace_regs *fregs)
 {
 	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
 	const char *type = fixture->store_type_name;
@@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;
 
 /* Wrap the real function entry probe to avoid possible hanging */
 static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
-				      struct fgraph_ops *gops)
+				      struct fgraph_ops *gops,
+				      struct ftrace_regs *fregs)
 {
 	/* This is harmlessly racy, we want to approximately detect a hang */
 	if (unlikely(++graph_hang_thresh > GRAPH_MAX_FUNC_TEST)) {
@@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
 		return 0;
 	}
 
-	return trace_graph_entry(trace, gops);
+	return trace_graph_entry(trace, gops, fregs);
 }
 
 static struct fgraph_ops fgraph_ops __initdata  = {


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (2 preceding siblings ...)
  2024-09-12 15:08 ` [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc Masami Hiramatsu (Google)
@ 2024-09-12 15:08 ` Masami Hiramatsu (Google)
  2024-09-15  9:11   ` Steven Rostedt
                     ` (3 more replies)
  2024-09-12 15:09 ` [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc Masami Hiramatsu (Google)
                   ` (14 subsequent siblings)
  18 siblings, 4 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:08 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Use ftrace_regs instead of fgraph_ret_regs for tracing return value
on function_graph tracer because of simplifying the callback interface.

The CONFIG_HAVE_FUNCTION_GRAPH_RETVAL is also replaced by
CONFIG_HAVE_FUNCTION_GRAPH_FREGS.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 Changes in v8:
  - Newly added.
---
 arch/arm64/Kconfig                  |    1 +
 arch/arm64/include/asm/ftrace.h     |   23 ++++++-----------------
 arch/arm64/kernel/asm-offsets.c     |   12 ------------
 arch/arm64/kernel/entry-ftrace.S    |   32 ++++++++++++++++++--------------
 arch/loongarch/Kconfig              |    2 +-
 arch/loongarch/include/asm/ftrace.h |   24 ++----------------------
 arch/loongarch/kernel/asm-offsets.c |   12 ------------
 arch/loongarch/kernel/mcount.S      |   17 ++++++++++-------
 arch/loongarch/kernel/mcount_dyn.S  |   14 +++++++-------
 arch/riscv/Kconfig                  |    2 +-
 arch/riscv/include/asm/ftrace.h     |   26 +++++---------------------
 arch/riscv/kernel/mcount.S          |   24 +++++++++++++-----------
 arch/s390/Kconfig                   |    2 +-
 arch/s390/include/asm/ftrace.h      |   26 +++++++++-----------------
 arch/s390/kernel/asm-offsets.c      |    6 ------
 arch/s390/kernel/mcount.S           |    9 +++++----
 arch/x86/Kconfig                    |    2 +-
 arch/x86/include/asm/ftrace.h       |   22 ++--------------------
 arch/x86/kernel/ftrace_32.S         |   15 +++++++++------
 arch/x86/kernel/ftrace_64.S         |   17 +++++++++--------
 include/linux/ftrace.h              |   14 +++++++++++---
 kernel/trace/Kconfig                |    4 ++--
 kernel/trace/fgraph.c               |   21 +++++++++------------
 23 files changed, 122 insertions(+), 205 deletions(-)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index a2f8ff354ca6..17947f625b06 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -211,6 +211,7 @@ config ARM64
 	select HAVE_FTRACE_MCOUNT_RECORD
 	select HAVE_FUNCTION_TRACER
 	select HAVE_FUNCTION_ERROR_INJECTION
+	select HAVE_FUNCTION_GRAPH_FREGS
 	select HAVE_FUNCTION_GRAPH_TRACER
 	select HAVE_FUNCTION_GRAPH_RETVAL
 	select HAVE_GCC_PLUGINS
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
index dc9cf0bd2a4c..dffaab3dd1f1 100644
--- a/arch/arm64/include/asm/ftrace.h
+++ b/arch/arm64/include/asm/ftrace.h
@@ -126,6 +126,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs)
 	fregs->pc = fregs->lr;
 }
 
+static __always_inline unsigned long
+ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
+{
+	return fregs->fp;
+}
+
 int ftrace_regs_query_register_offset(const char *name);
 
 int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
@@ -183,23 +189,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym,
 
 #ifndef __ASSEMBLY__
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
-struct fgraph_ret_regs {
-	/* x0 - x7 */
-	unsigned long regs[8];
-
-	unsigned long fp;
-	unsigned long __unused;
-};
-
-static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
-{
-	return ret_regs->regs[0];
-}
-
-static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
-{
-	return ret_regs->fp;
-}
 
 void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
 			   unsigned long frame_pointer);
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 27de1dddb0ab..9e03c9a7e5c3 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -201,18 +201,6 @@ int main(void)
   DEFINE(FTRACE_OPS_FUNC,		offsetof(struct ftrace_ops, func));
 #endif
   BLANK();
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-  DEFINE(FGRET_REGS_X0,			offsetof(struct fgraph_ret_regs, regs[0]));
-  DEFINE(FGRET_REGS_X1,			offsetof(struct fgraph_ret_regs, regs[1]));
-  DEFINE(FGRET_REGS_X2,			offsetof(struct fgraph_ret_regs, regs[2]));
-  DEFINE(FGRET_REGS_X3,			offsetof(struct fgraph_ret_regs, regs[3]));
-  DEFINE(FGRET_REGS_X4,			offsetof(struct fgraph_ret_regs, regs[4]));
-  DEFINE(FGRET_REGS_X5,			offsetof(struct fgraph_ret_regs, regs[5]));
-  DEFINE(FGRET_REGS_X6,			offsetof(struct fgraph_ret_regs, regs[6]));
-  DEFINE(FGRET_REGS_X7,			offsetof(struct fgraph_ret_regs, regs[7]));
-  DEFINE(FGRET_REGS_FP,			offsetof(struct fgraph_ret_regs, fp));
-  DEFINE(FGRET_REGS_SIZE,		sizeof(struct fgraph_ret_regs));
-#endif
 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
   DEFINE(FTRACE_OPS_DIRECT_CALL,	offsetof(struct ftrace_ops, direct_call));
 #endif
diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S
index f0c16640ef21..169ccf600066 100644
--- a/arch/arm64/kernel/entry-ftrace.S
+++ b/arch/arm64/kernel/entry-ftrace.S
@@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph)
  * @fp is checked against the value passed by ftrace_graph_caller().
  */
 SYM_CODE_START(return_to_handler)
-	/* save return value regs */
-	sub sp, sp, #FGRET_REGS_SIZE
-	stp x0, x1, [sp, #FGRET_REGS_X0]
-	stp x2, x3, [sp, #FGRET_REGS_X2]
-	stp x4, x5, [sp, #FGRET_REGS_X4]
-	stp x6, x7, [sp, #FGRET_REGS_X6]
-	str x29,    [sp, #FGRET_REGS_FP]	// parent's fp
+	/* Make room for ftrace_regs */
+	sub	sp, sp, #FREGS_SIZE
+
+	/* Save return value regs */
+	stp	x0, x1, [sp, #FREGS_X0]
+	stp	x2, x3, [sp, #FREGS_X2]
+	stp	x4, x5, [sp, #FREGS_X4]
+	stp	x6, x7, [sp, #FREGS_X6]
+
+	/* Save the callsite's FP */
+	str	x29, [sp, #FREGS_FP]
 
 	mov	x0, sp
-	bl	ftrace_return_to_handler	// addr = ftrace_return_to_hander(regs);
+	bl	ftrace_return_to_handler	// addr = ftrace_return_to_hander(fregs);
 	mov	x30, x0				// restore the original return address
 
-	/* restore return value regs */
-	ldp x0, x1, [sp, #FGRET_REGS_X0]
-	ldp x2, x3, [sp, #FGRET_REGS_X2]
-	ldp x4, x5, [sp, #FGRET_REGS_X4]
-	ldp x6, x7, [sp, #FGRET_REGS_X6]
-	add sp, sp, #FGRET_REGS_SIZE
+	/* Restore return value regs */
+	ldp	x0, x1, [sp, #FREGS_X0]
+	ldp	x2, x3, [sp, #FREGS_X2]
+	ldp	x4, x5, [sp, #FREGS_X4]
+	ldp	x6, x7, [sp, #FREGS_X6]
+	add	sp, sp, #FREGS_SIZE
 
 	ret
 SYM_CODE_END(return_to_handler)
diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 70f169210b52..974f08f65f63 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -131,7 +131,7 @@ config LOONGARCH
 	select HAVE_FTRACE_MCOUNT_RECORD
 	select HAVE_FUNCTION_ARG_ACCESS_API
 	select HAVE_FUNCTION_ERROR_INJECTION
-	select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER
+	select HAVE_FUNCTION_GRAPH_FREGS
 	select HAVE_FUNCTION_GRAPH_TRACER
 	select HAVE_FUNCTION_TRACER
 	select HAVE_GCC_PLUGINS
diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
index 6f8517d59954..1a73f35ea9af 100644
--- a/arch/loongarch/include/asm/ftrace.h
+++ b/arch/loongarch/include/asm/ftrace.h
@@ -77,6 +77,8 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
 	override_function_with_return(&(fregs)->regs)
 #define ftrace_regs_query_register_offset(name) \
 	regs_query_register_offset(name)
+#define ftrace_regs_get_frame_pointer(fregs) \
+	((fregs)->regs.regs[22])
 
 #define ftrace_graph_func ftrace_graph_func
 void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
@@ -99,26 +101,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)
 
 #endif /* CONFIG_FUNCTION_TRACER */
 
-#ifndef __ASSEMBLY__
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-struct fgraph_ret_regs {
-	/* a0 - a1 */
-	unsigned long regs[2];
-
-	unsigned long fp;
-	unsigned long __unused;
-};
-
-static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
-{
-	return ret_regs->regs[0];
-}
-
-static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
-{
-	return ret_regs->fp;
-}
-#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
-#endif
-
 #endif /* _ASM_LOONGARCH_FTRACE_H */
diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c
index bee9f7a3108f..714f5b5f1956 100644
--- a/arch/loongarch/kernel/asm-offsets.c
+++ b/arch/loongarch/kernel/asm-offsets.c
@@ -279,18 +279,6 @@ static void __used output_pbe_defines(void)
 }
 #endif
 
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-static void __used output_fgraph_ret_regs_defines(void)
-{
-	COMMENT("LoongArch fgraph_ret_regs offsets.");
-	OFFSET(FGRET_REGS_A0, fgraph_ret_regs, regs[0]);
-	OFFSET(FGRET_REGS_A1, fgraph_ret_regs, regs[1]);
-	OFFSET(FGRET_REGS_FP, fgraph_ret_regs, fp);
-	DEFINE(FGRET_REGS_SIZE, sizeof(struct fgraph_ret_regs));
-	BLANK();
-}
-#endif
-
 static void __used output_kvm_defines(void)
 {
 	COMMENT("KVM/LoongArch Specific offsets.");
diff --git a/arch/loongarch/kernel/mcount.S b/arch/loongarch/kernel/mcount.S
index 3015896016a0..b6850503e061 100644
--- a/arch/loongarch/kernel/mcount.S
+++ b/arch/loongarch/kernel/mcount.S
@@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller)
 SYM_FUNC_END(ftrace_graph_caller)
 
 SYM_FUNC_START(return_to_handler)
-	PTR_ADDI	sp, sp, -FGRET_REGS_SIZE
-	PTR_S		a0, sp, FGRET_REGS_A0
-	PTR_S		a1, sp, FGRET_REGS_A1
-	PTR_S		zero, sp, FGRET_REGS_FP
+	/* Save return value regs */
+	PTR_ADDI	sp, sp, -PT_SIZE
+	PTR_S		a0, sp, PT_R4
+	PTR_S		a1, sp, PT_R5
+	PTR_S		zero, sp, PT_R22
 
 	move		a0, sp
 	bl		ftrace_return_to_handler
@@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler)
 	/* Restore the real parent address: a0 -> ra */
 	move		ra, a0
 
-	PTR_L		a0, sp, FGRET_REGS_A0
-	PTR_L		a1, sp, FGRET_REGS_A1
-	PTR_ADDI	sp, sp, FGRET_REGS_SIZE
+	/* Restore return value regs */
+	PTR_L		a0, sp, PT_R4
+	PTR_L		a1, sp, PT_R5
+	PTR_ADDI	sp, sp, PT_SIZE
+
 	jr		ra
 SYM_FUNC_END(return_to_handler)
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/loongarch/kernel/mcount_dyn.S b/arch/loongarch/kernel/mcount_dyn.S
index 0c65cf09110c..d6b474ad1d5e 100644
--- a/arch/loongarch/kernel/mcount_dyn.S
+++ b/arch/loongarch/kernel/mcount_dyn.S
@@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller)
 SYM_CODE_START(return_to_handler)
 	UNWIND_HINT_UNDEFINED
 	/* Save return value regs */
-	PTR_ADDI	sp, sp, -FGRET_REGS_SIZE
-	PTR_S		a0, sp, FGRET_REGS_A0
-	PTR_S		a1, sp, FGRET_REGS_A1
-	PTR_S		zero, sp, FGRET_REGS_FP
+	PTR_ADDI	sp, sp, -PT_SIZE
+	PTR_S		a0, sp, PT_R4
+	PTR_S		a1, sp, PT_R5
+	PTR_S		zero, sp, PT_R22
 
 	move		a0, sp
 	bl		ftrace_return_to_handler
 	move		ra, a0
 
 	/* Restore return value regs */
-	PTR_L		a0, sp, FGRET_REGS_A0
-	PTR_L		a1, sp, FGRET_REGS_A1
-	PTR_ADDI	sp, sp, FGRET_REGS_SIZE
+	PTR_L		a0, sp, PT_R4
+	PTR_L		a1, sp, PT_R5
+	PTR_ADDI	sp, sp, PT_SIZE
 
 	jr		ra
 SYM_CODE_END(return_to_handler)
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 0f3cd7c3a436..6e8422269ba4 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -140,7 +140,7 @@ config RISCV
 	select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE
 	select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL
 	select HAVE_FUNCTION_GRAPH_TRACER
-	select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER
+	select HAVE_FUNCTION_GRAPH_FREGS
 	select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION
 	select HAVE_EBPF_JIT if MMU
 	select HAVE_GUP_FAST if MMU
diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
index 2cddd79ff21b..e9f364ce9fe8 100644
--- a/arch/riscv/include/asm/ftrace.h
+++ b/arch/riscv/include/asm/ftrace.h
@@ -164,6 +164,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const struct
 	return fregs->sp;
 }
 
+static __always_inline unsigned long ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
+{
+	return fregs->s0;
+}
+
 static __always_inline unsigned long ftrace_regs_get_argument(struct ftrace_regs *fregs,
 							      unsigned int n)
 {
@@ -204,25 +209,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi
 
 #endif /* CONFIG_DYNAMIC_FTRACE */
 
-#ifndef __ASSEMBLY__
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-struct fgraph_ret_regs {
-	unsigned long a1;
-	unsigned long a0;
-	unsigned long s0;
-	unsigned long ra;
-};
-
-static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
-{
-	return ret_regs->a0;
-}
-
-static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
-{
-	return ret_regs->s0;
-}
-#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
-#endif
-
 #endif /* _ASM_RISCV_FTRACE_H */
diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S
index 3a42f6287909..068168046e0e 100644
--- a/arch/riscv/kernel/mcount.S
+++ b/arch/riscv/kernel/mcount.S
@@ -12,6 +12,8 @@
 #include <asm/asm-offsets.h>
 #include <asm/ftrace.h>
 
+#define ABI_SIZE_ON_STACK	80
+
 	.text
 
 	.macro SAVE_ABI_STATE
@@ -26,12 +28,12 @@
 	 * register if a0 was not saved.
 	 */
 	.macro SAVE_RET_ABI_STATE
-	addi	sp, sp, -4*SZREG
-	REG_S	s0, 2*SZREG(sp)
-	REG_S	ra, 3*SZREG(sp)
-	REG_S	a0, 1*SZREG(sp)
-	REG_S	a1, 0*SZREG(sp)
-	addi	s0, sp, 4*SZREG
+	addi	sp, sp, -ABI_SIZE_ON_STACK
+	REG_S	ra, 1*SZREG(sp)
+	REG_S	s0, 8*SZREG(sp)
+	REG_S	a0, 10*SZREG(sp)
+	REG_S	a1, 11*SZREG(sp)
+	addi	s0, sp, ABI_SIZE_ON_STACK
 	.endm
 
 	.macro RESTORE_ABI_STATE
@@ -41,11 +43,11 @@
 	.endm
 
 	.macro RESTORE_RET_ABI_STATE
-	REG_L	ra, 3*SZREG(sp)
-	REG_L	s0, 2*SZREG(sp)
-	REG_L	a0, 1*SZREG(sp)
-	REG_L	a1, 0*SZREG(sp)
-	addi	sp, sp, 4*SZREG
+	REG_L	ra, 1*SZREG(sp)
+	REG_L	s0, 8*SZREG(sp)
+	REG_L	a0, 10*SZREG(sp)
+	REG_L	a1, 11*SZREG(sp)
+	addi	sp, sp, ABI_SIZE_ON_STACK
 	.endm
 
 SYM_TYPED_FUNC_START(ftrace_stub)
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index a822f952f64a..12e942cfbcde 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -184,7 +184,7 @@ config S390
 	select HAVE_FTRACE_MCOUNT_RECORD
 	select HAVE_FUNCTION_ARG_ACCESS_API
 	select HAVE_FUNCTION_ERROR_INJECTION
-	select HAVE_FUNCTION_GRAPH_RETVAL
+	select HAVE_FUNCTION_GRAPH_FREGS
 	select HAVE_FUNCTION_GRAPH_TRACER
 	select HAVE_FUNCTION_TRACER
 	select HAVE_GCC_PLUGINS
diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
index de76c21eb4a3..9cdd48a46bf7 100644
--- a/arch/s390/include/asm/ftrace.h
+++ b/arch/s390/include/asm/ftrace.h
@@ -49,23 +49,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *
 	return NULL;
 }
 
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-struct fgraph_ret_regs {
-	unsigned long gpr2;
-	unsigned long fp;
-};
-
-static __always_inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
-{
-	return ret_regs->gpr2;
-}
-
-static __always_inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
-{
-	return ret_regs->fp;
-}
-#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
-
 static __always_inline unsigned long
 ftrace_regs_get_instruction_pointer(const struct ftrace_regs *fregs)
 {
@@ -92,6 +75,15 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
 #define ftrace_regs_query_register_offset(name) \
 	regs_query_register_offset(name)
 
+static __always_inline unsigned long
+ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)
+{
+	unsigned long *sp;
+
+	sp = (void *)ftrace_regs_get_stack_pointer(fregs);
+	return sp[0];	/* return backchain */
+}
+
 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
 /*
  * When an ftrace registered caller is tracing a function that is
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index ffa0dd2dbaac..d38ed80615d5 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -179,12 +179,6 @@ int main(void)
 	DEFINE(OLDMEM_SIZE, PARMAREA + offsetof(struct parmarea, oldmem_size));
 	DEFINE(COMMAND_LINE, PARMAREA + offsetof(struct parmarea, command_line));
 	DEFINE(MAX_COMMAND_LINE_SIZE, PARMAREA + offsetof(struct parmarea, max_command_line_size));
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-	/* function graph return value tracing */
-	OFFSET(__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2);
-	OFFSET(__FGRAPH_RET_FP, fgraph_ret_regs, fp);
-	DEFINE(__FGRAPH_RET_SIZE, sizeof(struct fgraph_ret_regs));
-#endif
 	OFFSET(__FTRACE_REGS_PT_REGS, ftrace_regs, regs);
 	DEFINE(__FTRACE_REGS_SIZE, sizeof(struct ftrace_regs));
 
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
index ae4d4fd9afcd..cda798b976de 100644
--- a/arch/s390/kernel/mcount.S
+++ b/arch/s390/kernel/mcount.S
@@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common)
 SYM_FUNC_START(return_to_handler)
 	stmg	%r2,%r5,32(%r15)
 	lgr	%r1,%r15
-	aghi	%r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE)
+# Allocate ftrace_regs + backchain on the stack
+	aghi	%r15,-STACK_FRAME_SIZE_FREGS
 	stg	%r1,__SF_BACKCHAIN(%r15)
 	la	%r3,STACK_FRAME_OVERHEAD(%r15)
-	stg	%r1,__FGRAPH_RET_FP(%r3)
-	stg	%r2,__FGRAPH_RET_GPR2(%r3)
+	stg	%r2,(__SF_GPRS+2*8)(%r15)
+	stg	%r15,(__SF_GPRS+15*8)(%r15)
 	lgr	%r2,%r3
 	brasl	%r14,ftrace_return_to_handler
-	aghi	%r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE
+	aghi	%r15,STACK_FRAME_SIZE_FREGS
 	lgr	%r14,%r2
 	lmg	%r2,%r5,32(%r15)
 	BR_EX	%r14
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 007bab9f2a0e..047384e4d93a 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -228,7 +228,7 @@ config X86
 	select HAVE_GUP_FAST
 	select HAVE_FENTRY			if X86_64 || DYNAMIC_FTRACE
 	select HAVE_FTRACE_MCOUNT_RECORD
-	select HAVE_FUNCTION_GRAPH_RETVAL	if HAVE_FUNCTION_GRAPH_TRACER
+	select HAVE_FUNCTION_GRAPH_FREGS	if HAVE_FUNCTION_GRAPH_TRACER
 	select HAVE_FUNCTION_GRAPH_TRACER	if X86_32 || (X86_64 && DYNAMIC_FTRACE)
 	select HAVE_FUNCTION_TRACER
 	select HAVE_GCC_PLUGINS
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 78f6a200e15b..669771ef3b5b 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -64,6 +64,8 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
 	override_function_with_return(&(fregs)->regs)
 #define ftrace_regs_query_register_offset(name) \
 	regs_query_register_offset(name)
+#define ftrace_regs_get_frame_pointer(fregs) \
+	frame_pointer(&(fregs)->regs)
 
 struct ftrace_ops;
 #define ftrace_graph_func ftrace_graph_func
@@ -148,24 +150,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
 #endif /* !COMPILE_OFFSETS */
 #endif /* !__ASSEMBLY__ */
 
-#ifndef __ASSEMBLY__
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-struct fgraph_ret_regs {
-	unsigned long ax;
-	unsigned long dx;
-	unsigned long bp;
-};
-
-static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
-{
-	return ret_regs->ax;
-}
-
-static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
-{
-	return ret_regs->bp;
-}
-#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
-#endif
-
 #endif /* _ASM_X86_FTRACE_H */
diff --git a/arch/x86/kernel/ftrace_32.S b/arch/x86/kernel/ftrace_32.S
index 58d9ed50fe61..4b265884d06c 100644
--- a/arch/x86/kernel/ftrace_32.S
+++ b/arch/x86/kernel/ftrace_32.S
@@ -23,6 +23,8 @@ SYM_FUNC_START(__fentry__)
 SYM_FUNC_END(__fentry__)
 EXPORT_SYMBOL(__fentry__)
 
+#define FRAME_SIZE	PT_OLDSS+4
+
 SYM_CODE_START(ftrace_caller)
 
 #ifdef CONFIG_FRAME_POINTER
@@ -187,14 +189,15 @@ SYM_CODE_END(ftrace_graph_caller)
 
 .globl return_to_handler
 return_to_handler:
-	pushl	$0
-	pushl	%edx
-	pushl	%eax
+	subl	$(FRAME_SIZE), %esp
+	movl	$0, PT_EBP(%esp)
+	movl	%edx, PT_EDX(%esp)
+	movl	%eax, PT_EAX(%esp)
 	movl	%esp, %eax
 	call	ftrace_return_to_handler
 	movl	%eax, %ecx
-	popl	%eax
-	popl	%edx
-	addl	$4, %esp		# skip ebp
+	movl	%eax, PT_EAX(%esp)
+	movl	%edx, PT_EDX(%esp)
+	addl	$(FRAME_SIZE), %esp
 	JMP_NOSPEC ecx
 #endif
diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
index 214f30e9f0c0..d51647228596 100644
--- a/arch/x86/kernel/ftrace_64.S
+++ b/arch/x86/kernel/ftrace_64.S
@@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__)
 SYM_CODE_START(return_to_handler)
 	UNWIND_HINT_UNDEFINED
 	ANNOTATE_NOENDBR
-	subq  $24, %rsp
 
-	/* Save the return values */
-	movq %rax, (%rsp)
-	movq %rdx, 8(%rsp)
-	movq %rbp, 16(%rsp)
+	/* Save ftrace_regs for function exit context  */
+	subq $(FRAME_SIZE), %rsp
+
+	movq %rax, RAX(%rsp)
+	movq %rdx, RDX(%rsp)
+	movq %rbp, RBP(%rsp)
 	movq %rsp, %rdi
 
 	call ftrace_return_to_handler
 
 	movq %rax, %rdi
-	movq 8(%rsp), %rdx
-	movq (%rsp), %rax
+	movq RDX(%rsp), %rdx
+	movq RAX(%rsp), %rax
 
-	addq $24, %rsp
+	addq $(FRAME_SIZE), %rsp
 	/*
 	 * Jump back to the old return address. This cannot be JMP_NOSPEC rdi
 	 * since IBT would demand that contain ENDBR, which simply isn't so for
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 1fe49a28de2d..13987cd63553 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -43,9 +43,8 @@ struct dyn_ftrace;
 
 char *arch_ftrace_match_adjust(char *str, const char *search);
 
-#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
-struct fgraph_ret_regs;
-unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs);
+#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS
+unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs);
 #else
 unsigned long ftrace_return_to_handler(unsigned long frame_pointer);
 #endif
@@ -134,6 +133,13 @@ extern int ftrace_enabled;
  * Also, architecture dependent fields can be used for internal process.
  * (e.g. orig_ax on x86_64)
  *
+ * Basically, ftrace_regs stores the registers related to the context.
+ * On function entry, registers for function parameters and hooking the
+ * function call are stored, and on function exit, registers for function
+ * return value and frame pointers are stored.
+ *
+ * And also, it dpends on the context that which registers are restored
+ * from the ftrace_regs.
  * On the function entry, those registers will be restored except for
  * the stack pointer, so that user can change the function parameters
  * and instruction pointer (e.g. live patching.)
@@ -191,6 +197,8 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs)
 	override_function_with_return(ftrace_get_regs(fregs))
 #define ftrace_regs_query_register_offset(name) \
 	regs_query_register_offset(name)
+#define ftrace_regs_get_frame_pointer(fregs) \
+	frame_pointer(&(fregs)->regs)
 #endif
 
 typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 721c3b221048..ab277eff80dc 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER
 	help
 	  See Documentation/trace/ftrace-design.rst
 
-config HAVE_FUNCTION_GRAPH_RETVAL
+config HAVE_FUNCTION_GRAPH_FREGS
 	bool
 
 config HAVE_DYNAMIC_FTRACE
@@ -232,7 +232,7 @@ config FUNCTION_GRAPH_TRACER
 
 config FUNCTION_GRAPH_RETVAL
 	bool "Kernel Function Graph Return Value"
-	depends on HAVE_FUNCTION_GRAPH_RETVAL
+	depends on HAVE_FUNCTION_GRAPH_FREGS
 	depends on FUNCTION_GRAPH_TRACER
 	default n
 	help
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index 0322c5723748..30bebe43607d 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -763,15 +763,12 @@ static struct notifier_block ftrace_suspend_notifier = {
 	.notifier_call = ftrace_suspend_notifier_call,
 };
 
-/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */
-struct fgraph_ret_regs;
-
 /*
  * Send the trace to the ring-buffer.
  * @return the original return address.
  */
-static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs,
-						unsigned long frame_pointer)
+static inline unsigned long
+__ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointer)
 {
 	struct ftrace_ret_stack *ret_stack;
 	struct ftrace_graph_ret trace;
@@ -791,7 +788,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
 
 	trace.rettime = trace_clock_local();
 #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
-	trace.retval = fgraph_ret_regs_return_value(ret_regs);
+	trace.retval = ftrace_regs_get_return_value(fregs);
 #endif
 
 	bitmap = get_bitmap_bits(current, offset);
@@ -826,14 +823,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
 }
 
 /*
- * After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can
- * leave only ftrace_return_to_handler(ret_regs).
+ * After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can
+ * leave only ftrace_return_to_handler(fregs).
  */
-#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
-unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs)
+#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS
+unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs)
 {
-	return __ftrace_return_to_handler(ret_regs,
-				fgraph_ret_regs_frame_pointer(ret_regs));
+	return __ftrace_return_to_handler(fregs,
+				ftrace_regs_get_frame_pointer(fregs));
 }
 #else
 unsigned long ftrace_return_to_handler(unsigned long frame_pointer)


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (3 preceding siblings ...)
  2024-09-12 15:08 ` [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs Masami Hiramatsu (Google)
@ 2024-09-12 15:09 ` Masami Hiramatsu (Google)
  2024-09-15  8:49   ` Steven Rostedt
                     ` (4 more replies)
  2024-09-12 15:09 ` [PATCH v14 06/19] fprobe: Use ftrace_regs in fprobe entry handler Masami Hiramatsu (Google)
                   ` (13 subsequent siblings)
  18 siblings, 5 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:09 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Pass ftrace_regs to the fgraph_ops::retfunc(). If ftrace_regs is not
available, it passes a NULL instead. User callback function can access
some registers (including return address) via this ftrace_regs.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 Changes in v8:
  - Pass ftrace_regs to retfunc, instead of adding retregfunc.
 Changes in v6:
  - update to use ftrace_regs_get_return_value() because of reordering
    patches.
 Changes in v3:
  - Update for new multiple fgraph.
  - Save the return address to instruction pointer in ftrace_regs.
---
 include/linux/ftrace.h               |    3 ++-
 kernel/trace/fgraph.c                |   16 +++++++++++-----
 kernel/trace/ftrace.c                |    3 ++-
 kernel/trace/trace.h                 |    3 ++-
 kernel/trace/trace_functions_graph.c |    7 ++++---
 kernel/trace/trace_irqsoff.c         |    3 ++-
 kernel/trace/trace_sched_wakeup.c    |    3 ++-
 kernel/trace/trace_selftest.c        |    3 ++-
 8 files changed, 27 insertions(+), 14 deletions(-)

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 13987cd63553..e7c41d9988e1 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -1069,7 +1069,8 @@ struct fgraph_ops;
 
 /* Type of the callback handlers for tracing function graph*/
 typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
-				       struct fgraph_ops *); /* return */
+				       struct fgraph_ops *,
+				       struct ftrace_regs *); /* return */
 typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
 				      struct fgraph_ops *,
 				      struct ftrace_regs *); /* entry */
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index 30bebe43607d..6a3e2db16aa4 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
 }
 
 /* ftrace_graph_return set to this to tell some archs to run function graph */
-static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops)
+static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops,
+		       struct ftrace_regs *fregs)
 {
 }
 
@@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
 }
 
 static void ftrace_graph_ret_stub(struct ftrace_graph_ret *trace,
-				  struct fgraph_ops *gops)
+				  struct fgraph_ops *gops,
+				  struct ftrace_regs *fregs)
 {
 }
 
@@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
 	}
 
 	trace.rettime = trace_clock_local();
+	if (fregs)
+		ftrace_regs_set_instruction_pointer(fregs, ret);
+
 #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
 	trace.retval = ftrace_regs_get_return_value(fregs);
 #endif
@@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
 #ifdef CONFIG_HAVE_STATIC_CALL
 	if (static_branch_likely(&fgraph_do_direct)) {
 		if (test_bit(fgraph_direct_gops->idx, &bitmap))
-			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops);
+			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops, fregs);
 	} else
 #endif
 	{
@@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
 			if (gops == &fgraph_stub)
 				continue;
 
-			gops->retfunc(&trace, gops);
+			gops->retfunc(&trace, gops, fregs);
 		}
 	}
 
@@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)
  * Simply points to ftrace_stub, but with the proper protocol.
  * Defined by the linker script in linux/vmlinux.lds.h
  */
-void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
+void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
+		       struct ftrace_regs *fregs);
 
 /* The callbacks that hook a function */
 trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 775040a9f541..fd6c5a50c5e5 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace,
 }
 
 static void profile_graph_return(struct ftrace_graph_ret *trace,
-				 struct fgraph_ops *gops)
+				 struct fgraph_ops *gops,
+				 struct ftrace_regs *fregs)
 {
 	struct ftrace_ret_stack *ret_stack;
 	struct ftrace_profile_stat *stat;
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 28d8ad5e31e6..f4a3f75bd916 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);
 void trace_default_header(struct seq_file *m);
 void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
 
-void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
+void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
+			struct ftrace_regs *fregs);
 int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
 		      struct ftrace_regs *fregs);
 
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index b9785fc919c9..241407000109 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr,
 }
 
 void trace_graph_return(struct ftrace_graph_ret *trace,
-			struct fgraph_ops *gops)
+			struct fgraph_ops *gops, struct ftrace_regs *fregs)
 {
 	unsigned long *task_var = fgraph_get_task_var(gops);
 	struct trace_array *tr = gops->private;
@@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace,
 }
 
 static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
-				      struct fgraph_ops *gops)
+				      struct fgraph_ops *gops,
+				      struct ftrace_regs *fregs)
 {
 	ftrace_graph_addr_finish(gops, trace);
 
@@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
 	    (trace->rettime - trace->calltime < tracing_thresh))
 		return;
 	else
-		trace_graph_return(trace, gops);
+		trace_graph_return(trace, gops, fregs);
 }
 
 static struct fgraph_ops funcgraph_ops = {
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index ad739d76fc86..504de7a05498 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
 }
 
 static void irqsoff_graph_return(struct ftrace_graph_ret *trace,
-				 struct fgraph_ops *gops)
+				 struct fgraph_ops *gops,
+				 struct ftrace_regs *fregs)
 {
 	struct trace_array *tr = irqsoff_trace;
 	struct trace_array_cpu *data;
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
index 23360a2700de..9ffbd9326898 100644
--- a/kernel/trace/trace_sched_wakeup.c
+++ b/kernel/trace/trace_sched_wakeup.c
@@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
 }
 
 static void wakeup_graph_return(struct ftrace_graph_ret *trace,
-				struct fgraph_ops *gops)
+				struct fgraph_ops *gops,
+				struct ftrace_regs *fregs)
 {
 	struct trace_array *tr = wakeup_trace;
 	struct trace_array_cpu *data;
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
index 89067f02094a..1ebd0899238f 100644
--- a/kernel/trace/trace_selftest.c
+++ b/kernel/trace/trace_selftest.c
@@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace,
 }
 
 static __init void store_return(struct ftrace_graph_ret *trace,
-				struct fgraph_ops *gops)
+				struct fgraph_ops *gops,
+				struct ftrace_regs *fregs)
 {
 	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
 	const char *type = fixture->store_type_name;


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 06/19] fprobe: Use ftrace_regs in fprobe entry handler
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (4 preceding siblings ...)
  2024-09-12 15:09 ` [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc Masami Hiramatsu (Google)
@ 2024-09-12 15:09 ` Masami Hiramatsu (Google)
  2024-09-12 15:09 ` [PATCH v14 07/19] fprobe: Use ftrace_regs in fprobe exit handler Masami Hiramatsu (Google)
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:09 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

This allows fprobes to be available with CONFIG_DYNAMIC_FTRACE_WITH_ARGS
instead of CONFIG_DYNAMIC_FTRACE_WITH_REGS, then we can enable fprobe
on arm64.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Florent Revest <revest@chromium.org>
---
 Changes in v6:
  - Keep using SAVE_REGS flag to avoid breaking bpf kprobe-multi test.
---
 include/linux/fprobe.h          |    2 +-
 kernel/trace/Kconfig            |    3 ++-
 kernel/trace/bpf_trace.c        |   10 +++++++---
 kernel/trace/fprobe.c           |    3 ++-
 kernel/trace/trace_fprobe.c     |    6 +++++-
 lib/test_fprobe.c               |    4 ++--
 samples/fprobe/fprobe_example.c |    2 +-
 7 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h
index f39869588117..ca64ee5e45d2 100644
--- a/include/linux/fprobe.h
+++ b/include/linux/fprobe.h
@@ -10,7 +10,7 @@
 struct fprobe;
 
 typedef int (*fprobe_entry_cb)(struct fprobe *fp, unsigned long entry_ip,
-			       unsigned long ret_ip, struct pt_regs *regs,
+			       unsigned long ret_ip, struct ftrace_regs *regs,
 			       void *entry_data);
 
 typedef void (*fprobe_exit_cb)(struct fprobe *fp, unsigned long entry_ip,
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index ab277eff80dc..0c6a03554c13 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -287,7 +287,7 @@ config DYNAMIC_FTRACE_WITH_ARGS
 config FPROBE
 	bool "Kernel Function Probe (fprobe)"
 	depends on FUNCTION_TRACER
-	depends on DYNAMIC_FTRACE_WITH_REGS
+	depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS
 	depends on HAVE_RETHOOK
 	select RETHOOK
 	default n
@@ -672,6 +672,7 @@ config FPROBE_EVENTS
 	select TRACING
 	select PROBE_EVENTS
 	select DYNAMIC_EVENTS
+	depends on DYNAMIC_FTRACE_WITH_REGS
 	default y
 	help
 	  This allows user to add tracing events on the function entry and
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index cd098846e251..947808a002d0 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -2605,7 +2605,7 @@ struct bpf_session_run_ctx {
 	void *data;
 };
 
-#ifdef CONFIG_FPROBE
+#if defined(CONFIG_FPROBE) && defined(CONFIG_DYNAMIC_FTRACE_WITH_REGS)
 struct bpf_kprobe_multi_link {
 	struct bpf_link link;
 	struct fprobe fp;
@@ -2857,12 +2857,16 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,
 
 static int
 kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip,
-			  unsigned long ret_ip, struct pt_regs *regs,
+			  unsigned long ret_ip, struct ftrace_regs *fregs,
 			  void *data)
 {
+	struct pt_regs *regs = ftrace_get_regs(fregs);
 	struct bpf_kprobe_multi_link *link;
 	int err;
 
+	if (!regs)
+		return 0;
+
 	link = container_of(fp, struct bpf_kprobe_multi_link, fp);
 	err = kprobe_multi_link_prog_run(link, get_entry_ip(fentry_ip), regs, false, data);
 	return is_kprobe_session(link->link.prog) ? err : 0;
@@ -3137,7 +3141,7 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
 	kvfree(cookies);
 	return err;
 }
-#else /* !CONFIG_FPROBE */
+#else /* !CONFIG_FPROBE || !CONFIG_DYNAMIC_FTRACE_WITH_REGS */
 int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
 {
 	return -EOPNOTSUPP;
diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
index 9ff018245840..3d3789283873 100644
--- a/kernel/trace/fprobe.c
+++ b/kernel/trace/fprobe.c
@@ -46,7 +46,7 @@ static inline void __fprobe_handler(unsigned long ip, unsigned long parent_ip,
 	}
 
 	if (fp->entry_handler)
-		ret = fp->entry_handler(fp, ip, parent_ip, ftrace_get_regs(fregs), entry_data);
+		ret = fp->entry_handler(fp, ip, parent_ip, fregs, entry_data);
 
 	/* If entry_handler returns !0, nmissed is not counted. */
 	if (rh) {
@@ -182,6 +182,7 @@ static void fprobe_init(struct fprobe *fp)
 		fp->ops.func = fprobe_kprobe_handler;
 	else
 		fp->ops.func = fprobe_handler;
+
 	fp->ops.flags |= FTRACE_OPS_FL_SAVE_REGS;
 }
 
diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c
index 62e6a8f4aae9..b2c20d4fdfd7 100644
--- a/kernel/trace/trace_fprobe.c
+++ b/kernel/trace/trace_fprobe.c
@@ -338,12 +338,16 @@ NOKPROBE_SYMBOL(fexit_perf_func);
 #endif	/* CONFIG_PERF_EVENTS */
 
 static int fentry_dispatcher(struct fprobe *fp, unsigned long entry_ip,
-			     unsigned long ret_ip, struct pt_regs *regs,
+			     unsigned long ret_ip, struct ftrace_regs *fregs,
 			     void *entry_data)
 {
 	struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
+	struct pt_regs *regs = ftrace_get_regs(fregs);
 	int ret = 0;
 
+	if (!regs)
+		return 0;
+
 	if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
 		fentry_trace_func(tf, entry_ip, regs);
 #ifdef CONFIG_PERF_EVENTS
diff --git a/lib/test_fprobe.c b/lib/test_fprobe.c
index 24de0e5ff859..ff607babba18 100644
--- a/lib/test_fprobe.c
+++ b/lib/test_fprobe.c
@@ -40,7 +40,7 @@ static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32))
 
 static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,
 				    unsigned long ret_ip,
-				    struct pt_regs *regs, void *data)
+				    struct ftrace_regs *fregs, void *data)
 {
 	KUNIT_EXPECT_FALSE(current_test, preemptible());
 	/* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */
@@ -81,7 +81,7 @@ static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,
 
 static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,
 				      unsigned long ret_ip,
-				      struct pt_regs *regs, void *data)
+				      struct ftrace_regs *fregs, void *data)
 {
 	KUNIT_EXPECT_FALSE(current_test, preemptible());
 	return 0;
diff --git a/samples/fprobe/fprobe_example.c b/samples/fprobe/fprobe_example.c
index 0a50b05add96..c234afae52d6 100644
--- a/samples/fprobe/fprobe_example.c
+++ b/samples/fprobe/fprobe_example.c
@@ -50,7 +50,7 @@ static void show_backtrace(void)
 
 static int sample_entry_handler(struct fprobe *fp, unsigned long ip,
 				unsigned long ret_ip,
-				struct pt_regs *regs, void *data)
+				struct ftrace_regs *fregs, void *data)
 {
 	if (use_trace)
 		/*


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 07/19] fprobe: Use ftrace_regs in fprobe exit handler
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (5 preceding siblings ...)
  2024-09-12 15:09 ` [PATCH v14 06/19] fprobe: Use ftrace_regs in fprobe entry handler Masami Hiramatsu (Google)
@ 2024-09-12 15:09 ` Masami Hiramatsu (Google)
  2024-09-12 15:09 ` [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs Masami Hiramatsu (Google)
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:09 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Change the fprobe exit handler to use ftrace_regs structure instead of
pt_regs. This also introduce HAVE_PT_REGS_TO_FTRACE_REGS_CAST which means
the ftrace_regs's memory layout is equal to the pt_regs so that those are
able to cast. Fprobe introduces a new dependency with that.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
  Changes in v3:
   - Use ftrace_regs_get_return_value()
  Changes from previous series: NOTHING, just forward ported.
---
 arch/loongarch/Kconfig          |    1 +
 arch/s390/Kconfig               |    1 +
 arch/x86/Kconfig                |    1 +
 include/linux/fprobe.h          |    2 +-
 include/linux/ftrace.h          |    6 ++++++
 kernel/trace/Kconfig            |    8 ++++++++
 kernel/trace/bpf_trace.c        |    6 +++++-
 kernel/trace/fprobe.c           |    3 ++-
 kernel/trace/trace_fprobe.c     |    6 +++++-
 lib/test_fprobe.c               |    6 +++---
 samples/fprobe/fprobe_example.c |    2 +-
 11 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 974f08f65f63..73cb657496c8 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -122,6 +122,7 @@ config LOONGARCH
 	select HAVE_DMA_CONTIGUOUS
 	select HAVE_DYNAMIC_FTRACE
 	select HAVE_DYNAMIC_FTRACE_WITH_ARGS
+	select HAVE_PT_REGS_TO_FTRACE_REGS_CAST
 	select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
 	select HAVE_DYNAMIC_FTRACE_WITH_REGS
 	select HAVE_EBPF_JIT
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 12e942cfbcde..eebc299f424b 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -175,6 +175,7 @@ config S390
 	select HAVE_DMA_CONTIGUOUS
 	select HAVE_DYNAMIC_FTRACE
 	select HAVE_DYNAMIC_FTRACE_WITH_ARGS
+	select HAVE_PT_REGS_TO_FTRACE_REGS_CAST
 	select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
 	select HAVE_DYNAMIC_FTRACE_WITH_REGS
 	select HAVE_EBPF_JIT if HAVE_MARCH_Z196_FEATURES
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 047384e4d93a..59788d8b220e 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -218,6 +218,7 @@ config X86
 	select HAVE_DYNAMIC_FTRACE
 	select HAVE_DYNAMIC_FTRACE_WITH_REGS
 	select HAVE_DYNAMIC_FTRACE_WITH_ARGS	if X86_64
+	select HAVE_PT_REGS_TO_FTRACE_REGS_CAST	if X86_64
 	select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
 	select HAVE_SAMPLE_FTRACE_DIRECT	if X86_64
 	select HAVE_SAMPLE_FTRACE_DIRECT_MULTI	if X86_64
diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h
index ca64ee5e45d2..ef609bcca0f9 100644
--- a/include/linux/fprobe.h
+++ b/include/linux/fprobe.h
@@ -14,7 +14,7 @@ typedef int (*fprobe_entry_cb)(struct fprobe *fp, unsigned long entry_ip,
 			       void *entry_data);
 
 typedef void (*fprobe_exit_cb)(struct fprobe *fp, unsigned long entry_ip,
-			       unsigned long ret_ip, struct pt_regs *regs,
+			       unsigned long ret_ip, struct ftrace_regs *regs,
 			       void *entry_data);
 
 /**
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index e7c41d9988e1..a1b2ef492c7f 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -162,6 +162,12 @@ struct ftrace_regs {
 #define ftrace_regs_set_instruction_pointer(fregs, ip) do { } while (0)
 #endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
 
+#ifdef CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST
+
+static_assert(sizeof(struct pt_regs) == sizeof(struct ftrace_regs));
+
+#endif /* CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST */
+
 static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs)
 {
 	if (!fregs)
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 0c6a03554c13..2e2b39699542 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -57,6 +57,13 @@ config HAVE_DYNAMIC_FTRACE_WITH_ARGS
 	 This allows for use of ftrace_regs_get_argument() and
 	 ftrace_regs_get_stack_pointer().
 
+config HAVE_PT_REGS_TO_FTRACE_REGS_CAST
+	bool
+	help
+	 If this is set, the memory layout of the ftrace_regs data structure
+	 is the same as the pt_regs. So the pt_regs is possible to be casted
+	 to ftrace_regs.
+
 config HAVE_DYNAMIC_FTRACE_NO_PATCHABLE
 	bool
 	help
@@ -288,6 +295,7 @@ config FPROBE
 	bool "Kernel Function Probe (fprobe)"
 	depends on FUNCTION_TRACER
 	depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS
+	depends on HAVE_PT_REGS_TO_FTRACE_REGS_CAST || !HAVE_DYNAMIC_FTRACE_WITH_ARGS
 	depends on HAVE_RETHOOK
 	select RETHOOK
 	default n
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 947808a002d0..cdba9981b048 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -2874,10 +2874,14 @@ kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip,
 
 static void
 kprobe_multi_link_exit_handler(struct fprobe *fp, unsigned long fentry_ip,
-			       unsigned long ret_ip, struct pt_regs *regs,
+			       unsigned long ret_ip, struct ftrace_regs *fregs,
 			       void *data)
 {
 	struct bpf_kprobe_multi_link *link;
+	struct pt_regs *regs = ftrace_get_regs(fregs);
+
+	if (!regs)
+		return;
 
 	link = container_of(fp, struct bpf_kprobe_multi_link, fp);
 	kprobe_multi_link_prog_run(link, get_entry_ip(fentry_ip), regs, true, data);
diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
index 3d3789283873..90a3c8e2bbdf 100644
--- a/kernel/trace/fprobe.c
+++ b/kernel/trace/fprobe.c
@@ -124,6 +124,7 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data,
 {
 	struct fprobe *fp = (struct fprobe *)data;
 	struct fprobe_rethook_node *fpr;
+	struct ftrace_regs *fregs = (struct ftrace_regs *)regs;
 	int bit;
 
 	if (!fp || fprobe_disabled(fp))
@@ -141,7 +142,7 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data,
 		return;
 	}
 
-	fp->exit_handler(fp, fpr->entry_ip, ret_ip, regs,
+	fp->exit_handler(fp, fpr->entry_ip, ret_ip, fregs,
 			 fp->entry_data_size ? (void *)fpr->data : NULL);
 	ftrace_test_recursion_unlock(bit);
 }
diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c
index b2c20d4fdfd7..273cdf3cf70c 100644
--- a/kernel/trace/trace_fprobe.c
+++ b/kernel/trace/trace_fprobe.c
@@ -359,10 +359,14 @@ static int fentry_dispatcher(struct fprobe *fp, unsigned long entry_ip,
 NOKPROBE_SYMBOL(fentry_dispatcher);
 
 static void fexit_dispatcher(struct fprobe *fp, unsigned long entry_ip,
-			     unsigned long ret_ip, struct pt_regs *regs,
+			     unsigned long ret_ip, struct ftrace_regs *fregs,
 			     void *entry_data)
 {
 	struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
+	struct pt_regs *regs = ftrace_get_regs(fregs);
+
+	if (!regs)
+		return;
 
 	if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
 		fexit_trace_func(tf, entry_ip, ret_ip, regs, entry_data);
diff --git a/lib/test_fprobe.c b/lib/test_fprobe.c
index ff607babba18..271ce0caeec0 100644
--- a/lib/test_fprobe.c
+++ b/lib/test_fprobe.c
@@ -59,9 +59,9 @@ static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,
 
 static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,
 				    unsigned long ret_ip,
-				    struct pt_regs *regs, void *data)
+				    struct ftrace_regs *fregs, void *data)
 {
-	unsigned long ret = regs_return_value(regs);
+	unsigned long ret = ftrace_regs_get_return_value(fregs);
 
 	KUNIT_EXPECT_FALSE(current_test, preemptible());
 	if (ip != target_ip) {
@@ -89,7 +89,7 @@ static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,
 
 static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip,
 				      unsigned long ret_ip,
-				      struct pt_regs *regs, void *data)
+				      struct ftrace_regs *fregs, void *data)
 {
 	KUNIT_EXPECT_FALSE(current_test, preemptible());
 	KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip);
diff --git a/samples/fprobe/fprobe_example.c b/samples/fprobe/fprobe_example.c
index c234afae52d6..bfe98ce826f3 100644
--- a/samples/fprobe/fprobe_example.c
+++ b/samples/fprobe/fprobe_example.c
@@ -67,7 +67,7 @@ static int sample_entry_handler(struct fprobe *fp, unsigned long ip,
 }
 
 static void sample_exit_handler(struct fprobe *fp, unsigned long ip,
-				unsigned long ret_ip, struct pt_regs *regs,
+				unsigned long ret_ip, struct ftrace_regs *regs,
 				void *data)
 {
 	unsigned long rip = ret_ip;


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (6 preceding siblings ...)
  2024-09-12 15:09 ` [PATCH v14 07/19] fprobe: Use ftrace_regs in fprobe exit handler Masami Hiramatsu (Google)
@ 2024-09-12 15:09 ` Masami Hiramatsu (Google)
  2024-09-15  9:22   ` Steven Rostedt
  2024-09-12 15:09 ` [PATCH v14 09/19] tracing: Add ftrace_fill_perf_regs() for perf event Masami Hiramatsu (Google)
                   ` (10 subsequent siblings)
  18 siblings, 1 reply; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:09 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Add ftrace_partial_regs() which converts the ftrace_regs to pt_regs.
This is for the eBPF which needs this to keep the same pt_regs interface
to access registers.
Thus when replacing the pt_regs with ftrace_regs in fprobes (which is
used by kprobe_multi eBPF event), this will be used.

If the architecture defines its own ftrace_regs, this copies partial
registers to pt_regs and returns it. If not, ftrace_regs is the same as
pt_regs and ftrace_partial_regs() will return ftrace_regs::regs.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Florent Revest <revest@chromium.org>
---
 Changes in v14:
  - Add riscv change.
 Changes in v8:
  - Add the reason why this required in changelog.
 Changes from previous series: NOTHING, just forward ported.
---
 arch/arm64/include/asm/ftrace.h |   11 +++++++++++
 arch/riscv/include/asm/ftrace.h |   12 ++++++++++++
 include/linux/ftrace.h          |   17 +++++++++++++++++
 3 files changed, 40 insertions(+)

diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
index dffaab3dd1f1..5cd587afab6d 100644
--- a/arch/arm64/include/asm/ftrace.h
+++ b/arch/arm64/include/asm/ftrace.h
@@ -132,6 +132,17 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
 	return fregs->fp;
 }
 
+static __always_inline struct pt_regs *
+ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
+{
+	memcpy(regs->regs, fregs->regs, sizeof(u64) * 9);
+	regs->sp = fregs->sp;
+	regs->pc = fregs->pc;
+	regs->regs[29] = fregs->fp;
+	regs->regs[30] = fregs->lr;
+	return regs;
+}
+
 int ftrace_regs_query_register_offset(const char *name);
 
 int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
index e9f364ce9fe8..897779dec402 100644
--- a/arch/riscv/include/asm/ftrace.h
+++ b/arch/riscv/include/asm/ftrace.h
@@ -193,6 +193,18 @@ static __always_inline void ftrace_override_function_with_return(struct ftrace_r
 	fregs->epc = fregs->ra;
 }
 
+static __always_inline struct pt_regs *
+ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
+{
+	memcpy(&regs->a0, fregs->args, sizeof(u64) * 8);
+	regs->epc = fregs->epc;
+	regs->ra = fregs->ra;
+	regs->sp = fregs->sp;
+	regs->s0 = fregs->s0;
+	regs->t1 = fregs->t1;
+	return regs;
+}
+
 int ftrace_regs_query_register_offset(const char *name);
 
 void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index a1b2ef492c7f..bd9a26bdf660 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -176,6 +176,23 @@ static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs
 	return arch_ftrace_get_regs(fregs);
 }
 
+#if !defined(CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS) || \
+	defined(CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST)
+
+static __always_inline struct pt_regs *
+ftrace_partial_regs(struct ftrace_regs *fregs, struct pt_regs *regs)
+{
+	/*
+	 * If CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST=y, ftrace_regs memory
+	 * layout is the same as pt_regs. So always returns that address.
+	 * Since arch_ftrace_get_regs() will check some members and may return
+	 * NULL, we can not use it.
+	 */
+	return &fregs->regs;
+}
+
+#endif /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS || CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST */
+
 /*
  * When true, the ftrace_regs_{get,set}_*() functions may be used on fregs.
  * Note: this can be true even when ftrace_get_regs() cannot provide a pt_regs.


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 09/19] tracing: Add ftrace_fill_perf_regs() for perf event
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (7 preceding siblings ...)
  2024-09-12 15:09 ` [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs Masami Hiramatsu (Google)
@ 2024-09-12 15:09 ` Masami Hiramatsu (Google)
  2024-09-12 15:09 ` [PATCH v14 10/19] tracing/fprobe: Enable fprobe events with CONFIG_DYNAMIC_FTRACE_WITH_ARGS Masami Hiramatsu (Google)
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:09 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Add ftrace_fill_perf_regs() which should be compatible with the
perf_fetch_caller_regs(). In other words, the pt_regs returned from the
ftrace_fill_perf_regs() must satisfy 'user_mode(regs) == false' and can be
used for stack tracing.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
  Changes from previous series: NOTHING, just forward ported.
---
 arch/arm64/include/asm/ftrace.h   |    7 +++++++
 arch/powerpc/include/asm/ftrace.h |    7 +++++++
 arch/s390/include/asm/ftrace.h    |    5 +++++
 arch/x86/include/asm/ftrace.h     |    7 +++++++
 include/linux/ftrace.h            |   31 +++++++++++++++++++++++++++++++
 5 files changed, 57 insertions(+)

diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
index 5cd587afab6d..14ecb9a418d9 100644
--- a/arch/arm64/include/asm/ftrace.h
+++ b/arch/arm64/include/asm/ftrace.h
@@ -143,6 +143,13 @@ ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
 	return regs;
 }
 
+#define arch_ftrace_fill_perf_regs(fregs, _regs) do {		\
+		(_regs)->pc = (fregs)->pc;			\
+		(_regs)->regs[29] = (fregs)->fp;		\
+		(_regs)->sp = (fregs)->sp;			\
+		(_regs)->pstate = PSR_MODE_EL1h;		\
+	} while (0)
+
 int ftrace_regs_query_register_offset(const char *name);
 
 int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
diff --git a/arch/powerpc/include/asm/ftrace.h b/arch/powerpc/include/asm/ftrace.h
index 23d26f3afae4..e6ff6834bf7e 100644
--- a/arch/powerpc/include/asm/ftrace.h
+++ b/arch/powerpc/include/asm/ftrace.h
@@ -42,6 +42,13 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *
 	return fregs->regs.msr ? &fregs->regs : NULL;
 }
 
+#define arch_ftrace_fill_perf_regs(fregs, _regs) do {		\
+		(_regs)->result = 0;				\
+		(_regs)->nip = (fregs)->regs.nip;		\
+		(_regs)->gpr[1] = (fregs)->regs.gpr[1];		\
+		asm volatile("mfmsr %0" : "=r" ((_regs)->msr));	\
+	} while (0)
+
 static __always_inline void
 ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
 				    unsigned long ip)
diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
index 9cdd48a46bf7..0d9f6df21f81 100644
--- a/arch/s390/include/asm/ftrace.h
+++ b/arch/s390/include/asm/ftrace.h
@@ -84,6 +84,11 @@ ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)
 	return sp[0];	/* return backchain */
 }
 
+#define arch_ftrace_fill_perf_regs(fregs, _regs)	 do {		\
+		(_regs)->psw.addr = (fregs)->regs.psw.addr;		\
+		(_regs)->gprs[15] = (fregs)->regs.gprs[15];		\
+	} while (0)
+
 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
 /*
  * When an ftrace registered caller is tracing a function that is
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 669771ef3b5b..1f4d1f7b19ed 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -46,6 +46,13 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
 	return &fregs->regs;
 }
 
+#define arch_ftrace_fill_perf_regs(fregs, _regs) do {	\
+		(_regs)->ip = (fregs)->regs.ip;		\
+		(_regs)->sp = (fregs)->regs.sp;		\
+		(_regs)->cs = __KERNEL_CS;		\
+		(_regs)->flags = 0;			\
+	} while (0)
+
 #define ftrace_regs_set_instruction_pointer(fregs, _ip)	\
 	do { (fregs)->regs.ip = (_ip); } while (0)
 
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index bd9a26bdf660..3edf2427ae73 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -193,6 +193,37 @@ ftrace_partial_regs(struct ftrace_regs *fregs, struct pt_regs *regs)
 
 #endif /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS || CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST */
 
+#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
+
+/*
+ * Please define arch dependent pt_regs which compatible to the
+ * perf_arch_fetch_caller_regs() but based on ftrace_regs.
+ * This requires
+ *   - user_mode(_regs) returns false (always kernel mode).
+ *   - able to use the _regs for stack trace.
+ */
+#ifndef arch_ftrace_fill_perf_regs
+/* As same as perf_arch_fetch_caller_regs(), do nothing by default */
+#define arch_ftrace_fill_perf_regs(fregs, _regs) do {} while (0)
+#endif
+
+static __always_inline struct pt_regs *
+ftrace_fill_perf_regs(struct ftrace_regs *fregs, struct pt_regs *regs)
+{
+	arch_ftrace_fill_perf_regs(fregs, regs);
+	return regs;
+}
+
+#else /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
+
+static __always_inline struct pt_regs *
+ftrace_fill_perf_regs(struct ftrace_regs *fregs, struct pt_regs *regs)
+{
+	return &fregs->regs;
+}
+
+#endif
+
 /*
  * When true, the ftrace_regs_{get,set}_*() functions may be used on fregs.
  * Note: this can be true even when ftrace_get_regs() cannot provide a pt_regs.


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 10/19] tracing/fprobe: Enable fprobe events with CONFIG_DYNAMIC_FTRACE_WITH_ARGS
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (8 preceding siblings ...)
  2024-09-12 15:09 ` [PATCH v14 09/19] tracing: Add ftrace_fill_perf_regs() for perf event Masami Hiramatsu (Google)
@ 2024-09-12 15:09 ` Masami Hiramatsu (Google)
  2024-09-12 15:10 ` [PATCH v14 11/19] bpf: Enable kprobe_multi feature if CONFIG_FPROBE is enabled Masami Hiramatsu (Google)
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:09 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Allow fprobe events to be enabled with CONFIG_DYNAMIC_FTRACE_WITH_ARGS.
With this change, fprobe events mostly use ftrace_regs instead of pt_regs.
Note that if the arch doesn't enable HAVE_PT_REGS_COMPAT_FTRACE_REGS,
fprobe events will not be able to be used from perf.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 Changes in v9:
  - Copy store_trace_entry_data() as store_fprobe_entry_data() for
    fprobe.
 Chagnes in v3:
  - Use ftrace_regs_get_return_value().
 Changes in v2:
  - Define ftrace_regs_get_kernel_stack_nth() for
    !CONFIG_HAVE_REGS_AND_STACK_ACCESS_API.
 Changes from previous series: Update against the new series.
---
 include/linux/ftrace.h          |   17 ++++++
 kernel/trace/Kconfig            |    1 
 kernel/trace/trace_fprobe.c     |  107 +++++++++++++++++++++++++--------------
 kernel/trace/trace_probe_tmpl.h |    2 -
 4 files changed, 86 insertions(+), 41 deletions(-)

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 3edf2427ae73..63fb91088a23 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -255,6 +255,23 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs)
 	frame_pointer(&(fregs)->regs)
 #endif
 
+#ifdef CONFIG_HAVE_REGS_AND_STACK_ACCESS_API
+static __always_inline unsigned long
+ftrace_regs_get_kernel_stack_nth(struct ftrace_regs *fregs, unsigned int nth)
+{
+	unsigned long *stackp;
+
+	stackp = (unsigned long *)ftrace_regs_get_stack_pointer(fregs);
+	if (((unsigned long)(stackp + nth) & ~(THREAD_SIZE - 1)) ==
+	    ((unsigned long)stackp & ~(THREAD_SIZE - 1)))
+		return *(stackp + nth);
+
+	return 0;
+}
+#else /* !CONFIG_HAVE_REGS_AND_STACK_ACCESS_API */
+#define ftrace_regs_get_kernel_stack_nth(fregs, nth)	(0L)
+#endif /* CONFIG_HAVE_REGS_AND_STACK_ACCESS_API */
+
 typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
 			      struct ftrace_ops *op, struct ftrace_regs *fregs);
 
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 2e2b39699542..0fc4c3129c19 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -680,7 +680,6 @@ config FPROBE_EVENTS
 	select TRACING
 	select PROBE_EVENTS
 	select DYNAMIC_EVENTS
-	depends on DYNAMIC_FTRACE_WITH_REGS
 	default y
 	help
 	  This allows user to add tracing events on the function entry and
diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c
index 273cdf3cf70c..86cd6a8c806a 100644
--- a/kernel/trace/trace_fprobe.c
+++ b/kernel/trace/trace_fprobe.c
@@ -133,7 +133,7 @@ static int
 process_fetch_insn(struct fetch_insn *code, void *rec, void *edata,
 		   void *dest, void *base)
 {
-	struct pt_regs *regs = rec;
+	struct ftrace_regs *fregs = rec;
 	unsigned long val;
 	int ret;
 
@@ -141,17 +141,17 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *edata,
 	/* 1st stage: get value from context */
 	switch (code->op) {
 	case FETCH_OP_STACK:
-		val = regs_get_kernel_stack_nth(regs, code->param);
+		val = ftrace_regs_get_kernel_stack_nth(fregs, code->param);
 		break;
 	case FETCH_OP_STACKP:
-		val = kernel_stack_pointer(regs);
+		val = ftrace_regs_get_stack_pointer(fregs);
 		break;
 	case FETCH_OP_RETVAL:
-		val = regs_return_value(regs);
+		val = ftrace_regs_get_return_value(fregs);
 		break;
 #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
 	case FETCH_OP_ARG:
-		val = regs_get_kernel_argument(regs, code->param);
+		val = ftrace_regs_get_argument(fregs, code->param);
 		break;
 	case FETCH_OP_EDATA:
 		val = *(unsigned long *)((unsigned long)edata + code->offset);
@@ -174,7 +174,7 @@ NOKPROBE_SYMBOL(process_fetch_insn)
 /* function entry handler */
 static nokprobe_inline void
 __fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
-		    struct pt_regs *regs,
+		    struct ftrace_regs *fregs,
 		    struct trace_event_file *trace_file)
 {
 	struct fentry_trace_entry_head *entry;
@@ -188,41 +188,71 @@ __fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
 	if (trace_trigger_soft_disabled(trace_file))
 		return;
 
-	dsize = __get_data_size(&tf->tp, regs, NULL);
+	dsize = __get_data_size(&tf->tp, fregs, NULL);
 
 	entry = trace_event_buffer_reserve(&fbuffer, trace_file,
 					   sizeof(*entry) + tf->tp.size + dsize);
 	if (!entry)
 		return;
 
-	fbuffer.regs = regs;
+	fbuffer.regs = ftrace_get_regs(fregs);
 	entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
 	entry->ip = entry_ip;
-	store_trace_args(&entry[1], &tf->tp, regs, NULL, sizeof(*entry), dsize);
+	store_trace_args(&entry[1], &tf->tp, fregs, NULL, sizeof(*entry), dsize);
 
 	trace_event_buffer_commit(&fbuffer);
 }
 
 static void
 fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
-		  struct pt_regs *regs)
+		  struct ftrace_regs *fregs)
 {
 	struct event_file_link *link;
 
 	trace_probe_for_each_link_rcu(link, &tf->tp)
-		__fentry_trace_func(tf, entry_ip, regs, link->file);
+		__fentry_trace_func(tf, entry_ip, fregs, link->file);
 }
 NOKPROBE_SYMBOL(fentry_trace_func);
 
+static nokprobe_inline
+void store_fprobe_entry_data(void *edata, struct trace_probe *tp, struct ftrace_regs *fregs)
+{
+	struct probe_entry_arg *earg = tp->entry_arg;
+	unsigned long val = 0;
+	int i;
+
+	if (!earg)
+		return;
+
+	for (i = 0; i < earg->size; i++) {
+		struct fetch_insn *code = &earg->code[i];
+
+		switch (code->op) {
+		case FETCH_OP_ARG:
+			val = ftrace_regs_get_argument(fregs, code->param);
+			break;
+		case FETCH_OP_ST_EDATA:
+			*(unsigned long *)((unsigned long)edata + code->offset) = val;
+			break;
+		case FETCH_OP_END:
+			goto end;
+		default:
+			break;
+		}
+	}
+end:
+	return;
+}
+
 /* function exit handler */
 static int trace_fprobe_entry_handler(struct fprobe *fp, unsigned long entry_ip,
-				unsigned long ret_ip, struct pt_regs *regs,
+				unsigned long ret_ip, struct ftrace_regs *fregs,
 				void *entry_data)
 {
 	struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
 
 	if (tf->tp.entry_arg)
-		store_trace_entry_data(entry_data, &tf->tp, regs);
+		store_fprobe_entry_data(entry_data, &tf->tp, fregs);
 
 	return 0;
 }
@@ -230,7 +260,7 @@ NOKPROBE_SYMBOL(trace_fprobe_entry_handler)
 
 static nokprobe_inline void
 __fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
-		   unsigned long ret_ip, struct pt_regs *regs,
+		   unsigned long ret_ip, struct ftrace_regs *fregs,
 		   void *entry_data, struct trace_event_file *trace_file)
 {
 	struct fexit_trace_entry_head *entry;
@@ -244,60 +274,63 @@ __fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
 	if (trace_trigger_soft_disabled(trace_file))
 		return;
 
-	dsize = __get_data_size(&tf->tp, regs, entry_data);
+	dsize = __get_data_size(&tf->tp, fregs, entry_data);
 
 	entry = trace_event_buffer_reserve(&fbuffer, trace_file,
 					   sizeof(*entry) + tf->tp.size + dsize);
 	if (!entry)
 		return;
 
-	fbuffer.regs = regs;
+	fbuffer.regs = ftrace_get_regs(fregs);
 	entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
 	entry->func = entry_ip;
 	entry->ret_ip = ret_ip;
-	store_trace_args(&entry[1], &tf->tp, regs, entry_data, sizeof(*entry), dsize);
+	store_trace_args(&entry[1], &tf->tp, fregs, entry_data, sizeof(*entry), dsize);
 
 	trace_event_buffer_commit(&fbuffer);
 }
 
 static void
 fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
-		 unsigned long ret_ip, struct pt_regs *regs, void *entry_data)
+		 unsigned long ret_ip, struct ftrace_regs *fregs, void *entry_data)
 {
 	struct event_file_link *link;
 
 	trace_probe_for_each_link_rcu(link, &tf->tp)
-		__fexit_trace_func(tf, entry_ip, ret_ip, regs, entry_data, link->file);
+		__fexit_trace_func(tf, entry_ip, ret_ip, fregs, entry_data, link->file);
 }
 NOKPROBE_SYMBOL(fexit_trace_func);
 
 #ifdef CONFIG_PERF_EVENTS
 
 static int fentry_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
-			    struct pt_regs *regs)
+			    struct ftrace_regs *fregs)
 {
 	struct trace_event_call *call = trace_probe_event_call(&tf->tp);
 	struct fentry_trace_entry_head *entry;
 	struct hlist_head *head;
 	int size, __size, dsize;
+	struct pt_regs *regs;
 	int rctx;
 
 	head = this_cpu_ptr(call->perf_events);
 	if (hlist_empty(head))
 		return 0;
 
-	dsize = __get_data_size(&tf->tp, regs, NULL);
+	dsize = __get_data_size(&tf->tp, fregs, NULL);
 	__size = sizeof(*entry) + tf->tp.size + dsize;
 	size = ALIGN(__size + sizeof(u32), sizeof(u64));
 	size -= sizeof(u32);
 
-	entry = perf_trace_buf_alloc(size, NULL, &rctx);
+	entry = perf_trace_buf_alloc(size, &regs, &rctx);
 	if (!entry)
 		return 0;
 
+	regs = ftrace_fill_perf_regs(fregs, regs);
+
 	entry->ip = entry_ip;
 	memset(&entry[1], 0, dsize);
-	store_trace_args(&entry[1], &tf->tp, regs, NULL, sizeof(*entry), dsize);
+	store_trace_args(&entry[1], &tf->tp, fregs, NULL, sizeof(*entry), dsize);
 	perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
 			      head, NULL);
 	return 0;
@@ -306,31 +339,34 @@ NOKPROBE_SYMBOL(fentry_perf_func);
 
 static void
 fexit_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
-		unsigned long ret_ip, struct pt_regs *regs,
+		unsigned long ret_ip, struct ftrace_regs *fregs,
 		void *entry_data)
 {
 	struct trace_event_call *call = trace_probe_event_call(&tf->tp);
 	struct fexit_trace_entry_head *entry;
 	struct hlist_head *head;
 	int size, __size, dsize;
+	struct pt_regs *regs;
 	int rctx;
 
 	head = this_cpu_ptr(call->perf_events);
 	if (hlist_empty(head))
 		return;
 
-	dsize = __get_data_size(&tf->tp, regs, entry_data);
+	dsize = __get_data_size(&tf->tp, fregs, entry_data);
 	__size = sizeof(*entry) + tf->tp.size + dsize;
 	size = ALIGN(__size + sizeof(u32), sizeof(u64));
 	size -= sizeof(u32);
 
-	entry = perf_trace_buf_alloc(size, NULL, &rctx);
+	entry = perf_trace_buf_alloc(size, &regs, &rctx);
 	if (!entry)
 		return;
 
+	regs = ftrace_fill_perf_regs(fregs, regs);
+
 	entry->func = entry_ip;
 	entry->ret_ip = ret_ip;
-	store_trace_args(&entry[1], &tf->tp, regs, entry_data, sizeof(*entry), dsize);
+	store_trace_args(&entry[1], &tf->tp, fregs, entry_data, sizeof(*entry), dsize);
 	perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
 			      head, NULL);
 }
@@ -342,17 +378,14 @@ static int fentry_dispatcher(struct fprobe *fp, unsigned long entry_ip,
 			     void *entry_data)
 {
 	struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
-	struct pt_regs *regs = ftrace_get_regs(fregs);
 	int ret = 0;
 
-	if (!regs)
-		return 0;
-
 	if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
-		fentry_trace_func(tf, entry_ip, regs);
+		fentry_trace_func(tf, entry_ip, fregs);
+
 #ifdef CONFIG_PERF_EVENTS
 	if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE))
-		ret = fentry_perf_func(tf, entry_ip, regs);
+		ret = fentry_perf_func(tf, entry_ip, fregs);
 #endif
 	return ret;
 }
@@ -363,16 +396,12 @@ static void fexit_dispatcher(struct fprobe *fp, unsigned long entry_ip,
 			     void *entry_data)
 {
 	struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
-	struct pt_regs *regs = ftrace_get_regs(fregs);
-
-	if (!regs)
-		return;
 
 	if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
-		fexit_trace_func(tf, entry_ip, ret_ip, regs, entry_data);
+		fexit_trace_func(tf, entry_ip, ret_ip, fregs, entry_data);
 #ifdef CONFIG_PERF_EVENTS
 	if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE))
-		fexit_perf_func(tf, entry_ip, ret_ip, regs, entry_data);
+		fexit_perf_func(tf, entry_ip, ret_ip, fregs, entry_data);
 #endif
 }
 NOKPROBE_SYMBOL(fexit_dispatcher);
diff --git a/kernel/trace/trace_probe_tmpl.h b/kernel/trace/trace_probe_tmpl.h
index 2caf0d2afb32..f39b37fcdb3b 100644
--- a/kernel/trace/trace_probe_tmpl.h
+++ b/kernel/trace/trace_probe_tmpl.h
@@ -232,7 +232,7 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
 
 /* Sum up total data length for dynamic arrays (strings) */
 static nokprobe_inline int
-__get_data_size(struct trace_probe *tp, struct pt_regs *regs, void *edata)
+__get_data_size(struct trace_probe *tp, void *regs, void *edata)
 {
 	struct probe_arg *arg;
 	int i, len, ret = 0;


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 11/19] bpf: Enable kprobe_multi feature if CONFIG_FPROBE is enabled
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (9 preceding siblings ...)
  2024-09-12 15:09 ` [PATCH v14 10/19] tracing/fprobe: Enable fprobe events with CONFIG_DYNAMIC_FTRACE_WITH_ARGS Masami Hiramatsu (Google)
@ 2024-09-12 15:10 ` Masami Hiramatsu (Google)
  2024-09-12 15:10 ` [PATCH v14 12/19] ftrace: Add CONFIG_HAVE_FTRACE_GRAPH_FUNC Masami Hiramatsu (Google)
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:10 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Enable kprobe_multi feature if CONFIG_FPROBE is enabled. The pt_regs is
converted from ftrace_regs by ftrace_partial_regs(), thus some registers
may always returns 0. But it should be enough for function entry (access
arguments) and exit (access return value).

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Florent Revest <revest@chromium.org>
---
 Changes in v9:
  - Avoid wasting memory for bpf_kprobe_multi_pt_regs when
    CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST=y
---
 kernel/trace/bpf_trace.c |   27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index cdba9981b048..deb629f4a510 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -2605,7 +2605,7 @@ struct bpf_session_run_ctx {
 	void *data;
 };
 
-#if defined(CONFIG_FPROBE) && defined(CONFIG_DYNAMIC_FTRACE_WITH_REGS)
+#ifdef CONFIG_FPROBE
 struct bpf_kprobe_multi_link {
 	struct bpf_link link;
 	struct fprobe fp;
@@ -2628,6 +2628,13 @@ struct user_syms {
 	char *buf;
 };
 
+#ifndef CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST
+static DEFINE_PER_CPU(struct pt_regs, bpf_kprobe_multi_pt_regs);
+#define bpf_kprobe_multi_pt_regs_ptr()	this_cpu_ptr(&bpf_kprobe_multi_pt_regs)
+#else
+#define bpf_kprobe_multi_pt_regs_ptr()	(NULL)
+#endif
+
 static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 cnt)
 {
 	unsigned long __user usymbol;
@@ -2822,7 +2829,7 @@ static u64 bpf_kprobe_multi_entry_ip(struct bpf_run_ctx *ctx)
 
 static int
 kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,
-			   unsigned long entry_ip, struct pt_regs *regs,
+			   unsigned long entry_ip, struct ftrace_regs *fregs,
 			   bool is_return, void *data)
 {
 	struct bpf_kprobe_multi_run_ctx run_ctx = {
@@ -2834,6 +2841,7 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,
 		.entry_ip = entry_ip,
 	};
 	struct bpf_run_ctx *old_run_ctx;
+	struct pt_regs *regs;
 	int err;
 
 	if (unlikely(__this_cpu_inc_return(bpf_prog_active) != 1)) {
@@ -2844,6 +2852,7 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,
 
 	migrate_disable();
 	rcu_read_lock();
+	regs = ftrace_partial_regs(fregs, bpf_kprobe_multi_pt_regs_ptr());
 	old_run_ctx = bpf_set_run_ctx(&run_ctx.session_ctx.run_ctx);
 	err = bpf_prog_run(link->link.prog, regs);
 	bpf_reset_run_ctx(old_run_ctx);
@@ -2860,15 +2869,11 @@ kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip,
 			  unsigned long ret_ip, struct ftrace_regs *fregs,
 			  void *data)
 {
-	struct pt_regs *regs = ftrace_get_regs(fregs);
 	struct bpf_kprobe_multi_link *link;
 	int err;
 
-	if (!regs)
-		return 0;
-
 	link = container_of(fp, struct bpf_kprobe_multi_link, fp);
-	err = kprobe_multi_link_prog_run(link, get_entry_ip(fentry_ip), regs, false, data);
+	err = kprobe_multi_link_prog_run(link, get_entry_ip(fentry_ip), fregs, false, data);
 	return is_kprobe_session(link->link.prog) ? err : 0;
 }
 
@@ -2878,13 +2883,9 @@ kprobe_multi_link_exit_handler(struct fprobe *fp, unsigned long fentry_ip,
 			       void *data)
 {
 	struct bpf_kprobe_multi_link *link;
-	struct pt_regs *regs = ftrace_get_regs(fregs);
-
-	if (!regs)
-		return;
 
 	link = container_of(fp, struct bpf_kprobe_multi_link, fp);
-	kprobe_multi_link_prog_run(link, get_entry_ip(fentry_ip), regs, true, data);
+	kprobe_multi_link_prog_run(link, get_entry_ip(fentry_ip), fregs, true, data);
 }
 
 static int symbols_cmp_r(const void *a, const void *b, const void *priv)
@@ -3145,7 +3146,7 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
 	kvfree(cookies);
 	return err;
 }
-#else /* !CONFIG_FPROBE || !CONFIG_DYNAMIC_FTRACE_WITH_REGS */
+#else /* !CONFIG_FPROBE */
 int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
 {
 	return -EOPNOTSUPP;


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 12/19] ftrace: Add CONFIG_HAVE_FTRACE_GRAPH_FUNC
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (10 preceding siblings ...)
  2024-09-12 15:10 ` [PATCH v14 11/19] bpf: Enable kprobe_multi feature if CONFIG_FPROBE is enabled Masami Hiramatsu (Google)
@ 2024-09-12 15:10 ` Masami Hiramatsu (Google)
  2024-09-12 15:10 ` [PATCH v14 13/19] fprobe: Rewrite fprobe on function-graph tracer Masami Hiramatsu (Google)
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:10 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Add CONFIG_HAVE_FTRACE_GRAPH_FUNC kconfig in addition to ftrace_graph_func
macro check. This is for the other feature (e.g. FPROBE) which requires to
access ftrace_regs from fgraph_ops::entryfunc() can avoid compiling if
the fgraph can not pass the valid ftrace_regs.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 Changes in v8:
  - Newly added.
---
 arch/arm64/Kconfig     |    1 +
 arch/loongarch/Kconfig |    1 +
 arch/powerpc/Kconfig   |    1 +
 arch/riscv/Kconfig     |    1 +
 arch/x86/Kconfig       |    1 +
 kernel/trace/Kconfig   |    5 +++++
 6 files changed, 10 insertions(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 17947f625b06..53eb9f36842d 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -208,6 +208,7 @@ config ARM64
 	select HAVE_SAMPLE_FTRACE_DIRECT_MULTI
 	select HAVE_EFFICIENT_UNALIGNED_ACCESS
 	select HAVE_GUP_FAST
+	select HAVE_FTRACE_GRAPH_FUNC
 	select HAVE_FTRACE_MCOUNT_RECORD
 	select HAVE_FUNCTION_TRACER
 	select HAVE_FUNCTION_ERROR_INJECTION
diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 73cb657496c8..9f7adca388ec 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -129,6 +129,7 @@ config LOONGARCH
 	select HAVE_EFFICIENT_UNALIGNED_ACCESS if !ARCH_STRICT_ALIGN
 	select HAVE_EXIT_THREAD
 	select HAVE_GUP_FAST
+	select HAVE_FTRACE_GRAPH_FUNC
 	select HAVE_FTRACE_MCOUNT_RECORD
 	select HAVE_FUNCTION_ARG_ACCESS_API
 	select HAVE_FUNCTION_ERROR_INJECTION
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index d7b09b064a8a..aa2669f5b314 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -238,6 +238,7 @@ config PPC
 	select HAVE_EBPF_JIT
 	select HAVE_EFFICIENT_UNALIGNED_ACCESS
 	select HAVE_GUP_FAST
+	select HAVE_FTRACE_GRAPH_FUNC
 	select HAVE_FTRACE_MCOUNT_RECORD
 	select HAVE_FUNCTION_ARG_ACCESS_API
 	select HAVE_FUNCTION_DESCRIPTORS	if PPC64_ELF_ABI_V1
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 6e8422269ba4..8f05e9fb7803 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -138,6 +138,7 @@ config RISCV
 	select HAVE_DYNAMIC_FTRACE if !XIP_KERNEL && MMU && (CLANG_SUPPORTS_DYNAMIC_FTRACE || GCC_SUPPORTS_DYNAMIC_FTRACE)
 	select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
 	select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE
+	select HAVE_FTRACE_GRAPH_FUNC
 	select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL
 	select HAVE_FUNCTION_GRAPH_TRACER
 	select HAVE_FUNCTION_GRAPH_FREGS
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 59788d8b220e..02863509ebd1 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -228,6 +228,7 @@ config X86
 	select HAVE_EXIT_THREAD
 	select HAVE_GUP_FAST
 	select HAVE_FENTRY			if X86_64 || DYNAMIC_FTRACE
+	select HAVE_FTRACE_GRAPH_FUNC		if HAVE_FUNCTION_GRAPH_TRACER
 	select HAVE_FTRACE_MCOUNT_RECORD
 	select HAVE_FUNCTION_GRAPH_FREGS	if HAVE_FUNCTION_GRAPH_TRACER
 	select HAVE_FUNCTION_GRAPH_TRACER	if X86_32 || (X86_64 && DYNAMIC_FTRACE)
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 0fc4c3129c19..c8dfd3a233c6 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -34,6 +34,11 @@ config HAVE_FUNCTION_GRAPH_TRACER
 config HAVE_FUNCTION_GRAPH_FREGS
 	bool
 
+config HAVE_FTRACE_GRAPH_FUNC
+	bool
+	help
+	  True if ftrace_graph_func() is defined.
+
 config HAVE_DYNAMIC_FTRACE
 	bool
 	help


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 13/19] fprobe: Rewrite fprobe on function-graph tracer
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (11 preceding siblings ...)
  2024-09-12 15:10 ` [PATCH v14 12/19] ftrace: Add CONFIG_HAVE_FTRACE_GRAPH_FUNC Masami Hiramatsu (Google)
@ 2024-09-12 15:10 ` Masami Hiramatsu (Google)
  2024-09-12 15:10 ` [PATCH v14 14/19] tracing: Fix function timing profiler to initialize hashtable Masami Hiramatsu (Google)
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:10 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Rewrite fprobe implementation on function-graph tracer.
Major API changes are:
 -  'nr_maxactive' field is deprecated.
 -  This depends on CONFIG_DYNAMIC_FTRACE_WITH_ARGS or
    !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS, and
    CONFIG_HAVE_FUNCTION_GRAPH_FREGS. So currently works only
    on x86_64.
 -  Currently the entry size is limited in 15 * sizeof(long).
 -  If there is too many fprobe exit handler set on the same
    function, it will fail to probe.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 Changes in v14:
  - Add ftrace_regs_get_return_addresss() for riscv.
 Changes in v12:
  - Skip updating ftrace hash if not required.
 Changes in v9:
  - Remove unneeded prototype of ftrace_regs_get_return_address().
  - Fix entry data address calculation.
  - Remove DIV_ROUND_UP() from hotpath.
 Changes in v8:
  - Use trace_func_graph_ret/ent_t for fgraph_ops.
  - Update CONFIG_FPROBE dependencies.
  - Add ftrace_regs_get_return_address() for each arch.
 Changes in v3:
  - Update for new reserve_data/retrieve_data API.
  - Fix internal push/pop on fgraph data logic so that it can
    correctly save/restore the returning fprobes.
 Changes in v2:
  - Add more lockdep_assert_held(fprobe_mutex)
  - Use READ_ONCE() and WRITE_ONCE() for fprobe_hlist_node::fp.
  - Add NOKPROBE_SYMBOL() for the functions which is called from
    entry/exit callback.
---
 arch/arm64/include/asm/ftrace.h     |    6 
 arch/loongarch/include/asm/ftrace.h |    6 
 arch/powerpc/include/asm/ftrace.h   |    6 
 arch/riscv/include/asm/ftrace.h     |    5 
 arch/s390/include/asm/ftrace.h      |    6 
 arch/x86/include/asm/ftrace.h       |    6 
 include/linux/fprobe.h              |   53 ++-
 kernel/trace/Kconfig                |    8 
 kernel/trace/fprobe.c               |  639 +++++++++++++++++++++++++----------
 lib/test_fprobe.c                   |   45 --
 10 files changed, 535 insertions(+), 245 deletions(-)

diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
index 14ecb9a418d9..27e32f323048 100644
--- a/arch/arm64/include/asm/ftrace.h
+++ b/arch/arm64/include/asm/ftrace.h
@@ -132,6 +132,12 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
 	return fregs->fp;
 }
 
+static __always_inline unsigned long
+ftrace_regs_get_return_address(const struct ftrace_regs *fregs)
+{
+	return fregs->lr;
+}
+
 static __always_inline struct pt_regs *
 ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
 {
diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
index 1a73f35ea9af..c021aa3194f3 100644
--- a/arch/loongarch/include/asm/ftrace.h
+++ b/arch/loongarch/include/asm/ftrace.h
@@ -80,6 +80,12 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
 #define ftrace_regs_get_frame_pointer(fregs) \
 	((fregs)->regs.regs[22])
 
+static __always_inline unsigned long
+ftrace_regs_get_return_address(struct ftrace_regs *fregs)
+{
+	return *(unsigned long *)(fregs->regs.regs[1]);
+}
+
 #define ftrace_graph_func ftrace_graph_func
 void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
 		       struct ftrace_ops *op, struct ftrace_regs *fregs);
diff --git a/arch/powerpc/include/asm/ftrace.h b/arch/powerpc/include/asm/ftrace.h
index e6ff6834bf7e..2a2d070dd23c 100644
--- a/arch/powerpc/include/asm/ftrace.h
+++ b/arch/powerpc/include/asm/ftrace.h
@@ -75,6 +75,12 @@ ftrace_regs_get_instruction_pointer(struct ftrace_regs *fregs)
 #define ftrace_regs_query_register_offset(name) \
 	regs_query_register_offset(name)
 
+static __always_inline unsigned long
+ftrace_regs_get_return_address(struct ftrace_regs *fregs)
+{
+	return fregs->regs.link;
+}
+
 struct ftrace_ops;
 
 #define ftrace_graph_func ftrace_graph_func
diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
index 897779dec402..c2bfe61b9f8e 100644
--- a/arch/riscv/include/asm/ftrace.h
+++ b/arch/riscv/include/asm/ftrace.h
@@ -182,6 +182,11 @@ static __always_inline unsigned long ftrace_regs_get_return_value(const struct f
 	return fregs->a0;
 }
 
+static __always_inline unsigned long ftrace_regs_get_return_address(const struct ftrace_regs *fregs)
+{
+	return fregs->ra;
+}
+
 static __always_inline void ftrace_regs_set_return_value(struct ftrace_regs *fregs,
 							 unsigned long ret)
 {
diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
index 0d9f6df21f81..7b80ff4d3386 100644
--- a/arch/s390/include/asm/ftrace.h
+++ b/arch/s390/include/asm/ftrace.h
@@ -84,6 +84,12 @@ ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)
 	return sp[0];	/* return backchain */
 }
 
+static __always_inline unsigned long
+ftrace_regs_get_return_address(const struct ftrace_regs *fregs)
+{
+	return fregs->regs.gprs[14];
+}
+
 #define arch_ftrace_fill_perf_regs(fregs, _regs)	 do {		\
 		(_regs)->psw.addr = (fregs)->regs.psw.addr;		\
 		(_regs)->gprs[15] = (fregs)->regs.gprs[15];		\
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 1f4d1f7b19ed..8472ba394091 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -74,6 +74,12 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
 #define ftrace_regs_get_frame_pointer(fregs) \
 	frame_pointer(&(fregs)->regs)
 
+static __always_inline unsigned long
+ftrace_regs_get_return_address(struct ftrace_regs *fregs)
+{
+	return *(unsigned long *)ftrace_regs_get_stack_pointer(fregs);
+}
+
 struct ftrace_ops;
 #define ftrace_graph_func ftrace_graph_func
 void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h
index ef609bcca0f9..2d06bbd99601 100644
--- a/include/linux/fprobe.h
+++ b/include/linux/fprobe.h
@@ -5,10 +5,11 @@
 
 #include <linux/compiler.h>
 #include <linux/ftrace.h>
-#include <linux/rethook.h>
+#include <linux/rcupdate.h>
+#include <linux/refcount.h>
+#include <linux/slab.h>
 
 struct fprobe;
-
 typedef int (*fprobe_entry_cb)(struct fprobe *fp, unsigned long entry_ip,
 			       unsigned long ret_ip, struct ftrace_regs *regs,
 			       void *entry_data);
@@ -17,35 +18,57 @@ typedef void (*fprobe_exit_cb)(struct fprobe *fp, unsigned long entry_ip,
 			       unsigned long ret_ip, struct ftrace_regs *regs,
 			       void *entry_data);
 
+/**
+ * strcut fprobe_hlist_node - address based hash list node for fprobe.
+ *
+ * @hlist: The hlist node for address search hash table.
+ * @addr: The address represented by this.
+ * @fp: The fprobe which owns this.
+ */
+struct fprobe_hlist_node {
+	struct hlist_node	hlist;
+	unsigned long		addr;
+	struct fprobe		*fp;
+};
+
+/**
+ * struct fprobe_hlist - hash list nodes for fprobe.
+ *
+ * @hlist: The hlist node for existence checking hash table.
+ * @rcu: rcu_head for RCU deferred release.
+ * @fp: The fprobe which owns this fprobe_hlist.
+ * @size: The size of @array.
+ * @array: The fprobe_hlist_node for each address to probe.
+ */
+struct fprobe_hlist {
+	struct hlist_node		hlist;
+	struct rcu_head			rcu;
+	struct fprobe			*fp;
+	int				size;
+	struct fprobe_hlist_node	array[];
+};
+
 /**
  * struct fprobe - ftrace based probe.
- * @ops: The ftrace_ops.
+ *
  * @nmissed: The counter for missing events.
  * @flags: The status flag.
- * @rethook: The rethook data structure. (internal data)
  * @entry_data_size: The private data storage size.
- * @nr_maxactive: The max number of active functions.
+ * @nr_maxactive: The max number of active functions. (*deprecated)
  * @entry_handler: The callback function for function entry.
  * @exit_handler: The callback function for function exit.
+ * @hlist_array: The fprobe_hlist for fprobe search from IP hash table.
  */
 struct fprobe {
-#ifdef CONFIG_FUNCTION_TRACER
-	/*
-	 * If CONFIG_FUNCTION_TRACER is not set, CONFIG_FPROBE is disabled too.
-	 * But user of fprobe may keep embedding the struct fprobe on their own
-	 * code. To avoid build error, this will keep the fprobe data structure
-	 * defined here, but remove ftrace_ops data structure.
-	 */
-	struct ftrace_ops	ops;
-#endif
 	unsigned long		nmissed;
 	unsigned int		flags;
-	struct rethook		*rethook;
 	size_t			entry_data_size;
 	int			nr_maxactive;
 
 	fprobe_entry_cb entry_handler;
 	fprobe_exit_cb  exit_handler;
+
+	struct fprobe_hlist	*hlist_array;
 };
 
 /* This fprobe is soft-disabled. */
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index c8dfd3a233c6..90c7f8a2d2c9 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -298,11 +298,9 @@ config DYNAMIC_FTRACE_WITH_ARGS
 
 config FPROBE
 	bool "Kernel Function Probe (fprobe)"
-	depends on FUNCTION_TRACER
-	depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS
-	depends on HAVE_PT_REGS_TO_FTRACE_REGS_CAST || !HAVE_DYNAMIC_FTRACE_WITH_ARGS
-	depends on HAVE_RETHOOK
-	select RETHOOK
+	depends on FUNCTION_GRAPH_TRACER
+	depends on HAVE_FUNCTION_GRAPH_FREGS && HAVE_FTRACE_GRAPH_FUNC
+	depends on DYNAMIC_FTRACE_WITH_ARGS
 	default n
 	help
 	  This option enables kernel function probe (fprobe) based on ftrace.
diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
index 90a3c8e2bbdf..5a0b4ef52fa7 100644
--- a/kernel/trace/fprobe.c
+++ b/kernel/trace/fprobe.c
@@ -8,98 +8,195 @@
 #include <linux/fprobe.h>
 #include <linux/kallsyms.h>
 #include <linux/kprobes.h>
-#include <linux/rethook.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/sort.h>
 
 #include "trace.h"
 
-struct fprobe_rethook_node {
-	struct rethook_node node;
-	unsigned long entry_ip;
-	unsigned long entry_parent_ip;
-	char data[];
-};
+#define FPROBE_IP_HASH_BITS 8
+#define FPROBE_IP_TABLE_SIZE (1 << FPROBE_IP_HASH_BITS)
 
-static inline void __fprobe_handler(unsigned long ip, unsigned long parent_ip,
-			struct ftrace_ops *ops, struct ftrace_regs *fregs)
-{
-	struct fprobe_rethook_node *fpr;
-	struct rethook_node *rh = NULL;
-	struct fprobe *fp;
-	void *entry_data = NULL;
-	int ret = 0;
+#define FPROBE_HASH_BITS 6
+#define FPROBE_TABLE_SIZE (1 << FPROBE_HASH_BITS)
 
-	fp = container_of(ops, struct fprobe, ops);
+#define SIZE_IN_LONG(x) ((x + sizeof(long) - 1) >> (sizeof(long) == 8 ? 3 : 2))
 
-	if (fp->exit_handler) {
-		rh = rethook_try_get(fp->rethook);
-		if (!rh) {
-			fp->nmissed++;
-			return;
-		}
-		fpr = container_of(rh, struct fprobe_rethook_node, node);
-		fpr->entry_ip = ip;
-		fpr->entry_parent_ip = parent_ip;
-		if (fp->entry_data_size)
-			entry_data = fpr->data;
+/*
+ * fprobe_table: hold 'fprobe_hlist::hlist' for checking the fprobe still
+ *   exists. The key is the address of fprobe instance.
+ * fprobe_ip_table: hold 'fprobe_hlist::array[*]' for searching the fprobe
+ *   instance related to the funciton address. The key is the ftrace IP
+ *   address.
+ *
+ * When unregistering the fprobe, fprobe_hlist::fp and fprobe_hlist::array[*].fp
+ * are set NULL and delete those from both hash tables (by hlist_del_rcu).
+ * After an RCU grace period, the fprobe_hlist itself will be released.
+ *
+ * fprobe_table and fprobe_ip_table can be accessed from either
+ *  - Normal hlist traversal and RCU add/del under 'fprobe_mutex' is held.
+ *  - RCU hlist traversal under disabling preempt
+ */
+static struct hlist_head fprobe_table[FPROBE_TABLE_SIZE];
+static struct hlist_head fprobe_ip_table[FPROBE_IP_TABLE_SIZE];
+static DEFINE_MUTEX(fprobe_mutex);
+
+/*
+ * Find first fprobe in the hlist. It will be iterated twice in the entry
+ * probe, once for correcting the total required size, the second time is
+ * calling back the user handlers.
+ * Thus the hlist in the fprobe_table must be sorted and new probe needs to
+ * be added *before* the first fprobe.
+ */
+static struct fprobe_hlist_node *find_first_fprobe_node(unsigned long ip)
+{
+	struct fprobe_hlist_node *node;
+	struct hlist_head *head;
+
+	head = &fprobe_ip_table[hash_ptr((void *)ip, FPROBE_IP_HASH_BITS)];
+	hlist_for_each_entry_rcu(node, head, hlist,
+				 lockdep_is_held(&fprobe_mutex)) {
+		if (node->addr == ip)
+			return node;
 	}
+	return NULL;
+}
+NOKPROBE_SYMBOL(find_first_fprobe_node);
+
+/* Node insertion and deletion requires the fprobe_mutex */
+static void insert_fprobe_node(struct fprobe_hlist_node *node)
+{
+	unsigned long ip = node->addr;
+	struct fprobe_hlist_node *next;
+	struct hlist_head *head;
 
-	if (fp->entry_handler)
-		ret = fp->entry_handler(fp, ip, parent_ip, fregs, entry_data);
+	lockdep_assert_held(&fprobe_mutex);
 
-	/* If entry_handler returns !0, nmissed is not counted. */
-	if (rh) {
-		if (ret)
-			rethook_recycle(rh);
-		else
-			rethook_hook(rh, ftrace_get_regs(fregs), true);
+	next = find_first_fprobe_node(ip);
+	if (next) {
+		hlist_add_before_rcu(&node->hlist, &next->hlist);
+		return;
 	}
+	head = &fprobe_ip_table[hash_ptr((void *)ip, FPROBE_IP_HASH_BITS)];
+	hlist_add_head_rcu(&node->hlist, head);
 }
 
-static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
-		struct ftrace_ops *ops, struct ftrace_regs *fregs)
+/* Return true if there are synonims */
+static bool delete_fprobe_node(struct fprobe_hlist_node *node)
 {
-	struct fprobe *fp;
-	int bit;
+	lockdep_assert_held(&fprobe_mutex);
 
-	fp = container_of(ops, struct fprobe, ops);
-	if (fprobe_disabled(fp))
-		return;
+	WRITE_ONCE(node->fp, NULL);
+	hlist_del_rcu(&node->hlist);
+	return !!find_first_fprobe_node(node->addr);
+}
 
-	/* recursion detection has to go before any traceable function and
-	 * all functions before this point should be marked as notrace
-	 */
-	bit = ftrace_test_recursion_trylock(ip, parent_ip);
-	if (bit < 0) {
-		fp->nmissed++;
-		return;
+/* Check existence of the fprobe */
+static bool is_fprobe_still_exist(struct fprobe *fp)
+{
+	struct hlist_head *head;
+	struct fprobe_hlist *fph;
+
+	head = &fprobe_table[hash_ptr(fp, FPROBE_HASH_BITS)];
+	hlist_for_each_entry_rcu(fph, head, hlist,
+				 lockdep_is_held(&fprobe_mutex)) {
+		if (fph->fp == fp)
+			return true;
 	}
-	__fprobe_handler(ip, parent_ip, ops, fregs);
-	ftrace_test_recursion_unlock(bit);
+	return false;
+}
+NOKPROBE_SYMBOL(is_fprobe_still_exist);
+
+static int add_fprobe_hash(struct fprobe *fp)
+{
+	struct fprobe_hlist *fph = fp->hlist_array;
+	struct hlist_head *head;
+
+	lockdep_assert_held(&fprobe_mutex);
 
+	if (WARN_ON_ONCE(!fph))
+		return -EINVAL;
+
+	if (is_fprobe_still_exist(fp))
+		return -EEXIST;
+
+	head = &fprobe_table[hash_ptr(fp, FPROBE_HASH_BITS)];
+	hlist_add_head_rcu(&fp->hlist_array->hlist, head);
+	return 0;
 }
-NOKPROBE_SYMBOL(fprobe_handler);
 
-static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip,
-				  struct ftrace_ops *ops, struct ftrace_regs *fregs)
+static int del_fprobe_hash(struct fprobe *fp)
 {
-	struct fprobe *fp;
-	int bit;
+	struct fprobe_hlist *fph = fp->hlist_array;
 
-	fp = container_of(ops, struct fprobe, ops);
-	if (fprobe_disabled(fp))
-		return;
+	lockdep_assert_held(&fprobe_mutex);
 
-	/* recursion detection has to go before any traceable function and
-	 * all functions called before this point should be marked as notrace
-	 */
-	bit = ftrace_test_recursion_trylock(ip, parent_ip);
-	if (bit < 0) {
-		fp->nmissed++;
-		return;
+	if (WARN_ON_ONCE(!fph))
+		return -EINVAL;
+
+	if (!is_fprobe_still_exist(fp))
+		return -ENOENT;
+
+	fph->fp = NULL;
+	hlist_del_rcu(&fph->hlist);
+	return 0;
+}
+
+/* The entry data size is 4 bits (=16) * sizeof(long) in maximum */
+#define FPROBE_HEADER_SIZE_BITS		4
+#define MAX_FPROBE_DATA_SIZE_WORD	((1L << FPROBE_HEADER_SIZE_BITS) - 1)
+#define MAX_FPROBE_DATA_SIZE		(MAX_FPROBE_DATA_SIZE_WORD * sizeof(long))
+#define FPROBE_HEADER_PTR_BITS		(BITS_PER_LONG - FPROBE_HEADER_SIZE_BITS)
+#define FPROBE_HEADER_PTR_MASK		GENMASK(FPROBE_HEADER_PTR_BITS - 1, 0)
+#define FPROBE_HEADER_SIZE		sizeof(unsigned long)
+
+static inline unsigned long encode_fprobe_header(struct fprobe *fp, int size_words)
+{
+	if (WARN_ON_ONCE(size_words > MAX_FPROBE_DATA_SIZE_WORD ||
+	    ((unsigned long)fp & ~FPROBE_HEADER_PTR_MASK) !=
+	    ~FPROBE_HEADER_PTR_MASK)) {
+		return 0;
 	}
+	return ((unsigned long)size_words << FPROBE_HEADER_PTR_BITS) |
+		((unsigned long)fp & FPROBE_HEADER_PTR_MASK);
+}
+
+/* Return reserved data size in words */
+static inline int decode_fprobe_header(unsigned long val, struct fprobe **fp)
+{
+	unsigned long ptr;
+
+	ptr = (val & FPROBE_HEADER_PTR_MASK) | ~FPROBE_HEADER_PTR_MASK;
+	if (fp)
+		*fp = (struct fprobe *)ptr;
+	return val >> FPROBE_HEADER_PTR_BITS;
+}
+
+/*
+ * fprobe shadow stack management:
+ * Since fprobe shares a single fgraph_ops, it needs to share the stack entry
+ * among the probes on the same function exit. Note that a new probe can be
+ * registered before a target function is returning, we can not use the hash
+ * table to find the corresponding probes. Thus the probe address is stored on
+ * the shadow stack with its entry data size.
+ *
+ */
+static inline int __fprobe_handler(unsigned long ip, unsigned long parent_ip,
+				   struct fprobe *fp, struct ftrace_regs *fregs,
+				   void *data)
+{
+	if (!fp->entry_handler)
+		return 0;
+
+	return fp->entry_handler(fp, ip, parent_ip, fregs, data);
+}
 
+static inline int __fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip,
+					  struct fprobe *fp, struct ftrace_regs *fregs,
+					  void *data)
+{
+	int ret;
 	/*
 	 * This user handler is shared with other kprobes and is not expected to be
 	 * called recursively. So if any other kprobe handler is running, this will
@@ -108,45 +205,185 @@ static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip,
 	 */
 	if (unlikely(kprobe_running())) {
 		fp->nmissed++;
-		goto recursion_unlock;
+		return 0;
 	}
 
 	kprobe_busy_begin();
-	__fprobe_handler(ip, parent_ip, ops, fregs);
+	ret = __fprobe_handler(ip, parent_ip, fp, fregs, data);
 	kprobe_busy_end();
-
-recursion_unlock:
-	ftrace_test_recursion_unlock(bit);
+	return ret;
 }
 
-static void fprobe_exit_handler(struct rethook_node *rh, void *data,
-				unsigned long ret_ip, struct pt_regs *regs)
+static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
+			struct ftrace_regs *fregs)
 {
-	struct fprobe *fp = (struct fprobe *)data;
-	struct fprobe_rethook_node *fpr;
-	struct ftrace_regs *fregs = (struct ftrace_regs *)regs;
-	int bit;
+	struct fprobe_hlist_node *node, *first;
+	unsigned long *fgraph_data = NULL;
+	unsigned long func = trace->func;
+	unsigned long header, ret_ip;
+	int reserved_words;
+	struct fprobe *fp;
+	int used, ret;
 
-	if (!fp || fprobe_disabled(fp))
-		return;
+	if (WARN_ON_ONCE(!fregs))
+		return 0;
+
+	first = node = find_first_fprobe_node(func);
+	if (unlikely(!first))
+		return 0;
 
-	fpr = container_of(rh, struct fprobe_rethook_node, node);
+	reserved_words = 0;
+	hlist_for_each_entry_from_rcu(node, hlist) {
+		if (node->addr != func)
+			break;
+		fp = READ_ONCE(node->fp);
+		if (!fp || !fp->exit_handler)
+			continue;
+		/*
+		 * Since fprobe can be enabled until the next loop, we ignore the
+		 * fprobe's disabled flag in this loop.
+		 */
+		reserved_words +=
+			SIZE_IN_LONG(fp->entry_data_size) + 1;
+	}
+	node = first;
+	if (reserved_words) {
+		fgraph_data = fgraph_reserve_data(gops->idx, reserved_words * sizeof(long));
+		if (unlikely(!fgraph_data)) {
+			hlist_for_each_entry_from_rcu(node, hlist) {
+				if (node->addr != func)
+					break;
+				fp = READ_ONCE(node->fp);
+				if (fp && !fprobe_disabled(fp))
+					fp->nmissed++;
+			}
+			return 0;
+		}
+	}
 
 	/*
-	 * we need to assure no calls to traceable functions in-between the
-	 * end of fprobe_handler and the beginning of fprobe_exit_handler.
+	 * TODO: recursion detection has been done in the fgraph. Thus we need
+	 * to add a callback to increment missed counter.
 	 */
-	bit = ftrace_test_recursion_trylock(fpr->entry_ip, fpr->entry_parent_ip);
-	if (bit < 0) {
-		fp->nmissed++;
+	ret_ip = ftrace_regs_get_return_address(fregs);
+	used = 0;
+	hlist_for_each_entry_from_rcu(node, hlist) {
+		void *data;
+
+		if (node->addr != func)
+			break;
+		fp = READ_ONCE(node->fp);
+		if (!fp || fprobe_disabled(fp))
+			continue;
+
+		if (fp->entry_data_size && fp->exit_handler)
+			data = fgraph_data + used + 1;
+		else
+			data = NULL;
+
+		if (fprobe_shared_with_kprobes(fp))
+			ret = __fprobe_kprobe_handler(func, ret_ip, fp, fregs, data);
+		else
+			ret = __fprobe_handler(func, ret_ip, fp, fregs, data);
+		/* If entry_handler returns !0, nmissed is not counted but skips exit_handler. */
+		if (!ret && fp->exit_handler) {
+			int size_words = SIZE_IN_LONG(fp->entry_data_size);
+
+			header = encode_fprobe_header(fp, size_words);
+			if (likely(header)) {
+				fgraph_data[used] = header;
+				used += size_words + 1;
+			}
+		}
+	}
+	if (used < reserved_words)
+		memset(fgraph_data + used, 0, reserved_words - used);
+
+	/* If any exit_handler is set, data must be used. */
+	return used != 0;
+}
+NOKPROBE_SYMBOL(fprobe_entry);
+
+static void fprobe_return(struct ftrace_graph_ret *trace,
+			  struct fgraph_ops *gops,
+			  struct ftrace_regs *fregs)
+{
+	unsigned long *fgraph_data = NULL;
+	unsigned long ret_ip;
+	unsigned long val;
+	struct fprobe *fp;
+	int size, curr;
+	int size_words;
+
+	fgraph_data = (unsigned long *)fgraph_retrieve_data(gops->idx, &size);
+	if (WARN_ON_ONCE(!fgraph_data))
 		return;
+	size_words = SIZE_IN_LONG(size);
+	ret_ip = ftrace_regs_get_instruction_pointer(fregs);
+
+	preempt_disable();
+
+	curr = 0;
+	while (size_words > curr) {
+		val = fgraph_data[curr++];
+		if (!val)
+			break;
+
+		size = decode_fprobe_header(val, &fp);
+		if (fp && is_fprobe_still_exist(fp) && !fprobe_disabled(fp)) {
+			if (WARN_ON_ONCE(curr + size > size_words))
+				break;
+			fp->exit_handler(fp, trace->func, ret_ip, fregs,
+					 size ? fgraph_data + curr : NULL);
+		}
+		curr += size;
 	}
+	preempt_enable();
+}
+NOKPROBE_SYMBOL(fprobe_return);
+
+static struct fgraph_ops fprobe_graph_ops = {
+	.entryfunc	= fprobe_entry,
+	.retfunc	= fprobe_return,
+};
+static int fprobe_graph_active;
 
-	fp->exit_handler(fp, fpr->entry_ip, ret_ip, fregs,
-			 fp->entry_data_size ? (void *)fpr->data : NULL);
-	ftrace_test_recursion_unlock(bit);
+/* Add @addrs to the ftrace filter and register fgraph if needed. */
+static int fprobe_graph_add_ips(unsigned long *addrs, int num)
+{
+	int ret;
+
+	lockdep_assert_held(&fprobe_mutex);
+
+	ret = ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 0, 0);
+	if (ret)
+		return ret;
+
+	if (!fprobe_graph_active) {
+		ret = register_ftrace_graph(&fprobe_graph_ops);
+		if (WARN_ON_ONCE(ret)) {
+			ftrace_free_filter(&fprobe_graph_ops.ops);
+			return ret;
+		}
+	}
+	fprobe_graph_active++;
+	return 0;
+}
+
+/* Remove @addrs from the ftrace filter and unregister fgraph if possible. */
+static void fprobe_graph_remove_ips(unsigned long *addrs, int num)
+{
+	lockdep_assert_held(&fprobe_mutex);
+
+	fprobe_graph_active--;
+	if (!fprobe_graph_active) {
+		/* Q: should we unregister it ? */
+		unregister_ftrace_graph(&fprobe_graph_ops);
+		return;
+	}
+
+	ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 1, 0);
 }
-NOKPROBE_SYMBOL(fprobe_exit_handler);
 
 static int symbols_cmp(const void *a, const void *b)
 {
@@ -176,54 +413,97 @@ static unsigned long *get_ftrace_locations(const char **syms, int num)
 	return ERR_PTR(-ENOENT);
 }
 
-static void fprobe_init(struct fprobe *fp)
-{
-	fp->nmissed = 0;
-	if (fprobe_shared_with_kprobes(fp))
-		fp->ops.func = fprobe_kprobe_handler;
-	else
-		fp->ops.func = fprobe_handler;
-
-	fp->ops.flags |= FTRACE_OPS_FL_SAVE_REGS;
-}
+struct filter_match_data {
+	const char *filter;
+	const char *notfilter;
+	size_t index;
+	size_t size;
+	unsigned long *addrs;
+};
 
-static int fprobe_init_rethook(struct fprobe *fp, int num)
+static int filter_match_callback(void *data, const char *name, unsigned long addr)
 {
-	int size;
+	struct filter_match_data *match = data;
 
-	if (!fp->exit_handler) {
-		fp->rethook = NULL;
+	if (!glob_match(match->filter, name) ||
+	    (match->notfilter && glob_match(match->notfilter, name)))
 		return 0;
-	}
 
-	/* Initialize rethook if needed */
-	if (fp->nr_maxactive)
-		num = fp->nr_maxactive;
-	else
-		num *= num_possible_cpus() * 2;
-	if (num <= 0)
-		return -EINVAL;
+	if (!ftrace_location(addr))
+		return 0;
 
-	size = sizeof(struct fprobe_rethook_node) + fp->entry_data_size;
+	if (match->addrs)
+		match->addrs[match->index] = addr;
 
-	/* Initialize rethook */
-	fp->rethook = rethook_alloc((void *)fp, fprobe_exit_handler, size, num);
-	if (IS_ERR(fp->rethook))
-		return PTR_ERR(fp->rethook);
+	match->index++;
+	return match->index == match->size;
+}
 
-	return 0;
+/*
+ * Make IP list from the filter/no-filter glob patterns.
+ * Return the number of matched symbols, or -ENOENT.
+ */
+static int ip_list_from_filter(const char *filter, const char *notfilter,
+			       unsigned long *addrs, size_t size)
+{
+	struct filter_match_data match = { .filter = filter, .notfilter = notfilter,
+		.index = 0, .size = size, .addrs = addrs};
+	int ret;
+
+	ret = kallsyms_on_each_symbol(filter_match_callback, &match);
+	if (ret < 0)
+		return ret;
+	ret = module_kallsyms_on_each_symbol(NULL, filter_match_callback, &match);
+	if (ret < 0)
+		return ret;
+
+	return match.index ?: -ENOENT;
 }
 
 static void fprobe_fail_cleanup(struct fprobe *fp)
 {
-	if (!IS_ERR_OR_NULL(fp->rethook)) {
-		/* Don't need to cleanup rethook->handler because this is not used. */
-		rethook_free(fp->rethook);
-		fp->rethook = NULL;
+	kfree(fp->hlist_array);
+	fp->hlist_array = NULL;
+}
+
+/* Initialize the fprobe data structure. */
+static int fprobe_init(struct fprobe *fp, unsigned long *addrs, int num)
+{
+	struct fprobe_hlist *hlist_array;
+	unsigned long addr;
+	int size, i;
+
+	if (!fp || !addrs || num <= 0)
+		return -EINVAL;
+
+	size = ALIGN(fp->entry_data_size, sizeof(long));
+	if (size > MAX_FPROBE_DATA_SIZE)
+		return -E2BIG;
+	fp->entry_data_size = size;
+
+	hlist_array = kzalloc(struct_size(hlist_array, array, num), GFP_KERNEL);
+	if (!hlist_array)
+		return -ENOMEM;
+
+	fp->nmissed = 0;
+
+	hlist_array->size = num;
+	fp->hlist_array = hlist_array;
+	hlist_array->fp = fp;
+	for (i = 0; i < num; i++) {
+		hlist_array->array[i].fp = fp;
+		addr = ftrace_location(addrs[i]);
+		if (!addr) {
+			fprobe_fail_cleanup(fp);
+			return -ENOENT;
+		}
+		hlist_array->array[i].addr = addr;
 	}
-	ftrace_free_filter(&fp->ops);
+	return 0;
 }
 
+#define FPROBE_IPS_MAX	INT_MAX
+
 /**
  * register_fprobe() - Register fprobe to ftrace by pattern.
  * @fp: A fprobe data structure to be registered.
@@ -237,46 +517,24 @@ static void fprobe_fail_cleanup(struct fprobe *fp)
  */
 int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter)
 {
-	struct ftrace_hash *hash;
-	unsigned char *str;
-	int ret, len;
+	unsigned long *addrs;
+	int ret;
 
 	if (!fp || !filter)
 		return -EINVAL;
 
-	fprobe_init(fp);
-
-	len = strlen(filter);
-	str = kstrdup(filter, GFP_KERNEL);
-	ret = ftrace_set_filter(&fp->ops, str, len, 0);
-	kfree(str);
-	if (ret)
+	ret = ip_list_from_filter(filter, notfilter, NULL, FPROBE_IPS_MAX);
+	if (ret < 0)
 		return ret;
 
-	if (notfilter) {
-		len = strlen(notfilter);
-		str = kstrdup(notfilter, GFP_KERNEL);
-		ret = ftrace_set_notrace(&fp->ops, str, len, 0);
-		kfree(str);
-		if (ret)
-			goto out;
-	}
-
-	/* TODO:
-	 * correctly calculate the total number of filtered symbols
-	 * from both filter and notfilter.
-	 */
-	hash = rcu_access_pointer(fp->ops.local_hash.filter_hash);
-	if (WARN_ON_ONCE(!hash))
-		goto out;
-
-	ret = fprobe_init_rethook(fp, (int)hash->count);
-	if (!ret)
-		ret = register_ftrace_function(&fp->ops);
+	addrs = kcalloc(ret, sizeof(unsigned long), GFP_KERNEL);
+	if (!addrs)
+		return -ENOMEM;
+	ret = ip_list_from_filter(filter, notfilter, addrs, ret);
+	if (ret > 0)
+		ret = register_fprobe_ips(fp, addrs, ret);
 
-out:
-	if (ret)
-		fprobe_fail_cleanup(fp);
+	kfree(addrs);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(register_fprobe);
@@ -284,7 +542,7 @@ EXPORT_SYMBOL_GPL(register_fprobe);
 /**
  * register_fprobe_ips() - Register fprobe to ftrace by address.
  * @fp: A fprobe data structure to be registered.
- * @addrs: An array of target ftrace location addresses.
+ * @addrs: An array of target function address.
  * @num: The number of entries of @addrs.
  *
  * Register @fp to ftrace for enabling the probe on the address given by @addrs.
@@ -296,23 +554,27 @@ EXPORT_SYMBOL_GPL(register_fprobe);
  */
 int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num)
 {
-	int ret;
-
-	if (!fp || !addrs || num <= 0)
-		return -EINVAL;
-
-	fprobe_init(fp);
+	struct fprobe_hlist *hlist_array;
+	int ret, i;
 
-	ret = ftrace_set_filter_ips(&fp->ops, addrs, num, 0, 0);
+	ret = fprobe_init(fp, addrs, num);
 	if (ret)
 		return ret;
 
-	ret = fprobe_init_rethook(fp, num);
-	if (!ret)
-		ret = register_ftrace_function(&fp->ops);
+	mutex_lock(&fprobe_mutex);
+
+	hlist_array = fp->hlist_array;
+	ret = fprobe_graph_add_ips(addrs, num);
+	if (!ret) {
+		add_fprobe_hash(fp);
+		for (i = 0; i < hlist_array->size; i++)
+			insert_fprobe_node(&hlist_array->array[i]);
+	}
+	mutex_unlock(&fprobe_mutex);
 
 	if (ret)
 		fprobe_fail_cleanup(fp);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(register_fprobe_ips);
@@ -350,14 +612,13 @@ EXPORT_SYMBOL_GPL(register_fprobe_syms);
 
 bool fprobe_is_registered(struct fprobe *fp)
 {
-	if (!fp || (fp->ops.saved_func != fprobe_handler &&
-		    fp->ops.saved_func != fprobe_kprobe_handler))
+	if (!fp || !fp->hlist_array)
 		return false;
 	return true;
 }
 
 /**
- * unregister_fprobe() - Unregister fprobe from ftrace
+ * unregister_fprobe() - Unregister fprobe.
  * @fp: A fprobe data structure to be unregistered.
  *
  * Unregister fprobe (and remove ftrace hooks from the function entries).
@@ -366,23 +627,41 @@ bool fprobe_is_registered(struct fprobe *fp)
  */
 int unregister_fprobe(struct fprobe *fp)
 {
-	int ret;
+	struct fprobe_hlist *hlist_array;
+	unsigned long *addrs = NULL;
+	int ret = 0, i, count;
 
-	if (!fprobe_is_registered(fp))
-		return -EINVAL;
+	mutex_lock(&fprobe_mutex);
+	if (!fp || !is_fprobe_still_exist(fp)) {
+		ret = -EINVAL;
+		goto out;
+	}
 
-	if (!IS_ERR_OR_NULL(fp->rethook))
-		rethook_stop(fp->rethook);
+	hlist_array = fp->hlist_array;
+	addrs = kcalloc(hlist_array->size, sizeof(unsigned long), GFP_KERNEL);
+	if (!addrs) {
+		ret = -ENOMEM;	/* TODO: Fallback to one-by-one loop */
+		goto out;
+	}
 
-	ret = unregister_ftrace_function(&fp->ops);
-	if (ret < 0)
-		return ret;
+	/* Remove non-synonim ips from table and hash */
+	count = 0;
+	for (i = 0; i < hlist_array->size; i++) {
+		if (!delete_fprobe_node(&hlist_array->array[i]))
+			addrs[count++] = hlist_array->array[i].addr;
+	}
+	del_fprobe_hash(fp);
 
-	if (!IS_ERR_OR_NULL(fp->rethook))
-		rethook_free(fp->rethook);
+	if (count)
+		fprobe_graph_remove_ips(addrs, count);
 
-	ftrace_free_filter(&fp->ops);
+	kfree_rcu(hlist_array, rcu);
+	fp->hlist_array = NULL;
 
+out:
+	mutex_unlock(&fprobe_mutex);
+
+	kfree(addrs);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(unregister_fprobe);
diff --git a/lib/test_fprobe.c b/lib/test_fprobe.c
index 271ce0caeec0..cf92111b5c79 100644
--- a/lib/test_fprobe.c
+++ b/lib/test_fprobe.c
@@ -17,10 +17,8 @@ static u32 rand1, entry_val, exit_val;
 /* Use indirect calls to avoid inlining the target functions */
 static u32 (*target)(u32 value);
 static u32 (*target2)(u32 value);
-static u32 (*target_nest)(u32 value, u32 (*nest)(u32));
 static unsigned long target_ip;
 static unsigned long target2_ip;
-static unsigned long target_nest_ip;
 static int entry_return_value;
 
 static noinline u32 fprobe_selftest_target(u32 value)
@@ -33,11 +31,6 @@ static noinline u32 fprobe_selftest_target2(u32 value)
 	return (value / div_factor) + 1;
 }
 
-static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32))
-{
-	return nest(value + 2);
-}
-
 static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,
 				    unsigned long ret_ip,
 				    struct ftrace_regs *fregs, void *data)
@@ -79,22 +72,6 @@ static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,
 		KUNIT_EXPECT_NULL(current_test, data);
 }
 
-static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,
-				      unsigned long ret_ip,
-				      struct ftrace_regs *fregs, void *data)
-{
-	KUNIT_EXPECT_FALSE(current_test, preemptible());
-	return 0;
-}
-
-static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip,
-				      unsigned long ret_ip,
-				      struct ftrace_regs *fregs, void *data)
-{
-	KUNIT_EXPECT_FALSE(current_test, preemptible());
-	KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip);
-}
-
 /* Test entry only (no rethook) */
 static void test_fprobe_entry(struct kunit *test)
 {
@@ -191,25 +168,6 @@ static void test_fprobe_data(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
 }
 
-/* Test nr_maxactive */
-static void test_fprobe_nest(struct kunit *test)
-{
-	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_nest_target"};
-	struct fprobe fp = {
-		.entry_handler = nest_entry_handler,
-		.exit_handler = nest_exit_handler,
-		.nr_maxactive = 1,
-	};
-
-	current_test = test;
-	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));
-
-	target_nest(rand1, target);
-	KUNIT_EXPECT_EQ(test, 1, fp.nmissed);
-
-	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
-}
-
 static void test_fprobe_skip(struct kunit *test)
 {
 	struct fprobe fp = {
@@ -247,10 +205,8 @@ static int fprobe_test_init(struct kunit *test)
 	rand1 = get_random_u32_above(div_factor);
 	target = fprobe_selftest_target;
 	target2 = fprobe_selftest_target2;
-	target_nest = fprobe_selftest_nest_target;
 	target_ip = get_ftrace_location(target);
 	target2_ip = get_ftrace_location(target2);
-	target_nest_ip = get_ftrace_location(target_nest);
 
 	return 0;
 }
@@ -260,7 +216,6 @@ static struct kunit_case fprobe_testcases[] = {
 	KUNIT_CASE(test_fprobe),
 	KUNIT_CASE(test_fprobe_syms),
 	KUNIT_CASE(test_fprobe_data),
-	KUNIT_CASE(test_fprobe_nest),
 	KUNIT_CASE(test_fprobe_skip),
 	{}
 };


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 14/19] tracing: Fix function timing profiler to initialize hashtable
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (12 preceding siblings ...)
  2024-09-12 15:10 ` [PATCH v14 13/19] fprobe: Rewrite fprobe on function-graph tracer Masami Hiramatsu (Google)
@ 2024-09-12 15:10 ` Masami Hiramatsu (Google)
  2024-09-12 15:10 ` [PATCH v14 15/19] tracing/fprobe: Remove nr_maxactive from fprobe Masami Hiramatsu (Google)
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:10 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Since the new fgraph requires to initialize fgraph_ops.ops.func_hash before
calling register_ftrace_graph(), initialize it with default (tracing all
functions) parameter.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 kernel/trace/ftrace.c |    4 ++++
 1 file changed, 4 insertions(+)

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index fd6c5a50c5e5..c55cf21fd53c 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -885,6 +885,10 @@ static void profile_graph_return(struct ftrace_graph_ret *trace,
 }
 
 static struct fgraph_ops fprofiler_ops = {
+	.ops = {
+		.flags = FTRACE_OPS_FL_INITIALIZED,
+		INIT_OPS_HASH(fprofiler_ops.ops)
+	},
 	.entryfunc = &profile_graph_entry,
 	.retfunc = &profile_graph_return,
 };


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 15/19] tracing/fprobe: Remove nr_maxactive from fprobe
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (13 preceding siblings ...)
  2024-09-12 15:10 ` [PATCH v14 14/19] tracing: Fix function timing profiler to initialize hashtable Masami Hiramatsu (Google)
@ 2024-09-12 15:10 ` Masami Hiramatsu (Google)
  2024-09-12 15:11 ` [PATCH v14 16/19] selftests: ftrace: Remove obsolate maxactive syntax check Masami Hiramatsu (Google)
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:10 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Remove depercated fprobe::nr_maxactive. This involves fprobe events to
rejects the maxactive number.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 Changes in v2:
  - Newly added.
---
 include/linux/fprobe.h      |    2 --
 kernel/trace/trace_fprobe.c |   44 ++++++-------------------------------------
 2 files changed, 6 insertions(+), 40 deletions(-)

diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h
index 2d06bbd99601..a86b3e4df2a0 100644
--- a/include/linux/fprobe.h
+++ b/include/linux/fprobe.h
@@ -54,7 +54,6 @@ struct fprobe_hlist {
  * @nmissed: The counter for missing events.
  * @flags: The status flag.
  * @entry_data_size: The private data storage size.
- * @nr_maxactive: The max number of active functions. (*deprecated)
  * @entry_handler: The callback function for function entry.
  * @exit_handler: The callback function for function exit.
  * @hlist_array: The fprobe_hlist for fprobe search from IP hash table.
@@ -63,7 +62,6 @@ struct fprobe {
 	unsigned long		nmissed;
 	unsigned int		flags;
 	size_t			entry_data_size;
-	int			nr_maxactive;
 
 	fprobe_entry_cb entry_handler;
 	fprobe_exit_cb  exit_handler;
diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c
index 86cd6a8c806a..20ef5cd5d419 100644
--- a/kernel/trace/trace_fprobe.c
+++ b/kernel/trace/trace_fprobe.c
@@ -422,7 +422,6 @@ static struct trace_fprobe *alloc_trace_fprobe(const char *group,
 					       const char *event,
 					       const char *symbol,
 					       struct tracepoint *tpoint,
-					       int maxactive,
 					       int nargs, bool is_return)
 {
 	struct trace_fprobe *tf;
@@ -442,7 +441,6 @@ static struct trace_fprobe *alloc_trace_fprobe(const char *group,
 		tf->fp.entry_handler = fentry_dispatcher;
 
 	tf->tpoint = tpoint;
-	tf->fp.nr_maxactive = maxactive;
 
 	ret = trace_probe_init(&tf->tp, event, group, false, nargs);
 	if (ret < 0)
@@ -1021,12 +1019,11 @@ static int __trace_fprobe_create(int argc, const char *argv[])
 	 *  FETCHARG:TYPE : use TYPE instead of unsigned long.
 	 */
 	struct trace_fprobe *tf = NULL;
-	int i, len, new_argc = 0, ret = 0;
+	int i, new_argc = 0, ret = 0;
 	bool is_return = false;
 	char *symbol = NULL;
 	const char *event = NULL, *group = FPROBE_EVENT_SYSTEM;
 	const char **new_argv = NULL;
-	int maxactive = 0;
 	char buf[MAX_EVENT_NAME_LEN];
 	char gbuf[MAX_EVENT_NAME_LEN];
 	char sbuf[KSYM_NAME_LEN];
@@ -1048,33 +1045,13 @@ static int __trace_fprobe_create(int argc, const char *argv[])
 
 	trace_probe_log_init("trace_fprobe", argc, argv);
 
-	event = strchr(&argv[0][1], ':');
-	if (event)
-		event++;
-
-	if (isdigit(argv[0][1])) {
-		if (event)
-			len = event - &argv[0][1] - 1;
-		else
-			len = strlen(&argv[0][1]);
-		if (len > MAX_EVENT_NAME_LEN - 1) {
-			trace_probe_log_err(1, BAD_MAXACT);
-			goto parse_error;
-		}
-		memcpy(buf, &argv[0][1], len);
-		buf[len] = '\0';
-		ret = kstrtouint(buf, 0, &maxactive);
-		if (ret || !maxactive) {
+	if (argv[0][1] != '\0') {
+		if (argv[0][1] != ':') {
+			trace_probe_log_set_index(0);
 			trace_probe_log_err(1, BAD_MAXACT);
 			goto parse_error;
 		}
-		/* fprobe rethook instances are iterated over via a list. The
-		 * maximum should stay reasonable.
-		 */
-		if (maxactive > RETHOOK_MAXACTIVE_MAX) {
-			trace_probe_log_err(1, MAXACT_TOO_BIG);
-			goto parse_error;
-		}
+		event = &argv[0][2];
 	}
 
 	trace_probe_log_set_index(1);
@@ -1084,12 +1061,6 @@ static int __trace_fprobe_create(int argc, const char *argv[])
 	if (ret < 0)
 		goto parse_error;
 
-	if (!is_return && maxactive) {
-		trace_probe_log_set_index(0);
-		trace_probe_log_err(1, BAD_MAXACT_TYPE);
-		goto parse_error;
-	}
-
 	trace_probe_log_set_index(0);
 	if (event) {
 		ret = traceprobe_parse_event_name(&event, &group, gbuf,
@@ -1147,8 +1118,7 @@ static int __trace_fprobe_create(int argc, const char *argv[])
 		goto out;
 
 	/* setup a probe */
-	tf = alloc_trace_fprobe(group, event, symbol, tpoint, maxactive,
-				argc, is_return);
+	tf = alloc_trace_fprobe(group, event, symbol, tpoint, argc, is_return);
 	if (IS_ERR(tf)) {
 		ret = PTR_ERR(tf);
 		/* This must return -ENOMEM, else there is a bug */
@@ -1230,8 +1200,6 @@ static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev)
 		seq_putc(m, 't');
 	else
 		seq_putc(m, 'f');
-	if (trace_fprobe_is_return(tf) && tf->fp.nr_maxactive)
-		seq_printf(m, "%d", tf->fp.nr_maxactive);
 	seq_printf(m, ":%s/%s", trace_probe_group_name(&tf->tp),
 				trace_probe_name(&tf->tp));
 


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 16/19] selftests: ftrace: Remove obsolate maxactive syntax check
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (14 preceding siblings ...)
  2024-09-12 15:10 ` [PATCH v14 15/19] tracing/fprobe: Remove nr_maxactive from fprobe Masami Hiramatsu (Google)
@ 2024-09-12 15:11 ` Masami Hiramatsu (Google)
  2024-09-12 15:11 ` [PATCH v14 17/19] selftests/ftrace: Add a test case for repeating register/unregister fprobe Masami Hiramatsu (Google)
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:11 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Since the fprobe event does not support maxactive anymore, stop
testing the maxactive syntax error checking.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 .../ftrace/test.d/dynevent/fprobe_syntax_errors.tc |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc
index 61877d166451..c9425a34fae3 100644
--- a/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc
@@ -16,9 +16,7 @@ aarch64)
   REG=%r0 ;;
 esac
 
-check_error 'f^100 vfs_read'		# MAXACT_NO_KPROBE
-check_error 'f^1a111 vfs_read'		# BAD_MAXACT
-check_error 'f^100000 vfs_read'		# MAXACT_TOO_BIG
+check_error 'f^100 vfs_read'		# BAD_MAXACT
 
 check_error 'f ^non_exist_func'		# BAD_PROBE_ADDR (enoent)
 check_error 'f ^vfs_read+10'		# BAD_PROBE_ADDR


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 17/19] selftests/ftrace: Add a test case for repeating register/unregister fprobe
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (15 preceding siblings ...)
  2024-09-12 15:11 ` [PATCH v14 16/19] selftests: ftrace: Remove obsolate maxactive syntax check Masami Hiramatsu (Google)
@ 2024-09-12 15:11 ` Masami Hiramatsu (Google)
  2024-09-12 15:11 ` [PATCH v14 18/19] Documentation: probes: Update fprobe on function-graph tracer Masami Hiramatsu (Google)
  2024-09-12 15:11 ` [PATCH v14 19/19] fgraph: Skip recording calltime/rettime if it is not nneeded Masami Hiramatsu (Google)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:11 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

This test case repeats define and undefine the fprobe dynamic event to
ensure that the fprobe does not cause any issue with such operations.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 .../test.d/dynevent/add_remove_fprobe_repeat.tc    |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_repeat.tc

diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_repeat.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_repeat.tc
new file mode 100644
index 000000000000..b4ad09237e2a
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_repeat.tc
@@ -0,0 +1,19 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: Generic dynamic event - Repeating add/remove fprobe events
+# requires: dynamic_events "f[:[<group>/][<event>]] <func-name>[%return] [<args>]":README
+
+echo 0 > events/enable
+echo > dynamic_events
+
+PLACE=$FUNCTION_FORK
+REPEAT_TIMES=64
+
+for i in `seq 1 $REPEAT_TIMES`; do
+  echo "f:myevent $PLACE" >> dynamic_events
+  grep -q myevent dynamic_events
+  test -d events/fprobes/myevent
+  echo > dynamic_events
+done
+
+clear_trace


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 18/19] Documentation: probes: Update fprobe on function-graph tracer
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (16 preceding siblings ...)
  2024-09-12 15:11 ` [PATCH v14 17/19] selftests/ftrace: Add a test case for repeating register/unregister fprobe Masami Hiramatsu (Google)
@ 2024-09-12 15:11 ` Masami Hiramatsu (Google)
  2024-09-12 15:11 ` [PATCH v14 19/19] fgraph: Skip recording calltime/rettime if it is not nneeded Masami Hiramatsu (Google)
  18 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:11 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Update fprobe documentation for the new fprobe on function-graph
tracer. This includes some bahvior changes and pt_regs to
ftrace_regs interface change.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 Changes in v2:
  - Update @fregs parameter explanation.
---
 Documentation/trace/fprobe.rst |   42 ++++++++++++++++++++++++++--------------
 1 file changed, 27 insertions(+), 15 deletions(-)

diff --git a/Documentation/trace/fprobe.rst b/Documentation/trace/fprobe.rst
index 196f52386aaa..f58bdc64504f 100644
--- a/Documentation/trace/fprobe.rst
+++ b/Documentation/trace/fprobe.rst
@@ -9,9 +9,10 @@ Fprobe - Function entry/exit probe
 Introduction
 ============
 
-Fprobe is a function entry/exit probe mechanism based on ftrace.
-Instead of using ftrace full feature, if you only want to attach callbacks
-on function entry and exit, similar to the kprobes and kretprobes, you can
+Fprobe is a function entry/exit probe mechanism based on the function-graph
+tracer.
+Instead of tracing all functions, if you want to attach callbacks on specific
+function entry and exit, similar to the kprobes and kretprobes, you can
 use fprobe. Compared with kprobes and kretprobes, fprobe gives faster
 instrumentation for multiple functions with single handler. This document
 describes how to use fprobe.
@@ -91,12 +92,14 @@ The prototype of the entry/exit callback function are as follows:
 
 .. code-block:: c
 
- int entry_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct pt_regs *regs, void *entry_data);
+ int entry_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct ftrace_regs *fregs, void *entry_data);
 
- void exit_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct pt_regs *regs, void *entry_data);
+ void exit_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct ftrace_regs *fregs, void *entry_data);
 
-Note that the @entry_ip is saved at function entry and passed to exit handler.
-If the entry callback function returns !0, the corresponding exit callback will be cancelled.
+Note that the @entry_ip is saved at function entry and passed to exit
+handler.
+If the entry callback function returns !0, the corresponding exit callback
+will be cancelled.
 
 @fp
         This is the address of `fprobe` data structure related to this handler.
@@ -112,12 +115,10 @@ If the entry callback function returns !0, the corresponding exit callback will
         This is the return address that the traced function will return to,
         somewhere in the caller. This can be used at both entry and exit.
 
-@regs
-        This is the `pt_regs` data structure at the entry and exit. Note that
-        the instruction pointer of @regs may be different from the @entry_ip
-        in the entry_handler. If you need traced instruction pointer, you need
-        to use @entry_ip. On the other hand, in the exit_handler, the instruction
-        pointer of @regs is set to the current return address.
+@fregs
+        This is the `ftrace_regs` data structure at the entry and exit. This
+        includes the function parameters, or the return values. So user can
+        access thos values via appropriate `ftrace_regs_*` APIs.
 
 @entry_data
         This is a local storage to share the data between entry and exit handlers.
@@ -125,6 +126,17 @@ If the entry callback function returns !0, the corresponding exit callback will
         and `entry_data_size` field when registering the fprobe, the storage is
         allocated and passed to both `entry_handler` and `exit_handler`.
 
+Entry data size and exit handlers on the same function
+======================================================
+
+Since the entry data is passed via per-task stack and it is has limited size,
+the entry data size per probe is limited to `15 * sizeof(long)`. You also need
+to take care that the different fprobes are probing on the same function, this
+limit becomes smaller. The entry data size is aligned to `sizeof(long)` and
+each fprobe which has exit handler uses a `sizeof(long)` space on the stack,
+you should keep the number of fprobes on the same function as small as
+possible.
+
 Share the callbacks with kprobes
 ================================
 
@@ -165,8 +177,8 @@ This counter counts up when;
  - fprobe fails to take ftrace_recursion lock. This usually means that a function
    which is traced by other ftrace users is called from the entry_handler.
 
- - fprobe fails to setup the function exit because of the shortage of rethook
-   (the shadow stack for hooking the function return.)
+ - fprobe fails to setup the function exit because of failing to allocate the
+   data buffer from the per-task shadow stack.
 
 The `fprobe::nmissed` field counts up in both cases. Therefore, the former
 skips both of entry and exit callback and the latter skips the exit


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v14 19/19] fgraph: Skip recording calltime/rettime if it is not nneeded
  2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
                   ` (17 preceding siblings ...)
  2024-09-12 15:11 ` [PATCH v14 18/19] Documentation: probes: Update fprobe on function-graph tracer Masami Hiramatsu (Google)
@ 2024-09-12 15:11 ` Masami Hiramatsu (Google)
  2024-09-14 21:53   ` Steven Rostedt
  18 siblings, 1 reply; 53+ messages in thread
From: Masami Hiramatsu (Google) @ 2024-09-12 15:11 UTC (permalink / raw)
  To: Alexei Starovoitov, Steven Rostedt, Florent Revest
  Cc: linux-trace-kernel, LKML, Martin KaFai Lau, bpf, Sven Schnelle,
	Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland, Peter Zijlstra,
	Thomas Gleixner, Guo Ren, linux-arch

From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Skip recording calltime and rettime if the fgraph_ops does not need it.
This is a kind of performance optimization for fprobe. Since the fprobe
user does not use these entries, recording timestamp in fgraph is just
a overhead (e.g. eBPF, ftrace). So introduce the skip_timestamp flag,
and all fgraph_ops sets this flag, skip recording calltime and rettime.

Here is the performance results measured by
 tools/testing/selftests/bpf/benchs/run_bench_trigger.sh

Without this:
kprobe-multi   :    5.700 ± 0.065M/s
kretprobe-multi:    4.239 ± 0.006M/s

With skip-timestamp:
kprobe-multi   :    6.265 ± 0.033M/s	+9.91%
kretprobe-multi:    4.758 ± 0.009M/s	+12.24%

Suggested-by: Jiri Olsa <olsajiri@gmail.com>
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 Changes in v11:
  - Simplify it to be symmetric on push and pop. (Thus the timestamp
    getting place is a bit shifted.)
 Changes in v10:
  - Add likely() to skipping timestamp.
 Changes in v9:
  - Newly added.
---
 include/linux/ftrace.h |    2 ++
 kernel/trace/fgraph.c  |   36 +++++++++++++++++++++++++++++++++---
 kernel/trace/fprobe.c  |    1 +
 3 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 63fb91088a23..bab6fabb3fa1 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -1160,6 +1160,8 @@ struct fgraph_ops {
 	void				*private;
 	trace_func_graph_ent_t		saved_func;
 	int				idx;
+	/* If skip_timestamp is true, this does not record timestamps. */
+	bool				skip_timestamp;
 };
 
 void *fgraph_reserve_data(int idx, int size_bytes);
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index 6a3e2db16aa4..c116a92839ae 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -174,6 +174,7 @@ int ftrace_graph_active;
 
 static struct fgraph_ops *fgraph_array[FGRAPH_ARRAY_SIZE];
 static unsigned long fgraph_array_bitmask;
+static bool fgraph_skip_timestamp;
 
 /* LRU index table for fgraph_array */
 static int fgraph_lru_table[FGRAPH_ARRAY_SIZE];
@@ -557,7 +558,11 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,
 		return -EBUSY;
 	}
 
-	calltime = trace_clock_local();
+	/* This is not really 'likely' but for keeping the least path to be faster. */
+	if (likely(fgraph_skip_timestamp))
+		calltime = 0LL;
+	else
+		calltime = trace_clock_local();
 
 	offset = READ_ONCE(current->curr_ret_stack);
 	ret_stack = RET_STACK(current, offset);
@@ -728,6 +733,12 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret,
 	*ret = ret_stack->ret;
 	trace->func = ret_stack->func;
 	trace->calltime = ret_stack->calltime;
+	/* This is not really 'likely' but for keeping the least path to be faster. */
+	if (likely(!trace->calltime))
+		trace->rettime = 0LL;
+	else
+		trace->rettime = trace_clock_local();
+
 	trace->overrun = atomic_read(&current->trace_overrun);
 	trace->depth = current->curr_ret_depth;
 	/*
@@ -788,7 +799,6 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
 		return (unsigned long)panic;
 	}
 
-	trace.rettime = trace_clock_local();
 	if (fregs)
 		ftrace_regs_set_instruction_pointer(fregs, ret);
 
@@ -1248,6 +1258,24 @@ static void ftrace_graph_disable_direct(bool disable_branch)
 	fgraph_direct_gops = &fgraph_stub;
 }
 
+static void update_fgraph_skip_timestamp(void)
+{
+	int i;
+
+	for (i = 0; i < FGRAPH_ARRAY_SIZE; i++) {
+		struct fgraph_ops *gops = fgraph_array[i];
+
+		if (gops == &fgraph_stub)
+			continue;
+
+		if (!gops->skip_timestamp) {
+			fgraph_skip_timestamp = false;
+			return;
+		}
+	}
+	fgraph_skip_timestamp = true;
+}
+
 int register_ftrace_graph(struct fgraph_ops *gops)
 {
 	int command = 0;
@@ -1271,6 +1299,7 @@ int register_ftrace_graph(struct fgraph_ops *gops)
 	gops->idx = i;
 
 	ftrace_graph_active++;
+	update_fgraph_skip_timestamp();
 
 	if (ftrace_graph_active == 2)
 		ftrace_graph_disable_direct(true);
@@ -1303,6 +1332,7 @@ int register_ftrace_graph(struct fgraph_ops *gops)
 		ftrace_graph_active--;
 		gops->saved_func = NULL;
 		fgraph_lru_release_index(i);
+		update_fgraph_skip_timestamp();
 	}
 out:
 	mutex_unlock(&ftrace_lock);
@@ -1326,8 +1356,8 @@ void unregister_ftrace_graph(struct fgraph_ops *gops)
 		goto out;
 
 	fgraph_array[gops->idx] = &fgraph_stub;
-
 	ftrace_graph_active--;
+	update_fgraph_skip_timestamp();
 
 	if (!ftrace_graph_active)
 		command = FTRACE_STOP_FUNC_RET;
diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
index 5a0b4ef52fa7..b108d26d7ee5 100644
--- a/kernel/trace/fprobe.c
+++ b/kernel/trace/fprobe.c
@@ -345,6 +345,7 @@ NOKPROBE_SYMBOL(fprobe_return);
 static struct fgraph_ops fprobe_graph_ops = {
 	.entryfunc	= fprobe_entry,
 	.retfunc	= fprobe_return,
+	.skip_timestamp = true,
 };
 static int fprobe_graph_active;
 


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 19/19] fgraph: Skip recording calltime/rettime if it is not nneeded
  2024-09-12 15:11 ` [PATCH v14 19/19] fgraph: Skip recording calltime/rettime if it is not nneeded Masami Hiramatsu (Google)
@ 2024-09-14 21:53   ` Steven Rostedt
  0 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-14 21:53 UTC (permalink / raw)
  To: Masami Hiramatsu (Google)
  Cc: Alexei Starovoitov, Florent Revest, linux-trace-kernel, LKML,
	Martin KaFai Lau, bpf, Sven Schnelle, Alexei Starovoitov,
	Jiri Olsa, Arnaldo Carvalho de Melo, Daniel Borkmann,
	Alan Maguire, Mark Rutland, Peter Zijlstra, Thomas Gleixner,
	Guo Ren, linux-arch

On Fri, 13 Sep 2024 00:11:38 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Skip recording calltime and rettime if the fgraph_ops does not need it.
> This is a kind of performance optimization for fprobe. Since the fprobe
> user does not use these entries, recording timestamp in fgraph is just
> a overhead (e.g. eBPF, ftrace). So introduce the skip_timestamp flag,
> and all fgraph_ops sets this flag, skip recording calltime and rettime.
> 
> Here is the performance results measured by
>  tools/testing/selftests/bpf/benchs/run_bench_trigger.sh
> 
> Without this:
> kprobe-multi   :    5.700 ± 0.065M/s
> kretprobe-multi:    4.239 ± 0.006M/s
> 
> With skip-timestamp:
> kprobe-multi   :    6.265 ± 0.033M/s	+9.91%
> kretprobe-multi:    4.758 ± 0.009M/s	+12.24%
> 
> Suggested-by: Jiri Olsa <olsajiri@gmail.com>
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Can you try this series instead:

 https://lore.kernel.org/all/20240914214805.779822616@goodmis.org/

I rather get rid of the calltime completely from the generic logic, and
that series does just that.

That series only replaces this patch and can be applied before this
series (or after).

-- Steve

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
  2024-09-12 15:08 ` [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc Masami Hiramatsu (Google)
@ 2024-09-15  8:46   ` Steven Rostedt
  2024-09-17  8:26     ` Will Deacon
  2024-09-15  8:50   ` Steven Rostedt
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  8:46 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon, linux-arm-kernel
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland


Can I get an Acked-by from the AARCH64 maintainers for this patch?

Thanks!

-- Steve


On Fri, 13 Sep 2024 00:08:40 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Pass ftrace_regs to the fgraph_ops::entryfunc(). If ftrace_regs is not
> available, it passes a NULL instead. User callback function can access
> some registers (including return address) via this ftrace_regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v11:
>   - Update for the latest for-next branch.
>  Changes in v8:
>   - Just pass ftrace_regs to the handler instead of adding a new
>     entryregfunc.
>   - Update riscv ftrace_graph_func().
>  Changes in v3:
>   - Update for new multiple fgraph.
> ---
>  arch/arm64/kernel/ftrace.c               |   20 +++++++++++-
>  arch/loongarch/kernel/ftrace_dyn.c       |   10 +++++-
>  arch/powerpc/kernel/trace/ftrace.c       |    2 +
>  arch/powerpc/kernel/trace/ftrace_64_pg.c |   10 ++++--
>  arch/riscv/kernel/ftrace.c               |   17 ++++++++++
>  arch/x86/kernel/ftrace.c                 |   50 +++++++++++++++++++++---------
>  include/linux/ftrace.h                   |   18 ++++++++---
>  kernel/trace/fgraph.c                    |   23 ++++++++------
>  kernel/trace/ftrace.c                    |    3 +-
>  kernel/trace/trace.h                     |    3 +-
>  kernel/trace/trace_functions_graph.c     |    3 +-
>  kernel/trace/trace_irqsoff.c             |    3 +-
>  kernel/trace/trace_sched_wakeup.c        |    3 +-
>  kernel/trace/trace_selftest.c            |    8 +++--
>  14 files changed, 128 insertions(+), 45 deletions(-)
> 
> diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
> index a650f5e11fc5..bc647b725e6a 100644
> --- a/arch/arm64/kernel/ftrace.c
> +++ b/arch/arm64/kernel/ftrace.c
> @@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	prepare_ftrace_return(ip, &fregs->lr, fregs->fp);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long frame_pointer = fregs->fp;
> +	unsigned long *parent = &fregs->lr;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * Note:
> +	 * No protection against faulting at *parent, which may be seen
> +	 * on other archs. It's unlikely on AArch64.
> +	 */
> +	old = *parent;
> +
> +	if (!function_graph_enter_regs(old, ip, frame_pointer,
> +				       (void *)frame_pointer, fregs)) {
> +		*parent = return_hooker;
> +	}
>  }
>  #else
>  /*
> diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
> index bff058317062..966e0f7f7aca 100644
> --- a/arch/loongarch/kernel/ftrace_dyn.c
> +++ b/arch/loongarch/kernel/ftrace_dyn.c
> @@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  {
>  	struct pt_regs *regs = &fregs->regs;
>  	unsigned long *parent = (unsigned long *)&regs->regs[1];
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	old = *parent;
>  
> -	prepare_ftrace_return(ip, (unsigned long *)parent);
> +	if (!function_graph_enter_regs(old, ip, 0, parent, fregs))
> +		*parent = return_hooker;
>  }
>  #else
>  static int ftrace_modify_graph_caller(bool enable)
> diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c
> index d8d6b4fd9a14..a1a0e0b57662 100644
> --- a/arch/powerpc/kernel/trace/ftrace.c
> +++ b/arch/powerpc/kernel/trace/ftrace.c
> @@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  	if (bit < 0)
>  		goto out;
>  
> -	if (!function_graph_enter(parent_ip, ip, 0, (unsigned long *)sp))
> +	if (!function_graph_enter_regs(parent_ip, ip, 0, (unsigned long *)sp, fregs))
>  		parent_ip = ppc_function_entry(return_to_handler);
>  
>  	ftrace_test_recursion_unlock(bit);
> diff --git a/arch/powerpc/kernel/trace/ftrace_64_pg.c b/arch/powerpc/kernel/trace/ftrace_64_pg.c
> index 12fab1803bcf..4ae9eeb1c8f1 100644
> --- a/arch/powerpc/kernel/trace/ftrace_64_pg.c
> +++ b/arch/powerpc/kernel/trace/ftrace_64_pg.c
> @@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)
>   * in current thread info. Return the address we want to divert to.
>   */
>  static unsigned long
> -__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp)
> +__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp,
> +			struct ftrace_regs *fregs)
>  {
>  	unsigned long return_hooker;
>  	int bit;
> @@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
>  
>  	return_hooker = ppc_function_entry(return_to_handler);
>  
> -	if (!function_graph_enter(parent, ip, 0, (unsigned long *)sp))
> +	if (!function_graph_enter_regs(parent, ip, 0, (unsigned long *)sp, fregs))
>  		parent = return_hooker;
>  
>  	ftrace_test_recursion_unlock(bit);
> @@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip, fregs->regs.gpr[1]);
> +	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip,
> +						   fregs->regs.gpr[1], fregs);
>  }
>  #else
>  unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip,
>  				    unsigned long sp)
>  {
> -	return __prepare_ftrace_return(parent, ip, sp);
> +	return __prepare_ftrace_return(parent, ip, sp, NULL);
>  }
>  #endif
>  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
> index 4b95c574fd04..f5c3b5a752b0 100644
> --- a/arch/riscv/kernel/ftrace.c
> +++ b/arch/riscv/kernel/ftrace.c
> @@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	prepare_ftrace_return(&fregs->ra, ip, fregs->s0);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long frame_pointer = fregs->s0;
> +	unsigned long *parent = &fregs->ra;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * We don't suffer access faults, so no extra fault-recovery assembly
> +	 * is needed here.
> +	 */
> +	old = *parent;
> +
> +	if (!function_graph_enter_regs(old, ip, frame_pointer, parent, fregs))
> +		*parent = return_hooker;
>  }
>  #else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */
>  extern void ftrace_graph_call(void);
> diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
> index 8da0e66ca22d..decf4c11dcf3 100644
> --- a/arch/x86/kernel/ftrace.c
> +++ b/arch/x86/kernel/ftrace.c
> @@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void)
>  }
>  #endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
>  
> -/*
> - * Hook the return address and push it in the stack of return addrs
> - * in current thread info.
> - */
> -void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
> -			   unsigned long frame_pointer)
> +static inline bool skip_ftrace_return(void)
>  {
> -	unsigned long return_hooker = (unsigned long)&return_to_handler;
> -	int bit;
> -
>  	/*
>  	 * When resuming from suspend-to-ram, this function can be indirectly
>  	 * called from early CPU startup code while the CPU is in real mode,
> @@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
>  	 * This check isn't as accurate as virt_addr_valid(), but it should be
>  	 * good enough for this purpose, and it's fast.
>  	 */
> -	if (unlikely((long)__builtin_frame_address(0) >= 0))
> -		return;
> +	if ((long)__builtin_frame_address(0) >= 0)
> +		return true;
>  
> -	if (unlikely(ftrace_graph_is_dead()))
> -		return;
> +	if (ftrace_graph_is_dead())
> +		return true;
> +
> +	if (atomic_read(&current->tracing_graph_pause))
> +		return true;
> +	return false;
> +}
> +
> +/*
> + * Hook the return address and push it in the stack of return addrs
> + * in current thread info.
> + */
> +void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
> +			   unsigned long frame_pointer)
> +{
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	int bit;
>  
> -	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +	if (unlikely(skip_ftrace_return()))
>  		return;
>  
>  	bit = ftrace_test_recursion_trylock(ip, *parent);
> @@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  {
>  	struct pt_regs *regs = &fregs->regs;
>  	unsigned long *stack = (unsigned long *)kernel_stack_pointer(regs);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long *parent = (unsigned long *)stack;
> +	int bit;
>  
> -	prepare_ftrace_return(ip, (unsigned long *)stack, 0);
> +	if (unlikely(skip_ftrace_return()))
> +		return;
> +
> +	bit = ftrace_test_recursion_trylock(ip, *parent);
> +	if (bit < 0)
> +		return;
> +
> +	if (!function_graph_enter_regs(*parent, ip, 0, parent, fregs))
> +		*parent = return_hooker;
> +
> +	ftrace_test_recursion_unlock(bit);
>  }
>  #endif
>  
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index f84fb9635fb0..1fe49a28de2d 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -1063,9 +1063,12 @@ struct fgraph_ops;
>  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
>  				       struct fgraph_ops *); /* return */
>  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
> -				      struct fgraph_ops *); /* entry */
> +				      struct fgraph_ops *,
> +				      struct ftrace_regs *); /* entry */
>  
> -extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
> +extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> +				   struct fgraph_ops *gops,
> +				   struct ftrace_regs *fregs);
>  bool ftrace_pids_enabled(struct ftrace_ops *ops);
>  
>  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
> @@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {
>  extern void return_to_handler(void);
>  
>  extern int
> -function_graph_enter(unsigned long ret, unsigned long func,
> -		     unsigned long frame_pointer, unsigned long *retp);
> +function_graph_enter_regs(unsigned long ret, unsigned long func,
> +			  unsigned long frame_pointer, unsigned long *retp,
> +			  struct ftrace_regs *fregs);
> +
> +static inline int function_graph_enter(unsigned long ret, unsigned long func,
> +				       unsigned long fp, unsigned long *retp)
> +{
> +	return function_graph_enter_regs(ret, func, fp, retp, NULL);
> +}
>  
>  struct ftrace_ret_stack *
>  ftrace_graph_get_ret_stack(struct task_struct *task, int skip);
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index d7d4fb403f6f..0322c5723748 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset)
>  }
>  
>  /* ftrace_graph_entry set to this to tell some archs to run function graph */
> -static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops)
> +static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
> +		     struct ftrace_regs *fregs)
>  {
>  	return 0;
>  }
> @@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)
>  #endif
>  
>  int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> -			    struct fgraph_ops *gops)
> +			    struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	return 0;
>  }
> @@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,
>  #endif
>  
>  /* If the caller does not use ftrace, call this function. */
> -int function_graph_enter(unsigned long ret, unsigned long func,
> -			 unsigned long frame_pointer, unsigned long *retp)
> +int function_graph_enter_regs(unsigned long ret, unsigned long func,
> +			      unsigned long frame_pointer, unsigned long *retp,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct ftrace_graph_ent trace;
>  	unsigned long bitmap = 0;
> @@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
>  	if (static_branch_likely(&fgraph_do_direct)) {
>  		int save_curr_ret_stack = current->curr_ret_stack;
>  
> -		if (static_call(fgraph_func)(&trace, fgraph_direct_gops))
> +		if (static_call(fgraph_func)(&trace, fgraph_direct_gops, fregs))
>  			bitmap |= BIT(fgraph_direct_gops->idx);
>  		else
>  			/* Clear out any saved storage */
> @@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
>  
>  			save_curr_ret_stack = current->curr_ret_stack;
>  			if (ftrace_ops_test(&gops->ops, func, NULL) &&
> -			    gops->entryfunc(&trace, gops))
> +			    gops->entryfunc(&trace, gops, fregs))
>  				bitmap |= BIT(i);
>  			else
>  				/* Clear out any saved storage */
> @@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
>  
>  static struct ftrace_ops graph_ops = {
>  	.func			= ftrace_graph_func,
> -	.flags			= FTRACE_OPS_GRAPH_STUB,
> +	.flags			= FTRACE_OPS_GRAPH_STUB | FTRACE_OPS_FL_SAVE_ARGS,
>  #ifdef FTRACE_GRAPH_TRAMP_ADDR
>  	.trampoline		= FTRACE_GRAPH_TRAMP_ADDR,
>  	/* trampoline_size is only needed for dynamically allocated tramps */
> @@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {
>  void fgraph_init_ops(struct ftrace_ops *dst_ops,
>  		     struct ftrace_ops *src_ops)
>  {
> -	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB;
> +	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB |
> +			 FTRACE_OPS_FL_SAVE_ARGS;
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE
>  	if (src_ops) {
> @@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE
>  static int fgraph_pid_func(struct ftrace_graph_ent *trace,
> -			   struct fgraph_ops *gops)
> +			   struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = gops->ops.private;
>  	int pid;
> @@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,
>  			return 0;
>  	}
>  
> -	return gops->saved_func(trace, gops);
> +	return gops->saved_func(trace, gops, fregs);
>  }
>  
>  void fgraph_update_pid_func(void)
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 4c28dd177ca6..775040a9f541 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable)
>  }
>  
>  static int profile_graph_entry(struct ftrace_graph_ent *trace,
> -			       struct fgraph_ops *gops)
> +			       struct fgraph_ops *gops,
> +			       struct ftrace_regs *fregs)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index bd3e3069300e..28d8ad5e31e6 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);
>  void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
>  
>  void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> -int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
> +int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
> +		      struct ftrace_regs *fregs);
>  
>  void tracing_start_cmdline_record(void);
>  void tracing_stop_cmdline_record(void);
> diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> index 13d0387ac6a6..b9785fc919c9 100644
> --- a/kernel/trace/trace_functions_graph.c
> +++ b/kernel/trace/trace_functions_graph.c
> @@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void)
>  }
>  
>  int trace_graph_entry(struct ftrace_graph_ent *trace,
> -		      struct fgraph_ops *gops)
> +		      struct fgraph_ops *gops,
> +		      struct ftrace_regs *fregs)
>  {
>  	unsigned long *task_var = fgraph_get_task_var(gops);
>  	struct trace_array *tr = gops->private;
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index fce064e20570..ad739d76fc86 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set)
>  }
>  
>  static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
> -			       struct fgraph_ops *gops)
> +			       struct fgraph_ops *gops,
> +			       struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = irqsoff_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
> index 130ca7e7787e..23360a2700de 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set)
>  }
>  
>  static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
> -			      struct fgraph_ops *gops)
> +			      struct fgraph_ops *gops,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = wakeup_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
> index c4ad7cd7e778..89067f02094a 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -773,7 +773,8 @@ struct fgraph_fixture {
>  };
>  
>  static __init int store_entry(struct ftrace_graph_ent *trace,
> -			      struct fgraph_ops *gops)
> +			      struct fgraph_ops *gops,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
>  	const char *type = fixture->store_type_name;
> @@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;
>  
>  /* Wrap the real function entry probe to avoid possible hanging */
>  static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
> -				      struct fgraph_ops *gops)
> +				      struct fgraph_ops *gops,
> +				      struct ftrace_regs *fregs)
>  {
>  	/* This is harmlessly racy, we want to approximately detect a hang */
>  	if (unlikely(++graph_hang_thresh > GRAPH_MAX_FUNC_TEST)) {
> @@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
>  		return 0;
>  	}
>  
> -	return trace_graph_entry(trace, gops);
> +	return trace_graph_entry(trace, gops, fregs);
>  }
>  
>  static struct fgraph_ops fgraph_ops __initdata  = {


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
  2024-09-12 15:09 ` [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc Masami Hiramatsu (Google)
@ 2024-09-15  8:49   ` Steven Rostedt
  2024-09-17 10:08     ` Will Deacon
  2024-09-15  8:51   ` Steven Rostedt
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  8:49 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon, linux-arm-kernel
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland


Can I get an Acked-by from the AARCH64 maintainers for this patch?

Thanks!

-- Steve

[ Note this is modifies the return side ]

On Fri, 13 Sep 2024 00:09:02 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Pass ftrace_regs to the fgraph_ops::retfunc(). If ftrace_regs is not
> available, it passes a NULL instead. User callback function can access
> some registers (including return address) via this ftrace_regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v8:
>   - Pass ftrace_regs to retfunc, instead of adding retregfunc.
>  Changes in v6:
>   - update to use ftrace_regs_get_return_value() because of reordering
>     patches.
>  Changes in v3:
>   - Update for new multiple fgraph.
>   - Save the return address to instruction pointer in ftrace_regs.
> ---
>  include/linux/ftrace.h               |    3 ++-
>  kernel/trace/fgraph.c                |   16 +++++++++++-----
>  kernel/trace/ftrace.c                |    3 ++-
>  kernel/trace/trace.h                 |    3 ++-
>  kernel/trace/trace_functions_graph.c |    7 ++++---
>  kernel/trace/trace_irqsoff.c         |    3 ++-
>  kernel/trace/trace_sched_wakeup.c    |    3 ++-
>  kernel/trace/trace_selftest.c        |    3 ++-
>  8 files changed, 27 insertions(+), 14 deletions(-)
> 
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 13987cd63553..e7c41d9988e1 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -1069,7 +1069,8 @@ struct fgraph_ops;
>  
>  /* Type of the callback handlers for tracing function graph*/
>  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
> -				       struct fgraph_ops *); /* return */
> +				       struct fgraph_ops *,
> +				       struct ftrace_regs *); /* return */
>  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
>  				      struct fgraph_ops *,
>  				      struct ftrace_regs *); /* entry */
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index 30bebe43607d..6a3e2db16aa4 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
>  }
>  
>  /* ftrace_graph_return set to this to tell some archs to run function graph */
> -static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops)
> +static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops,
> +		       struct ftrace_regs *fregs)
>  {
>  }
>  
> @@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
>  }
>  
>  static void ftrace_graph_ret_stub(struct ftrace_graph_ret *trace,
> -				  struct fgraph_ops *gops)
> +				  struct fgraph_ops *gops,
> +				  struct ftrace_regs *fregs)
>  {
>  }
>  
> @@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  	}
>  
>  	trace.rettime = trace_clock_local();
> +	if (fregs)
> +		ftrace_regs_set_instruction_pointer(fregs, ret);
> +
>  #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
>  	trace.retval = ftrace_regs_get_return_value(fregs);
>  #endif
> @@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  #ifdef CONFIG_HAVE_STATIC_CALL
>  	if (static_branch_likely(&fgraph_do_direct)) {
>  		if (test_bit(fgraph_direct_gops->idx, &bitmap))
> -			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops);
> +			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops, fregs);
>  	} else
>  #endif
>  	{
> @@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  			if (gops == &fgraph_stub)
>  				continue;
>  
> -			gops->retfunc(&trace, gops);
> +			gops->retfunc(&trace, gops, fregs);
>  		}
>  	}
>  
> @@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)
>   * Simply points to ftrace_stub, but with the proper protocol.
>   * Defined by the linker script in linux/vmlinux.lds.h
>   */
> -void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> +void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
> +		       struct ftrace_regs *fregs);
>  
>  /* The callbacks that hook a function */
>  trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 775040a9f541..fd6c5a50c5e5 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void profile_graph_return(struct ftrace_graph_ret *trace,
> -				 struct fgraph_ops *gops)
> +				 struct fgraph_ops *gops,
> +				 struct ftrace_regs *fregs)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  	struct ftrace_profile_stat *stat;
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index 28d8ad5e31e6..f4a3f75bd916 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);
>  void trace_default_header(struct seq_file *m);
>  void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
>  
> -void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> +void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
> +			struct ftrace_regs *fregs);
>  int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
>  		      struct ftrace_regs *fregs);
>  
> diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> index b9785fc919c9..241407000109 100644
> --- a/kernel/trace/trace_functions_graph.c
> +++ b/kernel/trace/trace_functions_graph.c
> @@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr,
>  }
>  
>  void trace_graph_return(struct ftrace_graph_ret *trace,
> -			struct fgraph_ops *gops)
> +			struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	unsigned long *task_var = fgraph_get_task_var(gops);
>  	struct trace_array *tr = gops->private;
> @@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace,
>  }
>  
>  static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
> -				      struct fgraph_ops *gops)
> +				      struct fgraph_ops *gops,
> +				      struct ftrace_regs *fregs)
>  {
>  	ftrace_graph_addr_finish(gops, trace);
>  
> @@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
>  	    (trace->rettime - trace->calltime < tracing_thresh))
>  		return;
>  	else
> -		trace_graph_return(trace, gops);
> +		trace_graph_return(trace, gops, fregs);
>  }
>  
>  static struct fgraph_ops funcgraph_ops = {
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index ad739d76fc86..504de7a05498 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void irqsoff_graph_return(struct ftrace_graph_ret *trace,
> -				 struct fgraph_ops *gops)
> +				 struct fgraph_ops *gops,
> +				 struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = irqsoff_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
> index 23360a2700de..9ffbd9326898 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void wakeup_graph_return(struct ftrace_graph_ret *trace,
> -				struct fgraph_ops *gops)
> +				struct fgraph_ops *gops,
> +				struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = wakeup_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
> index 89067f02094a..1ebd0899238f 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static __init void store_return(struct ftrace_graph_ret *trace,
> -				struct fgraph_ops *gops)
> +				struct fgraph_ops *gops,
> +				struct ftrace_regs *fregs)
>  {
>  	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
>  	const char *type = fixture->store_type_name;


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
  2024-09-12 15:08 ` [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc Masami Hiramatsu (Google)
  2024-09-15  8:46   ` Steven Rostedt
@ 2024-09-15  8:50   ` Steven Rostedt
  2024-09-15  8:53   ` Steven Rostedt
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  8:50 UTC (permalink / raw)
  To: Huacai Chen, loongarch
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland


Can I get an Acked-by from the LOONGARCH maintainers for this patch?

Thanks!

-- Steve


On Fri, 13 Sep 2024 00:08:40 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Pass ftrace_regs to the fgraph_ops::entryfunc(). If ftrace_regs is not
> available, it passes a NULL instead. User callback function can access
> some registers (including return address) via this ftrace_regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v11:
>   - Update for the latest for-next branch.
>  Changes in v8:
>   - Just pass ftrace_regs to the handler instead of adding a new
>     entryregfunc.
>   - Update riscv ftrace_graph_func().
>  Changes in v3:
>   - Update for new multiple fgraph.
> ---
>  arch/arm64/kernel/ftrace.c               |   20 +++++++++++-
>  arch/loongarch/kernel/ftrace_dyn.c       |   10 +++++-
>  arch/powerpc/kernel/trace/ftrace.c       |    2 +
>  arch/powerpc/kernel/trace/ftrace_64_pg.c |   10 ++++--
>  arch/riscv/kernel/ftrace.c               |   17 ++++++++++
>  arch/x86/kernel/ftrace.c                 |   50 +++++++++++++++++++++---------
>  include/linux/ftrace.h                   |   18 ++++++++---
>  kernel/trace/fgraph.c                    |   23 ++++++++------
>  kernel/trace/ftrace.c                    |    3 +-
>  kernel/trace/trace.h                     |    3 +-
>  kernel/trace/trace_functions_graph.c     |    3 +-
>  kernel/trace/trace_irqsoff.c             |    3 +-
>  kernel/trace/trace_sched_wakeup.c        |    3 +-
>  kernel/trace/trace_selftest.c            |    8 +++--
>  14 files changed, 128 insertions(+), 45 deletions(-)
> 
> diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
> index a650f5e11fc5..bc647b725e6a 100644
> --- a/arch/arm64/kernel/ftrace.c
> +++ b/arch/arm64/kernel/ftrace.c
> @@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	prepare_ftrace_return(ip, &fregs->lr, fregs->fp);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long frame_pointer = fregs->fp;
> +	unsigned long *parent = &fregs->lr;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * Note:
> +	 * No protection against faulting at *parent, which may be seen
> +	 * on other archs. It's unlikely on AArch64.
> +	 */
> +	old = *parent;
> +
> +	if (!function_graph_enter_regs(old, ip, frame_pointer,
> +				       (void *)frame_pointer, fregs)) {
> +		*parent = return_hooker;
> +	}
>  }
>  #else
>  /*
> diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
> index bff058317062..966e0f7f7aca 100644
> --- a/arch/loongarch/kernel/ftrace_dyn.c
> +++ b/arch/loongarch/kernel/ftrace_dyn.c
> @@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  {
>  	struct pt_regs *regs = &fregs->regs;
>  	unsigned long *parent = (unsigned long *)&regs->regs[1];
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	old = *parent;
>  
> -	prepare_ftrace_return(ip, (unsigned long *)parent);
> +	if (!function_graph_enter_regs(old, ip, 0, parent, fregs))
> +		*parent = return_hooker;
>  }
>  #else
>  static int ftrace_modify_graph_caller(bool enable)
> diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c
> index d8d6b4fd9a14..a1a0e0b57662 100644
> --- a/arch/powerpc/kernel/trace/ftrace.c
> +++ b/arch/powerpc/kernel/trace/ftrace.c
> @@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  	if (bit < 0)
>  		goto out;
>  
> -	if (!function_graph_enter(parent_ip, ip, 0, (unsigned long *)sp))
> +	if (!function_graph_enter_regs(parent_ip, ip, 0, (unsigned long *)sp, fregs))
>  		parent_ip = ppc_function_entry(return_to_handler);
>  
>  	ftrace_test_recursion_unlock(bit);
> diff --git a/arch/powerpc/kernel/trace/ftrace_64_pg.c b/arch/powerpc/kernel/trace/ftrace_64_pg.c
> index 12fab1803bcf..4ae9eeb1c8f1 100644
> --- a/arch/powerpc/kernel/trace/ftrace_64_pg.c
> +++ b/arch/powerpc/kernel/trace/ftrace_64_pg.c
> @@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)
>   * in current thread info. Return the address we want to divert to.
>   */
>  static unsigned long
> -__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp)
> +__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp,
> +			struct ftrace_regs *fregs)
>  {
>  	unsigned long return_hooker;
>  	int bit;
> @@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
>  
>  	return_hooker = ppc_function_entry(return_to_handler);
>  
> -	if (!function_graph_enter(parent, ip, 0, (unsigned long *)sp))
> +	if (!function_graph_enter_regs(parent, ip, 0, (unsigned long *)sp, fregs))
>  		parent = return_hooker;
>  
>  	ftrace_test_recursion_unlock(bit);
> @@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip, fregs->regs.gpr[1]);
> +	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip,
> +						   fregs->regs.gpr[1], fregs);
>  }
>  #else
>  unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip,
>  				    unsigned long sp)
>  {
> -	return __prepare_ftrace_return(parent, ip, sp);
> +	return __prepare_ftrace_return(parent, ip, sp, NULL);
>  }
>  #endif
>  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
> index 4b95c574fd04..f5c3b5a752b0 100644
> --- a/arch/riscv/kernel/ftrace.c
> +++ b/arch/riscv/kernel/ftrace.c
> @@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	prepare_ftrace_return(&fregs->ra, ip, fregs->s0);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long frame_pointer = fregs->s0;
> +	unsigned long *parent = &fregs->ra;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * We don't suffer access faults, so no extra fault-recovery assembly
> +	 * is needed here.
> +	 */
> +	old = *parent;
> +
> +	if (!function_graph_enter_regs(old, ip, frame_pointer, parent, fregs))
> +		*parent = return_hooker;
>  }
>  #else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */
>  extern void ftrace_graph_call(void);
> diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
> index 8da0e66ca22d..decf4c11dcf3 100644
> --- a/arch/x86/kernel/ftrace.c
> +++ b/arch/x86/kernel/ftrace.c
> @@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void)
>  }
>  #endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
>  
> -/*
> - * Hook the return address and push it in the stack of return addrs
> - * in current thread info.
> - */
> -void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
> -			   unsigned long frame_pointer)
> +static inline bool skip_ftrace_return(void)
>  {
> -	unsigned long return_hooker = (unsigned long)&return_to_handler;
> -	int bit;
> -
>  	/*
>  	 * When resuming from suspend-to-ram, this function can be indirectly
>  	 * called from early CPU startup code while the CPU is in real mode,
> @@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
>  	 * This check isn't as accurate as virt_addr_valid(), but it should be
>  	 * good enough for this purpose, and it's fast.
>  	 */
> -	if (unlikely((long)__builtin_frame_address(0) >= 0))
> -		return;
> +	if ((long)__builtin_frame_address(0) >= 0)
> +		return true;
>  
> -	if (unlikely(ftrace_graph_is_dead()))
> -		return;
> +	if (ftrace_graph_is_dead())
> +		return true;
> +
> +	if (atomic_read(&current->tracing_graph_pause))
> +		return true;
> +	return false;
> +}
> +
> +/*
> + * Hook the return address and push it in the stack of return addrs
> + * in current thread info.
> + */
> +void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
> +			   unsigned long frame_pointer)
> +{
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	int bit;
>  
> -	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +	if (unlikely(skip_ftrace_return()))
>  		return;
>  
>  	bit = ftrace_test_recursion_trylock(ip, *parent);
> @@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  {
>  	struct pt_regs *regs = &fregs->regs;
>  	unsigned long *stack = (unsigned long *)kernel_stack_pointer(regs);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long *parent = (unsigned long *)stack;
> +	int bit;
>  
> -	prepare_ftrace_return(ip, (unsigned long *)stack, 0);
> +	if (unlikely(skip_ftrace_return()))
> +		return;
> +
> +	bit = ftrace_test_recursion_trylock(ip, *parent);
> +	if (bit < 0)
> +		return;
> +
> +	if (!function_graph_enter_regs(*parent, ip, 0, parent, fregs))
> +		*parent = return_hooker;
> +
> +	ftrace_test_recursion_unlock(bit);
>  }
>  #endif
>  
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index f84fb9635fb0..1fe49a28de2d 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -1063,9 +1063,12 @@ struct fgraph_ops;
>  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
>  				       struct fgraph_ops *); /* return */
>  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
> -				      struct fgraph_ops *); /* entry */
> +				      struct fgraph_ops *,
> +				      struct ftrace_regs *); /* entry */
>  
> -extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
> +extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> +				   struct fgraph_ops *gops,
> +				   struct ftrace_regs *fregs);
>  bool ftrace_pids_enabled(struct ftrace_ops *ops);
>  
>  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
> @@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {
>  extern void return_to_handler(void);
>  
>  extern int
> -function_graph_enter(unsigned long ret, unsigned long func,
> -		     unsigned long frame_pointer, unsigned long *retp);
> +function_graph_enter_regs(unsigned long ret, unsigned long func,
> +			  unsigned long frame_pointer, unsigned long *retp,
> +			  struct ftrace_regs *fregs);
> +
> +static inline int function_graph_enter(unsigned long ret, unsigned long func,
> +				       unsigned long fp, unsigned long *retp)
> +{
> +	return function_graph_enter_regs(ret, func, fp, retp, NULL);
> +}
>  
>  struct ftrace_ret_stack *
>  ftrace_graph_get_ret_stack(struct task_struct *task, int skip);
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index d7d4fb403f6f..0322c5723748 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset)
>  }
>  
>  /* ftrace_graph_entry set to this to tell some archs to run function graph */
> -static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops)
> +static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
> +		     struct ftrace_regs *fregs)
>  {
>  	return 0;
>  }
> @@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)
>  #endif
>  
>  int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> -			    struct fgraph_ops *gops)
> +			    struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	return 0;
>  }
> @@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,
>  #endif
>  
>  /* If the caller does not use ftrace, call this function. */
> -int function_graph_enter(unsigned long ret, unsigned long func,
> -			 unsigned long frame_pointer, unsigned long *retp)
> +int function_graph_enter_regs(unsigned long ret, unsigned long func,
> +			      unsigned long frame_pointer, unsigned long *retp,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct ftrace_graph_ent trace;
>  	unsigned long bitmap = 0;
> @@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
>  	if (static_branch_likely(&fgraph_do_direct)) {
>  		int save_curr_ret_stack = current->curr_ret_stack;
>  
> -		if (static_call(fgraph_func)(&trace, fgraph_direct_gops))
> +		if (static_call(fgraph_func)(&trace, fgraph_direct_gops, fregs))
>  			bitmap |= BIT(fgraph_direct_gops->idx);
>  		else
>  			/* Clear out any saved storage */
> @@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
>  
>  			save_curr_ret_stack = current->curr_ret_stack;
>  			if (ftrace_ops_test(&gops->ops, func, NULL) &&
> -			    gops->entryfunc(&trace, gops))
> +			    gops->entryfunc(&trace, gops, fregs))
>  				bitmap |= BIT(i);
>  			else
>  				/* Clear out any saved storage */
> @@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
>  
>  static struct ftrace_ops graph_ops = {
>  	.func			= ftrace_graph_func,
> -	.flags			= FTRACE_OPS_GRAPH_STUB,
> +	.flags			= FTRACE_OPS_GRAPH_STUB | FTRACE_OPS_FL_SAVE_ARGS,
>  #ifdef FTRACE_GRAPH_TRAMP_ADDR
>  	.trampoline		= FTRACE_GRAPH_TRAMP_ADDR,
>  	/* trampoline_size is only needed for dynamically allocated tramps */
> @@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {
>  void fgraph_init_ops(struct ftrace_ops *dst_ops,
>  		     struct ftrace_ops *src_ops)
>  {
> -	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB;
> +	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB |
> +			 FTRACE_OPS_FL_SAVE_ARGS;
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE
>  	if (src_ops) {
> @@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE
>  static int fgraph_pid_func(struct ftrace_graph_ent *trace,
> -			   struct fgraph_ops *gops)
> +			   struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = gops->ops.private;
>  	int pid;
> @@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,
>  			return 0;
>  	}
>  
> -	return gops->saved_func(trace, gops);
> +	return gops->saved_func(trace, gops, fregs);
>  }
>  
>  void fgraph_update_pid_func(void)
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 4c28dd177ca6..775040a9f541 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable)
>  }
>  
>  static int profile_graph_entry(struct ftrace_graph_ent *trace,
> -			       struct fgraph_ops *gops)
> +			       struct fgraph_ops *gops,
> +			       struct ftrace_regs *fregs)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index bd3e3069300e..28d8ad5e31e6 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);
>  void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
>  
>  void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> -int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
> +int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
> +		      struct ftrace_regs *fregs);
>  
>  void tracing_start_cmdline_record(void);
>  void tracing_stop_cmdline_record(void);
> diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> index 13d0387ac6a6..b9785fc919c9 100644
> --- a/kernel/trace/trace_functions_graph.c
> +++ b/kernel/trace/trace_functions_graph.c
> @@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void)
>  }
>  
>  int trace_graph_entry(struct ftrace_graph_ent *trace,
> -		      struct fgraph_ops *gops)
> +		      struct fgraph_ops *gops,
> +		      struct ftrace_regs *fregs)
>  {
>  	unsigned long *task_var = fgraph_get_task_var(gops);
>  	struct trace_array *tr = gops->private;
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index fce064e20570..ad739d76fc86 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set)
>  }
>  
>  static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
> -			       struct fgraph_ops *gops)
> +			       struct fgraph_ops *gops,
> +			       struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = irqsoff_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
> index 130ca7e7787e..23360a2700de 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set)
>  }
>  
>  static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
> -			      struct fgraph_ops *gops)
> +			      struct fgraph_ops *gops,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = wakeup_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
> index c4ad7cd7e778..89067f02094a 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -773,7 +773,8 @@ struct fgraph_fixture {
>  };
>  
>  static __init int store_entry(struct ftrace_graph_ent *trace,
> -			      struct fgraph_ops *gops)
> +			      struct fgraph_ops *gops,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
>  	const char *type = fixture->store_type_name;
> @@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;
>  
>  /* Wrap the real function entry probe to avoid possible hanging */
>  static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
> -				      struct fgraph_ops *gops)
> +				      struct fgraph_ops *gops,
> +				      struct ftrace_regs *fregs)
>  {
>  	/* This is harmlessly racy, we want to approximately detect a hang */
>  	if (unlikely(++graph_hang_thresh > GRAPH_MAX_FUNC_TEST)) {
> @@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
>  		return 0;
>  	}
>  
> -	return trace_graph_entry(trace, gops);
> +	return trace_graph_entry(trace, gops, fregs);
>  }
>  
>  static struct fgraph_ops fgraph_ops __initdata  = {


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
  2024-09-12 15:09 ` [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc Masami Hiramatsu (Google)
  2024-09-15  8:49   ` Steven Rostedt
@ 2024-09-15  8:51   ` Steven Rostedt
  2024-09-15  8:54   ` Steven Rostedt
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  8:51 UTC (permalink / raw)
  To: Huacai Chen, loongarch
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland


Can I get an Acked-by from the LOONGARCH maintainers for this patch?

Thanks!

-- Steve

[ Note this is modifies the return side ]

On Fri, 13 Sep 2024 00:09:02 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Pass ftrace_regs to the fgraph_ops::retfunc(). If ftrace_regs is not
> available, it passes a NULL instead. User callback function can access
> some registers (including return address) via this ftrace_regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v8:
>   - Pass ftrace_regs to retfunc, instead of adding retregfunc.
>  Changes in v6:
>   - update to use ftrace_regs_get_return_value() because of reordering
>     patches.
>  Changes in v3:
>   - Update for new multiple fgraph.
>   - Save the return address to instruction pointer in ftrace_regs.
> ---
>  include/linux/ftrace.h               |    3 ++-
>  kernel/trace/fgraph.c                |   16 +++++++++++-----
>  kernel/trace/ftrace.c                |    3 ++-
>  kernel/trace/trace.h                 |    3 ++-
>  kernel/trace/trace_functions_graph.c |    7 ++++---
>  kernel/trace/trace_irqsoff.c         |    3 ++-
>  kernel/trace/trace_sched_wakeup.c    |    3 ++-
>  kernel/trace/trace_selftest.c        |    3 ++-
>  8 files changed, 27 insertions(+), 14 deletions(-)
> 
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 13987cd63553..e7c41d9988e1 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -1069,7 +1069,8 @@ struct fgraph_ops;
>  
>  /* Type of the callback handlers for tracing function graph*/
>  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
> -				       struct fgraph_ops *); /* return */
> +				       struct fgraph_ops *,
> +				       struct ftrace_regs *); /* return */
>  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
>  				      struct fgraph_ops *,
>  				      struct ftrace_regs *); /* entry */
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index 30bebe43607d..6a3e2db16aa4 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
>  }
>  
>  /* ftrace_graph_return set to this to tell some archs to run function graph */
> -static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops)
> +static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops,
> +		       struct ftrace_regs *fregs)
>  {
>  }
>  
> @@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
>  }
>  
>  static void ftrace_graph_ret_stub(struct ftrace_graph_ret *trace,
> -				  struct fgraph_ops *gops)
> +				  struct fgraph_ops *gops,
> +				  struct ftrace_regs *fregs)
>  {
>  }
>  
> @@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  	}
>  
>  	trace.rettime = trace_clock_local();
> +	if (fregs)
> +		ftrace_regs_set_instruction_pointer(fregs, ret);
> +
>  #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
>  	trace.retval = ftrace_regs_get_return_value(fregs);
>  #endif
> @@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  #ifdef CONFIG_HAVE_STATIC_CALL
>  	if (static_branch_likely(&fgraph_do_direct)) {
>  		if (test_bit(fgraph_direct_gops->idx, &bitmap))
> -			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops);
> +			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops, fregs);
>  	} else
>  #endif
>  	{
> @@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  			if (gops == &fgraph_stub)
>  				continue;
>  
> -			gops->retfunc(&trace, gops);
> +			gops->retfunc(&trace, gops, fregs);
>  		}
>  	}
>  
> @@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)
>   * Simply points to ftrace_stub, but with the proper protocol.
>   * Defined by the linker script in linux/vmlinux.lds.h
>   */
> -void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> +void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
> +		       struct ftrace_regs *fregs);
>  
>  /* The callbacks that hook a function */
>  trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 775040a9f541..fd6c5a50c5e5 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void profile_graph_return(struct ftrace_graph_ret *trace,
> -				 struct fgraph_ops *gops)
> +				 struct fgraph_ops *gops,
> +				 struct ftrace_regs *fregs)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  	struct ftrace_profile_stat *stat;
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index 28d8ad5e31e6..f4a3f75bd916 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);
>  void trace_default_header(struct seq_file *m);
>  void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
>  
> -void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> +void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
> +			struct ftrace_regs *fregs);
>  int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
>  		      struct ftrace_regs *fregs);
>  
> diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> index b9785fc919c9..241407000109 100644
> --- a/kernel/trace/trace_functions_graph.c
> +++ b/kernel/trace/trace_functions_graph.c
> @@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr,
>  }
>  
>  void trace_graph_return(struct ftrace_graph_ret *trace,
> -			struct fgraph_ops *gops)
> +			struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	unsigned long *task_var = fgraph_get_task_var(gops);
>  	struct trace_array *tr = gops->private;
> @@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace,
>  }
>  
>  static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
> -				      struct fgraph_ops *gops)
> +				      struct fgraph_ops *gops,
> +				      struct ftrace_regs *fregs)
>  {
>  	ftrace_graph_addr_finish(gops, trace);
>  
> @@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
>  	    (trace->rettime - trace->calltime < tracing_thresh))
>  		return;
>  	else
> -		trace_graph_return(trace, gops);
> +		trace_graph_return(trace, gops, fregs);
>  }
>  
>  static struct fgraph_ops funcgraph_ops = {
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index ad739d76fc86..504de7a05498 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void irqsoff_graph_return(struct ftrace_graph_ret *trace,
> -				 struct fgraph_ops *gops)
> +				 struct fgraph_ops *gops,
> +				 struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = irqsoff_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
> index 23360a2700de..9ffbd9326898 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void wakeup_graph_return(struct ftrace_graph_ret *trace,
> -				struct fgraph_ops *gops)
> +				struct fgraph_ops *gops,
> +				struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = wakeup_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
> index 89067f02094a..1ebd0899238f 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static __init void store_return(struct ftrace_graph_ret *trace,
> -				struct fgraph_ops *gops)
> +				struct fgraph_ops *gops,
> +				struct ftrace_regs *fregs)
>  {
>  	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
>  	const char *type = fixture->store_type_name;


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
  2024-09-12 15:08 ` [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc Masami Hiramatsu (Google)
  2024-09-15  8:46   ` Steven Rostedt
  2024-09-15  8:50   ` Steven Rostedt
@ 2024-09-15  8:53   ` Steven Rostedt
  2024-09-15  8:56   ` Steven Rostedt
  2024-09-15  8:58   ` Steven Rostedt
  4 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  8:53 UTC (permalink / raw)
  To: Michael Ellerman, linuxppc-dev
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland


Can I get an Acked-by from the POWERPC maintainers for this patch?

Thanks!

-- Steve


On Fri, 13 Sep 2024 00:08:40 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Pass ftrace_regs to the fgraph_ops::entryfunc(). If ftrace_regs is not
> available, it passes a NULL instead. User callback function can access
> some registers (including return address) via this ftrace_regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v11:
>   - Update for the latest for-next branch.
>  Changes in v8:
>   - Just pass ftrace_regs to the handler instead of adding a new
>     entryregfunc.
>   - Update riscv ftrace_graph_func().
>  Changes in v3:
>   - Update for new multiple fgraph.
> ---
>  arch/arm64/kernel/ftrace.c               |   20 +++++++++++-
>  arch/loongarch/kernel/ftrace_dyn.c       |   10 +++++-
>  arch/powerpc/kernel/trace/ftrace.c       |    2 +
>  arch/powerpc/kernel/trace/ftrace_64_pg.c |   10 ++++--
>  arch/riscv/kernel/ftrace.c               |   17 ++++++++++
>  arch/x86/kernel/ftrace.c                 |   50 +++++++++++++++++++++---------
>  include/linux/ftrace.h                   |   18 ++++++++---
>  kernel/trace/fgraph.c                    |   23 ++++++++------
>  kernel/trace/ftrace.c                    |    3 +-
>  kernel/trace/trace.h                     |    3 +-
>  kernel/trace/trace_functions_graph.c     |    3 +-
>  kernel/trace/trace_irqsoff.c             |    3 +-
>  kernel/trace/trace_sched_wakeup.c        |    3 +-
>  kernel/trace/trace_selftest.c            |    8 +++--
>  14 files changed, 128 insertions(+), 45 deletions(-)
> 
> diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
> index a650f5e11fc5..bc647b725e6a 100644
> --- a/arch/arm64/kernel/ftrace.c
> +++ b/arch/arm64/kernel/ftrace.c
> @@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	prepare_ftrace_return(ip, &fregs->lr, fregs->fp);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long frame_pointer = fregs->fp;
> +	unsigned long *parent = &fregs->lr;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * Note:
> +	 * No protection against faulting at *parent, which may be seen
> +	 * on other archs. It's unlikely on AArch64.
> +	 */
> +	old = *parent;
> +
> +	if (!function_graph_enter_regs(old, ip, frame_pointer,
> +				       (void *)frame_pointer, fregs)) {
> +		*parent = return_hooker;
> +	}
>  }
>  #else
>  /*
> diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
> index bff058317062..966e0f7f7aca 100644
> --- a/arch/loongarch/kernel/ftrace_dyn.c
> +++ b/arch/loongarch/kernel/ftrace_dyn.c
> @@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  {
>  	struct pt_regs *regs = &fregs->regs;
>  	unsigned long *parent = (unsigned long *)&regs->regs[1];
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	old = *parent;
>  
> -	prepare_ftrace_return(ip, (unsigned long *)parent);
> +	if (!function_graph_enter_regs(old, ip, 0, parent, fregs))
> +		*parent = return_hooker;
>  }
>  #else
>  static int ftrace_modify_graph_caller(bool enable)
> diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c
> index d8d6b4fd9a14..a1a0e0b57662 100644
> --- a/arch/powerpc/kernel/trace/ftrace.c
> +++ b/arch/powerpc/kernel/trace/ftrace.c
> @@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  	if (bit < 0)
>  		goto out;
>  
> -	if (!function_graph_enter(parent_ip, ip, 0, (unsigned long *)sp))
> +	if (!function_graph_enter_regs(parent_ip, ip, 0, (unsigned long *)sp, fregs))
>  		parent_ip = ppc_function_entry(return_to_handler);
>  
>  	ftrace_test_recursion_unlock(bit);
> diff --git a/arch/powerpc/kernel/trace/ftrace_64_pg.c b/arch/powerpc/kernel/trace/ftrace_64_pg.c
> index 12fab1803bcf..4ae9eeb1c8f1 100644
> --- a/arch/powerpc/kernel/trace/ftrace_64_pg.c
> +++ b/arch/powerpc/kernel/trace/ftrace_64_pg.c
> @@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)
>   * in current thread info. Return the address we want to divert to.
>   */
>  static unsigned long
> -__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp)
> +__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp,
> +			struct ftrace_regs *fregs)
>  {
>  	unsigned long return_hooker;
>  	int bit;
> @@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
>  
>  	return_hooker = ppc_function_entry(return_to_handler);
>  
> -	if (!function_graph_enter(parent, ip, 0, (unsigned long *)sp))
> +	if (!function_graph_enter_regs(parent, ip, 0, (unsigned long *)sp, fregs))
>  		parent = return_hooker;
>  
>  	ftrace_test_recursion_unlock(bit);
> @@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip, fregs->regs.gpr[1]);
> +	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip,
> +						   fregs->regs.gpr[1], fregs);
>  }
>  #else
>  unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip,
>  				    unsigned long sp)
>  {
> -	return __prepare_ftrace_return(parent, ip, sp);
> +	return __prepare_ftrace_return(parent, ip, sp, NULL);
>  }
>  #endif
>  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
> index 4b95c574fd04..f5c3b5a752b0 100644
> --- a/arch/riscv/kernel/ftrace.c
> +++ b/arch/riscv/kernel/ftrace.c
> @@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	prepare_ftrace_return(&fregs->ra, ip, fregs->s0);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long frame_pointer = fregs->s0;
> +	unsigned long *parent = &fregs->ra;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * We don't suffer access faults, so no extra fault-recovery assembly
> +	 * is needed here.
> +	 */
> +	old = *parent;
> +
> +	if (!function_graph_enter_regs(old, ip, frame_pointer, parent, fregs))
> +		*parent = return_hooker;
>  }
>  #else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */
>  extern void ftrace_graph_call(void);
> diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
> index 8da0e66ca22d..decf4c11dcf3 100644
> --- a/arch/x86/kernel/ftrace.c
> +++ b/arch/x86/kernel/ftrace.c
> @@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void)
>  }
>  #endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
>  
> -/*
> - * Hook the return address and push it in the stack of return addrs
> - * in current thread info.
> - */
> -void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
> -			   unsigned long frame_pointer)
> +static inline bool skip_ftrace_return(void)
>  {
> -	unsigned long return_hooker = (unsigned long)&return_to_handler;
> -	int bit;
> -
>  	/*
>  	 * When resuming from suspend-to-ram, this function can be indirectly
>  	 * called from early CPU startup code while the CPU is in real mode,
> @@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
>  	 * This check isn't as accurate as virt_addr_valid(), but it should be
>  	 * good enough for this purpose, and it's fast.
>  	 */
> -	if (unlikely((long)__builtin_frame_address(0) >= 0))
> -		return;
> +	if ((long)__builtin_frame_address(0) >= 0)
> +		return true;
>  
> -	if (unlikely(ftrace_graph_is_dead()))
> -		return;
> +	if (ftrace_graph_is_dead())
> +		return true;
> +
> +	if (atomic_read(&current->tracing_graph_pause))
> +		return true;
> +	return false;
> +}
> +
> +/*
> + * Hook the return address and push it in the stack of return addrs
> + * in current thread info.
> + */
> +void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
> +			   unsigned long frame_pointer)
> +{
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	int bit;
>  
> -	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +	if (unlikely(skip_ftrace_return()))
>  		return;
>  
>  	bit = ftrace_test_recursion_trylock(ip, *parent);
> @@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  {
>  	struct pt_regs *regs = &fregs->regs;
>  	unsigned long *stack = (unsigned long *)kernel_stack_pointer(regs);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long *parent = (unsigned long *)stack;
> +	int bit;
>  
> -	prepare_ftrace_return(ip, (unsigned long *)stack, 0);
> +	if (unlikely(skip_ftrace_return()))
> +		return;
> +
> +	bit = ftrace_test_recursion_trylock(ip, *parent);
> +	if (bit < 0)
> +		return;
> +
> +	if (!function_graph_enter_regs(*parent, ip, 0, parent, fregs))
> +		*parent = return_hooker;
> +
> +	ftrace_test_recursion_unlock(bit);
>  }
>  #endif
>  
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index f84fb9635fb0..1fe49a28de2d 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -1063,9 +1063,12 @@ struct fgraph_ops;
>  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
>  				       struct fgraph_ops *); /* return */
>  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
> -				      struct fgraph_ops *); /* entry */
> +				      struct fgraph_ops *,
> +				      struct ftrace_regs *); /* entry */
>  
> -extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
> +extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> +				   struct fgraph_ops *gops,
> +				   struct ftrace_regs *fregs);
>  bool ftrace_pids_enabled(struct ftrace_ops *ops);
>  
>  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
> @@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {
>  extern void return_to_handler(void);
>  
>  extern int
> -function_graph_enter(unsigned long ret, unsigned long func,
> -		     unsigned long frame_pointer, unsigned long *retp);
> +function_graph_enter_regs(unsigned long ret, unsigned long func,
> +			  unsigned long frame_pointer, unsigned long *retp,
> +			  struct ftrace_regs *fregs);
> +
> +static inline int function_graph_enter(unsigned long ret, unsigned long func,
> +				       unsigned long fp, unsigned long *retp)
> +{
> +	return function_graph_enter_regs(ret, func, fp, retp, NULL);
> +}
>  
>  struct ftrace_ret_stack *
>  ftrace_graph_get_ret_stack(struct task_struct *task, int skip);
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index d7d4fb403f6f..0322c5723748 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset)
>  }
>  
>  /* ftrace_graph_entry set to this to tell some archs to run function graph */
> -static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops)
> +static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
> +		     struct ftrace_regs *fregs)
>  {
>  	return 0;
>  }
> @@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)
>  #endif
>  
>  int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> -			    struct fgraph_ops *gops)
> +			    struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	return 0;
>  }
> @@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,
>  #endif
>  
>  /* If the caller does not use ftrace, call this function. */
> -int function_graph_enter(unsigned long ret, unsigned long func,
> -			 unsigned long frame_pointer, unsigned long *retp)
> +int function_graph_enter_regs(unsigned long ret, unsigned long func,
> +			      unsigned long frame_pointer, unsigned long *retp,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct ftrace_graph_ent trace;
>  	unsigned long bitmap = 0;
> @@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
>  	if (static_branch_likely(&fgraph_do_direct)) {
>  		int save_curr_ret_stack = current->curr_ret_stack;
>  
> -		if (static_call(fgraph_func)(&trace, fgraph_direct_gops))
> +		if (static_call(fgraph_func)(&trace, fgraph_direct_gops, fregs))
>  			bitmap |= BIT(fgraph_direct_gops->idx);
>  		else
>  			/* Clear out any saved storage */
> @@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
>  
>  			save_curr_ret_stack = current->curr_ret_stack;
>  			if (ftrace_ops_test(&gops->ops, func, NULL) &&
> -			    gops->entryfunc(&trace, gops))
> +			    gops->entryfunc(&trace, gops, fregs))
>  				bitmap |= BIT(i);
>  			else
>  				/* Clear out any saved storage */
> @@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
>  
>  static struct ftrace_ops graph_ops = {
>  	.func			= ftrace_graph_func,
> -	.flags			= FTRACE_OPS_GRAPH_STUB,
> +	.flags			= FTRACE_OPS_GRAPH_STUB | FTRACE_OPS_FL_SAVE_ARGS,
>  #ifdef FTRACE_GRAPH_TRAMP_ADDR
>  	.trampoline		= FTRACE_GRAPH_TRAMP_ADDR,
>  	/* trampoline_size is only needed for dynamically allocated tramps */
> @@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {
>  void fgraph_init_ops(struct ftrace_ops *dst_ops,
>  		     struct ftrace_ops *src_ops)
>  {
> -	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB;
> +	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB |
> +			 FTRACE_OPS_FL_SAVE_ARGS;
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE
>  	if (src_ops) {
> @@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE
>  static int fgraph_pid_func(struct ftrace_graph_ent *trace,
> -			   struct fgraph_ops *gops)
> +			   struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = gops->ops.private;
>  	int pid;
> @@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,
>  			return 0;
>  	}
>  
> -	return gops->saved_func(trace, gops);
> +	return gops->saved_func(trace, gops, fregs);
>  }
>  
>  void fgraph_update_pid_func(void)
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 4c28dd177ca6..775040a9f541 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable)
>  }
>  
>  static int profile_graph_entry(struct ftrace_graph_ent *trace,
> -			       struct fgraph_ops *gops)
> +			       struct fgraph_ops *gops,
> +			       struct ftrace_regs *fregs)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index bd3e3069300e..28d8ad5e31e6 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);
>  void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
>  
>  void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> -int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
> +int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
> +		      struct ftrace_regs *fregs);
>  
>  void tracing_start_cmdline_record(void);
>  void tracing_stop_cmdline_record(void);
> diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> index 13d0387ac6a6..b9785fc919c9 100644
> --- a/kernel/trace/trace_functions_graph.c
> +++ b/kernel/trace/trace_functions_graph.c
> @@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void)
>  }
>  
>  int trace_graph_entry(struct ftrace_graph_ent *trace,
> -		      struct fgraph_ops *gops)
> +		      struct fgraph_ops *gops,
> +		      struct ftrace_regs *fregs)
>  {
>  	unsigned long *task_var = fgraph_get_task_var(gops);
>  	struct trace_array *tr = gops->private;
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index fce064e20570..ad739d76fc86 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set)
>  }
>  
>  static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
> -			       struct fgraph_ops *gops)
> +			       struct fgraph_ops *gops,
> +			       struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = irqsoff_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
> index 130ca7e7787e..23360a2700de 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set)
>  }
>  
>  static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
> -			      struct fgraph_ops *gops)
> +			      struct fgraph_ops *gops,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = wakeup_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
> index c4ad7cd7e778..89067f02094a 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -773,7 +773,8 @@ struct fgraph_fixture {
>  };
>  
>  static __init int store_entry(struct ftrace_graph_ent *trace,
> -			      struct fgraph_ops *gops)
> +			      struct fgraph_ops *gops,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
>  	const char *type = fixture->store_type_name;
> @@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;
>  
>  /* Wrap the real function entry probe to avoid possible hanging */
>  static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
> -				      struct fgraph_ops *gops)
> +				      struct fgraph_ops *gops,
> +				      struct ftrace_regs *fregs)
>  {
>  	/* This is harmlessly racy, we want to approximately detect a hang */
>  	if (unlikely(++graph_hang_thresh > GRAPH_MAX_FUNC_TEST)) {
> @@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
>  		return 0;
>  	}
>  
> -	return trace_graph_entry(trace, gops);
> +	return trace_graph_entry(trace, gops, fregs);
>  }
>  
>  static struct fgraph_ops fgraph_ops __initdata  = {


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
  2024-09-12 15:09 ` [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc Masami Hiramatsu (Google)
  2024-09-15  8:49   ` Steven Rostedt
  2024-09-15  8:51   ` Steven Rostedt
@ 2024-09-15  8:54   ` Steven Rostedt
  2024-09-15  8:57   ` Steven Rostedt
  2024-09-15  9:00   ` Steven Rostedt
  4 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  8:54 UTC (permalink / raw)
  To: Michael Ellerman, linuxppc-dev
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland


Can I get an Acked-by from the POWERPC maintainers for this patch?

Thanks!

-- Steve

[ Note this is modifies the return side ]

On Fri, 13 Sep 2024 00:09:02 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Pass ftrace_regs to the fgraph_ops::retfunc(). If ftrace_regs is not
> available, it passes a NULL instead. User callback function can access
> some registers (including return address) via this ftrace_regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v8:
>   - Pass ftrace_regs to retfunc, instead of adding retregfunc.
>  Changes in v6:
>   - update to use ftrace_regs_get_return_value() because of reordering
>     patches.
>  Changes in v3:
>   - Update for new multiple fgraph.
>   - Save the return address to instruction pointer in ftrace_regs.
> ---
>  include/linux/ftrace.h               |    3 ++-
>  kernel/trace/fgraph.c                |   16 +++++++++++-----
>  kernel/trace/ftrace.c                |    3 ++-
>  kernel/trace/trace.h                 |    3 ++-
>  kernel/trace/trace_functions_graph.c |    7 ++++---
>  kernel/trace/trace_irqsoff.c         |    3 ++-
>  kernel/trace/trace_sched_wakeup.c    |    3 ++-
>  kernel/trace/trace_selftest.c        |    3 ++-
>  8 files changed, 27 insertions(+), 14 deletions(-)
> 
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 13987cd63553..e7c41d9988e1 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -1069,7 +1069,8 @@ struct fgraph_ops;
>  
>  /* Type of the callback handlers for tracing function graph*/
>  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
> -				       struct fgraph_ops *); /* return */
> +				       struct fgraph_ops *,
> +				       struct ftrace_regs *); /* return */
>  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
>  				      struct fgraph_ops *,
>  				      struct ftrace_regs *); /* entry */
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index 30bebe43607d..6a3e2db16aa4 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
>  }
>  
>  /* ftrace_graph_return set to this to tell some archs to run function graph */
> -static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops)
> +static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops,
> +		       struct ftrace_regs *fregs)
>  {
>  }
>  
> @@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
>  }
>  
>  static void ftrace_graph_ret_stub(struct ftrace_graph_ret *trace,
> -				  struct fgraph_ops *gops)
> +				  struct fgraph_ops *gops,
> +				  struct ftrace_regs *fregs)
>  {
>  }
>  
> @@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  	}
>  
>  	trace.rettime = trace_clock_local();
> +	if (fregs)
> +		ftrace_regs_set_instruction_pointer(fregs, ret);
> +
>  #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
>  	trace.retval = ftrace_regs_get_return_value(fregs);
>  #endif
> @@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  #ifdef CONFIG_HAVE_STATIC_CALL
>  	if (static_branch_likely(&fgraph_do_direct)) {
>  		if (test_bit(fgraph_direct_gops->idx, &bitmap))
> -			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops);
> +			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops, fregs);
>  	} else
>  #endif
>  	{
> @@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  			if (gops == &fgraph_stub)
>  				continue;
>  
> -			gops->retfunc(&trace, gops);
> +			gops->retfunc(&trace, gops, fregs);
>  		}
>  	}
>  
> @@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)
>   * Simply points to ftrace_stub, but with the proper protocol.
>   * Defined by the linker script in linux/vmlinux.lds.h
>   */
> -void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> +void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
> +		       struct ftrace_regs *fregs);
>  
>  /* The callbacks that hook a function */
>  trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 775040a9f541..fd6c5a50c5e5 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void profile_graph_return(struct ftrace_graph_ret *trace,
> -				 struct fgraph_ops *gops)
> +				 struct fgraph_ops *gops,
> +				 struct ftrace_regs *fregs)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  	struct ftrace_profile_stat *stat;
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index 28d8ad5e31e6..f4a3f75bd916 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);
>  void trace_default_header(struct seq_file *m);
>  void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
>  
> -void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> +void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
> +			struct ftrace_regs *fregs);
>  int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
>  		      struct ftrace_regs *fregs);
>  
> diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> index b9785fc919c9..241407000109 100644
> --- a/kernel/trace/trace_functions_graph.c
> +++ b/kernel/trace/trace_functions_graph.c
> @@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr,
>  }
>  
>  void trace_graph_return(struct ftrace_graph_ret *trace,
> -			struct fgraph_ops *gops)
> +			struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	unsigned long *task_var = fgraph_get_task_var(gops);
>  	struct trace_array *tr = gops->private;
> @@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace,
>  }
>  
>  static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
> -				      struct fgraph_ops *gops)
> +				      struct fgraph_ops *gops,
> +				      struct ftrace_regs *fregs)
>  {
>  	ftrace_graph_addr_finish(gops, trace);
>  
> @@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
>  	    (trace->rettime - trace->calltime < tracing_thresh))
>  		return;
>  	else
> -		trace_graph_return(trace, gops);
> +		trace_graph_return(trace, gops, fregs);
>  }
>  
>  static struct fgraph_ops funcgraph_ops = {
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index ad739d76fc86..504de7a05498 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void irqsoff_graph_return(struct ftrace_graph_ret *trace,
> -				 struct fgraph_ops *gops)
> +				 struct fgraph_ops *gops,
> +				 struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = irqsoff_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
> index 23360a2700de..9ffbd9326898 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void wakeup_graph_return(struct ftrace_graph_ret *trace,
> -				struct fgraph_ops *gops)
> +				struct fgraph_ops *gops,
> +				struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = wakeup_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
> index 89067f02094a..1ebd0899238f 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static __init void store_return(struct ftrace_graph_ret *trace,
> -				struct fgraph_ops *gops)
> +				struct fgraph_ops *gops,
> +				struct ftrace_regs *fregs)
>  {
>  	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
>  	const char *type = fixture->store_type_name;


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
  2024-09-12 15:08 ` [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc Masami Hiramatsu (Google)
                     ` (2 preceding siblings ...)
  2024-09-15  8:53   ` Steven Rostedt
@ 2024-09-15  8:56   ` Steven Rostedt
  2024-09-15  8:58   ` Steven Rostedt
  4 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  8:56 UTC (permalink / raw)
  To: Paul Walmsley, Palmer Dabbelt, Albert Ou, linux-riscv
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland


Can I get an Acked-by from the RISC-V maintainers for this patch?

Thanks!

-- Steve


On Fri, 13 Sep 2024 00:08:40 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Pass ftrace_regs to the fgraph_ops::entryfunc(). If ftrace_regs is not
> available, it passes a NULL instead. User callback function can access
> some registers (including return address) via this ftrace_regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v11:
>   - Update for the latest for-next branch.
>  Changes in v8:
>   - Just pass ftrace_regs to the handler instead of adding a new
>     entryregfunc.
>   - Update riscv ftrace_graph_func().
>  Changes in v3:
>   - Update for new multiple fgraph.
> ---
>  arch/arm64/kernel/ftrace.c               |   20 +++++++++++-
>  arch/loongarch/kernel/ftrace_dyn.c       |   10 +++++-
>  arch/powerpc/kernel/trace/ftrace.c       |    2 +
>  arch/powerpc/kernel/trace/ftrace_64_pg.c |   10 ++++--
>  arch/riscv/kernel/ftrace.c               |   17 ++++++++++
>  arch/x86/kernel/ftrace.c                 |   50 +++++++++++++++++++++---------
>  include/linux/ftrace.h                   |   18 ++++++++---
>  kernel/trace/fgraph.c                    |   23 ++++++++------
>  kernel/trace/ftrace.c                    |    3 +-
>  kernel/trace/trace.h                     |    3 +-
>  kernel/trace/trace_functions_graph.c     |    3 +-
>  kernel/trace/trace_irqsoff.c             |    3 +-
>  kernel/trace/trace_sched_wakeup.c        |    3 +-
>  kernel/trace/trace_selftest.c            |    8 +++--
>  14 files changed, 128 insertions(+), 45 deletions(-)
> 
> diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
> index a650f5e11fc5..bc647b725e6a 100644
> --- a/arch/arm64/kernel/ftrace.c
> +++ b/arch/arm64/kernel/ftrace.c
> @@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	prepare_ftrace_return(ip, &fregs->lr, fregs->fp);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long frame_pointer = fregs->fp;
> +	unsigned long *parent = &fregs->lr;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * Note:
> +	 * No protection against faulting at *parent, which may be seen
> +	 * on other archs. It's unlikely on AArch64.
> +	 */
> +	old = *parent;
> +
> +	if (!function_graph_enter_regs(old, ip, frame_pointer,
> +				       (void *)frame_pointer, fregs)) {
> +		*parent = return_hooker;
> +	}
>  }
>  #else
>  /*
> diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
> index bff058317062..966e0f7f7aca 100644
> --- a/arch/loongarch/kernel/ftrace_dyn.c
> +++ b/arch/loongarch/kernel/ftrace_dyn.c
> @@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  {
>  	struct pt_regs *regs = &fregs->regs;
>  	unsigned long *parent = (unsigned long *)&regs->regs[1];
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	old = *parent;
>  
> -	prepare_ftrace_return(ip, (unsigned long *)parent);
> +	if (!function_graph_enter_regs(old, ip, 0, parent, fregs))
> +		*parent = return_hooker;
>  }
>  #else
>  static int ftrace_modify_graph_caller(bool enable)
> diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c
> index d8d6b4fd9a14..a1a0e0b57662 100644
> --- a/arch/powerpc/kernel/trace/ftrace.c
> +++ b/arch/powerpc/kernel/trace/ftrace.c
> @@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  	if (bit < 0)
>  		goto out;
>  
> -	if (!function_graph_enter(parent_ip, ip, 0, (unsigned long *)sp))
> +	if (!function_graph_enter_regs(parent_ip, ip, 0, (unsigned long *)sp, fregs))
>  		parent_ip = ppc_function_entry(return_to_handler);
>  
>  	ftrace_test_recursion_unlock(bit);
> diff --git a/arch/powerpc/kernel/trace/ftrace_64_pg.c b/arch/powerpc/kernel/trace/ftrace_64_pg.c
> index 12fab1803bcf..4ae9eeb1c8f1 100644
> --- a/arch/powerpc/kernel/trace/ftrace_64_pg.c
> +++ b/arch/powerpc/kernel/trace/ftrace_64_pg.c
> @@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)
>   * in current thread info. Return the address we want to divert to.
>   */
>  static unsigned long
> -__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp)
> +__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp,
> +			struct ftrace_regs *fregs)
>  {
>  	unsigned long return_hooker;
>  	int bit;
> @@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
>  
>  	return_hooker = ppc_function_entry(return_to_handler);
>  
> -	if (!function_graph_enter(parent, ip, 0, (unsigned long *)sp))
> +	if (!function_graph_enter_regs(parent, ip, 0, (unsigned long *)sp, fregs))
>  		parent = return_hooker;
>  
>  	ftrace_test_recursion_unlock(bit);
> @@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip, fregs->regs.gpr[1]);
> +	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip,
> +						   fregs->regs.gpr[1], fregs);
>  }
>  #else
>  unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip,
>  				    unsigned long sp)
>  {
> -	return __prepare_ftrace_return(parent, ip, sp);
> +	return __prepare_ftrace_return(parent, ip, sp, NULL);
>  }
>  #endif
>  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
> index 4b95c574fd04..f5c3b5a752b0 100644
> --- a/arch/riscv/kernel/ftrace.c
> +++ b/arch/riscv/kernel/ftrace.c
> @@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	prepare_ftrace_return(&fregs->ra, ip, fregs->s0);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long frame_pointer = fregs->s0;
> +	unsigned long *parent = &fregs->ra;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * We don't suffer access faults, so no extra fault-recovery assembly
> +	 * is needed here.
> +	 */
> +	old = *parent;
> +
> +	if (!function_graph_enter_regs(old, ip, frame_pointer, parent, fregs))
> +		*parent = return_hooker;
>  }
>  #else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */
>  extern void ftrace_graph_call(void);
> diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
> index 8da0e66ca22d..decf4c11dcf3 100644
> --- a/arch/x86/kernel/ftrace.c
> +++ b/arch/x86/kernel/ftrace.c
> @@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void)
>  }
>  #endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
>  
> -/*
> - * Hook the return address and push it in the stack of return addrs
> - * in current thread info.
> - */
> -void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
> -			   unsigned long frame_pointer)
> +static inline bool skip_ftrace_return(void)
>  {
> -	unsigned long return_hooker = (unsigned long)&return_to_handler;
> -	int bit;
> -
>  	/*
>  	 * When resuming from suspend-to-ram, this function can be indirectly
>  	 * called from early CPU startup code while the CPU is in real mode,
> @@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
>  	 * This check isn't as accurate as virt_addr_valid(), but it should be
>  	 * good enough for this purpose, and it's fast.
>  	 */
> -	if (unlikely((long)__builtin_frame_address(0) >= 0))
> -		return;
> +	if ((long)__builtin_frame_address(0) >= 0)
> +		return true;
>  
> -	if (unlikely(ftrace_graph_is_dead()))
> -		return;
> +	if (ftrace_graph_is_dead())
> +		return true;
> +
> +	if (atomic_read(&current->tracing_graph_pause))
> +		return true;
> +	return false;
> +}
> +
> +/*
> + * Hook the return address and push it in the stack of return addrs
> + * in current thread info.
> + */
> +void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
> +			   unsigned long frame_pointer)
> +{
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	int bit;
>  
> -	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +	if (unlikely(skip_ftrace_return()))
>  		return;
>  
>  	bit = ftrace_test_recursion_trylock(ip, *parent);
> @@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  {
>  	struct pt_regs *regs = &fregs->regs;
>  	unsigned long *stack = (unsigned long *)kernel_stack_pointer(regs);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long *parent = (unsigned long *)stack;
> +	int bit;
>  
> -	prepare_ftrace_return(ip, (unsigned long *)stack, 0);
> +	if (unlikely(skip_ftrace_return()))
> +		return;
> +
> +	bit = ftrace_test_recursion_trylock(ip, *parent);
> +	if (bit < 0)
> +		return;
> +
> +	if (!function_graph_enter_regs(*parent, ip, 0, parent, fregs))
> +		*parent = return_hooker;
> +
> +	ftrace_test_recursion_unlock(bit);
>  }
>  #endif
>  
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index f84fb9635fb0..1fe49a28de2d 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -1063,9 +1063,12 @@ struct fgraph_ops;
>  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
>  				       struct fgraph_ops *); /* return */
>  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
> -				      struct fgraph_ops *); /* entry */
> +				      struct fgraph_ops *,
> +				      struct ftrace_regs *); /* entry */
>  
> -extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
> +extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> +				   struct fgraph_ops *gops,
> +				   struct ftrace_regs *fregs);
>  bool ftrace_pids_enabled(struct ftrace_ops *ops);
>  
>  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
> @@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {
>  extern void return_to_handler(void);
>  
>  extern int
> -function_graph_enter(unsigned long ret, unsigned long func,
> -		     unsigned long frame_pointer, unsigned long *retp);
> +function_graph_enter_regs(unsigned long ret, unsigned long func,
> +			  unsigned long frame_pointer, unsigned long *retp,
> +			  struct ftrace_regs *fregs);
> +
> +static inline int function_graph_enter(unsigned long ret, unsigned long func,
> +				       unsigned long fp, unsigned long *retp)
> +{
> +	return function_graph_enter_regs(ret, func, fp, retp, NULL);
> +}
>  
>  struct ftrace_ret_stack *
>  ftrace_graph_get_ret_stack(struct task_struct *task, int skip);
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index d7d4fb403f6f..0322c5723748 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset)
>  }
>  
>  /* ftrace_graph_entry set to this to tell some archs to run function graph */
> -static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops)
> +static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
> +		     struct ftrace_regs *fregs)
>  {
>  	return 0;
>  }
> @@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)
>  #endif
>  
>  int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> -			    struct fgraph_ops *gops)
> +			    struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	return 0;
>  }
> @@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,
>  #endif
>  
>  /* If the caller does not use ftrace, call this function. */
> -int function_graph_enter(unsigned long ret, unsigned long func,
> -			 unsigned long frame_pointer, unsigned long *retp)
> +int function_graph_enter_regs(unsigned long ret, unsigned long func,
> +			      unsigned long frame_pointer, unsigned long *retp,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct ftrace_graph_ent trace;
>  	unsigned long bitmap = 0;
> @@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
>  	if (static_branch_likely(&fgraph_do_direct)) {
>  		int save_curr_ret_stack = current->curr_ret_stack;
>  
> -		if (static_call(fgraph_func)(&trace, fgraph_direct_gops))
> +		if (static_call(fgraph_func)(&trace, fgraph_direct_gops, fregs))
>  			bitmap |= BIT(fgraph_direct_gops->idx);
>  		else
>  			/* Clear out any saved storage */
> @@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
>  
>  			save_curr_ret_stack = current->curr_ret_stack;
>  			if (ftrace_ops_test(&gops->ops, func, NULL) &&
> -			    gops->entryfunc(&trace, gops))
> +			    gops->entryfunc(&trace, gops, fregs))
>  				bitmap |= BIT(i);
>  			else
>  				/* Clear out any saved storage */
> @@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
>  
>  static struct ftrace_ops graph_ops = {
>  	.func			= ftrace_graph_func,
> -	.flags			= FTRACE_OPS_GRAPH_STUB,
> +	.flags			= FTRACE_OPS_GRAPH_STUB | FTRACE_OPS_FL_SAVE_ARGS,
>  #ifdef FTRACE_GRAPH_TRAMP_ADDR
>  	.trampoline		= FTRACE_GRAPH_TRAMP_ADDR,
>  	/* trampoline_size is only needed for dynamically allocated tramps */
> @@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {
>  void fgraph_init_ops(struct ftrace_ops *dst_ops,
>  		     struct ftrace_ops *src_ops)
>  {
> -	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB;
> +	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB |
> +			 FTRACE_OPS_FL_SAVE_ARGS;
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE
>  	if (src_ops) {
> @@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE
>  static int fgraph_pid_func(struct ftrace_graph_ent *trace,
> -			   struct fgraph_ops *gops)
> +			   struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = gops->ops.private;
>  	int pid;
> @@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,
>  			return 0;
>  	}
>  
> -	return gops->saved_func(trace, gops);
> +	return gops->saved_func(trace, gops, fregs);
>  }
>  
>  void fgraph_update_pid_func(void)
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 4c28dd177ca6..775040a9f541 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable)
>  }
>  
>  static int profile_graph_entry(struct ftrace_graph_ent *trace,
> -			       struct fgraph_ops *gops)
> +			       struct fgraph_ops *gops,
> +			       struct ftrace_regs *fregs)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index bd3e3069300e..28d8ad5e31e6 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);
>  void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
>  
>  void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> -int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
> +int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
> +		      struct ftrace_regs *fregs);
>  
>  void tracing_start_cmdline_record(void);
>  void tracing_stop_cmdline_record(void);
> diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> index 13d0387ac6a6..b9785fc919c9 100644
> --- a/kernel/trace/trace_functions_graph.c
> +++ b/kernel/trace/trace_functions_graph.c
> @@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void)
>  }
>  
>  int trace_graph_entry(struct ftrace_graph_ent *trace,
> -		      struct fgraph_ops *gops)
> +		      struct fgraph_ops *gops,
> +		      struct ftrace_regs *fregs)
>  {
>  	unsigned long *task_var = fgraph_get_task_var(gops);
>  	struct trace_array *tr = gops->private;
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index fce064e20570..ad739d76fc86 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set)
>  }
>  
>  static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
> -			       struct fgraph_ops *gops)
> +			       struct fgraph_ops *gops,
> +			       struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = irqsoff_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
> index 130ca7e7787e..23360a2700de 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set)
>  }
>  
>  static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
> -			      struct fgraph_ops *gops)
> +			      struct fgraph_ops *gops,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = wakeup_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
> index c4ad7cd7e778..89067f02094a 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -773,7 +773,8 @@ struct fgraph_fixture {
>  };
>  
>  static __init int store_entry(struct ftrace_graph_ent *trace,
> -			      struct fgraph_ops *gops)
> +			      struct fgraph_ops *gops,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
>  	const char *type = fixture->store_type_name;
> @@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;
>  
>  /* Wrap the real function entry probe to avoid possible hanging */
>  static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
> -				      struct fgraph_ops *gops)
> +				      struct fgraph_ops *gops,
> +				      struct ftrace_regs *fregs)
>  {
>  	/* This is harmlessly racy, we want to approximately detect a hang */
>  	if (unlikely(++graph_hang_thresh > GRAPH_MAX_FUNC_TEST)) {
> @@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
>  		return 0;
>  	}
>  
> -	return trace_graph_entry(trace, gops);
> +	return trace_graph_entry(trace, gops, fregs);
>  }
>  
>  static struct fgraph_ops fgraph_ops __initdata  = {


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
  2024-09-12 15:09 ` [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc Masami Hiramatsu (Google)
                     ` (2 preceding siblings ...)
  2024-09-15  8:54   ` Steven Rostedt
@ 2024-09-15  8:57   ` Steven Rostedt
  2024-09-15  9:00   ` Steven Rostedt
  4 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  8:57 UTC (permalink / raw)
  To: Paul Walmsley, Palmer Dabbelt, Albert Ou, linux-riscv
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland


Can I get an Acked-by from the RISC-V maintainers for this patch?

Thanks!

-- Steve

[ Note this is modifies the return side ]

On Fri, 13 Sep 2024 00:09:02 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Pass ftrace_regs to the fgraph_ops::retfunc(). If ftrace_regs is not
> available, it passes a NULL instead. User callback function can access
> some registers (including return address) via this ftrace_regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v8:
>   - Pass ftrace_regs to retfunc, instead of adding retregfunc.
>  Changes in v6:
>   - update to use ftrace_regs_get_return_value() because of reordering
>     patches.
>  Changes in v3:
>   - Update for new multiple fgraph.
>   - Save the return address to instruction pointer in ftrace_regs.
> ---
>  include/linux/ftrace.h               |    3 ++-
>  kernel/trace/fgraph.c                |   16 +++++++++++-----
>  kernel/trace/ftrace.c                |    3 ++-
>  kernel/trace/trace.h                 |    3 ++-
>  kernel/trace/trace_functions_graph.c |    7 ++++---
>  kernel/trace/trace_irqsoff.c         |    3 ++-
>  kernel/trace/trace_sched_wakeup.c    |    3 ++-
>  kernel/trace/trace_selftest.c        |    3 ++-
>  8 files changed, 27 insertions(+), 14 deletions(-)
> 
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 13987cd63553..e7c41d9988e1 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -1069,7 +1069,8 @@ struct fgraph_ops;
>  
>  /* Type of the callback handlers for tracing function graph*/
>  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
> -				       struct fgraph_ops *); /* return */
> +				       struct fgraph_ops *,
> +				       struct ftrace_regs *); /* return */
>  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
>  				      struct fgraph_ops *,
>  				      struct ftrace_regs *); /* entry */
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index 30bebe43607d..6a3e2db16aa4 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
>  }
>  
>  /* ftrace_graph_return set to this to tell some archs to run function graph */
> -static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops)
> +static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops,
> +		       struct ftrace_regs *fregs)
>  {
>  }
>  
> @@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
>  }
>  
>  static void ftrace_graph_ret_stub(struct ftrace_graph_ret *trace,
> -				  struct fgraph_ops *gops)
> +				  struct fgraph_ops *gops,
> +				  struct ftrace_regs *fregs)
>  {
>  }
>  
> @@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  	}
>  
>  	trace.rettime = trace_clock_local();
> +	if (fregs)
> +		ftrace_regs_set_instruction_pointer(fregs, ret);
> +
>  #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
>  	trace.retval = ftrace_regs_get_return_value(fregs);
>  #endif
> @@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  #ifdef CONFIG_HAVE_STATIC_CALL
>  	if (static_branch_likely(&fgraph_do_direct)) {
>  		if (test_bit(fgraph_direct_gops->idx, &bitmap))
> -			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops);
> +			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops, fregs);
>  	} else
>  #endif
>  	{
> @@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  			if (gops == &fgraph_stub)
>  				continue;
>  
> -			gops->retfunc(&trace, gops);
> +			gops->retfunc(&trace, gops, fregs);
>  		}
>  	}
>  
> @@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)
>   * Simply points to ftrace_stub, but with the proper protocol.
>   * Defined by the linker script in linux/vmlinux.lds.h
>   */
> -void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> +void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
> +		       struct ftrace_regs *fregs);
>  
>  /* The callbacks that hook a function */
>  trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 775040a9f541..fd6c5a50c5e5 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void profile_graph_return(struct ftrace_graph_ret *trace,
> -				 struct fgraph_ops *gops)
> +				 struct fgraph_ops *gops,
> +				 struct ftrace_regs *fregs)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  	struct ftrace_profile_stat *stat;
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index 28d8ad5e31e6..f4a3f75bd916 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);
>  void trace_default_header(struct seq_file *m);
>  void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
>  
> -void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> +void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
> +			struct ftrace_regs *fregs);
>  int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
>  		      struct ftrace_regs *fregs);
>  
> diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> index b9785fc919c9..241407000109 100644
> --- a/kernel/trace/trace_functions_graph.c
> +++ b/kernel/trace/trace_functions_graph.c
> @@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr,
>  }
>  
>  void trace_graph_return(struct ftrace_graph_ret *trace,
> -			struct fgraph_ops *gops)
> +			struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	unsigned long *task_var = fgraph_get_task_var(gops);
>  	struct trace_array *tr = gops->private;
> @@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace,
>  }
>  
>  static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
> -				      struct fgraph_ops *gops)
> +				      struct fgraph_ops *gops,
> +				      struct ftrace_regs *fregs)
>  {
>  	ftrace_graph_addr_finish(gops, trace);
>  
> @@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
>  	    (trace->rettime - trace->calltime < tracing_thresh))
>  		return;
>  	else
> -		trace_graph_return(trace, gops);
> +		trace_graph_return(trace, gops, fregs);
>  }
>  
>  static struct fgraph_ops funcgraph_ops = {
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index ad739d76fc86..504de7a05498 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void irqsoff_graph_return(struct ftrace_graph_ret *trace,
> -				 struct fgraph_ops *gops)
> +				 struct fgraph_ops *gops,
> +				 struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = irqsoff_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
> index 23360a2700de..9ffbd9326898 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void wakeup_graph_return(struct ftrace_graph_ret *trace,
> -				struct fgraph_ops *gops)
> +				struct fgraph_ops *gops,
> +				struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = wakeup_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
> index 89067f02094a..1ebd0899238f 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static __init void store_return(struct ftrace_graph_ret *trace,
> -				struct fgraph_ops *gops)
> +				struct fgraph_ops *gops,
> +				struct ftrace_regs *fregs)
>  {
>  	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
>  	const char *type = fixture->store_type_name;


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
  2024-09-12 15:08 ` [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc Masami Hiramatsu (Google)
                     ` (3 preceding siblings ...)
  2024-09-15  8:56   ` Steven Rostedt
@ 2024-09-15  8:58   ` Steven Rostedt
  4 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  8:58 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	Peter Zijlstra
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland


Can I get an Acked-by from the X86 maintainers for this patch?

Thanks!

-- Steve


On Fri, 13 Sep 2024 00:08:40 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Pass ftrace_regs to the fgraph_ops::entryfunc(). If ftrace_regs is not
> available, it passes a NULL instead. User callback function can access
> some registers (including return address) via this ftrace_regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v11:
>   - Update for the latest for-next branch.
>  Changes in v8:
>   - Just pass ftrace_regs to the handler instead of adding a new
>     entryregfunc.
>   - Update riscv ftrace_graph_func().
>  Changes in v3:
>   - Update for new multiple fgraph.
> ---
>  arch/arm64/kernel/ftrace.c               |   20 +++++++++++-
>  arch/loongarch/kernel/ftrace_dyn.c       |   10 +++++-
>  arch/powerpc/kernel/trace/ftrace.c       |    2 +
>  arch/powerpc/kernel/trace/ftrace_64_pg.c |   10 ++++--
>  arch/riscv/kernel/ftrace.c               |   17 ++++++++++
>  arch/x86/kernel/ftrace.c                 |   50 +++++++++++++++++++++---------
>  include/linux/ftrace.h                   |   18 ++++++++---
>  kernel/trace/fgraph.c                    |   23 ++++++++------
>  kernel/trace/ftrace.c                    |    3 +-
>  kernel/trace/trace.h                     |    3 +-
>  kernel/trace/trace_functions_graph.c     |    3 +-
>  kernel/trace/trace_irqsoff.c             |    3 +-
>  kernel/trace/trace_sched_wakeup.c        |    3 +-
>  kernel/trace/trace_selftest.c            |    8 +++--
>  14 files changed, 128 insertions(+), 45 deletions(-)
> 
> diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
> index a650f5e11fc5..bc647b725e6a 100644
> --- a/arch/arm64/kernel/ftrace.c
> +++ b/arch/arm64/kernel/ftrace.c
> @@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	prepare_ftrace_return(ip, &fregs->lr, fregs->fp);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long frame_pointer = fregs->fp;
> +	unsigned long *parent = &fregs->lr;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * Note:
> +	 * No protection against faulting at *parent, which may be seen
> +	 * on other archs. It's unlikely on AArch64.
> +	 */
> +	old = *parent;
> +
> +	if (!function_graph_enter_regs(old, ip, frame_pointer,
> +				       (void *)frame_pointer, fregs)) {
> +		*parent = return_hooker;
> +	}
>  }
>  #else
>  /*
> diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
> index bff058317062..966e0f7f7aca 100644
> --- a/arch/loongarch/kernel/ftrace_dyn.c
> +++ b/arch/loongarch/kernel/ftrace_dyn.c
> @@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  {
>  	struct pt_regs *regs = &fregs->regs;
>  	unsigned long *parent = (unsigned long *)&regs->regs[1];
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	old = *parent;
>  
> -	prepare_ftrace_return(ip, (unsigned long *)parent);
> +	if (!function_graph_enter_regs(old, ip, 0, parent, fregs))
> +		*parent = return_hooker;
>  }
>  #else
>  static int ftrace_modify_graph_caller(bool enable)
> diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c
> index d8d6b4fd9a14..a1a0e0b57662 100644
> --- a/arch/powerpc/kernel/trace/ftrace.c
> +++ b/arch/powerpc/kernel/trace/ftrace.c
> @@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  	if (bit < 0)
>  		goto out;
>  
> -	if (!function_graph_enter(parent_ip, ip, 0, (unsigned long *)sp))
> +	if (!function_graph_enter_regs(parent_ip, ip, 0, (unsigned long *)sp, fregs))
>  		parent_ip = ppc_function_entry(return_to_handler);
>  
>  	ftrace_test_recursion_unlock(bit);
> diff --git a/arch/powerpc/kernel/trace/ftrace_64_pg.c b/arch/powerpc/kernel/trace/ftrace_64_pg.c
> index 12fab1803bcf..4ae9eeb1c8f1 100644
> --- a/arch/powerpc/kernel/trace/ftrace_64_pg.c
> +++ b/arch/powerpc/kernel/trace/ftrace_64_pg.c
> @@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)
>   * in current thread info. Return the address we want to divert to.
>   */
>  static unsigned long
> -__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp)
> +__prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp,
> +			struct ftrace_regs *fregs)
>  {
>  	unsigned long return_hooker;
>  	int bit;
> @@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
>  
>  	return_hooker = ppc_function_entry(return_to_handler);
>  
> -	if (!function_graph_enter(parent, ip, 0, (unsigned long *)sp))
> +	if (!function_graph_enter_regs(parent, ip, 0, (unsigned long *)sp, fregs))
>  		parent = return_hooker;
>  
>  	ftrace_test_recursion_unlock(bit);
> @@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip, fregs->regs.gpr[1]);
> +	fregs->regs.link = __prepare_ftrace_return(parent_ip, ip,
> +						   fregs->regs.gpr[1], fregs);
>  }
>  #else
>  unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip,
>  				    unsigned long sp)
>  {
> -	return __prepare_ftrace_return(parent, ip, sp);
> +	return __prepare_ftrace_return(parent, ip, sp, NULL);
>  }
>  #endif
>  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
> index 4b95c574fd04..f5c3b5a752b0 100644
> --- a/arch/riscv/kernel/ftrace.c
> +++ b/arch/riscv/kernel/ftrace.c
> @@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	prepare_ftrace_return(&fregs->ra, ip, fregs->s0);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long frame_pointer = fregs->s0;
> +	unsigned long *parent = &fregs->ra;
> +	unsigned long old;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * We don't suffer access faults, so no extra fault-recovery assembly
> +	 * is needed here.
> +	 */
> +	old = *parent;
> +
> +	if (!function_graph_enter_regs(old, ip, frame_pointer, parent, fregs))
> +		*parent = return_hooker;
>  }
>  #else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */
>  extern void ftrace_graph_call(void);
> diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
> index 8da0e66ca22d..decf4c11dcf3 100644
> --- a/arch/x86/kernel/ftrace.c
> +++ b/arch/x86/kernel/ftrace.c
> @@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void)
>  }
>  #endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
>  
> -/*
> - * Hook the return address and push it in the stack of return addrs
> - * in current thread info.
> - */
> -void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
> -			   unsigned long frame_pointer)
> +static inline bool skip_ftrace_return(void)
>  {
> -	unsigned long return_hooker = (unsigned long)&return_to_handler;
> -	int bit;
> -
>  	/*
>  	 * When resuming from suspend-to-ram, this function can be indirectly
>  	 * called from early CPU startup code while the CPU is in real mode,
> @@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
>  	 * This check isn't as accurate as virt_addr_valid(), but it should be
>  	 * good enough for this purpose, and it's fast.
>  	 */
> -	if (unlikely((long)__builtin_frame_address(0) >= 0))
> -		return;
> +	if ((long)__builtin_frame_address(0) >= 0)
> +		return true;
>  
> -	if (unlikely(ftrace_graph_is_dead()))
> -		return;
> +	if (ftrace_graph_is_dead())
> +		return true;
> +
> +	if (atomic_read(&current->tracing_graph_pause))
> +		return true;
> +	return false;
> +}
> +
> +/*
> + * Hook the return address and push it in the stack of return addrs
> + * in current thread info.
> + */
> +void prepare_ftrace_return(unsigned long ip, unsigned long *parent,
> +			   unsigned long frame_pointer)
> +{
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	int bit;
>  
> -	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +	if (unlikely(skip_ftrace_return()))
>  		return;
>  
>  	bit = ftrace_test_recursion_trylock(ip, *parent);
> @@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>  {
>  	struct pt_regs *regs = &fregs->regs;
>  	unsigned long *stack = (unsigned long *)kernel_stack_pointer(regs);
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long *parent = (unsigned long *)stack;
> +	int bit;
>  
> -	prepare_ftrace_return(ip, (unsigned long *)stack, 0);
> +	if (unlikely(skip_ftrace_return()))
> +		return;
> +
> +	bit = ftrace_test_recursion_trylock(ip, *parent);
> +	if (bit < 0)
> +		return;
> +
> +	if (!function_graph_enter_regs(*parent, ip, 0, parent, fregs))
> +		*parent = return_hooker;
> +
> +	ftrace_test_recursion_unlock(bit);
>  }
>  #endif
>  
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index f84fb9635fb0..1fe49a28de2d 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -1063,9 +1063,12 @@ struct fgraph_ops;
>  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
>  				       struct fgraph_ops *); /* return */
>  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
> -				      struct fgraph_ops *); /* entry */
> +				      struct fgraph_ops *,
> +				      struct ftrace_regs *); /* entry */
>  
> -extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
> +extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> +				   struct fgraph_ops *gops,
> +				   struct ftrace_regs *fregs);
>  bool ftrace_pids_enabled(struct ftrace_ops *ops);
>  
>  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
> @@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {
>  extern void return_to_handler(void);
>  
>  extern int
> -function_graph_enter(unsigned long ret, unsigned long func,
> -		     unsigned long frame_pointer, unsigned long *retp);
> +function_graph_enter_regs(unsigned long ret, unsigned long func,
> +			  unsigned long frame_pointer, unsigned long *retp,
> +			  struct ftrace_regs *fregs);
> +
> +static inline int function_graph_enter(unsigned long ret, unsigned long func,
> +				       unsigned long fp, unsigned long *retp)
> +{
> +	return function_graph_enter_regs(ret, func, fp, retp, NULL);
> +}
>  
>  struct ftrace_ret_stack *
>  ftrace_graph_get_ret_stack(struct task_struct *task, int skip);
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index d7d4fb403f6f..0322c5723748 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset)
>  }
>  
>  /* ftrace_graph_entry set to this to tell some archs to run function graph */
> -static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops)
> +static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
> +		     struct ftrace_regs *fregs)
>  {
>  	return 0;
>  }
> @@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)
>  #endif
>  
>  int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> -			    struct fgraph_ops *gops)
> +			    struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	return 0;
>  }
> @@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,
>  #endif
>  
>  /* If the caller does not use ftrace, call this function. */
> -int function_graph_enter(unsigned long ret, unsigned long func,
> -			 unsigned long frame_pointer, unsigned long *retp)
> +int function_graph_enter_regs(unsigned long ret, unsigned long func,
> +			      unsigned long frame_pointer, unsigned long *retp,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct ftrace_graph_ent trace;
>  	unsigned long bitmap = 0;
> @@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
>  	if (static_branch_likely(&fgraph_do_direct)) {
>  		int save_curr_ret_stack = current->curr_ret_stack;
>  
> -		if (static_call(fgraph_func)(&trace, fgraph_direct_gops))
> +		if (static_call(fgraph_func)(&trace, fgraph_direct_gops, fregs))
>  			bitmap |= BIT(fgraph_direct_gops->idx);
>  		else
>  			/* Clear out any saved storage */
> @@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
>  
>  			save_curr_ret_stack = current->curr_ret_stack;
>  			if (ftrace_ops_test(&gops->ops, func, NULL) &&
> -			    gops->entryfunc(&trace, gops))
> +			    gops->entryfunc(&trace, gops, fregs))
>  				bitmap |= BIT(i);
>  			else
>  				/* Clear out any saved storage */
> @@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
>  
>  static struct ftrace_ops graph_ops = {
>  	.func			= ftrace_graph_func,
> -	.flags			= FTRACE_OPS_GRAPH_STUB,
> +	.flags			= FTRACE_OPS_GRAPH_STUB | FTRACE_OPS_FL_SAVE_ARGS,
>  #ifdef FTRACE_GRAPH_TRAMP_ADDR
>  	.trampoline		= FTRACE_GRAPH_TRAMP_ADDR,
>  	/* trampoline_size is only needed for dynamically allocated tramps */
> @@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {
>  void fgraph_init_ops(struct ftrace_ops *dst_ops,
>  		     struct ftrace_ops *src_ops)
>  {
> -	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB;
> +	dst_ops->flags = FTRACE_OPS_FL_PID | FTRACE_OPS_GRAPH_STUB |
> +			 FTRACE_OPS_FL_SAVE_ARGS;
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE
>  	if (src_ops) {
> @@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE
>  static int fgraph_pid_func(struct ftrace_graph_ent *trace,
> -			   struct fgraph_ops *gops)
> +			   struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = gops->ops.private;
>  	int pid;
> @@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,
>  			return 0;
>  	}
>  
> -	return gops->saved_func(trace, gops);
> +	return gops->saved_func(trace, gops, fregs);
>  }
>  
>  void fgraph_update_pid_func(void)
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 4c28dd177ca6..775040a9f541 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable)
>  }
>  
>  static int profile_graph_entry(struct ftrace_graph_ent *trace,
> -			       struct fgraph_ops *gops)
> +			       struct fgraph_ops *gops,
> +			       struct ftrace_regs *fregs)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index bd3e3069300e..28d8ad5e31e6 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);
>  void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
>  
>  void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> -int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
> +int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
> +		      struct ftrace_regs *fregs);
>  
>  void tracing_start_cmdline_record(void);
>  void tracing_stop_cmdline_record(void);
> diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> index 13d0387ac6a6..b9785fc919c9 100644
> --- a/kernel/trace/trace_functions_graph.c
> +++ b/kernel/trace/trace_functions_graph.c
> @@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void)
>  }
>  
>  int trace_graph_entry(struct ftrace_graph_ent *trace,
> -		      struct fgraph_ops *gops)
> +		      struct fgraph_ops *gops,
> +		      struct ftrace_regs *fregs)
>  {
>  	unsigned long *task_var = fgraph_get_task_var(gops);
>  	struct trace_array *tr = gops->private;
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index fce064e20570..ad739d76fc86 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set)
>  }
>  
>  static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
> -			       struct fgraph_ops *gops)
> +			       struct fgraph_ops *gops,
> +			       struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = irqsoff_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
> index 130ca7e7787e..23360a2700de 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set)
>  }
>  
>  static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
> -			      struct fgraph_ops *gops)
> +			      struct fgraph_ops *gops,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = wakeup_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
> index c4ad7cd7e778..89067f02094a 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -773,7 +773,8 @@ struct fgraph_fixture {
>  };
>  
>  static __init int store_entry(struct ftrace_graph_ent *trace,
> -			      struct fgraph_ops *gops)
> +			      struct fgraph_ops *gops,
> +			      struct ftrace_regs *fregs)
>  {
>  	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
>  	const char *type = fixture->store_type_name;
> @@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;
>  
>  /* Wrap the real function entry probe to avoid possible hanging */
>  static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
> -				      struct fgraph_ops *gops)
> +				      struct fgraph_ops *gops,
> +				      struct ftrace_regs *fregs)
>  {
>  	/* This is harmlessly racy, we want to approximately detect a hang */
>  	if (unlikely(++graph_hang_thresh > GRAPH_MAX_FUNC_TEST)) {
> @@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,
>  		return 0;
>  	}
>  
> -	return trace_graph_entry(trace, gops);
> +	return trace_graph_entry(trace, gops, fregs);
>  }
>  
>  static struct fgraph_ops fgraph_ops __initdata  = {


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
  2024-09-12 15:09 ` [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc Masami Hiramatsu (Google)
                     ` (3 preceding siblings ...)
  2024-09-15  8:57   ` Steven Rostedt
@ 2024-09-15  9:00   ` Steven Rostedt
  4 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  9:00 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	Peter Zijlstra
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland


Can I get an Acked-by from the X86 maintainers for this patch?

Thanks!

-- Steve

[ Note this is modifies the return side ]

On Fri, 13 Sep 2024 00:09:02 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Pass ftrace_regs to the fgraph_ops::retfunc(). If ftrace_regs is not
> available, it passes a NULL instead. User callback function can access
> some registers (including return address) via this ftrace_regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v8:
>   - Pass ftrace_regs to retfunc, instead of adding retregfunc.
>  Changes in v6:
>   - update to use ftrace_regs_get_return_value() because of reordering
>     patches.
>  Changes in v3:
>   - Update for new multiple fgraph.
>   - Save the return address to instruction pointer in ftrace_regs.
> ---
>  include/linux/ftrace.h               |    3 ++-
>  kernel/trace/fgraph.c                |   16 +++++++++++-----
>  kernel/trace/ftrace.c                |    3 ++-
>  kernel/trace/trace.h                 |    3 ++-
>  kernel/trace/trace_functions_graph.c |    7 ++++---
>  kernel/trace/trace_irqsoff.c         |    3 ++-
>  kernel/trace/trace_sched_wakeup.c    |    3 ++-
>  kernel/trace/trace_selftest.c        |    3 ++-
>  8 files changed, 27 insertions(+), 14 deletions(-)
> 
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 13987cd63553..e7c41d9988e1 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -1069,7 +1069,8 @@ struct fgraph_ops;
>  
>  /* Type of the callback handlers for tracing function graph*/
>  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
> -				       struct fgraph_ops *); /* return */
> +				       struct fgraph_ops *,
> +				       struct ftrace_regs *); /* return */
>  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
>  				      struct fgraph_ops *,
>  				      struct ftrace_regs *); /* entry */
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index 30bebe43607d..6a3e2db16aa4 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
>  }
>  
>  /* ftrace_graph_return set to this to tell some archs to run function graph */
> -static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops)
> +static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops,
> +		       struct ftrace_regs *fregs)
>  {
>  }
>  
> @@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
>  }
>  
>  static void ftrace_graph_ret_stub(struct ftrace_graph_ret *trace,
> -				  struct fgraph_ops *gops)
> +				  struct fgraph_ops *gops,
> +				  struct ftrace_regs *fregs)
>  {
>  }
>  
> @@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  	}
>  
>  	trace.rettime = trace_clock_local();
> +	if (fregs)
> +		ftrace_regs_set_instruction_pointer(fregs, ret);
> +
>  #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
>  	trace.retval = ftrace_regs_get_return_value(fregs);
>  #endif
> @@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  #ifdef CONFIG_HAVE_STATIC_CALL
>  	if (static_branch_likely(&fgraph_do_direct)) {
>  		if (test_bit(fgraph_direct_gops->idx, &bitmap))
> -			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops);
> +			static_call(fgraph_retfunc)(&trace, fgraph_direct_gops, fregs);
>  	} else
>  #endif
>  	{
> @@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
>  			if (gops == &fgraph_stub)
>  				continue;
>  
> -			gops->retfunc(&trace, gops);
> +			gops->retfunc(&trace, gops, fregs);
>  		}
>  	}
>  
> @@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)
>   * Simply points to ftrace_stub, but with the proper protocol.
>   * Defined by the linker script in linux/vmlinux.lds.h
>   */
> -void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> +void ftrace_stub_graph(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
> +		       struct ftrace_regs *fregs);
>  
>  /* The callbacks that hook a function */
>  trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 775040a9f541..fd6c5a50c5e5 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void profile_graph_return(struct ftrace_graph_ret *trace,
> -				 struct fgraph_ops *gops)
> +				 struct fgraph_ops *gops,
> +				 struct ftrace_regs *fregs)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  	struct ftrace_profile_stat *stat;
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index 28d8ad5e31e6..f4a3f75bd916 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);
>  void trace_default_header(struct seq_file *m);
>  void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
>  
> -void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
> +void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops,
> +			struct ftrace_regs *fregs);
>  int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
>  		      struct ftrace_regs *fregs);
>  
> diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> index b9785fc919c9..241407000109 100644
> --- a/kernel/trace/trace_functions_graph.c
> +++ b/kernel/trace/trace_functions_graph.c
> @@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr,
>  }
>  
>  void trace_graph_return(struct ftrace_graph_ret *trace,
> -			struct fgraph_ops *gops)
> +			struct fgraph_ops *gops, struct ftrace_regs *fregs)
>  {
>  	unsigned long *task_var = fgraph_get_task_var(gops);
>  	struct trace_array *tr = gops->private;
> @@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace,
>  }
>  
>  static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
> -				      struct fgraph_ops *gops)
> +				      struct fgraph_ops *gops,
> +				      struct ftrace_regs *fregs)
>  {
>  	ftrace_graph_addr_finish(gops, trace);
>  
> @@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
>  	    (trace->rettime - trace->calltime < tracing_thresh))
>  		return;
>  	else
> -		trace_graph_return(trace, gops);
> +		trace_graph_return(trace, gops, fregs);
>  }
>  
>  static struct fgraph_ops funcgraph_ops = {
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index ad739d76fc86..504de7a05498 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void irqsoff_graph_return(struct ftrace_graph_ret *trace,
> -				 struct fgraph_ops *gops)
> +				 struct fgraph_ops *gops,
> +				 struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = irqsoff_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
> index 23360a2700de..9ffbd9326898 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static void wakeup_graph_return(struct ftrace_graph_ret *trace,
> -				struct fgraph_ops *gops)
> +				struct fgraph_ops *gops,
> +				struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = wakeup_trace;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
> index 89067f02094a..1ebd0899238f 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace,
>  }
>  
>  static __init void store_return(struct ftrace_graph_ret *trace,
> -				struct fgraph_ops *gops)
> +				struct fgraph_ops *gops,
> +				struct ftrace_regs *fregs)
>  {
>  	struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);
>  	const char *type = fixture->store_type_name;


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-12 15:08 ` [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs Masami Hiramatsu (Google)
@ 2024-09-15  9:11   ` Steven Rostedt
  2024-09-17  9:55     ` Will Deacon
  2024-09-15  9:13   ` Steven Rostedt
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  9:11 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon, linux-arm-kernel
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland


Can I get an Acked-by from the AARCH64 maintainers for this patch?

Thanks!

-- Steve


On Fri, 13 Sep 2024 00:08:51 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Use ftrace_regs instead of fgraph_ret_regs for tracing return value
> on function_graph tracer because of simplifying the callback interface.
> 
> The CONFIG_HAVE_FUNCTION_GRAPH_RETVAL is also replaced by
> CONFIG_HAVE_FUNCTION_GRAPH_FREGS.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v8:
>   - Newly added.
> ---
>  arch/arm64/Kconfig                  |    1 +
>  arch/arm64/include/asm/ftrace.h     |   23 ++++++-----------------
>  arch/arm64/kernel/asm-offsets.c     |   12 ------------
>  arch/arm64/kernel/entry-ftrace.S    |   32 ++++++++++++++++++--------------
>  arch/loongarch/Kconfig              |    2 +-
>  arch/loongarch/include/asm/ftrace.h |   24 ++----------------------
>  arch/loongarch/kernel/asm-offsets.c |   12 ------------
>  arch/loongarch/kernel/mcount.S      |   17 ++++++++++-------
>  arch/loongarch/kernel/mcount_dyn.S  |   14 +++++++-------
>  arch/riscv/Kconfig                  |    2 +-
>  arch/riscv/include/asm/ftrace.h     |   26 +++++---------------------
>  arch/riscv/kernel/mcount.S          |   24 +++++++++++++-----------
>  arch/s390/Kconfig                   |    2 +-
>  arch/s390/include/asm/ftrace.h      |   26 +++++++++-----------------
>  arch/s390/kernel/asm-offsets.c      |    6 ------
>  arch/s390/kernel/mcount.S           |    9 +++++----
>  arch/x86/Kconfig                    |    2 +-
>  arch/x86/include/asm/ftrace.h       |   22 ++--------------------
>  arch/x86/kernel/ftrace_32.S         |   15 +++++++++------
>  arch/x86/kernel/ftrace_64.S         |   17 +++++++++--------
>  include/linux/ftrace.h              |   14 +++++++++++---
>  kernel/trace/Kconfig                |    4 ++--
>  kernel/trace/fgraph.c               |   21 +++++++++------------
>  23 files changed, 122 insertions(+), 205 deletions(-)
> 
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index a2f8ff354ca6..17947f625b06 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -211,6 +211,7 @@ config ARM64
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_FUNCTION_ERROR_INJECTION
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_GRAPH_RETVAL
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
> index dc9cf0bd2a4c..dffaab3dd1f1 100644
> --- a/arch/arm64/include/asm/ftrace.h
> +++ b/arch/arm64/include/asm/ftrace.h
> @@ -126,6 +126,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs)
>  	fregs->pc = fregs->lr;
>  }
>  
> +static __always_inline unsigned long
> +ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
> +{
> +	return fregs->fp;
> +}
> +
>  int ftrace_regs_query_register_offset(const char *name);
>  
>  int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
> @@ -183,23 +189,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym,
>  
>  #ifndef __ASSEMBLY__
>  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	/* x0 - x7 */
> -	unsigned long regs[8];
> -
> -	unsigned long fp;
> -	unsigned long __unused;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->regs[0];
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
>  
>  void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
>  			   unsigned long frame_pointer);
> diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
> index 27de1dddb0ab..9e03c9a7e5c3 100644
> --- a/arch/arm64/kernel/asm-offsets.c
> +++ b/arch/arm64/kernel/asm-offsets.c
> @@ -201,18 +201,6 @@ int main(void)
>    DEFINE(FTRACE_OPS_FUNC,		offsetof(struct ftrace_ops, func));
>  #endif
>    BLANK();
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -  DEFINE(FGRET_REGS_X0,			offsetof(struct fgraph_ret_regs, regs[0]));
> -  DEFINE(FGRET_REGS_X1,			offsetof(struct fgraph_ret_regs, regs[1]));
> -  DEFINE(FGRET_REGS_X2,			offsetof(struct fgraph_ret_regs, regs[2]));
> -  DEFINE(FGRET_REGS_X3,			offsetof(struct fgraph_ret_regs, regs[3]));
> -  DEFINE(FGRET_REGS_X4,			offsetof(struct fgraph_ret_regs, regs[4]));
> -  DEFINE(FGRET_REGS_X5,			offsetof(struct fgraph_ret_regs, regs[5]));
> -  DEFINE(FGRET_REGS_X6,			offsetof(struct fgraph_ret_regs, regs[6]));
> -  DEFINE(FGRET_REGS_X7,			offsetof(struct fgraph_ret_regs, regs[7]));
> -  DEFINE(FGRET_REGS_FP,			offsetof(struct fgraph_ret_regs, fp));
> -  DEFINE(FGRET_REGS_SIZE,		sizeof(struct fgraph_ret_regs));
> -#endif
>  #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
>    DEFINE(FTRACE_OPS_DIRECT_CALL,	offsetof(struct ftrace_ops, direct_call));
>  #endif
> diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S
> index f0c16640ef21..169ccf600066 100644
> --- a/arch/arm64/kernel/entry-ftrace.S
> +++ b/arch/arm64/kernel/entry-ftrace.S
> @@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph)
>   * @fp is checked against the value passed by ftrace_graph_caller().
>   */
>  SYM_CODE_START(return_to_handler)
> -	/* save return value regs */
> -	sub sp, sp, #FGRET_REGS_SIZE
> -	stp x0, x1, [sp, #FGRET_REGS_X0]
> -	stp x2, x3, [sp, #FGRET_REGS_X2]
> -	stp x4, x5, [sp, #FGRET_REGS_X4]
> -	stp x6, x7, [sp, #FGRET_REGS_X6]
> -	str x29,    [sp, #FGRET_REGS_FP]	// parent's fp
> +	/* Make room for ftrace_regs */
> +	sub	sp, sp, #FREGS_SIZE
> +
> +	/* Save return value regs */
> +	stp	x0, x1, [sp, #FREGS_X0]
> +	stp	x2, x3, [sp, #FREGS_X2]
> +	stp	x4, x5, [sp, #FREGS_X4]
> +	stp	x6, x7, [sp, #FREGS_X6]
> +
> +	/* Save the callsite's FP */
> +	str	x29, [sp, #FREGS_FP]
>  
>  	mov	x0, sp
> -	bl	ftrace_return_to_handler	// addr = ftrace_return_to_hander(regs);
> +	bl	ftrace_return_to_handler	// addr = ftrace_return_to_hander(fregs);
>  	mov	x30, x0				// restore the original return address
>  
> -	/* restore return value regs */
> -	ldp x0, x1, [sp, #FGRET_REGS_X0]
> -	ldp x2, x3, [sp, #FGRET_REGS_X2]
> -	ldp x4, x5, [sp, #FGRET_REGS_X4]
> -	ldp x6, x7, [sp, #FGRET_REGS_X6]
> -	add sp, sp, #FGRET_REGS_SIZE
> +	/* Restore return value regs */
> +	ldp	x0, x1, [sp, #FREGS_X0]
> +	ldp	x2, x3, [sp, #FREGS_X2]
> +	ldp	x4, x5, [sp, #FREGS_X4]
> +	ldp	x6, x7, [sp, #FREGS_X6]
> +	add	sp, sp, #FREGS_SIZE
>  
>  	ret
>  SYM_CODE_END(return_to_handler)
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 70f169210b52..974f08f65f63 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -131,7 +131,7 @@ config LOONGARCH
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_ARG_ACCESS_API
>  	select HAVE_FUNCTION_ERROR_INJECTION
> -	select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
> index 6f8517d59954..1a73f35ea9af 100644
> --- a/arch/loongarch/include/asm/ftrace.h
> +++ b/arch/loongarch/include/asm/ftrace.h
> @@ -77,6 +77,8 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
>  	override_function_with_return(&(fregs)->regs)
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	((fregs)->regs.regs[22])
>  
>  #define ftrace_graph_func ftrace_graph_func
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
> @@ -99,26 +101,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)
>  
>  #endif /* CONFIG_FUNCTION_TRACER */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	/* a0 - a1 */
> -	unsigned long regs[2];
> -
> -	unsigned long fp;
> -	unsigned long __unused;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->regs[0];
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_LOONGARCH_FTRACE_H */
> diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c
> index bee9f7a3108f..714f5b5f1956 100644
> --- a/arch/loongarch/kernel/asm-offsets.c
> +++ b/arch/loongarch/kernel/asm-offsets.c
> @@ -279,18 +279,6 @@ static void __used output_pbe_defines(void)
>  }
>  #endif
>  
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -static void __used output_fgraph_ret_regs_defines(void)
> -{
> -	COMMENT("LoongArch fgraph_ret_regs offsets.");
> -	OFFSET(FGRET_REGS_A0, fgraph_ret_regs, regs[0]);
> -	OFFSET(FGRET_REGS_A1, fgraph_ret_regs, regs[1]);
> -	OFFSET(FGRET_REGS_FP, fgraph_ret_regs, fp);
> -	DEFINE(FGRET_REGS_SIZE, sizeof(struct fgraph_ret_regs));
> -	BLANK();
> -}
> -#endif
> -
>  static void __used output_kvm_defines(void)
>  {
>  	COMMENT("KVM/LoongArch Specific offsets.");
> diff --git a/arch/loongarch/kernel/mcount.S b/arch/loongarch/kernel/mcount.S
> index 3015896016a0..b6850503e061 100644
> --- a/arch/loongarch/kernel/mcount.S
> +++ b/arch/loongarch/kernel/mcount.S
> @@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller)
>  SYM_FUNC_END(ftrace_graph_caller)
>  
>  SYM_FUNC_START(return_to_handler)
> -	PTR_ADDI	sp, sp, -FGRET_REGS_SIZE
> -	PTR_S		a0, sp, FGRET_REGS_A0
> -	PTR_S		a1, sp, FGRET_REGS_A1
> -	PTR_S		zero, sp, FGRET_REGS_FP
> +	/* Save return value regs */
> +	PTR_ADDI	sp, sp, -PT_SIZE
> +	PTR_S		a0, sp, PT_R4
> +	PTR_S		a1, sp, PT_R5
> +	PTR_S		zero, sp, PT_R22
>  
>  	move		a0, sp
>  	bl		ftrace_return_to_handler
> @@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler)
>  	/* Restore the real parent address: a0 -> ra */
>  	move		ra, a0
>  
> -	PTR_L		a0, sp, FGRET_REGS_A0
> -	PTR_L		a1, sp, FGRET_REGS_A1
> -	PTR_ADDI	sp, sp, FGRET_REGS_SIZE
> +	/* Restore return value regs */
> +	PTR_L		a0, sp, PT_R4
> +	PTR_L		a1, sp, PT_R5
> +	PTR_ADDI	sp, sp, PT_SIZE
> +
>  	jr		ra
>  SYM_FUNC_END(return_to_handler)
>  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> diff --git a/arch/loongarch/kernel/mcount_dyn.S b/arch/loongarch/kernel/mcount_dyn.S
> index 0c65cf09110c..d6b474ad1d5e 100644
> --- a/arch/loongarch/kernel/mcount_dyn.S
> +++ b/arch/loongarch/kernel/mcount_dyn.S
> @@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller)
>  SYM_CODE_START(return_to_handler)
>  	UNWIND_HINT_UNDEFINED
>  	/* Save return value regs */
> -	PTR_ADDI	sp, sp, -FGRET_REGS_SIZE
> -	PTR_S		a0, sp, FGRET_REGS_A0
> -	PTR_S		a1, sp, FGRET_REGS_A1
> -	PTR_S		zero, sp, FGRET_REGS_FP
> +	PTR_ADDI	sp, sp, -PT_SIZE
> +	PTR_S		a0, sp, PT_R4
> +	PTR_S		a1, sp, PT_R5
> +	PTR_S		zero, sp, PT_R22
>  
>  	move		a0, sp
>  	bl		ftrace_return_to_handler
>  	move		ra, a0
>  
>  	/* Restore return value regs */
> -	PTR_L		a0, sp, FGRET_REGS_A0
> -	PTR_L		a1, sp, FGRET_REGS_A1
> -	PTR_ADDI	sp, sp, FGRET_REGS_SIZE
> +	PTR_L		a0, sp, PT_R4
> +	PTR_L		a1, sp, PT_R5
> +	PTR_ADDI	sp, sp, PT_SIZE
>  
>  	jr		ra
>  SYM_CODE_END(return_to_handler)
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 0f3cd7c3a436..6e8422269ba4 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -140,7 +140,7 @@ config RISCV
>  	select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE
>  	select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL
>  	select HAVE_FUNCTION_GRAPH_TRACER
> -	select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION
>  	select HAVE_EBPF_JIT if MMU
>  	select HAVE_GUP_FAST if MMU
> diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
> index 2cddd79ff21b..e9f364ce9fe8 100644
> --- a/arch/riscv/include/asm/ftrace.h
> +++ b/arch/riscv/include/asm/ftrace.h
> @@ -164,6 +164,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const struct
>  	return fregs->sp;
>  }
>  
> +static __always_inline unsigned long ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
> +{
> +	return fregs->s0;
> +}
> +
>  static __always_inline unsigned long ftrace_regs_get_argument(struct ftrace_regs *fregs,
>  							      unsigned int n)
>  {
> @@ -204,25 +209,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi
>  
>  #endif /* CONFIG_DYNAMIC_FTRACE */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long a1;
> -	unsigned long a0;
> -	unsigned long s0;
> -	unsigned long ra;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->a0;
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->s0;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_RISCV_FTRACE_H */
> diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S
> index 3a42f6287909..068168046e0e 100644
> --- a/arch/riscv/kernel/mcount.S
> +++ b/arch/riscv/kernel/mcount.S
> @@ -12,6 +12,8 @@
>  #include <asm/asm-offsets.h>
>  #include <asm/ftrace.h>
>  
> +#define ABI_SIZE_ON_STACK	80
> +
>  	.text
>  
>  	.macro SAVE_ABI_STATE
> @@ -26,12 +28,12 @@
>  	 * register if a0 was not saved.
>  	 */
>  	.macro SAVE_RET_ABI_STATE
> -	addi	sp, sp, -4*SZREG
> -	REG_S	s0, 2*SZREG(sp)
> -	REG_S	ra, 3*SZREG(sp)
> -	REG_S	a0, 1*SZREG(sp)
> -	REG_S	a1, 0*SZREG(sp)
> -	addi	s0, sp, 4*SZREG
> +	addi	sp, sp, -ABI_SIZE_ON_STACK
> +	REG_S	ra, 1*SZREG(sp)
> +	REG_S	s0, 8*SZREG(sp)
> +	REG_S	a0, 10*SZREG(sp)
> +	REG_S	a1, 11*SZREG(sp)
> +	addi	s0, sp, ABI_SIZE_ON_STACK
>  	.endm
>  
>  	.macro RESTORE_ABI_STATE
> @@ -41,11 +43,11 @@
>  	.endm
>  
>  	.macro RESTORE_RET_ABI_STATE
> -	REG_L	ra, 3*SZREG(sp)
> -	REG_L	s0, 2*SZREG(sp)
> -	REG_L	a0, 1*SZREG(sp)
> -	REG_L	a1, 0*SZREG(sp)
> -	addi	sp, sp, 4*SZREG
> +	REG_L	ra, 1*SZREG(sp)
> +	REG_L	s0, 8*SZREG(sp)
> +	REG_L	a0, 10*SZREG(sp)
> +	REG_L	a1, 11*SZREG(sp)
> +	addi	sp, sp, ABI_SIZE_ON_STACK
>  	.endm
>  
>  SYM_TYPED_FUNC_START(ftrace_stub)
> diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
> index a822f952f64a..12e942cfbcde 100644
> --- a/arch/s390/Kconfig
> +++ b/arch/s390/Kconfig
> @@ -184,7 +184,7 @@ config S390
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_ARG_ACCESS_API
>  	select HAVE_FUNCTION_ERROR_INJECTION
> -	select HAVE_FUNCTION_GRAPH_RETVAL
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
> index de76c21eb4a3..9cdd48a46bf7 100644
> --- a/arch/s390/include/asm/ftrace.h
> +++ b/arch/s390/include/asm/ftrace.h
> @@ -49,23 +49,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *
>  	return NULL;
>  }
>  
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long gpr2;
> -	unsigned long fp;
> -};
> -
> -static __always_inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->gpr2;
> -}
> -
> -static __always_inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
> -#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> -
>  static __always_inline unsigned long
>  ftrace_regs_get_instruction_pointer(const struct ftrace_regs *fregs)
>  {
> @@ -92,6 +75,15 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
>  
> +static __always_inline unsigned long
> +ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)
> +{
> +	unsigned long *sp;
> +
> +	sp = (void *)ftrace_regs_get_stack_pointer(fregs);
> +	return sp[0];	/* return backchain */
> +}
> +
>  #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
>  /*
>   * When an ftrace registered caller is tracing a function that is
> diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
> index ffa0dd2dbaac..d38ed80615d5 100644
> --- a/arch/s390/kernel/asm-offsets.c
> +++ b/arch/s390/kernel/asm-offsets.c
> @@ -179,12 +179,6 @@ int main(void)
>  	DEFINE(OLDMEM_SIZE, PARMAREA + offsetof(struct parmarea, oldmem_size));
>  	DEFINE(COMMAND_LINE, PARMAREA + offsetof(struct parmarea, command_line));
>  	DEFINE(MAX_COMMAND_LINE_SIZE, PARMAREA + offsetof(struct parmarea, max_command_line_size));
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -	/* function graph return value tracing */
> -	OFFSET(__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2);
> -	OFFSET(__FGRAPH_RET_FP, fgraph_ret_regs, fp);
> -	DEFINE(__FGRAPH_RET_SIZE, sizeof(struct fgraph_ret_regs));
> -#endif
>  	OFFSET(__FTRACE_REGS_PT_REGS, ftrace_regs, regs);
>  	DEFINE(__FTRACE_REGS_SIZE, sizeof(struct ftrace_regs));
>  
> diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
> index ae4d4fd9afcd..cda798b976de 100644
> --- a/arch/s390/kernel/mcount.S
> +++ b/arch/s390/kernel/mcount.S
> @@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common)
>  SYM_FUNC_START(return_to_handler)
>  	stmg	%r2,%r5,32(%r15)
>  	lgr	%r1,%r15
> -	aghi	%r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE)
> +# Allocate ftrace_regs + backchain on the stack
> +	aghi	%r15,-STACK_FRAME_SIZE_FREGS
>  	stg	%r1,__SF_BACKCHAIN(%r15)
>  	la	%r3,STACK_FRAME_OVERHEAD(%r15)
> -	stg	%r1,__FGRAPH_RET_FP(%r3)
> -	stg	%r2,__FGRAPH_RET_GPR2(%r3)
> +	stg	%r2,(__SF_GPRS+2*8)(%r15)
> +	stg	%r15,(__SF_GPRS+15*8)(%r15)
>  	lgr	%r2,%r3
>  	brasl	%r14,ftrace_return_to_handler
> -	aghi	%r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE
> +	aghi	%r15,STACK_FRAME_SIZE_FREGS
>  	lgr	%r14,%r2
>  	lmg	%r2,%r5,32(%r15)
>  	BR_EX	%r14
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 007bab9f2a0e..047384e4d93a 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -228,7 +228,7 @@ config X86
>  	select HAVE_GUP_FAST
>  	select HAVE_FENTRY			if X86_64 || DYNAMIC_FTRACE
>  	select HAVE_FTRACE_MCOUNT_RECORD
> -	select HAVE_FUNCTION_GRAPH_RETVAL	if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS	if HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_GRAPH_TRACER	if X86_32 || (X86_64 && DYNAMIC_FTRACE)
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
> index 78f6a200e15b..669771ef3b5b 100644
> --- a/arch/x86/include/asm/ftrace.h
> +++ b/arch/x86/include/asm/ftrace.h
> @@ -64,6 +64,8 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
>  	override_function_with_return(&(fregs)->regs)
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	frame_pointer(&(fregs)->regs)
>  
>  struct ftrace_ops;
>  #define ftrace_graph_func ftrace_graph_func
> @@ -148,24 +150,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
>  #endif /* !COMPILE_OFFSETS */
>  #endif /* !__ASSEMBLY__ */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long ax;
> -	unsigned long dx;
> -	unsigned long bp;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->ax;
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->bp;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_X86_FTRACE_H */
> diff --git a/arch/x86/kernel/ftrace_32.S b/arch/x86/kernel/ftrace_32.S
> index 58d9ed50fe61..4b265884d06c 100644
> --- a/arch/x86/kernel/ftrace_32.S
> +++ b/arch/x86/kernel/ftrace_32.S
> @@ -23,6 +23,8 @@ SYM_FUNC_START(__fentry__)
>  SYM_FUNC_END(__fentry__)
>  EXPORT_SYMBOL(__fentry__)
>  
> +#define FRAME_SIZE	PT_OLDSS+4
> +
>  SYM_CODE_START(ftrace_caller)
>  
>  #ifdef CONFIG_FRAME_POINTER
> @@ -187,14 +189,15 @@ SYM_CODE_END(ftrace_graph_caller)
>  
>  .globl return_to_handler
>  return_to_handler:
> -	pushl	$0
> -	pushl	%edx
> -	pushl	%eax
> +	subl	$(FRAME_SIZE), %esp
> +	movl	$0, PT_EBP(%esp)
> +	movl	%edx, PT_EDX(%esp)
> +	movl	%eax, PT_EAX(%esp)
>  	movl	%esp, %eax
>  	call	ftrace_return_to_handler
>  	movl	%eax, %ecx
> -	popl	%eax
> -	popl	%edx
> -	addl	$4, %esp		# skip ebp
> +	movl	%eax, PT_EAX(%esp)
> +	movl	%edx, PT_EDX(%esp)
> +	addl	$(FRAME_SIZE), %esp
>  	JMP_NOSPEC ecx
>  #endif
> diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
> index 214f30e9f0c0..d51647228596 100644
> --- a/arch/x86/kernel/ftrace_64.S
> +++ b/arch/x86/kernel/ftrace_64.S
> @@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__)
>  SYM_CODE_START(return_to_handler)
>  	UNWIND_HINT_UNDEFINED
>  	ANNOTATE_NOENDBR
> -	subq  $24, %rsp
>  
> -	/* Save the return values */
> -	movq %rax, (%rsp)
> -	movq %rdx, 8(%rsp)
> -	movq %rbp, 16(%rsp)
> +	/* Save ftrace_regs for function exit context  */
> +	subq $(FRAME_SIZE), %rsp
> +
> +	movq %rax, RAX(%rsp)
> +	movq %rdx, RDX(%rsp)
> +	movq %rbp, RBP(%rsp)
>  	movq %rsp, %rdi
>  
>  	call ftrace_return_to_handler
>  
>  	movq %rax, %rdi
> -	movq 8(%rsp), %rdx
> -	movq (%rsp), %rax
> +	movq RDX(%rsp), %rdx
> +	movq RAX(%rsp), %rax
>  
> -	addq $24, %rsp
> +	addq $(FRAME_SIZE), %rsp
>  	/*
>  	 * Jump back to the old return address. This cannot be JMP_NOSPEC rdi
>  	 * since IBT would demand that contain ENDBR, which simply isn't so for
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 1fe49a28de2d..13987cd63553 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -43,9 +43,8 @@ struct dyn_ftrace;
>  
>  char *arch_ftrace_match_adjust(char *str, const char *search);
>  
> -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
> -struct fgraph_ret_regs;
> -unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs);
> +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS
> +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs);
>  #else
>  unsigned long ftrace_return_to_handler(unsigned long frame_pointer);
>  #endif
> @@ -134,6 +133,13 @@ extern int ftrace_enabled;
>   * Also, architecture dependent fields can be used for internal process.
>   * (e.g. orig_ax on x86_64)
>   *
> + * Basically, ftrace_regs stores the registers related to the context.
> + * On function entry, registers for function parameters and hooking the
> + * function call are stored, and on function exit, registers for function
> + * return value and frame pointers are stored.
> + *
> + * And also, it dpends on the context that which registers are restored
> + * from the ftrace_regs.
>   * On the function entry, those registers will be restored except for
>   * the stack pointer, so that user can change the function parameters
>   * and instruction pointer (e.g. live patching.)
> @@ -191,6 +197,8 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs)
>  	override_function_with_return(ftrace_get_regs(fregs))
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	frame_pointer(&(fregs)->regs)
>  #endif
>  
>  typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
> diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
> index 721c3b221048..ab277eff80dc 100644
> --- a/kernel/trace/Kconfig
> +++ b/kernel/trace/Kconfig
> @@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER
>  	help
>  	  See Documentation/trace/ftrace-design.rst
>  
> -config HAVE_FUNCTION_GRAPH_RETVAL
> +config HAVE_FUNCTION_GRAPH_FREGS
>  	bool
>  
>  config HAVE_DYNAMIC_FTRACE
> @@ -232,7 +232,7 @@ config FUNCTION_GRAPH_TRACER
>  
>  config FUNCTION_GRAPH_RETVAL
>  	bool "Kernel Function Graph Return Value"
> -	depends on HAVE_FUNCTION_GRAPH_RETVAL
> +	depends on HAVE_FUNCTION_GRAPH_FREGS
>  	depends on FUNCTION_GRAPH_TRACER
>  	default n
>  	help
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index 0322c5723748..30bebe43607d 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -763,15 +763,12 @@ static struct notifier_block ftrace_suspend_notifier = {
>  	.notifier_call = ftrace_suspend_notifier_call,
>  };
>  
> -/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */
> -struct fgraph_ret_regs;
> -
>  /*
>   * Send the trace to the ring-buffer.
>   * @return the original return address.
>   */
> -static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs,
> -						unsigned long frame_pointer)
> +static inline unsigned long
> +__ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointer)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  	struct ftrace_graph_ret trace;
> @@ -791,7 +788,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
>  
>  	trace.rettime = trace_clock_local();
>  #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
> -	trace.retval = fgraph_ret_regs_return_value(ret_regs);
> +	trace.retval = ftrace_regs_get_return_value(fregs);
>  #endif
>  
>  	bitmap = get_bitmap_bits(current, offset);
> @@ -826,14 +823,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
>  }
>  
>  /*
> - * After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can
> - * leave only ftrace_return_to_handler(ret_regs).
> + * After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can
> + * leave only ftrace_return_to_handler(fregs).
>   */
> -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
> -unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs)
> +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS
> +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs)
>  {
> -	return __ftrace_return_to_handler(ret_regs,
> -				fgraph_ret_regs_frame_pointer(ret_regs));
> +	return __ftrace_return_to_handler(fregs,
> +				ftrace_regs_get_frame_pointer(fregs));
>  }
>  #else
>  unsigned long ftrace_return_to_handler(unsigned long frame_pointer)


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-12 15:08 ` [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs Masami Hiramatsu (Google)
  2024-09-15  9:11   ` Steven Rostedt
@ 2024-09-15  9:13   ` Steven Rostedt
  2024-09-15  9:15   ` Steven Rostedt
  2024-09-15  9:17   ` Steven Rostedt
  3 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  9:13 UTC (permalink / raw)
  To: Paul Walmsley, Palmer Dabbelt, Albert Ou, linux-riscv
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland


Can I get an Acked-by from the RISC-V maintainers for this patch?

Thanks!

-- Steve


On Fri, 13 Sep 2024 00:08:51 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Use ftrace_regs instead of fgraph_ret_regs for tracing return value
> on function_graph tracer because of simplifying the callback interface.
> 
> The CONFIG_HAVE_FUNCTION_GRAPH_RETVAL is also replaced by
> CONFIG_HAVE_FUNCTION_GRAPH_FREGS.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v8:
>   - Newly added.
> ---
>  arch/arm64/Kconfig                  |    1 +
>  arch/arm64/include/asm/ftrace.h     |   23 ++++++-----------------
>  arch/arm64/kernel/asm-offsets.c     |   12 ------------
>  arch/arm64/kernel/entry-ftrace.S    |   32 ++++++++++++++++++--------------
>  arch/loongarch/Kconfig              |    2 +-
>  arch/loongarch/include/asm/ftrace.h |   24 ++----------------------
>  arch/loongarch/kernel/asm-offsets.c |   12 ------------
>  arch/loongarch/kernel/mcount.S      |   17 ++++++++++-------
>  arch/loongarch/kernel/mcount_dyn.S  |   14 +++++++-------
>  arch/riscv/Kconfig                  |    2 +-
>  arch/riscv/include/asm/ftrace.h     |   26 +++++---------------------
>  arch/riscv/kernel/mcount.S          |   24 +++++++++++++-----------
>  arch/s390/Kconfig                   |    2 +-
>  arch/s390/include/asm/ftrace.h      |   26 +++++++++-----------------
>  arch/s390/kernel/asm-offsets.c      |    6 ------
>  arch/s390/kernel/mcount.S           |    9 +++++----
>  arch/x86/Kconfig                    |    2 +-
>  arch/x86/include/asm/ftrace.h       |   22 ++--------------------
>  arch/x86/kernel/ftrace_32.S         |   15 +++++++++------
>  arch/x86/kernel/ftrace_64.S         |   17 +++++++++--------
>  include/linux/ftrace.h              |   14 +++++++++++---
>  kernel/trace/Kconfig                |    4 ++--
>  kernel/trace/fgraph.c               |   21 +++++++++------------
>  23 files changed, 122 insertions(+), 205 deletions(-)
> 
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index a2f8ff354ca6..17947f625b06 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -211,6 +211,7 @@ config ARM64
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_FUNCTION_ERROR_INJECTION
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_GRAPH_RETVAL
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
> index dc9cf0bd2a4c..dffaab3dd1f1 100644
> --- a/arch/arm64/include/asm/ftrace.h
> +++ b/arch/arm64/include/asm/ftrace.h
> @@ -126,6 +126,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs)
>  	fregs->pc = fregs->lr;
>  }
>  
> +static __always_inline unsigned long
> +ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
> +{
> +	return fregs->fp;
> +}
> +
>  int ftrace_regs_query_register_offset(const char *name);
>  
>  int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
> @@ -183,23 +189,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym,
>  
>  #ifndef __ASSEMBLY__
>  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	/* x0 - x7 */
> -	unsigned long regs[8];
> -
> -	unsigned long fp;
> -	unsigned long __unused;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->regs[0];
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
>  
>  void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
>  			   unsigned long frame_pointer);
> diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
> index 27de1dddb0ab..9e03c9a7e5c3 100644
> --- a/arch/arm64/kernel/asm-offsets.c
> +++ b/arch/arm64/kernel/asm-offsets.c
> @@ -201,18 +201,6 @@ int main(void)
>    DEFINE(FTRACE_OPS_FUNC,		offsetof(struct ftrace_ops, func));
>  #endif
>    BLANK();
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -  DEFINE(FGRET_REGS_X0,			offsetof(struct fgraph_ret_regs, regs[0]));
> -  DEFINE(FGRET_REGS_X1,			offsetof(struct fgraph_ret_regs, regs[1]));
> -  DEFINE(FGRET_REGS_X2,			offsetof(struct fgraph_ret_regs, regs[2]));
> -  DEFINE(FGRET_REGS_X3,			offsetof(struct fgraph_ret_regs, regs[3]));
> -  DEFINE(FGRET_REGS_X4,			offsetof(struct fgraph_ret_regs, regs[4]));
> -  DEFINE(FGRET_REGS_X5,			offsetof(struct fgraph_ret_regs, regs[5]));
> -  DEFINE(FGRET_REGS_X6,			offsetof(struct fgraph_ret_regs, regs[6]));
> -  DEFINE(FGRET_REGS_X7,			offsetof(struct fgraph_ret_regs, regs[7]));
> -  DEFINE(FGRET_REGS_FP,			offsetof(struct fgraph_ret_regs, fp));
> -  DEFINE(FGRET_REGS_SIZE,		sizeof(struct fgraph_ret_regs));
> -#endif
>  #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
>    DEFINE(FTRACE_OPS_DIRECT_CALL,	offsetof(struct ftrace_ops, direct_call));
>  #endif
> diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S
> index f0c16640ef21..169ccf600066 100644
> --- a/arch/arm64/kernel/entry-ftrace.S
> +++ b/arch/arm64/kernel/entry-ftrace.S
> @@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph)
>   * @fp is checked against the value passed by ftrace_graph_caller().
>   */
>  SYM_CODE_START(return_to_handler)
> -	/* save return value regs */
> -	sub sp, sp, #FGRET_REGS_SIZE
> -	stp x0, x1, [sp, #FGRET_REGS_X0]
> -	stp x2, x3, [sp, #FGRET_REGS_X2]
> -	stp x4, x5, [sp, #FGRET_REGS_X4]
> -	stp x6, x7, [sp, #FGRET_REGS_X6]
> -	str x29,    [sp, #FGRET_REGS_FP]	// parent's fp
> +	/* Make room for ftrace_regs */
> +	sub	sp, sp, #FREGS_SIZE
> +
> +	/* Save return value regs */
> +	stp	x0, x1, [sp, #FREGS_X0]
> +	stp	x2, x3, [sp, #FREGS_X2]
> +	stp	x4, x5, [sp, #FREGS_X4]
> +	stp	x6, x7, [sp, #FREGS_X6]
> +
> +	/* Save the callsite's FP */
> +	str	x29, [sp, #FREGS_FP]
>  
>  	mov	x0, sp
> -	bl	ftrace_return_to_handler	// addr = ftrace_return_to_hander(regs);
> +	bl	ftrace_return_to_handler	// addr = ftrace_return_to_hander(fregs);
>  	mov	x30, x0				// restore the original return address
>  
> -	/* restore return value regs */
> -	ldp x0, x1, [sp, #FGRET_REGS_X0]
> -	ldp x2, x3, [sp, #FGRET_REGS_X2]
> -	ldp x4, x5, [sp, #FGRET_REGS_X4]
> -	ldp x6, x7, [sp, #FGRET_REGS_X6]
> -	add sp, sp, #FGRET_REGS_SIZE
> +	/* Restore return value regs */
> +	ldp	x0, x1, [sp, #FREGS_X0]
> +	ldp	x2, x3, [sp, #FREGS_X2]
> +	ldp	x4, x5, [sp, #FREGS_X4]
> +	ldp	x6, x7, [sp, #FREGS_X6]
> +	add	sp, sp, #FREGS_SIZE
>  
>  	ret
>  SYM_CODE_END(return_to_handler)
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 70f169210b52..974f08f65f63 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -131,7 +131,7 @@ config LOONGARCH
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_ARG_ACCESS_API
>  	select HAVE_FUNCTION_ERROR_INJECTION
> -	select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
> index 6f8517d59954..1a73f35ea9af 100644
> --- a/arch/loongarch/include/asm/ftrace.h
> +++ b/arch/loongarch/include/asm/ftrace.h
> @@ -77,6 +77,8 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
>  	override_function_with_return(&(fregs)->regs)
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	((fregs)->regs.regs[22])
>  
>  #define ftrace_graph_func ftrace_graph_func
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
> @@ -99,26 +101,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)
>  
>  #endif /* CONFIG_FUNCTION_TRACER */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	/* a0 - a1 */
> -	unsigned long regs[2];
> -
> -	unsigned long fp;
> -	unsigned long __unused;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->regs[0];
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_LOONGARCH_FTRACE_H */
> diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c
> index bee9f7a3108f..714f5b5f1956 100644
> --- a/arch/loongarch/kernel/asm-offsets.c
> +++ b/arch/loongarch/kernel/asm-offsets.c
> @@ -279,18 +279,6 @@ static void __used output_pbe_defines(void)
>  }
>  #endif
>  
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -static void __used output_fgraph_ret_regs_defines(void)
> -{
> -	COMMENT("LoongArch fgraph_ret_regs offsets.");
> -	OFFSET(FGRET_REGS_A0, fgraph_ret_regs, regs[0]);
> -	OFFSET(FGRET_REGS_A1, fgraph_ret_regs, regs[1]);
> -	OFFSET(FGRET_REGS_FP, fgraph_ret_regs, fp);
> -	DEFINE(FGRET_REGS_SIZE, sizeof(struct fgraph_ret_regs));
> -	BLANK();
> -}
> -#endif
> -
>  static void __used output_kvm_defines(void)
>  {
>  	COMMENT("KVM/LoongArch Specific offsets.");
> diff --git a/arch/loongarch/kernel/mcount.S b/arch/loongarch/kernel/mcount.S
> index 3015896016a0..b6850503e061 100644
> --- a/arch/loongarch/kernel/mcount.S
> +++ b/arch/loongarch/kernel/mcount.S
> @@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller)
>  SYM_FUNC_END(ftrace_graph_caller)
>  
>  SYM_FUNC_START(return_to_handler)
> -	PTR_ADDI	sp, sp, -FGRET_REGS_SIZE
> -	PTR_S		a0, sp, FGRET_REGS_A0
> -	PTR_S		a1, sp, FGRET_REGS_A1
> -	PTR_S		zero, sp, FGRET_REGS_FP
> +	/* Save return value regs */
> +	PTR_ADDI	sp, sp, -PT_SIZE
> +	PTR_S		a0, sp, PT_R4
> +	PTR_S		a1, sp, PT_R5
> +	PTR_S		zero, sp, PT_R22
>  
>  	move		a0, sp
>  	bl		ftrace_return_to_handler
> @@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler)
>  	/* Restore the real parent address: a0 -> ra */
>  	move		ra, a0
>  
> -	PTR_L		a0, sp, FGRET_REGS_A0
> -	PTR_L		a1, sp, FGRET_REGS_A1
> -	PTR_ADDI	sp, sp, FGRET_REGS_SIZE
> +	/* Restore return value regs */
> +	PTR_L		a0, sp, PT_R4
> +	PTR_L		a1, sp, PT_R5
> +	PTR_ADDI	sp, sp, PT_SIZE
> +
>  	jr		ra
>  SYM_FUNC_END(return_to_handler)
>  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> diff --git a/arch/loongarch/kernel/mcount_dyn.S b/arch/loongarch/kernel/mcount_dyn.S
> index 0c65cf09110c..d6b474ad1d5e 100644
> --- a/arch/loongarch/kernel/mcount_dyn.S
> +++ b/arch/loongarch/kernel/mcount_dyn.S
> @@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller)
>  SYM_CODE_START(return_to_handler)
>  	UNWIND_HINT_UNDEFINED
>  	/* Save return value regs */
> -	PTR_ADDI	sp, sp, -FGRET_REGS_SIZE
> -	PTR_S		a0, sp, FGRET_REGS_A0
> -	PTR_S		a1, sp, FGRET_REGS_A1
> -	PTR_S		zero, sp, FGRET_REGS_FP
> +	PTR_ADDI	sp, sp, -PT_SIZE
> +	PTR_S		a0, sp, PT_R4
> +	PTR_S		a1, sp, PT_R5
> +	PTR_S		zero, sp, PT_R22
>  
>  	move		a0, sp
>  	bl		ftrace_return_to_handler
>  	move		ra, a0
>  
>  	/* Restore return value regs */
> -	PTR_L		a0, sp, FGRET_REGS_A0
> -	PTR_L		a1, sp, FGRET_REGS_A1
> -	PTR_ADDI	sp, sp, FGRET_REGS_SIZE
> +	PTR_L		a0, sp, PT_R4
> +	PTR_L		a1, sp, PT_R5
> +	PTR_ADDI	sp, sp, PT_SIZE
>  
>  	jr		ra
>  SYM_CODE_END(return_to_handler)
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 0f3cd7c3a436..6e8422269ba4 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -140,7 +140,7 @@ config RISCV
>  	select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE
>  	select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL
>  	select HAVE_FUNCTION_GRAPH_TRACER
> -	select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION
>  	select HAVE_EBPF_JIT if MMU
>  	select HAVE_GUP_FAST if MMU
> diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
> index 2cddd79ff21b..e9f364ce9fe8 100644
> --- a/arch/riscv/include/asm/ftrace.h
> +++ b/arch/riscv/include/asm/ftrace.h
> @@ -164,6 +164,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const struct
>  	return fregs->sp;
>  }
>  
> +static __always_inline unsigned long ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
> +{
> +	return fregs->s0;
> +}
> +
>  static __always_inline unsigned long ftrace_regs_get_argument(struct ftrace_regs *fregs,
>  							      unsigned int n)
>  {
> @@ -204,25 +209,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi
>  
>  #endif /* CONFIG_DYNAMIC_FTRACE */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long a1;
> -	unsigned long a0;
> -	unsigned long s0;
> -	unsigned long ra;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->a0;
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->s0;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_RISCV_FTRACE_H */
> diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S
> index 3a42f6287909..068168046e0e 100644
> --- a/arch/riscv/kernel/mcount.S
> +++ b/arch/riscv/kernel/mcount.S
> @@ -12,6 +12,8 @@
>  #include <asm/asm-offsets.h>
>  #include <asm/ftrace.h>
>  
> +#define ABI_SIZE_ON_STACK	80
> +
>  	.text
>  
>  	.macro SAVE_ABI_STATE
> @@ -26,12 +28,12 @@
>  	 * register if a0 was not saved.
>  	 */
>  	.macro SAVE_RET_ABI_STATE
> -	addi	sp, sp, -4*SZREG
> -	REG_S	s0, 2*SZREG(sp)
> -	REG_S	ra, 3*SZREG(sp)
> -	REG_S	a0, 1*SZREG(sp)
> -	REG_S	a1, 0*SZREG(sp)
> -	addi	s0, sp, 4*SZREG
> +	addi	sp, sp, -ABI_SIZE_ON_STACK
> +	REG_S	ra, 1*SZREG(sp)
> +	REG_S	s0, 8*SZREG(sp)
> +	REG_S	a0, 10*SZREG(sp)
> +	REG_S	a1, 11*SZREG(sp)
> +	addi	s0, sp, ABI_SIZE_ON_STACK
>  	.endm
>  
>  	.macro RESTORE_ABI_STATE
> @@ -41,11 +43,11 @@
>  	.endm
>  
>  	.macro RESTORE_RET_ABI_STATE
> -	REG_L	ra, 3*SZREG(sp)
> -	REG_L	s0, 2*SZREG(sp)
> -	REG_L	a0, 1*SZREG(sp)
> -	REG_L	a1, 0*SZREG(sp)
> -	addi	sp, sp, 4*SZREG
> +	REG_L	ra, 1*SZREG(sp)
> +	REG_L	s0, 8*SZREG(sp)
> +	REG_L	a0, 10*SZREG(sp)
> +	REG_L	a1, 11*SZREG(sp)
> +	addi	sp, sp, ABI_SIZE_ON_STACK
>  	.endm
>  
>  SYM_TYPED_FUNC_START(ftrace_stub)
> diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
> index a822f952f64a..12e942cfbcde 100644
> --- a/arch/s390/Kconfig
> +++ b/arch/s390/Kconfig
> @@ -184,7 +184,7 @@ config S390
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_ARG_ACCESS_API
>  	select HAVE_FUNCTION_ERROR_INJECTION
> -	select HAVE_FUNCTION_GRAPH_RETVAL
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
> index de76c21eb4a3..9cdd48a46bf7 100644
> --- a/arch/s390/include/asm/ftrace.h
> +++ b/arch/s390/include/asm/ftrace.h
> @@ -49,23 +49,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *
>  	return NULL;
>  }
>  
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long gpr2;
> -	unsigned long fp;
> -};
> -
> -static __always_inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->gpr2;
> -}
> -
> -static __always_inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
> -#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> -
>  static __always_inline unsigned long
>  ftrace_regs_get_instruction_pointer(const struct ftrace_regs *fregs)
>  {
> @@ -92,6 +75,15 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
>  
> +static __always_inline unsigned long
> +ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)
> +{
> +	unsigned long *sp;
> +
> +	sp = (void *)ftrace_regs_get_stack_pointer(fregs);
> +	return sp[0];	/* return backchain */
> +}
> +
>  #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
>  /*
>   * When an ftrace registered caller is tracing a function that is
> diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
> index ffa0dd2dbaac..d38ed80615d5 100644
> --- a/arch/s390/kernel/asm-offsets.c
> +++ b/arch/s390/kernel/asm-offsets.c
> @@ -179,12 +179,6 @@ int main(void)
>  	DEFINE(OLDMEM_SIZE, PARMAREA + offsetof(struct parmarea, oldmem_size));
>  	DEFINE(COMMAND_LINE, PARMAREA + offsetof(struct parmarea, command_line));
>  	DEFINE(MAX_COMMAND_LINE_SIZE, PARMAREA + offsetof(struct parmarea, max_command_line_size));
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -	/* function graph return value tracing */
> -	OFFSET(__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2);
> -	OFFSET(__FGRAPH_RET_FP, fgraph_ret_regs, fp);
> -	DEFINE(__FGRAPH_RET_SIZE, sizeof(struct fgraph_ret_regs));
> -#endif
>  	OFFSET(__FTRACE_REGS_PT_REGS, ftrace_regs, regs);
>  	DEFINE(__FTRACE_REGS_SIZE, sizeof(struct ftrace_regs));
>  
> diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
> index ae4d4fd9afcd..cda798b976de 100644
> --- a/arch/s390/kernel/mcount.S
> +++ b/arch/s390/kernel/mcount.S
> @@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common)
>  SYM_FUNC_START(return_to_handler)
>  	stmg	%r2,%r5,32(%r15)
>  	lgr	%r1,%r15
> -	aghi	%r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE)
> +# Allocate ftrace_regs + backchain on the stack
> +	aghi	%r15,-STACK_FRAME_SIZE_FREGS
>  	stg	%r1,__SF_BACKCHAIN(%r15)
>  	la	%r3,STACK_FRAME_OVERHEAD(%r15)
> -	stg	%r1,__FGRAPH_RET_FP(%r3)
> -	stg	%r2,__FGRAPH_RET_GPR2(%r3)
> +	stg	%r2,(__SF_GPRS+2*8)(%r15)
> +	stg	%r15,(__SF_GPRS+15*8)(%r15)
>  	lgr	%r2,%r3
>  	brasl	%r14,ftrace_return_to_handler
> -	aghi	%r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE
> +	aghi	%r15,STACK_FRAME_SIZE_FREGS
>  	lgr	%r14,%r2
>  	lmg	%r2,%r5,32(%r15)
>  	BR_EX	%r14
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 007bab9f2a0e..047384e4d93a 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -228,7 +228,7 @@ config X86
>  	select HAVE_GUP_FAST
>  	select HAVE_FENTRY			if X86_64 || DYNAMIC_FTRACE
>  	select HAVE_FTRACE_MCOUNT_RECORD
> -	select HAVE_FUNCTION_GRAPH_RETVAL	if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS	if HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_GRAPH_TRACER	if X86_32 || (X86_64 && DYNAMIC_FTRACE)
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
> index 78f6a200e15b..669771ef3b5b 100644
> --- a/arch/x86/include/asm/ftrace.h
> +++ b/arch/x86/include/asm/ftrace.h
> @@ -64,6 +64,8 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
>  	override_function_with_return(&(fregs)->regs)
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	frame_pointer(&(fregs)->regs)
>  
>  struct ftrace_ops;
>  #define ftrace_graph_func ftrace_graph_func
> @@ -148,24 +150,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
>  #endif /* !COMPILE_OFFSETS */
>  #endif /* !__ASSEMBLY__ */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long ax;
> -	unsigned long dx;
> -	unsigned long bp;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->ax;
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->bp;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_X86_FTRACE_H */
> diff --git a/arch/x86/kernel/ftrace_32.S b/arch/x86/kernel/ftrace_32.S
> index 58d9ed50fe61..4b265884d06c 100644
> --- a/arch/x86/kernel/ftrace_32.S
> +++ b/arch/x86/kernel/ftrace_32.S
> @@ -23,6 +23,8 @@ SYM_FUNC_START(__fentry__)
>  SYM_FUNC_END(__fentry__)
>  EXPORT_SYMBOL(__fentry__)
>  
> +#define FRAME_SIZE	PT_OLDSS+4
> +
>  SYM_CODE_START(ftrace_caller)
>  
>  #ifdef CONFIG_FRAME_POINTER
> @@ -187,14 +189,15 @@ SYM_CODE_END(ftrace_graph_caller)
>  
>  .globl return_to_handler
>  return_to_handler:
> -	pushl	$0
> -	pushl	%edx
> -	pushl	%eax
> +	subl	$(FRAME_SIZE), %esp
> +	movl	$0, PT_EBP(%esp)
> +	movl	%edx, PT_EDX(%esp)
> +	movl	%eax, PT_EAX(%esp)
>  	movl	%esp, %eax
>  	call	ftrace_return_to_handler
>  	movl	%eax, %ecx
> -	popl	%eax
> -	popl	%edx
> -	addl	$4, %esp		# skip ebp
> +	movl	%eax, PT_EAX(%esp)
> +	movl	%edx, PT_EDX(%esp)
> +	addl	$(FRAME_SIZE), %esp
>  	JMP_NOSPEC ecx
>  #endif
> diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
> index 214f30e9f0c0..d51647228596 100644
> --- a/arch/x86/kernel/ftrace_64.S
> +++ b/arch/x86/kernel/ftrace_64.S
> @@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__)
>  SYM_CODE_START(return_to_handler)
>  	UNWIND_HINT_UNDEFINED
>  	ANNOTATE_NOENDBR
> -	subq  $24, %rsp
>  
> -	/* Save the return values */
> -	movq %rax, (%rsp)
> -	movq %rdx, 8(%rsp)
> -	movq %rbp, 16(%rsp)
> +	/* Save ftrace_regs for function exit context  */
> +	subq $(FRAME_SIZE), %rsp
> +
> +	movq %rax, RAX(%rsp)
> +	movq %rdx, RDX(%rsp)
> +	movq %rbp, RBP(%rsp)
>  	movq %rsp, %rdi
>  
>  	call ftrace_return_to_handler
>  
>  	movq %rax, %rdi
> -	movq 8(%rsp), %rdx
> -	movq (%rsp), %rax
> +	movq RDX(%rsp), %rdx
> +	movq RAX(%rsp), %rax
>  
> -	addq $24, %rsp
> +	addq $(FRAME_SIZE), %rsp
>  	/*
>  	 * Jump back to the old return address. This cannot be JMP_NOSPEC rdi
>  	 * since IBT would demand that contain ENDBR, which simply isn't so for
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 1fe49a28de2d..13987cd63553 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -43,9 +43,8 @@ struct dyn_ftrace;
>  
>  char *arch_ftrace_match_adjust(char *str, const char *search);
>  
> -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
> -struct fgraph_ret_regs;
> -unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs);
> +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS
> +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs);
>  #else
>  unsigned long ftrace_return_to_handler(unsigned long frame_pointer);
>  #endif
> @@ -134,6 +133,13 @@ extern int ftrace_enabled;
>   * Also, architecture dependent fields can be used for internal process.
>   * (e.g. orig_ax on x86_64)
>   *
> + * Basically, ftrace_regs stores the registers related to the context.
> + * On function entry, registers for function parameters and hooking the
> + * function call are stored, and on function exit, registers for function
> + * return value and frame pointers are stored.
> + *
> + * And also, it dpends on the context that which registers are restored
> + * from the ftrace_regs.
>   * On the function entry, those registers will be restored except for
>   * the stack pointer, so that user can change the function parameters
>   * and instruction pointer (e.g. live patching.)
> @@ -191,6 +197,8 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs)
>  	override_function_with_return(ftrace_get_regs(fregs))
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	frame_pointer(&(fregs)->regs)
>  #endif
>  
>  typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
> diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
> index 721c3b221048..ab277eff80dc 100644
> --- a/kernel/trace/Kconfig
> +++ b/kernel/trace/Kconfig
> @@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER
>  	help
>  	  See Documentation/trace/ftrace-design.rst
>  
> -config HAVE_FUNCTION_GRAPH_RETVAL
> +config HAVE_FUNCTION_GRAPH_FREGS
>  	bool
>  
>  config HAVE_DYNAMIC_FTRACE
> @@ -232,7 +232,7 @@ config FUNCTION_GRAPH_TRACER
>  
>  config FUNCTION_GRAPH_RETVAL
>  	bool "Kernel Function Graph Return Value"
> -	depends on HAVE_FUNCTION_GRAPH_RETVAL
> +	depends on HAVE_FUNCTION_GRAPH_FREGS
>  	depends on FUNCTION_GRAPH_TRACER
>  	default n
>  	help
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index 0322c5723748..30bebe43607d 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -763,15 +763,12 @@ static struct notifier_block ftrace_suspend_notifier = {
>  	.notifier_call = ftrace_suspend_notifier_call,
>  };
>  
> -/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */
> -struct fgraph_ret_regs;
> -
>  /*
>   * Send the trace to the ring-buffer.
>   * @return the original return address.
>   */
> -static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs,
> -						unsigned long frame_pointer)
> +static inline unsigned long
> +__ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointer)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  	struct ftrace_graph_ret trace;
> @@ -791,7 +788,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
>  
>  	trace.rettime = trace_clock_local();
>  #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
> -	trace.retval = fgraph_ret_regs_return_value(ret_regs);
> +	trace.retval = ftrace_regs_get_return_value(fregs);
>  #endif
>  
>  	bitmap = get_bitmap_bits(current, offset);
> @@ -826,14 +823,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
>  }
>  
>  /*
> - * After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can
> - * leave only ftrace_return_to_handler(ret_regs).
> + * After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can
> + * leave only ftrace_return_to_handler(fregs).
>   */
> -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
> -unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs)
> +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS
> +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs)
>  {
> -	return __ftrace_return_to_handler(ret_regs,
> -				fgraph_ret_regs_frame_pointer(ret_regs));
> +	return __ftrace_return_to_handler(fregs,
> +				ftrace_regs_get_frame_pointer(fregs));
>  }
>  #else
>  unsigned long ftrace_return_to_handler(unsigned long frame_pointer)


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-12 15:08 ` [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs Masami Hiramatsu (Google)
  2024-09-15  9:11   ` Steven Rostedt
  2024-09-15  9:13   ` Steven Rostedt
@ 2024-09-15  9:15   ` Steven Rostedt
  2024-09-16 12:16     ` Heiko Carstens
  2024-09-15  9:17   ` Steven Rostedt
  3 siblings, 1 reply; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  9:15 UTC (permalink / raw)
  To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, linux-s390
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland


Can I get an Acked-by from the S390 maintainers for this patch?

Thanks!

-- Steve


On Fri, 13 Sep 2024 00:08:51 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Use ftrace_regs instead of fgraph_ret_regs for tracing return value
> on function_graph tracer because of simplifying the callback interface.
> 
> The CONFIG_HAVE_FUNCTION_GRAPH_RETVAL is also replaced by
> CONFIG_HAVE_FUNCTION_GRAPH_FREGS.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v8:
>   - Newly added.
> ---
>  arch/arm64/Kconfig                  |    1 +
>  arch/arm64/include/asm/ftrace.h     |   23 ++++++-----------------
>  arch/arm64/kernel/asm-offsets.c     |   12 ------------
>  arch/arm64/kernel/entry-ftrace.S    |   32 ++++++++++++++++++--------------
>  arch/loongarch/Kconfig              |    2 +-
>  arch/loongarch/include/asm/ftrace.h |   24 ++----------------------
>  arch/loongarch/kernel/asm-offsets.c |   12 ------------
>  arch/loongarch/kernel/mcount.S      |   17 ++++++++++-------
>  arch/loongarch/kernel/mcount_dyn.S  |   14 +++++++-------
>  arch/riscv/Kconfig                  |    2 +-
>  arch/riscv/include/asm/ftrace.h     |   26 +++++---------------------
>  arch/riscv/kernel/mcount.S          |   24 +++++++++++++-----------
>  arch/s390/Kconfig                   |    2 +-
>  arch/s390/include/asm/ftrace.h      |   26 +++++++++-----------------
>  arch/s390/kernel/asm-offsets.c      |    6 ------
>  arch/s390/kernel/mcount.S           |    9 +++++----
>  arch/x86/Kconfig                    |    2 +-
>  arch/x86/include/asm/ftrace.h       |   22 ++--------------------
>  arch/x86/kernel/ftrace_32.S         |   15 +++++++++------
>  arch/x86/kernel/ftrace_64.S         |   17 +++++++++--------
>  include/linux/ftrace.h              |   14 +++++++++++---
>  kernel/trace/Kconfig                |    4 ++--
>  kernel/trace/fgraph.c               |   21 +++++++++------------
>  23 files changed, 122 insertions(+), 205 deletions(-)
> 
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index a2f8ff354ca6..17947f625b06 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -211,6 +211,7 @@ config ARM64
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_FUNCTION_ERROR_INJECTION
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_GRAPH_RETVAL
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
> index dc9cf0bd2a4c..dffaab3dd1f1 100644
> --- a/arch/arm64/include/asm/ftrace.h
> +++ b/arch/arm64/include/asm/ftrace.h
> @@ -126,6 +126,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs)
>  	fregs->pc = fregs->lr;
>  }
>  
> +static __always_inline unsigned long
> +ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
> +{
> +	return fregs->fp;
> +}
> +
>  int ftrace_regs_query_register_offset(const char *name);
>  
>  int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
> @@ -183,23 +189,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym,
>  
>  #ifndef __ASSEMBLY__
>  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	/* x0 - x7 */
> -	unsigned long regs[8];
> -
> -	unsigned long fp;
> -	unsigned long __unused;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->regs[0];
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
>  
>  void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
>  			   unsigned long frame_pointer);
> diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
> index 27de1dddb0ab..9e03c9a7e5c3 100644
> --- a/arch/arm64/kernel/asm-offsets.c
> +++ b/arch/arm64/kernel/asm-offsets.c
> @@ -201,18 +201,6 @@ int main(void)
>    DEFINE(FTRACE_OPS_FUNC,		offsetof(struct ftrace_ops, func));
>  #endif
>    BLANK();
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -  DEFINE(FGRET_REGS_X0,			offsetof(struct fgraph_ret_regs, regs[0]));
> -  DEFINE(FGRET_REGS_X1,			offsetof(struct fgraph_ret_regs, regs[1]));
> -  DEFINE(FGRET_REGS_X2,			offsetof(struct fgraph_ret_regs, regs[2]));
> -  DEFINE(FGRET_REGS_X3,			offsetof(struct fgraph_ret_regs, regs[3]));
> -  DEFINE(FGRET_REGS_X4,			offsetof(struct fgraph_ret_regs, regs[4]));
> -  DEFINE(FGRET_REGS_X5,			offsetof(struct fgraph_ret_regs, regs[5]));
> -  DEFINE(FGRET_REGS_X6,			offsetof(struct fgraph_ret_regs, regs[6]));
> -  DEFINE(FGRET_REGS_X7,			offsetof(struct fgraph_ret_regs, regs[7]));
> -  DEFINE(FGRET_REGS_FP,			offsetof(struct fgraph_ret_regs, fp));
> -  DEFINE(FGRET_REGS_SIZE,		sizeof(struct fgraph_ret_regs));
> -#endif
>  #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
>    DEFINE(FTRACE_OPS_DIRECT_CALL,	offsetof(struct ftrace_ops, direct_call));
>  #endif
> diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S
> index f0c16640ef21..169ccf600066 100644
> --- a/arch/arm64/kernel/entry-ftrace.S
> +++ b/arch/arm64/kernel/entry-ftrace.S
> @@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph)
>   * @fp is checked against the value passed by ftrace_graph_caller().
>   */
>  SYM_CODE_START(return_to_handler)
> -	/* save return value regs */
> -	sub sp, sp, #FGRET_REGS_SIZE
> -	stp x0, x1, [sp, #FGRET_REGS_X0]
> -	stp x2, x3, [sp, #FGRET_REGS_X2]
> -	stp x4, x5, [sp, #FGRET_REGS_X4]
> -	stp x6, x7, [sp, #FGRET_REGS_X6]
> -	str x29,    [sp, #FGRET_REGS_FP]	// parent's fp
> +	/* Make room for ftrace_regs */
> +	sub	sp, sp, #FREGS_SIZE
> +
> +	/* Save return value regs */
> +	stp	x0, x1, [sp, #FREGS_X0]
> +	stp	x2, x3, [sp, #FREGS_X2]
> +	stp	x4, x5, [sp, #FREGS_X4]
> +	stp	x6, x7, [sp, #FREGS_X6]
> +
> +	/* Save the callsite's FP */
> +	str	x29, [sp, #FREGS_FP]
>  
>  	mov	x0, sp
> -	bl	ftrace_return_to_handler	// addr = ftrace_return_to_hander(regs);
> +	bl	ftrace_return_to_handler	// addr = ftrace_return_to_hander(fregs);
>  	mov	x30, x0				// restore the original return address
>  
> -	/* restore return value regs */
> -	ldp x0, x1, [sp, #FGRET_REGS_X0]
> -	ldp x2, x3, [sp, #FGRET_REGS_X2]
> -	ldp x4, x5, [sp, #FGRET_REGS_X4]
> -	ldp x6, x7, [sp, #FGRET_REGS_X6]
> -	add sp, sp, #FGRET_REGS_SIZE
> +	/* Restore return value regs */
> +	ldp	x0, x1, [sp, #FREGS_X0]
> +	ldp	x2, x3, [sp, #FREGS_X2]
> +	ldp	x4, x5, [sp, #FREGS_X4]
> +	ldp	x6, x7, [sp, #FREGS_X6]
> +	add	sp, sp, #FREGS_SIZE
>  
>  	ret
>  SYM_CODE_END(return_to_handler)
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 70f169210b52..974f08f65f63 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -131,7 +131,7 @@ config LOONGARCH
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_ARG_ACCESS_API
>  	select HAVE_FUNCTION_ERROR_INJECTION
> -	select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
> index 6f8517d59954..1a73f35ea9af 100644
> --- a/arch/loongarch/include/asm/ftrace.h
> +++ b/arch/loongarch/include/asm/ftrace.h
> @@ -77,6 +77,8 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
>  	override_function_with_return(&(fregs)->regs)
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	((fregs)->regs.regs[22])
>  
>  #define ftrace_graph_func ftrace_graph_func
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
> @@ -99,26 +101,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)
>  
>  #endif /* CONFIG_FUNCTION_TRACER */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	/* a0 - a1 */
> -	unsigned long regs[2];
> -
> -	unsigned long fp;
> -	unsigned long __unused;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->regs[0];
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_LOONGARCH_FTRACE_H */
> diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c
> index bee9f7a3108f..714f5b5f1956 100644
> --- a/arch/loongarch/kernel/asm-offsets.c
> +++ b/arch/loongarch/kernel/asm-offsets.c
> @@ -279,18 +279,6 @@ static void __used output_pbe_defines(void)
>  }
>  #endif
>  
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -static void __used output_fgraph_ret_regs_defines(void)
> -{
> -	COMMENT("LoongArch fgraph_ret_regs offsets.");
> -	OFFSET(FGRET_REGS_A0, fgraph_ret_regs, regs[0]);
> -	OFFSET(FGRET_REGS_A1, fgraph_ret_regs, regs[1]);
> -	OFFSET(FGRET_REGS_FP, fgraph_ret_regs, fp);
> -	DEFINE(FGRET_REGS_SIZE, sizeof(struct fgraph_ret_regs));
> -	BLANK();
> -}
> -#endif
> -
>  static void __used output_kvm_defines(void)
>  {
>  	COMMENT("KVM/LoongArch Specific offsets.");
> diff --git a/arch/loongarch/kernel/mcount.S b/arch/loongarch/kernel/mcount.S
> index 3015896016a0..b6850503e061 100644
> --- a/arch/loongarch/kernel/mcount.S
> +++ b/arch/loongarch/kernel/mcount.S
> @@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller)
>  SYM_FUNC_END(ftrace_graph_caller)
>  
>  SYM_FUNC_START(return_to_handler)
> -	PTR_ADDI	sp, sp, -FGRET_REGS_SIZE
> -	PTR_S		a0, sp, FGRET_REGS_A0
> -	PTR_S		a1, sp, FGRET_REGS_A1
> -	PTR_S		zero, sp, FGRET_REGS_FP
> +	/* Save return value regs */
> +	PTR_ADDI	sp, sp, -PT_SIZE
> +	PTR_S		a0, sp, PT_R4
> +	PTR_S		a1, sp, PT_R5
> +	PTR_S		zero, sp, PT_R22
>  
>  	move		a0, sp
>  	bl		ftrace_return_to_handler
> @@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler)
>  	/* Restore the real parent address: a0 -> ra */
>  	move		ra, a0
>  
> -	PTR_L		a0, sp, FGRET_REGS_A0
> -	PTR_L		a1, sp, FGRET_REGS_A1
> -	PTR_ADDI	sp, sp, FGRET_REGS_SIZE
> +	/* Restore return value regs */
> +	PTR_L		a0, sp, PT_R4
> +	PTR_L		a1, sp, PT_R5
> +	PTR_ADDI	sp, sp, PT_SIZE
> +
>  	jr		ra
>  SYM_FUNC_END(return_to_handler)
>  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> diff --git a/arch/loongarch/kernel/mcount_dyn.S b/arch/loongarch/kernel/mcount_dyn.S
> index 0c65cf09110c..d6b474ad1d5e 100644
> --- a/arch/loongarch/kernel/mcount_dyn.S
> +++ b/arch/loongarch/kernel/mcount_dyn.S
> @@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller)
>  SYM_CODE_START(return_to_handler)
>  	UNWIND_HINT_UNDEFINED
>  	/* Save return value regs */
> -	PTR_ADDI	sp, sp, -FGRET_REGS_SIZE
> -	PTR_S		a0, sp, FGRET_REGS_A0
> -	PTR_S		a1, sp, FGRET_REGS_A1
> -	PTR_S		zero, sp, FGRET_REGS_FP
> +	PTR_ADDI	sp, sp, -PT_SIZE
> +	PTR_S		a0, sp, PT_R4
> +	PTR_S		a1, sp, PT_R5
> +	PTR_S		zero, sp, PT_R22
>  
>  	move		a0, sp
>  	bl		ftrace_return_to_handler
>  	move		ra, a0
>  
>  	/* Restore return value regs */
> -	PTR_L		a0, sp, FGRET_REGS_A0
> -	PTR_L		a1, sp, FGRET_REGS_A1
> -	PTR_ADDI	sp, sp, FGRET_REGS_SIZE
> +	PTR_L		a0, sp, PT_R4
> +	PTR_L		a1, sp, PT_R5
> +	PTR_ADDI	sp, sp, PT_SIZE
>  
>  	jr		ra
>  SYM_CODE_END(return_to_handler)
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 0f3cd7c3a436..6e8422269ba4 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -140,7 +140,7 @@ config RISCV
>  	select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE
>  	select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL
>  	select HAVE_FUNCTION_GRAPH_TRACER
> -	select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION
>  	select HAVE_EBPF_JIT if MMU
>  	select HAVE_GUP_FAST if MMU
> diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
> index 2cddd79ff21b..e9f364ce9fe8 100644
> --- a/arch/riscv/include/asm/ftrace.h
> +++ b/arch/riscv/include/asm/ftrace.h
> @@ -164,6 +164,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const struct
>  	return fregs->sp;
>  }
>  
> +static __always_inline unsigned long ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
> +{
> +	return fregs->s0;
> +}
> +
>  static __always_inline unsigned long ftrace_regs_get_argument(struct ftrace_regs *fregs,
>  							      unsigned int n)
>  {
> @@ -204,25 +209,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi
>  
>  #endif /* CONFIG_DYNAMIC_FTRACE */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long a1;
> -	unsigned long a0;
> -	unsigned long s0;
> -	unsigned long ra;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->a0;
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->s0;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_RISCV_FTRACE_H */
> diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S
> index 3a42f6287909..068168046e0e 100644
> --- a/arch/riscv/kernel/mcount.S
> +++ b/arch/riscv/kernel/mcount.S
> @@ -12,6 +12,8 @@
>  #include <asm/asm-offsets.h>
>  #include <asm/ftrace.h>
>  
> +#define ABI_SIZE_ON_STACK	80
> +
>  	.text
>  
>  	.macro SAVE_ABI_STATE
> @@ -26,12 +28,12 @@
>  	 * register if a0 was not saved.
>  	 */
>  	.macro SAVE_RET_ABI_STATE
> -	addi	sp, sp, -4*SZREG
> -	REG_S	s0, 2*SZREG(sp)
> -	REG_S	ra, 3*SZREG(sp)
> -	REG_S	a0, 1*SZREG(sp)
> -	REG_S	a1, 0*SZREG(sp)
> -	addi	s0, sp, 4*SZREG
> +	addi	sp, sp, -ABI_SIZE_ON_STACK
> +	REG_S	ra, 1*SZREG(sp)
> +	REG_S	s0, 8*SZREG(sp)
> +	REG_S	a0, 10*SZREG(sp)
> +	REG_S	a1, 11*SZREG(sp)
> +	addi	s0, sp, ABI_SIZE_ON_STACK
>  	.endm
>  
>  	.macro RESTORE_ABI_STATE
> @@ -41,11 +43,11 @@
>  	.endm
>  
>  	.macro RESTORE_RET_ABI_STATE
> -	REG_L	ra, 3*SZREG(sp)
> -	REG_L	s0, 2*SZREG(sp)
> -	REG_L	a0, 1*SZREG(sp)
> -	REG_L	a1, 0*SZREG(sp)
> -	addi	sp, sp, 4*SZREG
> +	REG_L	ra, 1*SZREG(sp)
> +	REG_L	s0, 8*SZREG(sp)
> +	REG_L	a0, 10*SZREG(sp)
> +	REG_L	a1, 11*SZREG(sp)
> +	addi	sp, sp, ABI_SIZE_ON_STACK
>  	.endm
>  
>  SYM_TYPED_FUNC_START(ftrace_stub)
> diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
> index a822f952f64a..12e942cfbcde 100644
> --- a/arch/s390/Kconfig
> +++ b/arch/s390/Kconfig
> @@ -184,7 +184,7 @@ config S390
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_ARG_ACCESS_API
>  	select HAVE_FUNCTION_ERROR_INJECTION
> -	select HAVE_FUNCTION_GRAPH_RETVAL
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
> index de76c21eb4a3..9cdd48a46bf7 100644
> --- a/arch/s390/include/asm/ftrace.h
> +++ b/arch/s390/include/asm/ftrace.h
> @@ -49,23 +49,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *
>  	return NULL;
>  }
>  
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long gpr2;
> -	unsigned long fp;
> -};
> -
> -static __always_inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->gpr2;
> -}
> -
> -static __always_inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
> -#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> -
>  static __always_inline unsigned long
>  ftrace_regs_get_instruction_pointer(const struct ftrace_regs *fregs)
>  {
> @@ -92,6 +75,15 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
>  
> +static __always_inline unsigned long
> +ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)
> +{
> +	unsigned long *sp;
> +
> +	sp = (void *)ftrace_regs_get_stack_pointer(fregs);
> +	return sp[0];	/* return backchain */
> +}
> +
>  #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
>  /*
>   * When an ftrace registered caller is tracing a function that is
> diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
> index ffa0dd2dbaac..d38ed80615d5 100644
> --- a/arch/s390/kernel/asm-offsets.c
> +++ b/arch/s390/kernel/asm-offsets.c
> @@ -179,12 +179,6 @@ int main(void)
>  	DEFINE(OLDMEM_SIZE, PARMAREA + offsetof(struct parmarea, oldmem_size));
>  	DEFINE(COMMAND_LINE, PARMAREA + offsetof(struct parmarea, command_line));
>  	DEFINE(MAX_COMMAND_LINE_SIZE, PARMAREA + offsetof(struct parmarea, max_command_line_size));
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -	/* function graph return value tracing */
> -	OFFSET(__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2);
> -	OFFSET(__FGRAPH_RET_FP, fgraph_ret_regs, fp);
> -	DEFINE(__FGRAPH_RET_SIZE, sizeof(struct fgraph_ret_regs));
> -#endif
>  	OFFSET(__FTRACE_REGS_PT_REGS, ftrace_regs, regs);
>  	DEFINE(__FTRACE_REGS_SIZE, sizeof(struct ftrace_regs));
>  
> diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
> index ae4d4fd9afcd..cda798b976de 100644
> --- a/arch/s390/kernel/mcount.S
> +++ b/arch/s390/kernel/mcount.S
> @@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common)
>  SYM_FUNC_START(return_to_handler)
>  	stmg	%r2,%r5,32(%r15)
>  	lgr	%r1,%r15
> -	aghi	%r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE)
> +# Allocate ftrace_regs + backchain on the stack
> +	aghi	%r15,-STACK_FRAME_SIZE_FREGS
>  	stg	%r1,__SF_BACKCHAIN(%r15)
>  	la	%r3,STACK_FRAME_OVERHEAD(%r15)
> -	stg	%r1,__FGRAPH_RET_FP(%r3)
> -	stg	%r2,__FGRAPH_RET_GPR2(%r3)
> +	stg	%r2,(__SF_GPRS+2*8)(%r15)
> +	stg	%r15,(__SF_GPRS+15*8)(%r15)
>  	lgr	%r2,%r3
>  	brasl	%r14,ftrace_return_to_handler
> -	aghi	%r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE
> +	aghi	%r15,STACK_FRAME_SIZE_FREGS
>  	lgr	%r14,%r2
>  	lmg	%r2,%r5,32(%r15)
>  	BR_EX	%r14
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 007bab9f2a0e..047384e4d93a 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -228,7 +228,7 @@ config X86
>  	select HAVE_GUP_FAST
>  	select HAVE_FENTRY			if X86_64 || DYNAMIC_FTRACE
>  	select HAVE_FTRACE_MCOUNT_RECORD
> -	select HAVE_FUNCTION_GRAPH_RETVAL	if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS	if HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_GRAPH_TRACER	if X86_32 || (X86_64 && DYNAMIC_FTRACE)
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
> index 78f6a200e15b..669771ef3b5b 100644
> --- a/arch/x86/include/asm/ftrace.h
> +++ b/arch/x86/include/asm/ftrace.h
> @@ -64,6 +64,8 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
>  	override_function_with_return(&(fregs)->regs)
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	frame_pointer(&(fregs)->regs)
>  
>  struct ftrace_ops;
>  #define ftrace_graph_func ftrace_graph_func
> @@ -148,24 +150,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
>  #endif /* !COMPILE_OFFSETS */
>  #endif /* !__ASSEMBLY__ */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long ax;
> -	unsigned long dx;
> -	unsigned long bp;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->ax;
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->bp;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_X86_FTRACE_H */
> diff --git a/arch/x86/kernel/ftrace_32.S b/arch/x86/kernel/ftrace_32.S
> index 58d9ed50fe61..4b265884d06c 100644
> --- a/arch/x86/kernel/ftrace_32.S
> +++ b/arch/x86/kernel/ftrace_32.S
> @@ -23,6 +23,8 @@ SYM_FUNC_START(__fentry__)
>  SYM_FUNC_END(__fentry__)
>  EXPORT_SYMBOL(__fentry__)
>  
> +#define FRAME_SIZE	PT_OLDSS+4
> +
>  SYM_CODE_START(ftrace_caller)
>  
>  #ifdef CONFIG_FRAME_POINTER
> @@ -187,14 +189,15 @@ SYM_CODE_END(ftrace_graph_caller)
>  
>  .globl return_to_handler
>  return_to_handler:
> -	pushl	$0
> -	pushl	%edx
> -	pushl	%eax
> +	subl	$(FRAME_SIZE), %esp
> +	movl	$0, PT_EBP(%esp)
> +	movl	%edx, PT_EDX(%esp)
> +	movl	%eax, PT_EAX(%esp)
>  	movl	%esp, %eax
>  	call	ftrace_return_to_handler
>  	movl	%eax, %ecx
> -	popl	%eax
> -	popl	%edx
> -	addl	$4, %esp		# skip ebp
> +	movl	%eax, PT_EAX(%esp)
> +	movl	%edx, PT_EDX(%esp)
> +	addl	$(FRAME_SIZE), %esp
>  	JMP_NOSPEC ecx
>  #endif
> diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
> index 214f30e9f0c0..d51647228596 100644
> --- a/arch/x86/kernel/ftrace_64.S
> +++ b/arch/x86/kernel/ftrace_64.S
> @@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__)
>  SYM_CODE_START(return_to_handler)
>  	UNWIND_HINT_UNDEFINED
>  	ANNOTATE_NOENDBR
> -	subq  $24, %rsp
>  
> -	/* Save the return values */
> -	movq %rax, (%rsp)
> -	movq %rdx, 8(%rsp)
> -	movq %rbp, 16(%rsp)
> +	/* Save ftrace_regs for function exit context  */
> +	subq $(FRAME_SIZE), %rsp
> +
> +	movq %rax, RAX(%rsp)
> +	movq %rdx, RDX(%rsp)
> +	movq %rbp, RBP(%rsp)
>  	movq %rsp, %rdi
>  
>  	call ftrace_return_to_handler
>  
>  	movq %rax, %rdi
> -	movq 8(%rsp), %rdx
> -	movq (%rsp), %rax
> +	movq RDX(%rsp), %rdx
> +	movq RAX(%rsp), %rax
>  
> -	addq $24, %rsp
> +	addq $(FRAME_SIZE), %rsp
>  	/*
>  	 * Jump back to the old return address. This cannot be JMP_NOSPEC rdi
>  	 * since IBT would demand that contain ENDBR, which simply isn't so for
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 1fe49a28de2d..13987cd63553 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -43,9 +43,8 @@ struct dyn_ftrace;
>  
>  char *arch_ftrace_match_adjust(char *str, const char *search);
>  
> -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
> -struct fgraph_ret_regs;
> -unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs);
> +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS
> +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs);
>  #else
>  unsigned long ftrace_return_to_handler(unsigned long frame_pointer);
>  #endif
> @@ -134,6 +133,13 @@ extern int ftrace_enabled;
>   * Also, architecture dependent fields can be used for internal process.
>   * (e.g. orig_ax on x86_64)
>   *
> + * Basically, ftrace_regs stores the registers related to the context.
> + * On function entry, registers for function parameters and hooking the
> + * function call are stored, and on function exit, registers for function
> + * return value and frame pointers are stored.
> + *
> + * And also, it dpends on the context that which registers are restored
> + * from the ftrace_regs.
>   * On the function entry, those registers will be restored except for
>   * the stack pointer, so that user can change the function parameters
>   * and instruction pointer (e.g. live patching.)
> @@ -191,6 +197,8 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs)
>  	override_function_with_return(ftrace_get_regs(fregs))
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	frame_pointer(&(fregs)->regs)
>  #endif
>  
>  typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
> diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
> index 721c3b221048..ab277eff80dc 100644
> --- a/kernel/trace/Kconfig
> +++ b/kernel/trace/Kconfig
> @@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER
>  	help
>  	  See Documentation/trace/ftrace-design.rst
>  
> -config HAVE_FUNCTION_GRAPH_RETVAL
> +config HAVE_FUNCTION_GRAPH_FREGS
>  	bool
>  
>  config HAVE_DYNAMIC_FTRACE
> @@ -232,7 +232,7 @@ config FUNCTION_GRAPH_TRACER
>  
>  config FUNCTION_GRAPH_RETVAL
>  	bool "Kernel Function Graph Return Value"
> -	depends on HAVE_FUNCTION_GRAPH_RETVAL
> +	depends on HAVE_FUNCTION_GRAPH_FREGS
>  	depends on FUNCTION_GRAPH_TRACER
>  	default n
>  	help
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index 0322c5723748..30bebe43607d 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -763,15 +763,12 @@ static struct notifier_block ftrace_suspend_notifier = {
>  	.notifier_call = ftrace_suspend_notifier_call,
>  };
>  
> -/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */
> -struct fgraph_ret_regs;
> -
>  /*
>   * Send the trace to the ring-buffer.
>   * @return the original return address.
>   */
> -static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs,
> -						unsigned long frame_pointer)
> +static inline unsigned long
> +__ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointer)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  	struct ftrace_graph_ret trace;
> @@ -791,7 +788,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
>  
>  	trace.rettime = trace_clock_local();
>  #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
> -	trace.retval = fgraph_ret_regs_return_value(ret_regs);
> +	trace.retval = ftrace_regs_get_return_value(fregs);
>  #endif
>  
>  	bitmap = get_bitmap_bits(current, offset);
> @@ -826,14 +823,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
>  }
>  
>  /*
> - * After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can
> - * leave only ftrace_return_to_handler(ret_regs).
> + * After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can
> + * leave only ftrace_return_to_handler(fregs).
>   */
> -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
> -unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs)
> +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS
> +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs)
>  {
> -	return __ftrace_return_to_handler(ret_regs,
> -				fgraph_ret_regs_frame_pointer(ret_regs));
> +	return __ftrace_return_to_handler(fregs,
> +				ftrace_regs_get_frame_pointer(fregs));
>  }
>  #else
>  unsigned long ftrace_return_to_handler(unsigned long frame_pointer)


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-12 15:08 ` [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs Masami Hiramatsu (Google)
                     ` (2 preceding siblings ...)
  2024-09-15  9:15   ` Steven Rostedt
@ 2024-09-15  9:17   ` Steven Rostedt
  3 siblings, 0 replies; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  9:17 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	Peter Zijlstra
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland


Can I get an Acked-by from the X86 maintainers for this patch?

Thanks!

-- Steve


On Fri, 13 Sep 2024 00:08:51 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Use ftrace_regs instead of fgraph_ret_regs for tracing return value
> on function_graph tracer because of simplifying the callback interface.
> 
> The CONFIG_HAVE_FUNCTION_GRAPH_RETVAL is also replaced by
> CONFIG_HAVE_FUNCTION_GRAPH_FREGS.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
>  Changes in v8:
>   - Newly added.
> ---
>  arch/arm64/Kconfig                  |    1 +
>  arch/arm64/include/asm/ftrace.h     |   23 ++++++-----------------
>  arch/arm64/kernel/asm-offsets.c     |   12 ------------
>  arch/arm64/kernel/entry-ftrace.S    |   32 ++++++++++++++++++--------------
>  arch/loongarch/Kconfig              |    2 +-
>  arch/loongarch/include/asm/ftrace.h |   24 ++----------------------
>  arch/loongarch/kernel/asm-offsets.c |   12 ------------
>  arch/loongarch/kernel/mcount.S      |   17 ++++++++++-------
>  arch/loongarch/kernel/mcount_dyn.S  |   14 +++++++-------
>  arch/riscv/Kconfig                  |    2 +-
>  arch/riscv/include/asm/ftrace.h     |   26 +++++---------------------
>  arch/riscv/kernel/mcount.S          |   24 +++++++++++++-----------
>  arch/s390/Kconfig                   |    2 +-
>  arch/s390/include/asm/ftrace.h      |   26 +++++++++-----------------
>  arch/s390/kernel/asm-offsets.c      |    6 ------
>  arch/s390/kernel/mcount.S           |    9 +++++----
>  arch/x86/Kconfig                    |    2 +-
>  arch/x86/include/asm/ftrace.h       |   22 ++--------------------
>  arch/x86/kernel/ftrace_32.S         |   15 +++++++++------
>  arch/x86/kernel/ftrace_64.S         |   17 +++++++++--------
>  include/linux/ftrace.h              |   14 +++++++++++---
>  kernel/trace/Kconfig                |    4 ++--
>  kernel/trace/fgraph.c               |   21 +++++++++------------
>  23 files changed, 122 insertions(+), 205 deletions(-)
> 
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index a2f8ff354ca6..17947f625b06 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -211,6 +211,7 @@ config ARM64
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_FUNCTION_ERROR_INJECTION
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_GRAPH_RETVAL
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
> index dc9cf0bd2a4c..dffaab3dd1f1 100644
> --- a/arch/arm64/include/asm/ftrace.h
> +++ b/arch/arm64/include/asm/ftrace.h
> @@ -126,6 +126,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs)
>  	fregs->pc = fregs->lr;
>  }
>  
> +static __always_inline unsigned long
> +ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
> +{
> +	return fregs->fp;
> +}
> +
>  int ftrace_regs_query_register_offset(const char *name);
>  
>  int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
> @@ -183,23 +189,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym,
>  
>  #ifndef __ASSEMBLY__
>  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	/* x0 - x7 */
> -	unsigned long regs[8];
> -
> -	unsigned long fp;
> -	unsigned long __unused;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->regs[0];
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
>  
>  void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
>  			   unsigned long frame_pointer);
> diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
> index 27de1dddb0ab..9e03c9a7e5c3 100644
> --- a/arch/arm64/kernel/asm-offsets.c
> +++ b/arch/arm64/kernel/asm-offsets.c
> @@ -201,18 +201,6 @@ int main(void)
>    DEFINE(FTRACE_OPS_FUNC,		offsetof(struct ftrace_ops, func));
>  #endif
>    BLANK();
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -  DEFINE(FGRET_REGS_X0,			offsetof(struct fgraph_ret_regs, regs[0]));
> -  DEFINE(FGRET_REGS_X1,			offsetof(struct fgraph_ret_regs, regs[1]));
> -  DEFINE(FGRET_REGS_X2,			offsetof(struct fgraph_ret_regs, regs[2]));
> -  DEFINE(FGRET_REGS_X3,			offsetof(struct fgraph_ret_regs, regs[3]));
> -  DEFINE(FGRET_REGS_X4,			offsetof(struct fgraph_ret_regs, regs[4]));
> -  DEFINE(FGRET_REGS_X5,			offsetof(struct fgraph_ret_regs, regs[5]));
> -  DEFINE(FGRET_REGS_X6,			offsetof(struct fgraph_ret_regs, regs[6]));
> -  DEFINE(FGRET_REGS_X7,			offsetof(struct fgraph_ret_regs, regs[7]));
> -  DEFINE(FGRET_REGS_FP,			offsetof(struct fgraph_ret_regs, fp));
> -  DEFINE(FGRET_REGS_SIZE,		sizeof(struct fgraph_ret_regs));
> -#endif
>  #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
>    DEFINE(FTRACE_OPS_DIRECT_CALL,	offsetof(struct ftrace_ops, direct_call));
>  #endif
> diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S
> index f0c16640ef21..169ccf600066 100644
> --- a/arch/arm64/kernel/entry-ftrace.S
> +++ b/arch/arm64/kernel/entry-ftrace.S
> @@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph)
>   * @fp is checked against the value passed by ftrace_graph_caller().
>   */
>  SYM_CODE_START(return_to_handler)
> -	/* save return value regs */
> -	sub sp, sp, #FGRET_REGS_SIZE
> -	stp x0, x1, [sp, #FGRET_REGS_X0]
> -	stp x2, x3, [sp, #FGRET_REGS_X2]
> -	stp x4, x5, [sp, #FGRET_REGS_X4]
> -	stp x6, x7, [sp, #FGRET_REGS_X6]
> -	str x29,    [sp, #FGRET_REGS_FP]	// parent's fp
> +	/* Make room for ftrace_regs */
> +	sub	sp, sp, #FREGS_SIZE
> +
> +	/* Save return value regs */
> +	stp	x0, x1, [sp, #FREGS_X0]
> +	stp	x2, x3, [sp, #FREGS_X2]
> +	stp	x4, x5, [sp, #FREGS_X4]
> +	stp	x6, x7, [sp, #FREGS_X6]
> +
> +	/* Save the callsite's FP */
> +	str	x29, [sp, #FREGS_FP]
>  
>  	mov	x0, sp
> -	bl	ftrace_return_to_handler	// addr = ftrace_return_to_hander(regs);
> +	bl	ftrace_return_to_handler	// addr = ftrace_return_to_hander(fregs);
>  	mov	x30, x0				// restore the original return address
>  
> -	/* restore return value regs */
> -	ldp x0, x1, [sp, #FGRET_REGS_X0]
> -	ldp x2, x3, [sp, #FGRET_REGS_X2]
> -	ldp x4, x5, [sp, #FGRET_REGS_X4]
> -	ldp x6, x7, [sp, #FGRET_REGS_X6]
> -	add sp, sp, #FGRET_REGS_SIZE
> +	/* Restore return value regs */
> +	ldp	x0, x1, [sp, #FREGS_X0]
> +	ldp	x2, x3, [sp, #FREGS_X2]
> +	ldp	x4, x5, [sp, #FREGS_X4]
> +	ldp	x6, x7, [sp, #FREGS_X6]
> +	add	sp, sp, #FREGS_SIZE
>  
>  	ret
>  SYM_CODE_END(return_to_handler)
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 70f169210b52..974f08f65f63 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -131,7 +131,7 @@ config LOONGARCH
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_ARG_ACCESS_API
>  	select HAVE_FUNCTION_ERROR_INJECTION
> -	select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
> index 6f8517d59954..1a73f35ea9af 100644
> --- a/arch/loongarch/include/asm/ftrace.h
> +++ b/arch/loongarch/include/asm/ftrace.h
> @@ -77,6 +77,8 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
>  	override_function_with_return(&(fregs)->regs)
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	((fregs)->regs.regs[22])
>  
>  #define ftrace_graph_func ftrace_graph_func
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
> @@ -99,26 +101,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)
>  
>  #endif /* CONFIG_FUNCTION_TRACER */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	/* a0 - a1 */
> -	unsigned long regs[2];
> -
> -	unsigned long fp;
> -	unsigned long __unused;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->regs[0];
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_LOONGARCH_FTRACE_H */
> diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c
> index bee9f7a3108f..714f5b5f1956 100644
> --- a/arch/loongarch/kernel/asm-offsets.c
> +++ b/arch/loongarch/kernel/asm-offsets.c
> @@ -279,18 +279,6 @@ static void __used output_pbe_defines(void)
>  }
>  #endif
>  
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -static void __used output_fgraph_ret_regs_defines(void)
> -{
> -	COMMENT("LoongArch fgraph_ret_regs offsets.");
> -	OFFSET(FGRET_REGS_A0, fgraph_ret_regs, regs[0]);
> -	OFFSET(FGRET_REGS_A1, fgraph_ret_regs, regs[1]);
> -	OFFSET(FGRET_REGS_FP, fgraph_ret_regs, fp);
> -	DEFINE(FGRET_REGS_SIZE, sizeof(struct fgraph_ret_regs));
> -	BLANK();
> -}
> -#endif
> -
>  static void __used output_kvm_defines(void)
>  {
>  	COMMENT("KVM/LoongArch Specific offsets.");
> diff --git a/arch/loongarch/kernel/mcount.S b/arch/loongarch/kernel/mcount.S
> index 3015896016a0..b6850503e061 100644
> --- a/arch/loongarch/kernel/mcount.S
> +++ b/arch/loongarch/kernel/mcount.S
> @@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller)
>  SYM_FUNC_END(ftrace_graph_caller)
>  
>  SYM_FUNC_START(return_to_handler)
> -	PTR_ADDI	sp, sp, -FGRET_REGS_SIZE
> -	PTR_S		a0, sp, FGRET_REGS_A0
> -	PTR_S		a1, sp, FGRET_REGS_A1
> -	PTR_S		zero, sp, FGRET_REGS_FP
> +	/* Save return value regs */
> +	PTR_ADDI	sp, sp, -PT_SIZE
> +	PTR_S		a0, sp, PT_R4
> +	PTR_S		a1, sp, PT_R5
> +	PTR_S		zero, sp, PT_R22
>  
>  	move		a0, sp
>  	bl		ftrace_return_to_handler
> @@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler)
>  	/* Restore the real parent address: a0 -> ra */
>  	move		ra, a0
>  
> -	PTR_L		a0, sp, FGRET_REGS_A0
> -	PTR_L		a1, sp, FGRET_REGS_A1
> -	PTR_ADDI	sp, sp, FGRET_REGS_SIZE
> +	/* Restore return value regs */
> +	PTR_L		a0, sp, PT_R4
> +	PTR_L		a1, sp, PT_R5
> +	PTR_ADDI	sp, sp, PT_SIZE
> +
>  	jr		ra
>  SYM_FUNC_END(return_to_handler)
>  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> diff --git a/arch/loongarch/kernel/mcount_dyn.S b/arch/loongarch/kernel/mcount_dyn.S
> index 0c65cf09110c..d6b474ad1d5e 100644
> --- a/arch/loongarch/kernel/mcount_dyn.S
> +++ b/arch/loongarch/kernel/mcount_dyn.S
> @@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller)
>  SYM_CODE_START(return_to_handler)
>  	UNWIND_HINT_UNDEFINED
>  	/* Save return value regs */
> -	PTR_ADDI	sp, sp, -FGRET_REGS_SIZE
> -	PTR_S		a0, sp, FGRET_REGS_A0
> -	PTR_S		a1, sp, FGRET_REGS_A1
> -	PTR_S		zero, sp, FGRET_REGS_FP
> +	PTR_ADDI	sp, sp, -PT_SIZE
> +	PTR_S		a0, sp, PT_R4
> +	PTR_S		a1, sp, PT_R5
> +	PTR_S		zero, sp, PT_R22
>  
>  	move		a0, sp
>  	bl		ftrace_return_to_handler
>  	move		ra, a0
>  
>  	/* Restore return value regs */
> -	PTR_L		a0, sp, FGRET_REGS_A0
> -	PTR_L		a1, sp, FGRET_REGS_A1
> -	PTR_ADDI	sp, sp, FGRET_REGS_SIZE
> +	PTR_L		a0, sp, PT_R4
> +	PTR_L		a1, sp, PT_R5
> +	PTR_ADDI	sp, sp, PT_SIZE
>  
>  	jr		ra
>  SYM_CODE_END(return_to_handler)
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 0f3cd7c3a436..6e8422269ba4 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -140,7 +140,7 @@ config RISCV
>  	select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE
>  	select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL
>  	select HAVE_FUNCTION_GRAPH_TRACER
> -	select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION
>  	select HAVE_EBPF_JIT if MMU
>  	select HAVE_GUP_FAST if MMU
> diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
> index 2cddd79ff21b..e9f364ce9fe8 100644
> --- a/arch/riscv/include/asm/ftrace.h
> +++ b/arch/riscv/include/asm/ftrace.h
> @@ -164,6 +164,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const struct
>  	return fregs->sp;
>  }
>  
> +static __always_inline unsigned long ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
> +{
> +	return fregs->s0;
> +}
> +
>  static __always_inline unsigned long ftrace_regs_get_argument(struct ftrace_regs *fregs,
>  							      unsigned int n)
>  {
> @@ -204,25 +209,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi
>  
>  #endif /* CONFIG_DYNAMIC_FTRACE */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long a1;
> -	unsigned long a0;
> -	unsigned long s0;
> -	unsigned long ra;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->a0;
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->s0;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_RISCV_FTRACE_H */
> diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S
> index 3a42f6287909..068168046e0e 100644
> --- a/arch/riscv/kernel/mcount.S
> +++ b/arch/riscv/kernel/mcount.S
> @@ -12,6 +12,8 @@
>  #include <asm/asm-offsets.h>
>  #include <asm/ftrace.h>
>  
> +#define ABI_SIZE_ON_STACK	80
> +
>  	.text
>  
>  	.macro SAVE_ABI_STATE
> @@ -26,12 +28,12 @@
>  	 * register if a0 was not saved.
>  	 */
>  	.macro SAVE_RET_ABI_STATE
> -	addi	sp, sp, -4*SZREG
> -	REG_S	s0, 2*SZREG(sp)
> -	REG_S	ra, 3*SZREG(sp)
> -	REG_S	a0, 1*SZREG(sp)
> -	REG_S	a1, 0*SZREG(sp)
> -	addi	s0, sp, 4*SZREG
> +	addi	sp, sp, -ABI_SIZE_ON_STACK
> +	REG_S	ra, 1*SZREG(sp)
> +	REG_S	s0, 8*SZREG(sp)
> +	REG_S	a0, 10*SZREG(sp)
> +	REG_S	a1, 11*SZREG(sp)
> +	addi	s0, sp, ABI_SIZE_ON_STACK
>  	.endm
>  
>  	.macro RESTORE_ABI_STATE
> @@ -41,11 +43,11 @@
>  	.endm
>  
>  	.macro RESTORE_RET_ABI_STATE
> -	REG_L	ra, 3*SZREG(sp)
> -	REG_L	s0, 2*SZREG(sp)
> -	REG_L	a0, 1*SZREG(sp)
> -	REG_L	a1, 0*SZREG(sp)
> -	addi	sp, sp, 4*SZREG
> +	REG_L	ra, 1*SZREG(sp)
> +	REG_L	s0, 8*SZREG(sp)
> +	REG_L	a0, 10*SZREG(sp)
> +	REG_L	a1, 11*SZREG(sp)
> +	addi	sp, sp, ABI_SIZE_ON_STACK
>  	.endm
>  
>  SYM_TYPED_FUNC_START(ftrace_stub)
> diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
> index a822f952f64a..12e942cfbcde 100644
> --- a/arch/s390/Kconfig
> +++ b/arch/s390/Kconfig
> @@ -184,7 +184,7 @@ config S390
>  	select HAVE_FTRACE_MCOUNT_RECORD
>  	select HAVE_FUNCTION_ARG_ACCESS_API
>  	select HAVE_FUNCTION_ERROR_INJECTION
> -	select HAVE_FUNCTION_GRAPH_RETVAL
> +	select HAVE_FUNCTION_GRAPH_FREGS
>  	select HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
> index de76c21eb4a3..9cdd48a46bf7 100644
> --- a/arch/s390/include/asm/ftrace.h
> +++ b/arch/s390/include/asm/ftrace.h
> @@ -49,23 +49,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *
>  	return NULL;
>  }
>  
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long gpr2;
> -	unsigned long fp;
> -};
> -
> -static __always_inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->gpr2;
> -}
> -
> -static __always_inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->fp;
> -}
> -#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> -
>  static __always_inline unsigned long
>  ftrace_regs_get_instruction_pointer(const struct ftrace_regs *fregs)
>  {
> @@ -92,6 +75,15 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
>  
> +static __always_inline unsigned long
> +ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)
> +{
> +	unsigned long *sp;
> +
> +	sp = (void *)ftrace_regs_get_stack_pointer(fregs);
> +	return sp[0];	/* return backchain */
> +}
> +
>  #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
>  /*
>   * When an ftrace registered caller is tracing a function that is
> diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
> index ffa0dd2dbaac..d38ed80615d5 100644
> --- a/arch/s390/kernel/asm-offsets.c
> +++ b/arch/s390/kernel/asm-offsets.c
> @@ -179,12 +179,6 @@ int main(void)
>  	DEFINE(OLDMEM_SIZE, PARMAREA + offsetof(struct parmarea, oldmem_size));
>  	DEFINE(COMMAND_LINE, PARMAREA + offsetof(struct parmarea, command_line));
>  	DEFINE(MAX_COMMAND_LINE_SIZE, PARMAREA + offsetof(struct parmarea, max_command_line_size));
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -	/* function graph return value tracing */
> -	OFFSET(__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2);
> -	OFFSET(__FGRAPH_RET_FP, fgraph_ret_regs, fp);
> -	DEFINE(__FGRAPH_RET_SIZE, sizeof(struct fgraph_ret_regs));
> -#endif
>  	OFFSET(__FTRACE_REGS_PT_REGS, ftrace_regs, regs);
>  	DEFINE(__FTRACE_REGS_SIZE, sizeof(struct ftrace_regs));
>  
> diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
> index ae4d4fd9afcd..cda798b976de 100644
> --- a/arch/s390/kernel/mcount.S
> +++ b/arch/s390/kernel/mcount.S
> @@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common)
>  SYM_FUNC_START(return_to_handler)
>  	stmg	%r2,%r5,32(%r15)
>  	lgr	%r1,%r15
> -	aghi	%r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE)
> +# Allocate ftrace_regs + backchain on the stack
> +	aghi	%r15,-STACK_FRAME_SIZE_FREGS
>  	stg	%r1,__SF_BACKCHAIN(%r15)
>  	la	%r3,STACK_FRAME_OVERHEAD(%r15)
> -	stg	%r1,__FGRAPH_RET_FP(%r3)
> -	stg	%r2,__FGRAPH_RET_GPR2(%r3)
> +	stg	%r2,(__SF_GPRS+2*8)(%r15)
> +	stg	%r15,(__SF_GPRS+15*8)(%r15)
>  	lgr	%r2,%r3
>  	brasl	%r14,ftrace_return_to_handler
> -	aghi	%r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE
> +	aghi	%r15,STACK_FRAME_SIZE_FREGS
>  	lgr	%r14,%r2
>  	lmg	%r2,%r5,32(%r15)
>  	BR_EX	%r14
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 007bab9f2a0e..047384e4d93a 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -228,7 +228,7 @@ config X86
>  	select HAVE_GUP_FAST
>  	select HAVE_FENTRY			if X86_64 || DYNAMIC_FTRACE
>  	select HAVE_FTRACE_MCOUNT_RECORD
> -	select HAVE_FUNCTION_GRAPH_RETVAL	if HAVE_FUNCTION_GRAPH_TRACER
> +	select HAVE_FUNCTION_GRAPH_FREGS	if HAVE_FUNCTION_GRAPH_TRACER
>  	select HAVE_FUNCTION_GRAPH_TRACER	if X86_32 || (X86_64 && DYNAMIC_FTRACE)
>  	select HAVE_FUNCTION_TRACER
>  	select HAVE_GCC_PLUGINS
> diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
> index 78f6a200e15b..669771ef3b5b 100644
> --- a/arch/x86/include/asm/ftrace.h
> +++ b/arch/x86/include/asm/ftrace.h
> @@ -64,6 +64,8 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
>  	override_function_with_return(&(fregs)->regs)
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	frame_pointer(&(fregs)->regs)
>  
>  struct ftrace_ops;
>  #define ftrace_graph_func ftrace_graph_func
> @@ -148,24 +150,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
>  #endif /* !COMPILE_OFFSETS */
>  #endif /* !__ASSEMBLY__ */
>  
> -#ifndef __ASSEMBLY__
> -#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> -struct fgraph_ret_regs {
> -	unsigned long ax;
> -	unsigned long dx;
> -	unsigned long bp;
> -};
> -
> -static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->ax;
> -}
> -
> -static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs)
> -{
> -	return ret_regs->bp;
> -}
> -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */
> -#endif
> -
>  #endif /* _ASM_X86_FTRACE_H */
> diff --git a/arch/x86/kernel/ftrace_32.S b/arch/x86/kernel/ftrace_32.S
> index 58d9ed50fe61..4b265884d06c 100644
> --- a/arch/x86/kernel/ftrace_32.S
> +++ b/arch/x86/kernel/ftrace_32.S
> @@ -23,6 +23,8 @@ SYM_FUNC_START(__fentry__)
>  SYM_FUNC_END(__fentry__)
>  EXPORT_SYMBOL(__fentry__)
>  
> +#define FRAME_SIZE	PT_OLDSS+4
> +
>  SYM_CODE_START(ftrace_caller)
>  
>  #ifdef CONFIG_FRAME_POINTER
> @@ -187,14 +189,15 @@ SYM_CODE_END(ftrace_graph_caller)
>  
>  .globl return_to_handler
>  return_to_handler:
> -	pushl	$0
> -	pushl	%edx
> -	pushl	%eax
> +	subl	$(FRAME_SIZE), %esp
> +	movl	$0, PT_EBP(%esp)
> +	movl	%edx, PT_EDX(%esp)
> +	movl	%eax, PT_EAX(%esp)
>  	movl	%esp, %eax
>  	call	ftrace_return_to_handler
>  	movl	%eax, %ecx
> -	popl	%eax
> -	popl	%edx
> -	addl	$4, %esp		# skip ebp
> +	movl	%eax, PT_EAX(%esp)
> +	movl	%edx, PT_EDX(%esp)
> +	addl	$(FRAME_SIZE), %esp
>  	JMP_NOSPEC ecx
>  #endif
> diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
> index 214f30e9f0c0..d51647228596 100644
> --- a/arch/x86/kernel/ftrace_64.S
> +++ b/arch/x86/kernel/ftrace_64.S
> @@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__)
>  SYM_CODE_START(return_to_handler)
>  	UNWIND_HINT_UNDEFINED
>  	ANNOTATE_NOENDBR
> -	subq  $24, %rsp
>  
> -	/* Save the return values */
> -	movq %rax, (%rsp)
> -	movq %rdx, 8(%rsp)
> -	movq %rbp, 16(%rsp)
> +	/* Save ftrace_regs for function exit context  */
> +	subq $(FRAME_SIZE), %rsp
> +
> +	movq %rax, RAX(%rsp)
> +	movq %rdx, RDX(%rsp)
> +	movq %rbp, RBP(%rsp)
>  	movq %rsp, %rdi
>  
>  	call ftrace_return_to_handler
>  
>  	movq %rax, %rdi
> -	movq 8(%rsp), %rdx
> -	movq (%rsp), %rax
> +	movq RDX(%rsp), %rdx
> +	movq RAX(%rsp), %rax
>  
> -	addq $24, %rsp
> +	addq $(FRAME_SIZE), %rsp
>  	/*
>  	 * Jump back to the old return address. This cannot be JMP_NOSPEC rdi
>  	 * since IBT would demand that contain ENDBR, which simply isn't so for
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 1fe49a28de2d..13987cd63553 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -43,9 +43,8 @@ struct dyn_ftrace;
>  
>  char *arch_ftrace_match_adjust(char *str, const char *search);
>  
> -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
> -struct fgraph_ret_regs;
> -unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs);
> +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS
> +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs);
>  #else
>  unsigned long ftrace_return_to_handler(unsigned long frame_pointer);
>  #endif
> @@ -134,6 +133,13 @@ extern int ftrace_enabled;
>   * Also, architecture dependent fields can be used for internal process.
>   * (e.g. orig_ax on x86_64)
>   *
> + * Basically, ftrace_regs stores the registers related to the context.
> + * On function entry, registers for function parameters and hooking the
> + * function call are stored, and on function exit, registers for function
> + * return value and frame pointers are stored.
> + *
> + * And also, it dpends on the context that which registers are restored
> + * from the ftrace_regs.
>   * On the function entry, those registers will be restored except for
>   * the stack pointer, so that user can change the function parameters
>   * and instruction pointer (e.g. live patching.)
> @@ -191,6 +197,8 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs)
>  	override_function_with_return(ftrace_get_regs(fregs))
>  #define ftrace_regs_query_register_offset(name) \
>  	regs_query_register_offset(name)
> +#define ftrace_regs_get_frame_pointer(fregs) \
> +	frame_pointer(&(fregs)->regs)
>  #endif
>  
>  typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
> diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
> index 721c3b221048..ab277eff80dc 100644
> --- a/kernel/trace/Kconfig
> +++ b/kernel/trace/Kconfig
> @@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER
>  	help
>  	  See Documentation/trace/ftrace-design.rst
>  
> -config HAVE_FUNCTION_GRAPH_RETVAL
> +config HAVE_FUNCTION_GRAPH_FREGS
>  	bool
>  
>  config HAVE_DYNAMIC_FTRACE
> @@ -232,7 +232,7 @@ config FUNCTION_GRAPH_TRACER
>  
>  config FUNCTION_GRAPH_RETVAL
>  	bool "Kernel Function Graph Return Value"
> -	depends on HAVE_FUNCTION_GRAPH_RETVAL
> +	depends on HAVE_FUNCTION_GRAPH_FREGS
>  	depends on FUNCTION_GRAPH_TRACER
>  	default n
>  	help
> diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> index 0322c5723748..30bebe43607d 100644
> --- a/kernel/trace/fgraph.c
> +++ b/kernel/trace/fgraph.c
> @@ -763,15 +763,12 @@ static struct notifier_block ftrace_suspend_notifier = {
>  	.notifier_call = ftrace_suspend_notifier_call,
>  };
>  
> -/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */
> -struct fgraph_ret_regs;
> -
>  /*
>   * Send the trace to the ring-buffer.
>   * @return the original return address.
>   */
> -static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs,
> -						unsigned long frame_pointer)
> +static inline unsigned long
> +__ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointer)
>  {
>  	struct ftrace_ret_stack *ret_stack;
>  	struct ftrace_graph_ret trace;
> @@ -791,7 +788,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
>  
>  	trace.rettime = trace_clock_local();
>  #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
> -	trace.retval = fgraph_ret_regs_return_value(ret_regs);
> +	trace.retval = ftrace_regs_get_return_value(fregs);
>  #endif
>  
>  	bitmap = get_bitmap_bits(current, offset);
> @@ -826,14 +823,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
>  }
>  
>  /*
> - * After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can
> - * leave only ftrace_return_to_handler(ret_regs).
> + * After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can
> + * leave only ftrace_return_to_handler(fregs).
>   */
> -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
> -unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs)
> +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS
> +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs)
>  {
> -	return __ftrace_return_to_handler(ret_regs,
> -				fgraph_ret_regs_frame_pointer(ret_regs));
> +	return __ftrace_return_to_handler(fregs,
> +				ftrace_regs_get_frame_pointer(fregs));
>  }
>  #else
>  unsigned long ftrace_return_to_handler(unsigned long frame_pointer)


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs
  2024-09-12 15:09 ` [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs Masami Hiramatsu (Google)
@ 2024-09-15  9:22   ` Steven Rostedt
  2024-09-17 10:14     ` Will Deacon
  0 siblings, 1 reply; 53+ messages in thread
From: Steven Rostedt @ 2024-09-15  9:22 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon, linux-arm-kernel
  Cc: Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland


Can I get an Acked-by from the AARCH64 maintainers for this patch?

Thanks!

-- Steve


On Fri, 13 Sep 2024 00:09:35 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> 
> Add ftrace_partial_regs() which converts the ftrace_regs to pt_regs.
> This is for the eBPF which needs this to keep the same pt_regs interface
> to access registers.
> Thus when replacing the pt_regs with ftrace_regs in fprobes (which is
> used by kprobe_multi eBPF event), this will be used.
> 
> If the architecture defines its own ftrace_regs, this copies partial
> registers to pt_regs and returns it. If not, ftrace_regs is the same as
> pt_regs and ftrace_partial_regs() will return ftrace_regs::regs.
> 
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> Acked-by: Florent Revest <revest@chromium.org>
> ---
>  Changes in v14:
>   - Add riscv change.
>  Changes in v8:
>   - Add the reason why this required in changelog.
>  Changes from previous series: NOTHING, just forward ported.
> ---
>  arch/arm64/include/asm/ftrace.h |   11 +++++++++++
>  arch/riscv/include/asm/ftrace.h |   12 ++++++++++++
>  include/linux/ftrace.h          |   17 +++++++++++++++++
>  3 files changed, 40 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
> index dffaab3dd1f1..5cd587afab6d 100644
> --- a/arch/arm64/include/asm/ftrace.h
> +++ b/arch/arm64/include/asm/ftrace.h
> @@ -132,6 +132,17 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
>  	return fregs->fp;
>  }
>  
> +static __always_inline struct pt_regs *
> +ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
> +{
> +	memcpy(regs->regs, fregs->regs, sizeof(u64) * 9);
> +	regs->sp = fregs->sp;
> +	regs->pc = fregs->pc;
> +	regs->regs[29] = fregs->fp;
> +	regs->regs[30] = fregs->lr;
> +	return regs;
> +}
> +
>  int ftrace_regs_query_register_offset(const char *name);
>  
>  int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
> diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
> index e9f364ce9fe8..897779dec402 100644
> --- a/arch/riscv/include/asm/ftrace.h
> +++ b/arch/riscv/include/asm/ftrace.h
> @@ -193,6 +193,18 @@ static __always_inline void ftrace_override_function_with_return(struct ftrace_r
>  	fregs->epc = fregs->ra;
>  }
>  
> +static __always_inline struct pt_regs *
> +ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
> +{
> +	memcpy(&regs->a0, fregs->args, sizeof(u64) * 8);
> +	regs->epc = fregs->epc;
> +	regs->ra = fregs->ra;
> +	regs->sp = fregs->sp;
> +	regs->s0 = fregs->s0;
> +	regs->t1 = fregs->t1;
> +	return regs;
> +}
> +
>  int ftrace_regs_query_register_offset(const char *name);
>  
>  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index a1b2ef492c7f..bd9a26bdf660 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -176,6 +176,23 @@ static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs
>  	return arch_ftrace_get_regs(fregs);
>  }
>  
> +#if !defined(CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS) || \
> +	defined(CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST)
> +
> +static __always_inline struct pt_regs *
> +ftrace_partial_regs(struct ftrace_regs *fregs, struct pt_regs *regs)
> +{
> +	/*
> +	 * If CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST=y, ftrace_regs memory
> +	 * layout is the same as pt_regs. So always returns that address.
> +	 * Since arch_ftrace_get_regs() will check some members and may return
> +	 * NULL, we can not use it.
> +	 */
> +	return &fregs->regs;
> +}
> +
> +#endif /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS || CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST */
> +
>  /*
>   * When true, the ftrace_regs_{get,set}_*() functions may be used on fregs.
>   * Note: this can be true even when ftrace_get_regs() cannot provide a pt_regs.


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-15  9:15   ` Steven Rostedt
@ 2024-09-16 12:16     ` Heiko Carstens
  2024-09-16 16:29       ` Steven Rostedt
  0 siblings, 1 reply; 53+ messages in thread
From: Heiko Carstens @ 2024-09-16 12:16 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
	linux-s390, Masami Hiramatsu (Google), Florent Revest,
	linux-trace-kernel, LKML, Alexei Starovoitov, Jiri Olsa,
	Arnaldo Carvalho de Melo, Daniel Borkmann, Mark Rutland

On Sun, Sep 15, 2024 at 05:15:59AM -0400, Steven Rostedt wrote:
> 
> Can I get an Acked-by from the S390 maintainers for this patch?

...

> > +static __always_inline unsigned long
> > +ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)
> > +{
> > +	unsigned long *sp;
> > +
> > +	sp = (void *)ftrace_regs_get_stack_pointer(fregs);
> > +	return sp[0];	/* return backchain */
> > +}
> > +

...

> > diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
> > index ae4d4fd9afcd..cda798b976de 100644
> > --- a/arch/s390/kernel/mcount.S
> > +++ b/arch/s390/kernel/mcount.S
> > @@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common)
> >  SYM_FUNC_START(return_to_handler)
> >  	stmg	%r2,%r5,32(%r15)
> >  	lgr	%r1,%r15
> > -	aghi	%r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE)
> > +# Allocate ftrace_regs + backchain on the stack
> > +	aghi	%r15,-STACK_FRAME_SIZE_FREGS
> >  	stg	%r1,__SF_BACKCHAIN(%r15)
> >  	la	%r3,STACK_FRAME_OVERHEAD(%r15)
> > -	stg	%r1,__FGRAPH_RET_FP(%r3)
> > -	stg	%r2,__FGRAPH_RET_GPR2(%r3)
> > +	stg	%r2,(__SF_GPRS+2*8)(%r15)
> > +	stg	%r15,(__SF_GPRS+15*8)(%r15)
> >  	lgr	%r2,%r3
> >  	brasl	%r14,ftrace_return_to_handler
> > -	aghi	%r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE
> > +	aghi	%r15,STACK_FRAME_SIZE_FREGS

This does not pass the ftrace selftests. Please merge the patch below
into this patch. With that:

Acked-by: Heiko Carstens <hca@linux.ibm.com>

diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
index 7b80ff4d3386..df5a0f8d3445 100644
--- a/arch/s390/include/asm/ftrace.h
+++ b/arch/s390/include/asm/ftrace.h
@@ -78,10 +78,7 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
 static __always_inline unsigned long
 ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)
 {
-	unsigned long *sp;
-
-	sp = (void *)ftrace_regs_get_stack_pointer(fregs);
-	return sp[0];	/* return backchain */
+	return ftrace_regs_get_stack_pointer(fregs);
 }
 
 static __always_inline unsigned long
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
index cda798b976de..10b08e617306 100644
--- a/arch/s390/kernel/mcount.S
+++ b/arch/s390/kernel/mcount.S
@@ -133,13 +133,12 @@ SYM_CODE_END(ftrace_common)
 SYM_FUNC_START(return_to_handler)
 	stmg	%r2,%r5,32(%r15)
 	lgr	%r1,%r15
-# Allocate ftrace_regs + backchain on the stack
+	# allocate ftrace_regs and stack frame for ftrace_return_to_handler
 	aghi	%r15,-STACK_FRAME_SIZE_FREGS
 	stg	%r1,__SF_BACKCHAIN(%r15)
-	la	%r3,STACK_FRAME_OVERHEAD(%r15)
-	stg	%r2,(__SF_GPRS+2*8)(%r15)
-	stg	%r15,(__SF_GPRS+15*8)(%r15)
-	lgr	%r2,%r3
+	stg	%r2,(STACK_FREGS_PTREGS_GPRS+2*8)(%r15)
+	stg	%r1,(STACK_FREGS_PTREGS_GPRS+15*8)(%r15)
+	la	%r2,STACK_FRAME_OVERHEAD(%r15)
 	brasl	%r14,ftrace_return_to_handler
 	aghi	%r15,STACK_FRAME_SIZE_FREGS
 	lgr	%r14,%r2

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-16 12:16     ` Heiko Carstens
@ 2024-09-16 16:29       ` Steven Rostedt
  2024-09-16 18:59         ` Heiko Carstens
  0 siblings, 1 reply; 53+ messages in thread
From: Steven Rostedt @ 2024-09-16 16:29 UTC (permalink / raw)
  To: Heiko Carstens
  Cc: Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
	linux-s390, Masami Hiramatsu (Google), Florent Revest,
	linux-trace-kernel, LKML, Alexei Starovoitov, Jiri Olsa,
	Arnaldo Carvalho de Melo, Daniel Borkmann, Mark Rutland

On Mon, 16 Sep 2024 14:16:56 +0200
Heiko Carstens <hca@linux.ibm.com> wrote:

> This does not pass the ftrace selftests. Please merge the patch below
> into this patch. With that:
> 
> Acked-by: Heiko Carstens <hca@linux.ibm.com>

Thank you very much, this is why I wanted arch maintainers acks before
taking anything.

There may be other patches in this series that I didn't Cc everyone
(yet). Did you look at the other patches? If not, I'll go and do the Cc.
It's a manual process.

-- Steve

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-16 16:29       ` Steven Rostedt
@ 2024-09-16 18:59         ` Heiko Carstens
  2024-10-01 12:55           ` Masami Hiramatsu
  0 siblings, 1 reply; 53+ messages in thread
From: Heiko Carstens @ 2024-09-16 18:59 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
	linux-s390, Masami Hiramatsu (Google), Florent Revest,
	linux-trace-kernel, LKML, Alexei Starovoitov, Jiri Olsa,
	Arnaldo Carvalho de Melo, Daniel Borkmann, Mark Rutland,
	Sven Schnelle

On Mon, Sep 16, 2024 at 12:29:30PM -0400, Steven Rostedt wrote:
> On Mon, 16 Sep 2024 14:16:56 +0200
> Heiko Carstens <hca@linux.ibm.com> wrote:
> 
> > This does not pass the ftrace selftests. Please merge the patch below
> > into this patch. With that:
> > 
> > Acked-by: Heiko Carstens <hca@linux.ibm.com>
> 
> Thank you very much, this is why I wanted arch maintainers acks before
> taking anything.
> 
> There may be other patches in this series that I didn't Cc everyone
> (yet). Did you look at the other patches? If not, I'll go and do the Cc.
> It's a manual process.

I just had a quick look and tried Masami's git tree with the
topic/fprobe-on-fgraph branch and tried the ftrace selftests, which
resulted in the provided small fix.

But now that you ask, I can see that FPROBE is not available on s390
anymore with the full series, since HAVE_FTRACE_GRAPH_FUNC is not
available.

Also the s390 variant of arch_ftrace_fill_perf_regs() seems to be
incorrect.

Looks like some work is needed from our side.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
  2024-09-15  8:46   ` Steven Rostedt
@ 2024-09-17  8:26     ` Will Deacon
  2024-09-30 18:46       ` Steven Rostedt
  0 siblings, 1 reply; 53+ messages in thread
From: Will Deacon @ 2024-09-17  8:26 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Catalin Marinas, linux-arm-kernel, Masami Hiramatsu (Google),
	Florent Revest, linux-trace-kernel, LKML, Alexei Starovoitov,
	Jiri Olsa, Arnaldo Carvalho de Melo, Daniel Borkmann,
	Alan Maguire, Mark Rutland

On Sun, Sep 15, 2024 at 04:46:14AM -0400, Steven Rostedt wrote:
> Can I get an Acked-by from the AARCH64 maintainers for this patch?

Sorry, I wasn't CC'd on the thread, so I missed this.

> On Fri, 13 Sep 2024 00:08:40 +0900
> "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> 
> > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > 
> > Pass ftrace_regs to the fgraph_ops::entryfunc(). If ftrace_regs is not
> > available, it passes a NULL instead. User callback function can access
> > some registers (including return address) via this ftrace_regs.

Under which circumstances is 'ftrace_regs' NULL?

The arm64 implementation of ftrace_graph_func() is:

> > diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
> > index a650f5e11fc5..bc647b725e6a 100644
> > --- a/arch/arm64/kernel/ftrace.c
> > +++ b/arch/arm64/kernel/ftrace.c
> > @@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
> >  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
> >  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
> >  {
> > -	prepare_ftrace_return(ip, &fregs->lr, fregs->fp);
> > +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> > +	unsigned long frame_pointer = fregs->fp;

Which dereferences the unchecked pointer here ^^.

Will

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-15  9:11   ` Steven Rostedt
@ 2024-09-17  9:55     ` Will Deacon
  2024-09-30 18:55       ` Steven Rostedt
  0 siblings, 1 reply; 53+ messages in thread
From: Will Deacon @ 2024-09-17  9:55 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Catalin Marinas, linux-arm-kernel, Masami Hiramatsu (Google),
	Florent Revest, linux-trace-kernel, LKML, Alexei Starovoitov,
	Jiri Olsa, Arnaldo Carvalho de Melo, Daniel Borkmann,
	Mark Rutland

On Sun, Sep 15, 2024 at 05:11:44AM -0400, Steven Rostedt wrote:
> On Fri, 13 Sep 2024 00:08:51 +0900
> "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> 
> > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > 
> > Use ftrace_regs instead of fgraph_ret_regs for tracing return value
> > on function_graph tracer because of simplifying the callback interface.
> > 
> > The CONFIG_HAVE_FUNCTION_GRAPH_RETVAL is also replaced by
> > CONFIG_HAVE_FUNCTION_GRAPH_FREGS.
> > 
> > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > ---
> >  Changes in v8:
> >   - Newly added.
> > ---
> >  arch/arm64/Kconfig                  |    1 +
> >  arch/arm64/include/asm/ftrace.h     |   23 ++++++-----------------
> >  arch/arm64/kernel/asm-offsets.c     |   12 ------------
> >  arch/arm64/kernel/entry-ftrace.S    |   32 ++++++++++++++++++--------------

The arm64 part looks good to me, although passing a partially-populated
struct out of asm feels like it's going to cause us hard-to-debug
problems down the line if any of those extra fields get used. How hard
would it be to poison the unpopulated members of 'ftrace_regs'?

Will

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
  2024-09-15  8:49   ` Steven Rostedt
@ 2024-09-17 10:08     ` Will Deacon
  2024-09-30 19:03       ` Steven Rostedt
  0 siblings, 1 reply; 53+ messages in thread
From: Will Deacon @ 2024-09-17 10:08 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Catalin Marinas, linux-arm-kernel, Masami Hiramatsu (Google),
	Florent Revest, linux-trace-kernel, LKML, Alexei Starovoitov,
	Jiri Olsa, Arnaldo Carvalho de Melo, Daniel Borkmann,
	Mark Rutland

On Sun, Sep 15, 2024 at 04:49:20AM -0400, Steven Rostedt wrote:
> On Fri, 13 Sep 2024 00:09:02 +0900
> "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> 
> > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > 
> > Pass ftrace_regs to the fgraph_ops::retfunc(). If ftrace_regs is not
> > available, it passes a NULL instead. User callback function can access
> > some registers (including return address) via this ftrace_regs.
> > 
> > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > ---
> >  Changes in v8:
> >   - Pass ftrace_regs to retfunc, instead of adding retregfunc.
> >  Changes in v6:
> >   - update to use ftrace_regs_get_return_value() because of reordering
> >     patches.
> >  Changes in v3:
> >   - Update for new multiple fgraph.
> >   - Save the return address to instruction pointer in ftrace_regs.
> > ---
> >  include/linux/ftrace.h               |    3 ++-
> >  kernel/trace/fgraph.c                |   16 +++++++++++-----
> >  kernel/trace/ftrace.c                |    3 ++-
> >  kernel/trace/trace.h                 |    3 ++-
> >  kernel/trace/trace_functions_graph.c |    7 ++++---
> >  kernel/trace/trace_irqsoff.c         |    3 ++-
> >  kernel/trace/trace_sched_wakeup.c    |    3 ++-
> >  kernel/trace/trace_selftest.c        |    3 ++-
> >  8 files changed, 27 insertions(+), 14 deletions(-)
> > 
> > diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> > index 13987cd63553..e7c41d9988e1 100644
> > --- a/include/linux/ftrace.h
> > +++ b/include/linux/ftrace.h
> > @@ -1069,7 +1069,8 @@ struct fgraph_ops;
> >  
> >  /* Type of the callback handlers for tracing function graph*/
> >  typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
> > -				       struct fgraph_ops *); /* return */
> > +				       struct fgraph_ops *,
> > +				       struct ftrace_regs *); /* return */
> >  typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
> >  				      struct fgraph_ops *,
> >  				      struct ftrace_regs *); /* entry */
> > diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> > index 30bebe43607d..6a3e2db16aa4 100644
> > --- a/kernel/trace/fgraph.c
> > +++ b/kernel/trace/fgraph.c
> > @@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
> >  }
> >  
> >  /* ftrace_graph_return set to this to tell some archs to run function graph */
> > -static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops)
> > +static void return_run(struct ftrace_graph_ret *trace, struct fgraph_ops *ops,
> > +		       struct ftrace_regs *fregs)
> >  {
> >  }
> >  
> > @@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
> >  }
> >  
> >  static void ftrace_graph_ret_stub(struct ftrace_graph_ret *trace,
> > -				  struct fgraph_ops *gops)
> > +				  struct fgraph_ops *gops,
> > +				  struct ftrace_regs *fregs)
> >  {
> >  }
> >  
> > @@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
> >  	}
> >  
> >  	trace.rettime = trace_clock_local();
> > +	if (fregs)
> > +		ftrace_regs_set_instruction_pointer(fregs, ret);

Where does the instruction pointer get used after this? The arm64
'return_to_handler' function doesn't look at it when we return.

Will

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs
  2024-09-15  9:22   ` Steven Rostedt
@ 2024-09-17 10:14     ` Will Deacon
  2024-10-01 23:26       ` Masami Hiramatsu
  0 siblings, 1 reply; 53+ messages in thread
From: Will Deacon @ 2024-09-17 10:14 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Catalin Marinas, linux-arm-kernel, Masami Hiramatsu (Google),
	Florent Revest, linux-trace-kernel, LKML, Alexei Starovoitov,
	Jiri Olsa, Arnaldo Carvalho de Melo, Daniel Borkmann,
	Mark Rutland

On Sun, Sep 15, 2024 at 05:22:04AM -0400, Steven Rostedt wrote:
> On Fri, 13 Sep 2024 00:09:35 +0900
> "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> 
> > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > 
> > Add ftrace_partial_regs() which converts the ftrace_regs to pt_regs.
> > This is for the eBPF which needs this to keep the same pt_regs interface
> > to access registers.
> > Thus when replacing the pt_regs with ftrace_regs in fprobes (which is
> > used by kprobe_multi eBPF event), this will be used.
> > 
> > If the architecture defines its own ftrace_regs, this copies partial
> > registers to pt_regs and returns it. If not, ftrace_regs is the same as
> > pt_regs and ftrace_partial_regs() will return ftrace_regs::regs.
> > 
> > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > Acked-by: Florent Revest <revest@chromium.org>
> > ---
> >  Changes in v14:
> >   - Add riscv change.
> >  Changes in v8:
> >   - Add the reason why this required in changelog.
> >  Changes from previous series: NOTHING, just forward ported.
> > ---
> >  arch/arm64/include/asm/ftrace.h |   11 +++++++++++
> >  arch/riscv/include/asm/ftrace.h |   12 ++++++++++++
> >  include/linux/ftrace.h          |   17 +++++++++++++++++
> >  3 files changed, 40 insertions(+)
> > 
> > diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
> > index dffaab3dd1f1..5cd587afab6d 100644
> > --- a/arch/arm64/include/asm/ftrace.h
> > +++ b/arch/arm64/include/asm/ftrace.h
> > @@ -132,6 +132,17 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
> >  	return fregs->fp;
> >  }
> >  
> > +static __always_inline struct pt_regs *
> > +ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
> > +{
> > +	memcpy(regs->regs, fregs->regs, sizeof(u64) * 9);
> > +	regs->sp = fregs->sp;
> > +	regs->pc = fregs->pc;
> > +	regs->regs[29] = fregs->fp;
> > +	regs->regs[30] = fregs->lr;
> > +	return regs;
> > +}

Ah, I guess this is where we pick up the lr that was set in patch 5.

Acked-by: Will Deacon <will@kernel.org>

Will

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
  2024-09-17  8:26     ` Will Deacon
@ 2024-09-30 18:46       ` Steven Rostedt
  2024-10-01  1:57         ` Masami Hiramatsu
  0 siblings, 1 reply; 53+ messages in thread
From: Steven Rostedt @ 2024-09-30 18:46 UTC (permalink / raw)
  To: Will Deacon
  Cc: Catalin Marinas, linux-arm-kernel, Masami Hiramatsu (Google),
	Florent Revest, linux-trace-kernel, LKML, Alexei Starovoitov,
	Jiri Olsa, Arnaldo Carvalho de Melo, Daniel Borkmann,
	Alan Maguire, Mark Rutland

On Tue, 17 Sep 2024 09:26:25 +0100
Will Deacon <will@kernel.org> wrote:

> On Sun, Sep 15, 2024 at 04:46:14AM -0400, Steven Rostedt wrote:
> > Can I get an Acked-by from the AARCH64 maintainers for this patch?  
> 
> Sorry, I wasn't CC'd on the thread, so I missed this.

Here's the lore link that starts it all:

  https://lore.kernel.org/all/172615368656.133222.2336770908714920670.stgit@devnote2/

There's also a v15:

   https://lore.kernel.org/linux-trace-kernel/172639136989.366111.11359590127009702129.stgit@devnote2/

> 
> > On Fri, 13 Sep 2024 00:08:40 +0900
> > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> >   
> > > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > > 
> > > Pass ftrace_regs to the fgraph_ops::entryfunc(). If ftrace_regs is not
> > > available, it passes a NULL instead. User callback function can access
> > > some registers (including return address) via this ftrace_regs.  
> 
> Under which circumstances is 'ftrace_regs' NULL?

When the arch does not define: HAVE_DYNAMIC_FTRACE_WITH_ARGS or
HAVE_DYNAMIC_FTRACE_WITH_REGS

If HAVE_DYNAMIC_FTRACE_WITH_REGS is defined but not the WITH_ARGS, and the
ops used to register the function callback does not set FTRACE_OPS_FL_SAVE_REGS.

Otherwise it should be set.

> 
> The arm64 implementation of ftrace_graph_func() is:
> 
> > > diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
> > > index a650f5e11fc5..bc647b725e6a 100644
> > > --- a/arch/arm64/kernel/ftrace.c
> > > +++ b/arch/arm64/kernel/ftrace.c
> > > @@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
> > >  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
> > >  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
> > >  {
> > > -	prepare_ftrace_return(ip, &fregs->lr, fregs->fp);
> > > +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> > > +	unsigned long frame_pointer = fregs->fp;  
> 
> Which dereferences the unchecked pointer here ^^.

Just above the visible portion of the patch, we have:

#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
                       struct ftrace_ops *op, struct ftrace_regs *fregs)
{

Which means fregs will be available. The "WITH_ARGS" config tells us that
the arch will supply the function with valid fregs to the ftrace callback
(which this is).

-- Steve


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-17  9:55     ` Will Deacon
@ 2024-09-30 18:55       ` Steven Rostedt
  2024-10-01 23:10         ` Masami Hiramatsu
  0 siblings, 1 reply; 53+ messages in thread
From: Steven Rostedt @ 2024-09-30 18:55 UTC (permalink / raw)
  To: Will Deacon
  Cc: Catalin Marinas, linux-arm-kernel, Masami Hiramatsu (Google),
	Florent Revest, linux-trace-kernel, LKML, Alexei Starovoitov,
	Jiri Olsa, Arnaldo Carvalho de Melo, Daniel Borkmann,
	Mark Rutland

On Tue, 17 Sep 2024 10:55:39 +0100
Will Deacon <will@kernel.org> wrote:

> The arm64 part looks good to me, although passing a partially-populated
> struct out of asm feels like it's going to cause us hard-to-debug
> problems down the line if any of those extra fields get used. How hard
> would it be to poison the unpopulated members of 'ftrace_regs'?

The purpose of creating ftrace_regs was to allow a partially populated
pt_regs to be sent around, as Thomas Gleixner and Peter Zijlstra were
against using pt_regs that were not fully populated. Hence, I created
"ftrace_regs" for this purpose.

ftrace_regs should never be accessed via its internal elements but only with
its accessor functions, as depending on the arch or functionality used, the
content of the structure should never be trusted. The accessor functions
will do all the verification needed.

I may add some compiler hacks to enforce this. Something like:

struct ftrace_regs {
	void *nothing_to_see_here;
};

And then change the arch code to be something like:

// in arch/arm64/include/asm/ftrace.h:

struct arch_ftrace_regs {
        /* x0 - x8 */
        unsigned long regs[9];

#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
        unsigned long direct_tramp;
#else
        unsigned long __unused;
#endif

        unsigned long fp;
        unsigned long lr;

        unsigned long sp;
        unsigned long pc;
};

#define get_arch_ftrace_regs(fregs) ((struct arch_ftrace_regs *)(fregs))

static __always_inline void
ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
                                    unsigned long pc)
{
	struct arch_ftrace_regs *afregs = get_ftrace_regs(fregs);
        afregs->pc = pc;
}


-- Steve

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
  2024-09-17 10:08     ` Will Deacon
@ 2024-09-30 19:03       ` Steven Rostedt
  2024-10-01 23:24         ` Masami Hiramatsu
  0 siblings, 1 reply; 53+ messages in thread
From: Steven Rostedt @ 2024-09-30 19:03 UTC (permalink / raw)
  To: Will Deacon
  Cc: Catalin Marinas, linux-arm-kernel, Masami Hiramatsu (Google),
	Florent Revest, linux-trace-kernel, LKML, Alexei Starovoitov,
	Jiri Olsa, Arnaldo Carvalho de Melo, Daniel Borkmann,
	Mark Rutland

On Tue, 17 Sep 2024 11:08:48 +0100
Will Deacon <will@kernel.org> wrote:

> > > @@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
> > >  	}
> > >  
> > >  	trace.rettime = trace_clock_local();
> > > +	if (fregs)
> > > +		ftrace_regs_set_instruction_pointer(fregs, ret);  
> 
> Where does the instruction pointer get used after this? The arm64
> 'return_to_handler' function doesn't look at it when we return.

It's for the hooks to the return instruction. kretprobes will start using
function graph tracer to hook to a return of a function (via fprobes), and
the callbacks will need access to the return pointer. The callbacks get
passed the ftrace_regs, and this is how they can see what the function is
returning to. For example, BPF programs will need this.

So it's not needed for the infrastructure, only the callbacks that hook to
it.

-- Steve

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
  2024-09-30 18:46       ` Steven Rostedt
@ 2024-10-01  1:57         ` Masami Hiramatsu
  0 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu @ 2024-10-01  1:57 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Will Deacon, Catalin Marinas, linux-arm-kernel,
	Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Alan Maguire, Mark Rutland

On Mon, 30 Sep 2024 14:46:59 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Tue, 17 Sep 2024 09:26:25 +0100
> Will Deacon <will@kernel.org> wrote:
> 
> > On Sun, Sep 15, 2024 at 04:46:14AM -0400, Steven Rostedt wrote:
> > > Can I get an Acked-by from the AARCH64 maintainers for this patch?  
> > 
> > Sorry, I wasn't CC'd on the thread, so I missed this.
> 
> Here's the lore link that starts it all:
> 
>   https://lore.kernel.org/all/172615368656.133222.2336770908714920670.stgit@devnote2/
> 
> There's also a v15:
> 
>    https://lore.kernel.org/linux-trace-kernel/172639136989.366111.11359590127009702129.stgit@devnote2/
> 
> > 
> > > On Fri, 13 Sep 2024 00:08:40 +0900
> > > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> > >   
> > > > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > > > 
> > > > Pass ftrace_regs to the fgraph_ops::entryfunc(). If ftrace_regs is not
> > > > available, it passes a NULL instead. User callback function can access
> > > > some registers (including return address) via this ftrace_regs.  
> > 
> > Under which circumstances is 'ftrace_regs' NULL?
> 
> When the arch does not define: HAVE_DYNAMIC_FTRACE_WITH_ARGS or
> HAVE_DYNAMIC_FTRACE_WITH_REGS
> 
> If HAVE_DYNAMIC_FTRACE_WITH_REGS is defined but not the WITH_ARGS, and the
> ops used to register the function callback does not set FTRACE_OPS_FL_SAVE_REGS.
> 
> Otherwise it should be set.

I will add this condition in the description in next version.

Thank you,


> 
> > 
> > The arm64 implementation of ftrace_graph_func() is:
> > 
> > > > diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
> > > > index a650f5e11fc5..bc647b725e6a 100644
> > > > --- a/arch/arm64/kernel/ftrace.c
> > > > +++ b/arch/arm64/kernel/ftrace.c
> > > > @@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
> > > >  void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
> > > >  		       struct ftrace_ops *op, struct ftrace_regs *fregs)
> > > >  {
> > > > -	prepare_ftrace_return(ip, &fregs->lr, fregs->fp);
> > > > +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> > > > +	unsigned long frame_pointer = fregs->fp;  
> > 
> > Which dereferences the unchecked pointer here ^^.
> 
> Just above the visible portion of the patch, we have:
> 
> #ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
> void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
>                        struct ftrace_ops *op, struct ftrace_regs *fregs)
> {
> 
> Which means fregs will be available. The "WITH_ARGS" config tells us that
> the arch will supply the function with valid fregs to the ftrace callback
> (which this is).
> 
> -- Steve
> 


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-16 18:59         ` Heiko Carstens
@ 2024-10-01 12:55           ` Masami Hiramatsu
  0 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu @ 2024-10-01 12:55 UTC (permalink / raw)
  To: Heiko Carstens
  Cc: Steven Rostedt, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, linux-s390, Masami Hiramatsu (Google),
	Florent Revest, linux-trace-kernel, LKML, Alexei Starovoitov,
	Jiri Olsa, Arnaldo Carvalho de Melo, Daniel Borkmann,
	Mark Rutland, Sven Schnelle

On Mon, 16 Sep 2024 20:59:05 +0200
Heiko Carstens <hca@linux.ibm.com> wrote:

> On Mon, Sep 16, 2024 at 12:29:30PM -0400, Steven Rostedt wrote:
> > On Mon, 16 Sep 2024 14:16:56 +0200
> > Heiko Carstens <hca@linux.ibm.com> wrote:
> > 
> > > This does not pass the ftrace selftests. Please merge the patch below
> > > into this patch. With that:
> > > 
> > > Acked-by: Heiko Carstens <hca@linux.ibm.com>
> > 
> > Thank you very much, this is why I wanted arch maintainers acks before
> > taking anything.
> > 
> > There may be other patches in this series that I didn't Cc everyone
> > (yet). Did you look at the other patches? If not, I'll go and do the Cc.
> > It's a manual process.
> 
> I just had a quick look and tried Masami's git tree with the
> topic/fprobe-on-fgraph branch and tried the ftrace selftests, which
> resulted in the provided small fix.

Thanks, and sorry, my change on s390 was wrong. I confused that 
`stack_frame` can be a part of `ftrace_regs`. But at least in this
moment, `ftrace_regs` should replace only `fgraph_ret_regs`.

> 
> But now that you ask, I can see that FPROBE is not available on s390
> anymore with the full series, since HAVE_FTRACE_GRAPH_FUNC is not
> available.

Right, this is required for implementing fprobe.

> 
> Also the s390 variant of arch_ftrace_fill_perf_regs() seems to be
> incorrect.

Can you tell me what part will be wrong?

Thank you,

> 
> Looks like some work is needed from our side.


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-09-30 18:55       ` Steven Rostedt
@ 2024-10-01 23:10         ` Masami Hiramatsu
  2024-10-01 23:32           ` Steven Rostedt
  0 siblings, 1 reply; 53+ messages in thread
From: Masami Hiramatsu @ 2024-10-01 23:10 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Will Deacon, Catalin Marinas, linux-arm-kernel,
	Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland

On Mon, 30 Sep 2024 14:55:48 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Tue, 17 Sep 2024 10:55:39 +0100
> Will Deacon <will@kernel.org> wrote:
> 
> > The arm64 part looks good to me, although passing a partially-populated
> > struct out of asm feels like it's going to cause us hard-to-debug
> > problems down the line if any of those extra fields get used. How hard
> > would it be to poison the unpopulated members of 'ftrace_regs'?
> 
> The purpose of creating ftrace_regs was to allow a partially populated
> pt_regs to be sent around, as Thomas Gleixner and Peter Zijlstra were
> against using pt_regs that were not fully populated. Hence, I created
> "ftrace_regs" for this purpose.
> 
> ftrace_regs should never be accessed via its internal elements but only with
> its accessor functions, as depending on the arch or functionality used, the
> content of the structure should never be trusted. The accessor functions
> will do all the verification needed.
> 
> I may add some compiler hacks to enforce this. Something like:
> 
> struct ftrace_regs {
> 	void *nothing_to_see_here;
> };

Yeah, OK. But sizeof(fregs) may be changed. (Shouldn't we do too?)

> 
> And then change the arch code to be something like:
> 
> // in arch/arm64/include/asm/ftrace.h:
> 
> struct arch_ftrace_regs {
>         /* x0 - x8 */
>         unsigned long regs[9];
> 
> #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
>         unsigned long direct_tramp;
> #else
>         unsigned long __unused;
> #endif
> 
>         unsigned long fp;
>         unsigned long lr;
> 
>         unsigned long sp;
>         unsigned long pc;
> };

And if it is pt_regs compatible, 

#define arch_ftrace_regs pt_regs

?

> 
> #define get_arch_ftrace_regs(fregs) ((struct arch_ftrace_regs *)(fregs))
> 
> static __always_inline void
> ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
>                                     unsigned long pc)
> {
> 	struct arch_ftrace_regs *afregs = get_ftrace_regs(fregs);
>         afregs->pc = pc;
> }
> 
> 
> -- Steve


Thanks,

-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
  2024-09-30 19:03       ` Steven Rostedt
@ 2024-10-01 23:24         ` Masami Hiramatsu
  0 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu @ 2024-10-01 23:24 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Will Deacon, Catalin Marinas, linux-arm-kernel,
	Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland

On Mon, 30 Sep 2024 15:03:02 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Tue, 17 Sep 2024 11:08:48 +0100
> Will Deacon <will@kernel.org> wrote:
> 
> > > > @@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe
> > > >  	}
> > > >  
> > > >  	trace.rettime = trace_clock_local();
> > > > +	if (fregs)
> > > > +		ftrace_regs_set_instruction_pointer(fregs, ret);  
> > 
> > Where does the instruction pointer get used after this? The arm64
> > 'return_to_handler' function doesn't look at it when we return.
> 
> It's for the hooks to the return instruction. kretprobes will start using

not kretprobes, but fprobe. kretprobes continue using rethook.

> function graph tracer to hook to a return of a function (via fprobes), and
> the callbacks will need access to the return pointer. The callbacks get
> passed the ftrace_regs, and this is how they can see what the function is
> returning to. For example, BPF programs will need this.
> 
> So it's not needed for the infrastructure, only the callbacks that hook to
> it.

Yes, it will be used for showing where to return in the fprobe exit event.
More specifically, in the fprobe_return()@kernel/trace/fprobe.c in PATCH 13/19,
it is extracted from fregs.

+static void fprobe_return(struct ftrace_graph_ret *trace,
+			  struct fgraph_ops *gops,
+			  struct ftrace_regs *fregs)
+{
+	unsigned long *fgraph_data = NULL;
+	unsigned long ret_ip;
+	unsigned long val;
+	struct fprobe *fp;
+	int size, curr;
+	int size_words;
+
+	fgraph_data = (unsigned long *)fgraph_retrieve_data(gops->idx, &size);
+	if (WARN_ON_ONCE(!fgraph_data))
 		return;
+	size_words = SIZE_IN_LONG(size);
+	ret_ip = ftrace_regs_get_instruction_pointer(fregs);
+

Thank you,

> 
> -- Steve


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs
  2024-09-17 10:14     ` Will Deacon
@ 2024-10-01 23:26       ` Masami Hiramatsu
  0 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu @ 2024-10-01 23:26 UTC (permalink / raw)
  To: Will Deacon
  Cc: Steven Rostedt, Catalin Marinas, linux-arm-kernel,
	Masami Hiramatsu (Google), Florent Revest, linux-trace-kernel,
	LKML, Alexei Starovoitov, Jiri Olsa, Arnaldo Carvalho de Melo,
	Daniel Borkmann, Mark Rutland

On Tue, 17 Sep 2024 11:14:54 +0100
Will Deacon <will@kernel.org> wrote:

> On Sun, Sep 15, 2024 at 05:22:04AM -0400, Steven Rostedt wrote:
> > On Fri, 13 Sep 2024 00:09:35 +0900
> > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> > 
> > > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > > 
> > > Add ftrace_partial_regs() which converts the ftrace_regs to pt_regs.
> > > This is for the eBPF which needs this to keep the same pt_regs interface
> > > to access registers.
> > > Thus when replacing the pt_regs with ftrace_regs in fprobes (which is
> > > used by kprobe_multi eBPF event), this will be used.
> > > 
> > > If the architecture defines its own ftrace_regs, this copies partial
> > > registers to pt_regs and returns it. If not, ftrace_regs is the same as
> > > pt_regs and ftrace_partial_regs() will return ftrace_regs::regs.
> > > 
> > > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > > Acked-by: Florent Revest <revest@chromium.org>
> > > ---
> > >  Changes in v14:
> > >   - Add riscv change.
> > >  Changes in v8:
> > >   - Add the reason why this required in changelog.
> > >  Changes from previous series: NOTHING, just forward ported.
> > > ---
> > >  arch/arm64/include/asm/ftrace.h |   11 +++++++++++
> > >  arch/riscv/include/asm/ftrace.h |   12 ++++++++++++
> > >  include/linux/ftrace.h          |   17 +++++++++++++++++
> > >  3 files changed, 40 insertions(+)
> > > 
> > > diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
> > > index dffaab3dd1f1..5cd587afab6d 100644
> > > --- a/arch/arm64/include/asm/ftrace.h
> > > +++ b/arch/arm64/include/asm/ftrace.h
> > > @@ -132,6 +132,17 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
> > >  	return fregs->fp;
> > >  }
> > >  
> > > +static __always_inline struct pt_regs *
> > > +ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
> > > +{
> > > +	memcpy(regs->regs, fregs->regs, sizeof(u64) * 9);
> > > +	regs->sp = fregs->sp;
> > > +	regs->pc = fregs->pc;
> > > +	regs->regs[29] = fregs->fp;
> > > +	regs->regs[30] = fregs->lr;
> > > +	return regs;
> > > +}
> 
> Ah, I guess this is where we pick up the lr that was set in patch 5.

Yeah, this one also copies the fregs->pc to regs->pc, mainly for bpf.

> 
> Acked-by: Will Deacon <will@kernel.org>

Thank you!

> 
> Will


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-10-01 23:10         ` Masami Hiramatsu
@ 2024-10-01 23:32           ` Steven Rostedt
  2024-10-02 14:31             ` Masami Hiramatsu
  0 siblings, 1 reply; 53+ messages in thread
From: Steven Rostedt @ 2024-10-01 23:32 UTC (permalink / raw)
  To: Masami Hiramatsu (Google)
  Cc: Will Deacon, Catalin Marinas, linux-arm-kernel, Florent Revest,
	linux-trace-kernel, LKML, Alexei Starovoitov, Jiri Olsa,
	Arnaldo Carvalho de Melo, Daniel Borkmann, Mark Rutland

On Wed, 2 Oct 2024 08:10:37 +0900
Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:
> > 
> > I may add some compiler hacks to enforce this. Something like:
> > 
> > struct ftrace_regs {
> > 	void *nothing_to_see_here;
> > };  
> 
> Yeah, OK. But sizeof(fregs) may be changed. (Shouldn't we do too?)

Honestly, I don't think anything should be doing a sizeof(struct ftrace_regs)

Heck, perhaps we should make it totally zero!

  struct ftrace_regs {
	long nothing_here[];
  };

If someone needs to allocate, then we could provide a:

	ftrace_regs_size()

helper function.

> 
> > 
> > And then change the arch code to be something like:
> > 
> > // in arch/arm64/include/asm/ftrace.h:
> > 
> > struct arch_ftrace_regs {
> >         /* x0 - x8 */
> >         unsigned long regs[9];
> > 
> > #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
> >         unsigned long direct_tramp;
> > #else
> >         unsigned long __unused;
> > #endif
> > 
> >         unsigned long fp;
> >         unsigned long lr;
> > 
> >         unsigned long sp;
> >         unsigned long pc;
> > };  
> 
> And if it is pt_regs compatible, 
> 
> #define arch_ftrace_regs pt_regs
> 
> ?
> 

Only if it is fully pt_regs compatible.

-- Steve

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
  2024-10-01 23:32           ` Steven Rostedt
@ 2024-10-02 14:31             ` Masami Hiramatsu
  0 siblings, 0 replies; 53+ messages in thread
From: Masami Hiramatsu @ 2024-10-02 14:31 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Will Deacon, Catalin Marinas, linux-arm-kernel, Florent Revest,
	linux-trace-kernel, LKML, Alexei Starovoitov, Jiri Olsa,
	Arnaldo Carvalho de Melo, Daniel Borkmann, Mark Rutland

On Tue, 1 Oct 2024 19:32:34 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Wed, 2 Oct 2024 08:10:37 +0900
> Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:
> > > 
> > > I may add some compiler hacks to enforce this. Something like:
> > > 
> > > struct ftrace_regs {
> > > 	void *nothing_to_see_here;
> > > };  
> > 
> > Yeah, OK. But sizeof(fregs) may be changed. (Shouldn't we do too?)
> 
> Honestly, I don't think anything should be doing a sizeof(struct ftrace_regs)
> 
> Heck, perhaps we should make it totally zero!
> 
>   struct ftrace_regs {
> 	long nothing_here[];
>   };
> 
> If someone needs to allocate, then we could provide a:
> 
> 	ftrace_regs_size()
> 
> helper function.

Ah, Indeed.

> 
> > 
> > > 
> > > And then change the arch code to be something like:
> > > 
> > > // in arch/arm64/include/asm/ftrace.h:
> > > 
> > > struct arch_ftrace_regs {
> > >         /* x0 - x8 */
> > >         unsigned long regs[9];
> > > 
> > > #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
> > >         unsigned long direct_tramp;
> > > #else
> > >         unsigned long __unused;
> > > #endif
> > > 
> > >         unsigned long fp;
> > >         unsigned long lr;
> > > 
> > >         unsigned long sp;
> > >         unsigned long pc;
> > > };  
> > 
> > And if it is pt_regs compatible, 
> > 
> > #define arch_ftrace_regs pt_regs
> > 
> > ?
> > 
> 
> Only if it is fully pt_regs compatible.

Yeah, OK, this is good idea.

Thank you,

> 
> -- Steve


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2024-10-02 14:31 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-12 15:08 [PATCH v14 00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Masami Hiramatsu (Google)
2024-09-12 15:08 ` [PATCH v14 01/19] tracing: Add a comment about ftrace_regs definition Masami Hiramatsu (Google)
2024-09-12 15:08 ` [PATCH v14 02/19] tracing: Rename ftrace_regs_return_value to ftrace_regs_get_return_value Masami Hiramatsu (Google)
2024-09-12 15:08 ` [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc Masami Hiramatsu (Google)
2024-09-15  8:46   ` Steven Rostedt
2024-09-17  8:26     ` Will Deacon
2024-09-30 18:46       ` Steven Rostedt
2024-10-01  1:57         ` Masami Hiramatsu
2024-09-15  8:50   ` Steven Rostedt
2024-09-15  8:53   ` Steven Rostedt
2024-09-15  8:56   ` Steven Rostedt
2024-09-15  8:58   ` Steven Rostedt
2024-09-12 15:08 ` [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs Masami Hiramatsu (Google)
2024-09-15  9:11   ` Steven Rostedt
2024-09-17  9:55     ` Will Deacon
2024-09-30 18:55       ` Steven Rostedt
2024-10-01 23:10         ` Masami Hiramatsu
2024-10-01 23:32           ` Steven Rostedt
2024-10-02 14:31             ` Masami Hiramatsu
2024-09-15  9:13   ` Steven Rostedt
2024-09-15  9:15   ` Steven Rostedt
2024-09-16 12:16     ` Heiko Carstens
2024-09-16 16:29       ` Steven Rostedt
2024-09-16 18:59         ` Heiko Carstens
2024-10-01 12:55           ` Masami Hiramatsu
2024-09-15  9:17   ` Steven Rostedt
2024-09-12 15:09 ` [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc Masami Hiramatsu (Google)
2024-09-15  8:49   ` Steven Rostedt
2024-09-17 10:08     ` Will Deacon
2024-09-30 19:03       ` Steven Rostedt
2024-10-01 23:24         ` Masami Hiramatsu
2024-09-15  8:51   ` Steven Rostedt
2024-09-15  8:54   ` Steven Rostedt
2024-09-15  8:57   ` Steven Rostedt
2024-09-15  9:00   ` Steven Rostedt
2024-09-12 15:09 ` [PATCH v14 06/19] fprobe: Use ftrace_regs in fprobe entry handler Masami Hiramatsu (Google)
2024-09-12 15:09 ` [PATCH v14 07/19] fprobe: Use ftrace_regs in fprobe exit handler Masami Hiramatsu (Google)
2024-09-12 15:09 ` [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs Masami Hiramatsu (Google)
2024-09-15  9:22   ` Steven Rostedt
2024-09-17 10:14     ` Will Deacon
2024-10-01 23:26       ` Masami Hiramatsu
2024-09-12 15:09 ` [PATCH v14 09/19] tracing: Add ftrace_fill_perf_regs() for perf event Masami Hiramatsu (Google)
2024-09-12 15:09 ` [PATCH v14 10/19] tracing/fprobe: Enable fprobe events with CONFIG_DYNAMIC_FTRACE_WITH_ARGS Masami Hiramatsu (Google)
2024-09-12 15:10 ` [PATCH v14 11/19] bpf: Enable kprobe_multi feature if CONFIG_FPROBE is enabled Masami Hiramatsu (Google)
2024-09-12 15:10 ` [PATCH v14 12/19] ftrace: Add CONFIG_HAVE_FTRACE_GRAPH_FUNC Masami Hiramatsu (Google)
2024-09-12 15:10 ` [PATCH v14 13/19] fprobe: Rewrite fprobe on function-graph tracer Masami Hiramatsu (Google)
2024-09-12 15:10 ` [PATCH v14 14/19] tracing: Fix function timing profiler to initialize hashtable Masami Hiramatsu (Google)
2024-09-12 15:10 ` [PATCH v14 15/19] tracing/fprobe: Remove nr_maxactive from fprobe Masami Hiramatsu (Google)
2024-09-12 15:11 ` [PATCH v14 16/19] selftests: ftrace: Remove obsolate maxactive syntax check Masami Hiramatsu (Google)
2024-09-12 15:11 ` [PATCH v14 17/19] selftests/ftrace: Add a test case for repeating register/unregister fprobe Masami Hiramatsu (Google)
2024-09-12 15:11 ` [PATCH v14 18/19] Documentation: probes: Update fprobe on function-graph tracer Masami Hiramatsu (Google)
2024-09-12 15:11 ` [PATCH v14 19/19] fgraph: Skip recording calltime/rettime if it is not nneeded Masami Hiramatsu (Google)
2024-09-14 21:53   ` Steven Rostedt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).