linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3 v4] ftrace: Add access to function arguments for all callbacks
@ 2020-11-06 21:42 Steven Rostedt
  2020-11-06 21:42 ` [PATCH 1/3 v4] ftrace: Have the callbacks receive a struct ftrace_regs instead of pt_regs Steven Rostedt
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Steven Rostedt @ 2020-11-06 21:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: Ingo Molnar, Andrew Morton, Miroslav Benes, Peter Zijlstra,
	Thomas Gleixner, Masami Hiramatsu


This is something I wanted to implement a long time ago, but held off until
there was a good reason to do so. Now it appears that having access to the
arguments of the function by default is very useful. As a bonus, because
arguments must be saved regardless before calling a callback, because they
need to be restored before returning back to the start of the traced
function, there's not much work to do to have them always be there for
normal function callbacks.

The basic idea is that if CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS is set, then
all callbacks registered to ftrace can use the regs parameter for the stack
and arguments (kernel_stack_pointer(regs), regs_get_kernel_argument(regs, n)),
without the need to set REGS that causes overhead by saving all registers as
REGS simulates a breakpoint.

This could be extended to move the REGS portion to kprobes itself, and
remove the SAVE_REGS flag completely, but for now, kprobes still uses the
full SAVE_REGS support.

The last patch extends the WITH_ARGS to allow default function tracing to
modify the instruction pointer, where livepatching for x86 no longer needs
to save all registers.

The idea of this approach is to give enough information to a callback that
it could retrieve all arguments, which includes the stack pointer and
instruction pointer.

This can also be extended to modify the function graph tracer to use the
function tracer instead of having a separate trampoline.

Changes since v3:
  Have live patching depend on HAVE_DYNAMIC_FTRACE_WITH_REGS *or*
  HAVE_DYNAMIC_FTRACE_WITH_ARGS


Steven Rostedt (VMware) (3):
      ftrace: Have the callbacks receive a struct ftrace_regs instead of pt_regs
      ftrace/x86: Allow for arguments to be passed in to ftrace_regs by default
      livepatch: Use the default ftrace_ops instead of REGS when ARGS is available

----
 arch/powerpc/include/asm/livepatch.h |  4 +++-
 arch/s390/include/asm/livepatch.h    |  5 ++++-
 arch/x86/Kconfig                     |  1 +
 arch/x86/include/asm/ftrace.h        | 18 ++++++++++++++++++
 arch/x86/include/asm/livepatch.h     |  4 ++--
 arch/x86/kernel/ftrace_64.S          | 15 +++++++++++++--
 arch/x86/kernel/kprobes/ftrace.c     |  3 ++-
 include/linux/ftrace.h               | 28 ++++++++++++++++++++++++++--
 include/linux/kprobes.h              |  2 +-
 kernel/livepatch/Kconfig             |  2 +-
 kernel/livepatch/patch.c             | 10 ++++++----
 kernel/trace/Kconfig                 |  9 +++++++++
 kernel/trace/ftrace.c                | 27 +++++++++++++++------------
 kernel/trace/trace_event_perf.c      |  2 +-
 kernel/trace/trace_functions.c       |  9 ++++-----
 kernel/trace/trace_irqsoff.c         |  2 +-
 kernel/trace/trace_sched_wakeup.c    |  2 +-
 kernel/trace/trace_selftest.c        | 20 +++++++++++---------
 kernel/trace/trace_stack.c           |  2 +-
 19 files changed, 120 insertions(+), 45 deletions(-)

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

* [PATCH 1/3 v4] ftrace: Have the callbacks receive a struct ftrace_regs instead of pt_regs
  2020-11-06 21:42 [PATCH 0/3 v4] ftrace: Add access to function arguments for all callbacks Steven Rostedt
@ 2020-11-06 21:42 ` Steven Rostedt
  2020-11-07  4:29   ` Masami Hiramatsu
  2020-11-06 21:42 ` [PATCH 2/3 v4] ftrace/x86: Allow for arguments to be passed in to ftrace_regs by default Steven Rostedt
  2020-11-06 21:42 ` [PATCH 3/3 v4] livepatch: Use the default ftrace_ops instead of REGS when ARGS is available Steven Rostedt
  2 siblings, 1 reply; 8+ messages in thread
From: Steven Rostedt @ 2020-11-06 21:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: Ingo Molnar, Andrew Morton, Miroslav Benes, Peter Zijlstra,
	Thomas Gleixner, Masami Hiramatsu

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

In preparation to have arguments of a function passed to callbacks attached
to functions as default, change the default callback prototype to receive a
struct ftrace_regs as the forth parameter instead of a pt_regs.

For callbacks that set the FL_SAVE_REGS flag in their ftrace_ops flags, they
will now need to get the pt_regs via a ftrace_get_regs() helper call. If
this is called by a callback that their ftrace_ops did not have a
FL_SAVE_REGS flag set, it that helper function will return NULL.

This will allow the ftrace_regs to hold enough just to get the parameters
and stack pointer, but without the worry that callbacks may have a pt_regs
that is not completely filled.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 arch/x86/kernel/kprobes/ftrace.c  |  3 ++-
 include/linux/ftrace.h            | 16 ++++++++++++++--
 include/linux/kprobes.h           |  2 +-
 kernel/livepatch/patch.c          |  3 ++-
 kernel/trace/ftrace.c             | 27 +++++++++++++++------------
 kernel/trace/trace_event_perf.c   |  2 +-
 kernel/trace/trace_functions.c    |  9 ++++-----
 kernel/trace/trace_irqsoff.c      |  2 +-
 kernel/trace/trace_sched_wakeup.c |  2 +-
 kernel/trace/trace_selftest.c     | 20 +++++++++++---------
 kernel/trace/trace_stack.c        |  2 +-
 11 files changed, 53 insertions(+), 35 deletions(-)

diff --git a/arch/x86/kernel/kprobes/ftrace.c b/arch/x86/kernel/kprobes/ftrace.c
index 954d930a7127..373e5fa3ce1f 100644
--- a/arch/x86/kernel/kprobes/ftrace.c
+++ b/arch/x86/kernel/kprobes/ftrace.c
@@ -14,8 +14,9 @@
 
 /* Ftrace callback handler for kprobes -- called under preepmt disabed */
 void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
-			   struct ftrace_ops *ops, struct pt_regs *regs)
+			   struct ftrace_ops *ops, struct ftrace_regs *fregs)
 {
+	struct pt_regs *regs = ftrace_get_regs(fregs);
 	struct kprobe *p;
 	struct kprobe_ctlblk *kcb;
 	int bit;
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 8dde9c17aaa5..24e1fa52337d 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -90,8 +90,20 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
 
 struct ftrace_ops;
 
+struct ftrace_regs {
+	struct pt_regs		regs;
+};
+
+static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs)
+{
+	if (!fregs)
+		return NULL;
+
+	return &fregs->regs;
+}
+
 typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
-			      struct ftrace_ops *op, struct pt_regs *regs);
+			      struct ftrace_ops *op, struct ftrace_regs *fregs);
 
 ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
 
@@ -259,7 +271,7 @@ int register_ftrace_function(struct ftrace_ops *ops);
 int unregister_ftrace_function(struct ftrace_ops *ops);
 
 extern void ftrace_stub(unsigned long a0, unsigned long a1,
-			struct ftrace_ops *op, struct pt_regs *regs);
+			struct ftrace_ops *op, struct ftrace_regs *fregs);
 
 #else /* !CONFIG_FUNCTION_TRACER */
 /*
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 629abaf25681..be73350955e4 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -345,7 +345,7 @@ static inline void wait_for_kprobe_optimizer(void) { }
 #endif /* CONFIG_OPTPROBES */
 #ifdef CONFIG_KPROBES_ON_FTRACE
 extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
-				  struct ftrace_ops *ops, struct pt_regs *regs);
+				  struct ftrace_ops *ops, struct ftrace_regs *fregs);
 extern int arch_prepare_kprobe_ftrace(struct kprobe *p);
 #endif
 
diff --git a/kernel/livepatch/patch.c b/kernel/livepatch/patch.c
index 875c5dbbdd33..f89f9e7e9b07 100644
--- a/kernel/livepatch/patch.c
+++ b/kernel/livepatch/patch.c
@@ -40,8 +40,9 @@ struct klp_ops *klp_find_ops(void *old_func)
 static void notrace klp_ftrace_handler(unsigned long ip,
 				       unsigned long parent_ip,
 				       struct ftrace_ops *fops,
-				       struct pt_regs *regs)
+				       struct ftrace_regs *fregs)
 {
+	struct pt_regs *regs = ftrace_get_regs(fregs);
 	struct klp_ops *ops;
 	struct klp_func *func;
 	int patch_state;
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 3db64fb0cce8..67888311784e 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -121,7 +121,7 @@ struct ftrace_ops global_ops;
 
 #if ARCH_SUPPORTS_FTRACE_OPS
 static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
-				 struct ftrace_ops *op, struct pt_regs *regs);
+				 struct ftrace_ops *op, struct ftrace_regs *fregs);
 #else
 /* See comment below, where ftrace_ops_list_func is defined */
 static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip);
@@ -140,7 +140,7 @@ static inline void ftrace_ops_init(struct ftrace_ops *ops)
 }
 
 static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip,
-			    struct ftrace_ops *op, struct pt_regs *regs)
+			    struct ftrace_ops *op, struct ftrace_regs *fregs)
 {
 	struct trace_array *tr = op->private;
 	int pid;
@@ -154,7 +154,7 @@ static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip,
 			return;
 	}
 
-	op->saved_func(ip, parent_ip, op, regs);
+	op->saved_func(ip, parent_ip, op, fregs);
 }
 
 static void ftrace_sync_ipi(void *data)
@@ -754,7 +754,7 @@ ftrace_profile_alloc(struct ftrace_profile_stat *stat, unsigned long ip)
 
 static void
 function_profile_call(unsigned long ip, unsigned long parent_ip,
-		      struct ftrace_ops *ops, struct pt_regs *regs)
+		      struct ftrace_ops *ops, struct ftrace_regs *fregs)
 {
 	struct ftrace_profile_stat *stat;
 	struct ftrace_profile *rec;
@@ -2143,6 +2143,7 @@ static int ftrace_check_record(struct dyn_ftrace *rec, bool enable, bool update)
 				else
 					rec->flags &= ~FTRACE_FL_TRAMP_EN;
 			}
+
 			if (flag & FTRACE_FL_DIRECT) {
 				/*
 				 * If there's only one user (direct_ops helper)
@@ -2368,8 +2369,9 @@ unsigned long ftrace_find_rec_direct(unsigned long ip)
 }
 
 static void call_direct_funcs(unsigned long ip, unsigned long pip,
-			      struct ftrace_ops *ops, struct pt_regs *regs)
+			      struct ftrace_ops *ops, struct ftrace_regs *fregs)
 {
+	struct pt_regs *regs = ftrace_get_regs(fregs);
 	unsigned long addr;
 
 	addr = ftrace_find_rec_direct(ip);
@@ -4292,7 +4294,7 @@ static int __init ftrace_mod_cmd_init(void)
 core_initcall(ftrace_mod_cmd_init);
 
 static void function_trace_probe_call(unsigned long ip, unsigned long parent_ip,
-				      struct ftrace_ops *op, struct pt_regs *pt_regs)
+				      struct ftrace_ops *op, struct ftrace_regs *fregs)
 {
 	struct ftrace_probe_ops *probe_ops;
 	struct ftrace_func_probe *probe;
@@ -6911,8 +6913,9 @@ void ftrace_reset_array_ops(struct trace_array *tr)
 
 static nokprobe_inline void
 __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
-		       struct ftrace_ops *ignored, struct pt_regs *regs)
+		       struct ftrace_ops *ignored, struct ftrace_regs *fregs)
 {
+	struct pt_regs *regs = ftrace_get_regs(fregs);
 	struct ftrace_ops *op;
 	int bit;
 
@@ -6945,7 +6948,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
 				pr_warn("op=%p %pS\n", op, op);
 				goto out;
 			}
-			op->func(ip, parent_ip, op, regs);
+			op->func(ip, parent_ip, op, fregs);
 		}
 	} while_for_each_ftrace_op(op);
 out:
@@ -6968,9 +6971,9 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
  */
 #if ARCH_SUPPORTS_FTRACE_OPS
 static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
-				 struct ftrace_ops *op, struct pt_regs *regs)
+				 struct ftrace_ops *op, struct ftrace_regs *fregs)
 {
-	__ftrace_ops_list_func(ip, parent_ip, NULL, regs);
+	__ftrace_ops_list_func(ip, parent_ip, NULL, fregs);
 }
 NOKPROBE_SYMBOL(ftrace_ops_list_func);
 #else
@@ -6987,7 +6990,7 @@ NOKPROBE_SYMBOL(ftrace_ops_no_ops);
  * this function will be called by the mcount trampoline.
  */
 static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
-				   struct ftrace_ops *op, struct pt_regs *regs)
+				   struct ftrace_ops *op, struct ftrace_regs *fregs)
 {
 	int bit;
 
@@ -6998,7 +7001,7 @@ static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
 	preempt_disable_notrace();
 
 	if (!(op->flags & FTRACE_OPS_FL_RCU) || rcu_is_watching())
-		op->func(ip, parent_ip, op, regs);
+		op->func(ip, parent_ip, op, fregs);
 
 	preempt_enable_notrace();
 	trace_clear_recursion(bit);
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index 1b202e28dfaa..a71181655958 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -432,7 +432,7 @@ NOKPROBE_SYMBOL(perf_trace_buf_update);
 #ifdef CONFIG_FUNCTION_TRACER
 static void
 perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip,
-			  struct ftrace_ops *ops, struct pt_regs *pt_regs)
+			  struct ftrace_ops *ops,  struct ftrace_regs *fregs)
 {
 	struct ftrace_entry *entry;
 	struct perf_event *event;
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index 646eda6c44a5..c5095dd28e20 100644
--- a/kernel/trace/trace_functions.c
+++ b/kernel/trace/trace_functions.c
@@ -23,10 +23,10 @@ static void tracing_start_function_trace(struct trace_array *tr);
 static void tracing_stop_function_trace(struct trace_array *tr);
 static void
 function_trace_call(unsigned long ip, unsigned long parent_ip,
-		    struct ftrace_ops *op, struct pt_regs *pt_regs);
+		    struct ftrace_ops *op, struct ftrace_regs *fregs);
 static void
 function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
-			  struct ftrace_ops *op, struct pt_regs *pt_regs);
+			  struct ftrace_ops *op, struct ftrace_regs *fregs);
 static struct tracer_flags func_flags;
 
 /* Our option */
@@ -89,7 +89,6 @@ void ftrace_destroy_function_files(struct trace_array *tr)
 static int function_trace_init(struct trace_array *tr)
 {
 	ftrace_func_t func;
-
 	/*
 	 * Instance trace_arrays get their ops allocated
 	 * at instance creation. Unless it failed
@@ -129,7 +128,7 @@ static void function_trace_start(struct trace_array *tr)
 
 static void
 function_trace_call(unsigned long ip, unsigned long parent_ip,
-		    struct ftrace_ops *op, struct pt_regs *pt_regs)
+		    struct ftrace_ops *op, struct ftrace_regs *fregs)
 {
 	struct trace_array *tr = op->private;
 	struct trace_array_cpu *data;
@@ -178,7 +177,7 @@ function_trace_call(unsigned long ip, unsigned long parent_ip,
 
 static void
 function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
-			  struct ftrace_ops *op, struct pt_regs *pt_regs)
+			  struct ftrace_ops *op, struct ftrace_regs *fregs)
 {
 	struct trace_array *tr = op->private;
 	struct trace_array_cpu *data;
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index 10bbb0f381d5..d06aab4dcbb8 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -138,7 +138,7 @@ static int func_prolog_dec(struct trace_array *tr,
  */
 static void
 irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip,
-		    struct ftrace_ops *op, struct pt_regs *pt_regs)
+		    struct ftrace_ops *op, 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 97b10bb31a1f..c0181066dbe9 100644
--- a/kernel/trace/trace_sched_wakeup.c
+++ b/kernel/trace/trace_sched_wakeup.c
@@ -212,7 +212,7 @@ static void wakeup_print_header(struct seq_file *s)
  */
 static void
 wakeup_tracer_call(unsigned long ip, unsigned long parent_ip,
-		   struct ftrace_ops *op, struct pt_regs *pt_regs)
+		   struct ftrace_ops *op, 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 8ee3c0bb5d8a..5ed081c6471c 100644
--- a/kernel/trace/trace_selftest.c
+++ b/kernel/trace/trace_selftest.c
@@ -107,7 +107,7 @@ static int trace_selftest_test_probe1_cnt;
 static void trace_selftest_test_probe1_func(unsigned long ip,
 					    unsigned long pip,
 					    struct ftrace_ops *op,
-					    struct pt_regs *pt_regs)
+					    struct ftrace_regs *fregs)
 {
 	trace_selftest_test_probe1_cnt++;
 }
@@ -116,7 +116,7 @@ static int trace_selftest_test_probe2_cnt;
 static void trace_selftest_test_probe2_func(unsigned long ip,
 					    unsigned long pip,
 					    struct ftrace_ops *op,
-					    struct pt_regs *pt_regs)
+					    struct ftrace_regs *fregs)
 {
 	trace_selftest_test_probe2_cnt++;
 }
@@ -125,7 +125,7 @@ static int trace_selftest_test_probe3_cnt;
 static void trace_selftest_test_probe3_func(unsigned long ip,
 					    unsigned long pip,
 					    struct ftrace_ops *op,
-					    struct pt_regs *pt_regs)
+					    struct ftrace_regs *fregs)
 {
 	trace_selftest_test_probe3_cnt++;
 }
@@ -134,7 +134,7 @@ static int trace_selftest_test_global_cnt;
 static void trace_selftest_test_global_func(unsigned long ip,
 					    unsigned long pip,
 					    struct ftrace_ops *op,
-					    struct pt_regs *pt_regs)
+					    struct ftrace_regs *fregs)
 {
 	trace_selftest_test_global_cnt++;
 }
@@ -143,7 +143,7 @@ static int trace_selftest_test_dyn_cnt;
 static void trace_selftest_test_dyn_func(unsigned long ip,
 					 unsigned long pip,
 					 struct ftrace_ops *op,
-					 struct pt_regs *pt_regs)
+					 struct ftrace_regs *fregs)
 {
 	trace_selftest_test_dyn_cnt++;
 }
@@ -414,7 +414,7 @@ static int trace_selftest_recursion_cnt;
 static void trace_selftest_test_recursion_func(unsigned long ip,
 					       unsigned long pip,
 					       struct ftrace_ops *op,
-					       struct pt_regs *pt_regs)
+					       struct ftrace_regs *fregs)
 {
 	/*
 	 * This function is registered without the recursion safe flag.
@@ -429,7 +429,7 @@ static void trace_selftest_test_recursion_func(unsigned long ip,
 static void trace_selftest_test_recursion_safe_func(unsigned long ip,
 						    unsigned long pip,
 						    struct ftrace_ops *op,
-						    struct pt_regs *pt_regs)
+						    struct ftrace_regs *fregs)
 {
 	/*
 	 * We said we would provide our own recursion. By calling
@@ -548,9 +548,11 @@ static enum {
 static void trace_selftest_test_regs_func(unsigned long ip,
 					  unsigned long pip,
 					  struct ftrace_ops *op,
-					  struct pt_regs *pt_regs)
+					  struct ftrace_regs *fregs)
 {
-	if (pt_regs)
+	struct pt_regs *regs = ftrace_get_regs(fregs);
+
+	if (regs)
 		trace_selftest_regs_stat = TRACE_SELFTEST_REGS_FOUND;
 	else
 		trace_selftest_regs_stat = TRACE_SELFTEST_REGS_NOT_FOUND;
diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c
index 969db526a563..63c285042051 100644
--- a/kernel/trace/trace_stack.c
+++ b/kernel/trace/trace_stack.c
@@ -290,7 +290,7 @@ static void check_stack(unsigned long ip, unsigned long *stack)
 
 static void
 stack_trace_call(unsigned long ip, unsigned long parent_ip,
-		 struct ftrace_ops *op, struct pt_regs *pt_regs)
+		 struct ftrace_ops *op, struct ftrace_regs *fregs)
 {
 	unsigned long stack;
 
-- 
2.28.0



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

* [PATCH 2/3 v4] ftrace/x86: Allow for arguments to be passed in to ftrace_regs by default
  2020-11-06 21:42 [PATCH 0/3 v4] ftrace: Add access to function arguments for all callbacks Steven Rostedt
  2020-11-06 21:42 ` [PATCH 1/3 v4] ftrace: Have the callbacks receive a struct ftrace_regs instead of pt_regs Steven Rostedt
@ 2020-11-06 21:42 ` Steven Rostedt
  2020-11-09 11:10   ` Peter Zijlstra
  2020-11-06 21:42 ` [PATCH 3/3 v4] livepatch: Use the default ftrace_ops instead of REGS when ARGS is available Steven Rostedt
  2 siblings, 1 reply; 8+ messages in thread
From: Steven Rostedt @ 2020-11-06 21:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: Ingo Molnar, Andrew Morton, Miroslav Benes, Peter Zijlstra,
	Thomas Gleixner, Masami Hiramatsu

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

Currently, the only way to get access to the registers of a function via a
ftrace callback is to set the "FL_SAVE_REGS" bit in the ftrace_ops. But as this
saves all regs as if a breakpoint were to trigger (for use with kprobes), it
is expensive.

The regs are already saved on the stack for the default ftrace callbacks, as
that is required otherwise a function being traced will get the wrong
arguments and possibly crash. And on x86, the arguments are already stored
where they would be on a pt_regs structure to use that code for both the
regs version of a callback, it makes sense to pass that information always
to all functions.

If an architecture does this (as x86_64 now does), it is to set
HAVE_DYNAMIC_FTRACE_WITH_ARGS, and this will let the generic code that it
could have access to arguments without having to set the flags.

This also includes having the stack pointer being saved, which could be used
for accessing arguments on the stack, as well as having the function graph
tracer not require its own trampoline!

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 arch/x86/Kconfig              |  1 +
 arch/x86/include/asm/ftrace.h | 15 +++++++++++++++
 arch/x86/kernel/ftrace_64.S   | 11 +++++++++--
 include/linux/ftrace.h        |  7 ++++++-
 kernel/trace/Kconfig          |  9 +++++++++
 5 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index f6946b81f74a..478526aabe5d 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -167,6 +167,7 @@ config X86
 	select HAVE_DMA_CONTIGUOUS
 	select HAVE_DYNAMIC_FTRACE
 	select HAVE_DYNAMIC_FTRACE_WITH_REGS
+	select HAVE_DYNAMIC_FTRACE_WITH_ARGS	if X86_64
 	select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
 	select HAVE_EBPF_JIT
 	select HAVE_EFFICIENT_UNALIGNED_ACCESS
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 84b9449be080..6b175c2468e6 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -49,6 +49,21 @@ struct dyn_arch_ftrace {
 
 #define FTRACE_GRAPH_TRAMP_ADDR FTRACE_GRAPH_ADDR
 
+#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
+struct ftrace_regs {
+	struct pt_regs		regs;
+};
+
+static __always_inline struct pt_regs *
+arch_ftrace_get_regs(struct ftrace_regs *fregs)
+{
+	/* Only when FL_SAVE_REGS is set, cs will be non zero */
+	if (!fregs->regs.cs)
+		return NULL;
+	return &fregs->regs;
+}
+#endif
+
 #endif /*  CONFIG_DYNAMIC_FTRACE */
 #endif /* __ASSEMBLY__ */
 #endif /* CONFIG_FUNCTION_TRACER */
diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
index ac3d5f22fe64..60e3b64f5ea6 100644
--- a/arch/x86/kernel/ftrace_64.S
+++ b/arch/x86/kernel/ftrace_64.S
@@ -140,12 +140,19 @@ SYM_FUNC_START(ftrace_caller)
 	/* save_mcount_regs fills in first two parameters */
 	save_mcount_regs
 
+	/* Stack - skipping return address of ftrace_caller */
+	leaq MCOUNT_REG_SIZE+8(%rsp), %rcx
+	movq %rcx, RSP(%rsp)
+
 SYM_INNER_LABEL(ftrace_caller_op_ptr, SYM_L_GLOBAL)
 	/* Load the ftrace_ops into the 3rd parameter */
 	movq function_trace_op(%rip), %rdx
 
-	/* regs go into 4th parameter (but make it NULL) */
-	movq $0, %rcx
+	/* regs go into 4th parameter */
+	leaq (%rsp), %rcx
+
+	/* Only ops with REGS flag set should have CS register set */
+	movq $0, CS(%rsp)
 
 SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
 	call ftrace_stub
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 24e1fa52337d..588ea7023a7a 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -90,16 +90,21 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
 
 struct ftrace_ops;
 
+#ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
+
 struct ftrace_regs {
 	struct pt_regs		regs;
 };
+#define arch_ftrace_get_regs(fregs) (&(fregs)->regs)
+
+#endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
 
 static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs)
 {
 	if (!fregs)
 		return NULL;
 
-	return &fregs->regs;
+	return arch_ftrace_get_regs(fregs);
 }
 
 typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 6aa36ec73ccb..c9b64dea1216 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -31,6 +31,15 @@ config HAVE_DYNAMIC_FTRACE_WITH_REGS
 config HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
 	bool
 
+config HAVE_DYNAMIC_FTRACE_WITH_ARGS
+	bool
+	help
+	 If this is set, then arguments and stack can be found from
+	 the pt_regs passed into the function callback regs parameter
+	 by default, even without setting the REGS flag in the ftrace_ops.
+	 This allows for use of regs_get_kernel_argument() and
+	 kernel_stack_pointer().
+
 config HAVE_FTRACE_MCOUNT_RECORD
 	bool
 	help
-- 
2.28.0



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

* [PATCH 3/3 v4] livepatch: Use the default ftrace_ops instead of REGS when ARGS is available
  2020-11-06 21:42 [PATCH 0/3 v4] ftrace: Add access to function arguments for all callbacks Steven Rostedt
  2020-11-06 21:42 ` [PATCH 1/3 v4] ftrace: Have the callbacks receive a struct ftrace_regs instead of pt_regs Steven Rostedt
  2020-11-06 21:42 ` [PATCH 2/3 v4] ftrace/x86: Allow for arguments to be passed in to ftrace_regs by default Steven Rostedt
@ 2020-11-06 21:42 ` Steven Rostedt
  2 siblings, 0 replies; 8+ messages in thread
From: Steven Rostedt @ 2020-11-06 21:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: Ingo Molnar, Andrew Morton, Miroslav Benes, Peter Zijlstra,
	Thomas Gleixner, Masami Hiramatsu, Josh Poimboeuf, Jiri Kosina,
	live-patching

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

When CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS is available, the ftrace call
will be able to set the ip of the calling function. This will improve the
performance of live kernel patching where it does not need all the regs to
be stored just to change the instruction pointer.

If all archs that support live kernel patching also support
HAVE_DYNAMIC_FTRACE_WITH_ARGS, then the architecture specific function
klp_arch_set_pc() could be made generic.

It is possible that an arch can support HAVE_DYNAMIC_FTRACE_WITH_ARGS but
not HAVE_DYNAMIC_FTRACE_WITH_REGS and then have access to live patching.

Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: live-patching@vger.kernel.org
Acked-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
Changes since v3:
  Have live patching depend on HAVE_DYNAMIC_FTRACE_WITH_REGS *or*
  HAVE_DYNAMIC_FTRACE_WITH_ARGS

 arch/powerpc/include/asm/livepatch.h | 4 +++-
 arch/s390/include/asm/livepatch.h    | 5 ++++-
 arch/x86/include/asm/ftrace.h        | 3 +++
 arch/x86/include/asm/livepatch.h     | 4 ++--
 arch/x86/kernel/ftrace_64.S          | 4 ++++
 include/linux/ftrace.h               | 7 +++++++
 kernel/livepatch/Kconfig             | 2 +-
 kernel/livepatch/patch.c             | 9 +++++----
 8 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/arch/powerpc/include/asm/livepatch.h b/arch/powerpc/include/asm/livepatch.h
index 4a3d5d25fed5..ae25e6e72997 100644
--- a/arch/powerpc/include/asm/livepatch.h
+++ b/arch/powerpc/include/asm/livepatch.h
@@ -12,8 +12,10 @@
 #include <linux/sched/task_stack.h>
 
 #ifdef CONFIG_LIVEPATCH
-static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
+static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip)
 {
+	struct pt_regs *regs = ftrace_get_regs(fregs);
+
 	regs->nip = ip;
 }
 
diff --git a/arch/s390/include/asm/livepatch.h b/arch/s390/include/asm/livepatch.h
index 818612b784cd..d578a8c76676 100644
--- a/arch/s390/include/asm/livepatch.h
+++ b/arch/s390/include/asm/livepatch.h
@@ -11,10 +11,13 @@
 #ifndef ASM_LIVEPATCH_H
 #define ASM_LIVEPATCH_H
 
+#include <linux/ftrace.h>
 #include <asm/ptrace.h>
 
-static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
+static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip)
 {
+	struct pt_regs *regs = ftrace_get_regs(fregs);
+
 	regs->psw.addr = ip;
 }
 
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 6b175c2468e6..7042e80941e5 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -62,6 +62,9 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
 		return NULL;
 	return &fregs->regs;
 }
+
+#define ftrace_regs_set_ip(fregs, _ip)		\
+	do { (fregs)->regs.ip = (_ip); } while (0)
 #endif
 
 #endif /*  CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/x86/include/asm/livepatch.h b/arch/x86/include/asm/livepatch.h
index 1fde1ab6559e..59a08d5c6f1d 100644
--- a/arch/x86/include/asm/livepatch.h
+++ b/arch/x86/include/asm/livepatch.h
@@ -12,9 +12,9 @@
 #include <asm/setup.h>
 #include <linux/ftrace.h>
 
-static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
+static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip)
 {
-	regs->ip = ip;
+	ftrace_regs_set_ip(fregs, ip);
 }
 
 #endif /* _ASM_X86_LIVEPATCH_H */
diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
index 60e3b64f5ea6..0d54099c2a3a 100644
--- a/arch/x86/kernel/ftrace_64.S
+++ b/arch/x86/kernel/ftrace_64.S
@@ -157,6 +157,10 @@ SYM_INNER_LABEL(ftrace_caller_op_ptr, SYM_L_GLOBAL)
 SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
 	call ftrace_stub
 
+	/* Handlers can change the RIP */
+	movq RIP(%rsp), %rax
+	movq %rax, MCOUNT_REG_SIZE(%rsp)
+
 	restore_mcount_regs
 
 	/*
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 588ea7023a7a..b4eb962e2be9 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -97,6 +97,13 @@ struct ftrace_regs {
 };
 #define arch_ftrace_get_regs(fregs) (&(fregs)->regs)
 
+/*
+ * ftrace_regs_set_ip() is to be defined by the architecture if
+ * to allow setting of the instruction pointer from the ftrace_regs
+ * when HAVE_DYNAMIC_FTRACE_WITH_ARGS is set and it supports
+ * live kernel patching.
+ */
+#define ftrace_regs_set_ip(fregs, ip) do { } while (0)
 #endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
 
 static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs)
diff --git a/kernel/livepatch/Kconfig b/kernel/livepatch/Kconfig
index 54102deb50ba..53d51ed619a3 100644
--- a/kernel/livepatch/Kconfig
+++ b/kernel/livepatch/Kconfig
@@ -6,7 +6,7 @@ config HAVE_LIVEPATCH
 
 config LIVEPATCH
 	bool "Kernel Live Patching"
-	depends on DYNAMIC_FTRACE_WITH_REGS
+	depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS
 	depends on MODULES
 	depends on SYSFS
 	depends on KALLSYMS_ALL
diff --git a/kernel/livepatch/patch.c b/kernel/livepatch/patch.c
index f89f9e7e9b07..e8029aea67f1 100644
--- a/kernel/livepatch/patch.c
+++ b/kernel/livepatch/patch.c
@@ -42,7 +42,6 @@ static void notrace klp_ftrace_handler(unsigned long ip,
 				       struct ftrace_ops *fops,
 				       struct ftrace_regs *fregs)
 {
-	struct pt_regs *regs = ftrace_get_regs(fregs);
 	struct klp_ops *ops;
 	struct klp_func *func;
 	int patch_state;
@@ -118,7 +117,7 @@ static void notrace klp_ftrace_handler(unsigned long ip,
 	if (func->nop)
 		goto unlock;
 
-	klp_arch_set_pc(regs, (unsigned long)func->new_func);
+	klp_arch_set_pc(fregs, (unsigned long)func->new_func);
 
 unlock:
 	preempt_enable_notrace();
@@ -200,8 +199,10 @@ static int klp_patch_func(struct klp_func *func)
 			return -ENOMEM;
 
 		ops->fops.func = klp_ftrace_handler;
-		ops->fops.flags = FTRACE_OPS_FL_SAVE_REGS |
-				  FTRACE_OPS_FL_DYNAMIC |
+		ops->fops.flags = FTRACE_OPS_FL_DYNAMIC |
+#ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
+				  FTRACE_OPS_FL_SAVE_REGS |
+#endif
 				  FTRACE_OPS_FL_IPMODIFY |
 				  FTRACE_OPS_FL_PERMANENT;
 
-- 
2.28.0



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

* Re: [PATCH 1/3 v4] ftrace: Have the callbacks receive a struct ftrace_regs instead of pt_regs
  2020-11-06 21:42 ` [PATCH 1/3 v4] ftrace: Have the callbacks receive a struct ftrace_regs instead of pt_regs Steven Rostedt
@ 2020-11-07  4:29   ` Masami Hiramatsu
  0 siblings, 0 replies; 8+ messages in thread
From: Masami Hiramatsu @ 2020-11-07  4:29 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: linux-kernel, Ingo Molnar, Andrew Morton, Miroslav Benes,
	Peter Zijlstra, Thomas Gleixner, Masami Hiramatsu

On Fri, 06 Nov 2020 16:42:35 -0500
Steven Rostedt <rostedt@goodmis.org> wrote:

> From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
> 
> In preparation to have arguments of a function passed to callbacks attached
> to functions as default, change the default callback prototype to receive a
> struct ftrace_regs as the forth parameter instead of a pt_regs.
> 
> For callbacks that set the FL_SAVE_REGS flag in their ftrace_ops flags, they
> will now need to get the pt_regs via a ftrace_get_regs() helper call. If
> this is called by a callback that their ftrace_ops did not have a
> FL_SAVE_REGS flag set, it that helper function will return NULL.
> 
> This will allow the ftrace_regs to hold enough just to get the parameters
> and stack pointer, but without the worry that callbacks may have a pt_regs
> that is not completely filled.
> 

This looks good to me.

Reviewed-by: Masami Hiramatsu <mhiramat@kernel.org>

Thank you,

> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> ---
>  arch/x86/kernel/kprobes/ftrace.c  |  3 ++-
>  include/linux/ftrace.h            | 16 ++++++++++++++--
>  include/linux/kprobes.h           |  2 +-
>  kernel/livepatch/patch.c          |  3 ++-
>  kernel/trace/ftrace.c             | 27 +++++++++++++++------------
>  kernel/trace/trace_event_perf.c   |  2 +-
>  kernel/trace/trace_functions.c    |  9 ++++-----
>  kernel/trace/trace_irqsoff.c      |  2 +-
>  kernel/trace/trace_sched_wakeup.c |  2 +-
>  kernel/trace/trace_selftest.c     | 20 +++++++++++---------
>  kernel/trace/trace_stack.c        |  2 +-
>  11 files changed, 53 insertions(+), 35 deletions(-)
> 
> diff --git a/arch/x86/kernel/kprobes/ftrace.c b/arch/x86/kernel/kprobes/ftrace.c
> index 954d930a7127..373e5fa3ce1f 100644
> --- a/arch/x86/kernel/kprobes/ftrace.c
> +++ b/arch/x86/kernel/kprobes/ftrace.c
> @@ -14,8 +14,9 @@
>  
>  /* Ftrace callback handler for kprobes -- called under preepmt disabed */
>  void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
> -			   struct ftrace_ops *ops, struct pt_regs *regs)
> +			   struct ftrace_ops *ops, struct ftrace_regs *fregs)
>  {
> +	struct pt_regs *regs = ftrace_get_regs(fregs);
>  	struct kprobe *p;
>  	struct kprobe_ctlblk *kcb;
>  	int bit;
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 8dde9c17aaa5..24e1fa52337d 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -90,8 +90,20 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
>  
>  struct ftrace_ops;
>  
> +struct ftrace_regs {
> +	struct pt_regs		regs;
> +};
> +
> +static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs)
> +{
> +	if (!fregs)
> +		return NULL;
> +
> +	return &fregs->regs;
> +}
> +
>  typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
> -			      struct ftrace_ops *op, struct pt_regs *regs);
> +			      struct ftrace_ops *op, struct ftrace_regs *fregs);
>  
>  ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
>  
> @@ -259,7 +271,7 @@ int register_ftrace_function(struct ftrace_ops *ops);
>  int unregister_ftrace_function(struct ftrace_ops *ops);
>  
>  extern void ftrace_stub(unsigned long a0, unsigned long a1,
> -			struct ftrace_ops *op, struct pt_regs *regs);
> +			struct ftrace_ops *op, struct ftrace_regs *fregs);
>  
>  #else /* !CONFIG_FUNCTION_TRACER */
>  /*
> diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
> index 629abaf25681..be73350955e4 100644
> --- a/include/linux/kprobes.h
> +++ b/include/linux/kprobes.h
> @@ -345,7 +345,7 @@ static inline void wait_for_kprobe_optimizer(void) { }
>  #endif /* CONFIG_OPTPROBES */
>  #ifdef CONFIG_KPROBES_ON_FTRACE
>  extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
> -				  struct ftrace_ops *ops, struct pt_regs *regs);
> +				  struct ftrace_ops *ops, struct ftrace_regs *fregs);
>  extern int arch_prepare_kprobe_ftrace(struct kprobe *p);
>  #endif
>  
> diff --git a/kernel/livepatch/patch.c b/kernel/livepatch/patch.c
> index 875c5dbbdd33..f89f9e7e9b07 100644
> --- a/kernel/livepatch/patch.c
> +++ b/kernel/livepatch/patch.c
> @@ -40,8 +40,9 @@ struct klp_ops *klp_find_ops(void *old_func)
>  static void notrace klp_ftrace_handler(unsigned long ip,
>  				       unsigned long parent_ip,
>  				       struct ftrace_ops *fops,
> -				       struct pt_regs *regs)
> +				       struct ftrace_regs *fregs)
>  {
> +	struct pt_regs *regs = ftrace_get_regs(fregs);
>  	struct klp_ops *ops;
>  	struct klp_func *func;
>  	int patch_state;
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 3db64fb0cce8..67888311784e 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -121,7 +121,7 @@ struct ftrace_ops global_ops;
>  
>  #if ARCH_SUPPORTS_FTRACE_OPS
>  static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
> -				 struct ftrace_ops *op, struct pt_regs *regs);
> +				 struct ftrace_ops *op, struct ftrace_regs *fregs);
>  #else
>  /* See comment below, where ftrace_ops_list_func is defined */
>  static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip);
> @@ -140,7 +140,7 @@ static inline void ftrace_ops_init(struct ftrace_ops *ops)
>  }
>  
>  static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip,
> -			    struct ftrace_ops *op, struct pt_regs *regs)
> +			    struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = op->private;
>  	int pid;
> @@ -154,7 +154,7 @@ static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip,
>  			return;
>  	}
>  
> -	op->saved_func(ip, parent_ip, op, regs);
> +	op->saved_func(ip, parent_ip, op, fregs);
>  }
>  
>  static void ftrace_sync_ipi(void *data)
> @@ -754,7 +754,7 @@ ftrace_profile_alloc(struct ftrace_profile_stat *stat, unsigned long ip)
>  
>  static void
>  function_profile_call(unsigned long ip, unsigned long parent_ip,
> -		      struct ftrace_ops *ops, struct pt_regs *regs)
> +		      struct ftrace_ops *ops, struct ftrace_regs *fregs)
>  {
>  	struct ftrace_profile_stat *stat;
>  	struct ftrace_profile *rec;
> @@ -2143,6 +2143,7 @@ static int ftrace_check_record(struct dyn_ftrace *rec, bool enable, bool update)
>  				else
>  					rec->flags &= ~FTRACE_FL_TRAMP_EN;
>  			}
> +
>  			if (flag & FTRACE_FL_DIRECT) {
>  				/*
>  				 * If there's only one user (direct_ops helper)
> @@ -2368,8 +2369,9 @@ unsigned long ftrace_find_rec_direct(unsigned long ip)
>  }
>  
>  static void call_direct_funcs(unsigned long ip, unsigned long pip,
> -			      struct ftrace_ops *ops, struct pt_regs *regs)
> +			      struct ftrace_ops *ops, struct ftrace_regs *fregs)
>  {
> +	struct pt_regs *regs = ftrace_get_regs(fregs);
>  	unsigned long addr;
>  
>  	addr = ftrace_find_rec_direct(ip);
> @@ -4292,7 +4294,7 @@ static int __init ftrace_mod_cmd_init(void)
>  core_initcall(ftrace_mod_cmd_init);
>  
>  static void function_trace_probe_call(unsigned long ip, unsigned long parent_ip,
> -				      struct ftrace_ops *op, struct pt_regs *pt_regs)
> +				      struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
>  	struct ftrace_probe_ops *probe_ops;
>  	struct ftrace_func_probe *probe;
> @@ -6911,8 +6913,9 @@ void ftrace_reset_array_ops(struct trace_array *tr)
>  
>  static nokprobe_inline void
>  __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
> -		       struct ftrace_ops *ignored, struct pt_regs *regs)
> +		       struct ftrace_ops *ignored, struct ftrace_regs *fregs)
>  {
> +	struct pt_regs *regs = ftrace_get_regs(fregs);
>  	struct ftrace_ops *op;
>  	int bit;
>  
> @@ -6945,7 +6948,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
>  				pr_warn("op=%p %pS\n", op, op);
>  				goto out;
>  			}
> -			op->func(ip, parent_ip, op, regs);
> +			op->func(ip, parent_ip, op, fregs);
>  		}
>  	} while_for_each_ftrace_op(op);
>  out:
> @@ -6968,9 +6971,9 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
>   */
>  #if ARCH_SUPPORTS_FTRACE_OPS
>  static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
> -				 struct ftrace_ops *op, struct pt_regs *regs)
> +				 struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
> -	__ftrace_ops_list_func(ip, parent_ip, NULL, regs);
> +	__ftrace_ops_list_func(ip, parent_ip, NULL, fregs);
>  }
>  NOKPROBE_SYMBOL(ftrace_ops_list_func);
>  #else
> @@ -6987,7 +6990,7 @@ NOKPROBE_SYMBOL(ftrace_ops_no_ops);
>   * this function will be called by the mcount trampoline.
>   */
>  static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
> -				   struct ftrace_ops *op, struct pt_regs *regs)
> +				   struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
>  	int bit;
>  
> @@ -6998,7 +7001,7 @@ static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
>  	preempt_disable_notrace();
>  
>  	if (!(op->flags & FTRACE_OPS_FL_RCU) || rcu_is_watching())
> -		op->func(ip, parent_ip, op, regs);
> +		op->func(ip, parent_ip, op, fregs);
>  
>  	preempt_enable_notrace();
>  	trace_clear_recursion(bit);
> diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
> index 1b202e28dfaa..a71181655958 100644
> --- a/kernel/trace/trace_event_perf.c
> +++ b/kernel/trace/trace_event_perf.c
> @@ -432,7 +432,7 @@ NOKPROBE_SYMBOL(perf_trace_buf_update);
>  #ifdef CONFIG_FUNCTION_TRACER
>  static void
>  perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip,
> -			  struct ftrace_ops *ops, struct pt_regs *pt_regs)
> +			  struct ftrace_ops *ops,  struct ftrace_regs *fregs)
>  {
>  	struct ftrace_entry *entry;
>  	struct perf_event *event;
> diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
> index 646eda6c44a5..c5095dd28e20 100644
> --- a/kernel/trace/trace_functions.c
> +++ b/kernel/trace/trace_functions.c
> @@ -23,10 +23,10 @@ static void tracing_start_function_trace(struct trace_array *tr);
>  static void tracing_stop_function_trace(struct trace_array *tr);
>  static void
>  function_trace_call(unsigned long ip, unsigned long parent_ip,
> -		    struct ftrace_ops *op, struct pt_regs *pt_regs);
> +		    struct ftrace_ops *op, struct ftrace_regs *fregs);
>  static void
>  function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
> -			  struct ftrace_ops *op, struct pt_regs *pt_regs);
> +			  struct ftrace_ops *op, struct ftrace_regs *fregs);
>  static struct tracer_flags func_flags;
>  
>  /* Our option */
> @@ -89,7 +89,6 @@ void ftrace_destroy_function_files(struct trace_array *tr)
>  static int function_trace_init(struct trace_array *tr)
>  {
>  	ftrace_func_t func;
> -
>  	/*
>  	 * Instance trace_arrays get their ops allocated
>  	 * at instance creation. Unless it failed
> @@ -129,7 +128,7 @@ static void function_trace_start(struct trace_array *tr)
>  
>  static void
>  function_trace_call(unsigned long ip, unsigned long parent_ip,
> -		    struct ftrace_ops *op, struct pt_regs *pt_regs)
> +		    struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = op->private;
>  	struct trace_array_cpu *data;
> @@ -178,7 +177,7 @@ function_trace_call(unsigned long ip, unsigned long parent_ip,
>  
>  static void
>  function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
> -			  struct ftrace_ops *op, struct pt_regs *pt_regs)
> +			  struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
>  	struct trace_array *tr = op->private;
>  	struct trace_array_cpu *data;
> diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
> index 10bbb0f381d5..d06aab4dcbb8 100644
> --- a/kernel/trace/trace_irqsoff.c
> +++ b/kernel/trace/trace_irqsoff.c
> @@ -138,7 +138,7 @@ static int func_prolog_dec(struct trace_array *tr,
>   */
>  static void
>  irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip,
> -		    struct ftrace_ops *op, struct pt_regs *pt_regs)
> +		    struct ftrace_ops *op, 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 97b10bb31a1f..c0181066dbe9 100644
> --- a/kernel/trace/trace_sched_wakeup.c
> +++ b/kernel/trace/trace_sched_wakeup.c
> @@ -212,7 +212,7 @@ static void wakeup_print_header(struct seq_file *s)
>   */
>  static void
>  wakeup_tracer_call(unsigned long ip, unsigned long parent_ip,
> -		   struct ftrace_ops *op, struct pt_regs *pt_regs)
> +		   struct ftrace_ops *op, 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 8ee3c0bb5d8a..5ed081c6471c 100644
> --- a/kernel/trace/trace_selftest.c
> +++ b/kernel/trace/trace_selftest.c
> @@ -107,7 +107,7 @@ static int trace_selftest_test_probe1_cnt;
>  static void trace_selftest_test_probe1_func(unsigned long ip,
>  					    unsigned long pip,
>  					    struct ftrace_ops *op,
> -					    struct pt_regs *pt_regs)
> +					    struct ftrace_regs *fregs)
>  {
>  	trace_selftest_test_probe1_cnt++;
>  }
> @@ -116,7 +116,7 @@ static int trace_selftest_test_probe2_cnt;
>  static void trace_selftest_test_probe2_func(unsigned long ip,
>  					    unsigned long pip,
>  					    struct ftrace_ops *op,
> -					    struct pt_regs *pt_regs)
> +					    struct ftrace_regs *fregs)
>  {
>  	trace_selftest_test_probe2_cnt++;
>  }
> @@ -125,7 +125,7 @@ static int trace_selftest_test_probe3_cnt;
>  static void trace_selftest_test_probe3_func(unsigned long ip,
>  					    unsigned long pip,
>  					    struct ftrace_ops *op,
> -					    struct pt_regs *pt_regs)
> +					    struct ftrace_regs *fregs)
>  {
>  	trace_selftest_test_probe3_cnt++;
>  }
> @@ -134,7 +134,7 @@ static int trace_selftest_test_global_cnt;
>  static void trace_selftest_test_global_func(unsigned long ip,
>  					    unsigned long pip,
>  					    struct ftrace_ops *op,
> -					    struct pt_regs *pt_regs)
> +					    struct ftrace_regs *fregs)
>  {
>  	trace_selftest_test_global_cnt++;
>  }
> @@ -143,7 +143,7 @@ static int trace_selftest_test_dyn_cnt;
>  static void trace_selftest_test_dyn_func(unsigned long ip,
>  					 unsigned long pip,
>  					 struct ftrace_ops *op,
> -					 struct pt_regs *pt_regs)
> +					 struct ftrace_regs *fregs)
>  {
>  	trace_selftest_test_dyn_cnt++;
>  }
> @@ -414,7 +414,7 @@ static int trace_selftest_recursion_cnt;
>  static void trace_selftest_test_recursion_func(unsigned long ip,
>  					       unsigned long pip,
>  					       struct ftrace_ops *op,
> -					       struct pt_regs *pt_regs)
> +					       struct ftrace_regs *fregs)
>  {
>  	/*
>  	 * This function is registered without the recursion safe flag.
> @@ -429,7 +429,7 @@ static void trace_selftest_test_recursion_func(unsigned long ip,
>  static void trace_selftest_test_recursion_safe_func(unsigned long ip,
>  						    unsigned long pip,
>  						    struct ftrace_ops *op,
> -						    struct pt_regs *pt_regs)
> +						    struct ftrace_regs *fregs)
>  {
>  	/*
>  	 * We said we would provide our own recursion. By calling
> @@ -548,9 +548,11 @@ static enum {
>  static void trace_selftest_test_regs_func(unsigned long ip,
>  					  unsigned long pip,
>  					  struct ftrace_ops *op,
> -					  struct pt_regs *pt_regs)
> +					  struct ftrace_regs *fregs)
>  {
> -	if (pt_regs)
> +	struct pt_regs *regs = ftrace_get_regs(fregs);
> +
> +	if (regs)
>  		trace_selftest_regs_stat = TRACE_SELFTEST_REGS_FOUND;
>  	else
>  		trace_selftest_regs_stat = TRACE_SELFTEST_REGS_NOT_FOUND;
> diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c
> index 969db526a563..63c285042051 100644
> --- a/kernel/trace/trace_stack.c
> +++ b/kernel/trace/trace_stack.c
> @@ -290,7 +290,7 @@ static void check_stack(unsigned long ip, unsigned long *stack)
>  
>  static void
>  stack_trace_call(unsigned long ip, unsigned long parent_ip,
> -		 struct ftrace_ops *op, struct pt_regs *pt_regs)
> +		 struct ftrace_ops *op, struct ftrace_regs *fregs)
>  {
>  	unsigned long stack;
>  
> -- 
> 2.28.0
> 
> 


-- 
Masami Hiramatsu <mhiramat@kernel.org>

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

* Re: [PATCH 2/3 v4] ftrace/x86: Allow for arguments to be passed in to ftrace_regs by default
  2020-11-06 21:42 ` [PATCH 2/3 v4] ftrace/x86: Allow for arguments to be passed in to ftrace_regs by default Steven Rostedt
@ 2020-11-09 11:10   ` Peter Zijlstra
  2020-11-09 23:16     ` Steven Rostedt
  0 siblings, 1 reply; 8+ messages in thread
From: Peter Zijlstra @ 2020-11-09 11:10 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: linux-kernel, Ingo Molnar, Andrew Morton, Miroslav Benes,
	Thomas Gleixner, Masami Hiramatsu

On Fri, Nov 06, 2020 at 04:42:36PM -0500, Steven Rostedt wrote:
> +static __always_inline struct pt_regs *
> +arch_ftrace_get_regs(struct ftrace_regs *fregs)
> +{
> +	/* Only when FL_SAVE_REGS is set, cs will be non zero */
> +	if (!fregs->regs.cs)
> +		return NULL;
> +	return &fregs->regs;
> +}
> +#endif
> +
>  #endif /*  CONFIG_DYNAMIC_FTRACE */
>  #endif /* __ASSEMBLY__ */
>  #endif /* CONFIG_FUNCTION_TRACER */
> diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
> index ac3d5f22fe64..60e3b64f5ea6 100644
> --- a/arch/x86/kernel/ftrace_64.S
> +++ b/arch/x86/kernel/ftrace_64.S
> @@ -140,12 +140,19 @@ SYM_FUNC_START(ftrace_caller)
>  	/* save_mcount_regs fills in first two parameters */
>  	save_mcount_regs
>  
> +	/* Stack - skipping return address of ftrace_caller */
> +	leaq MCOUNT_REG_SIZE+8(%rsp), %rcx
> +	movq %rcx, RSP(%rsp)
> +
>  SYM_INNER_LABEL(ftrace_caller_op_ptr, SYM_L_GLOBAL)
>  	/* Load the ftrace_ops into the 3rd parameter */
>  	movq function_trace_op(%rip), %rdx
>  
> -	/* regs go into 4th parameter (but make it NULL) */
> -	movq $0, %rcx
> +	/* regs go into 4th parameter */
> +	leaq (%rsp), %rcx
> +
> +	/* Only ops with REGS flag set should have CS register set */
> +	movq $0, CS(%rsp)
>  
>  SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
>  	call ftrace_stub

You now seem to be relying on save_mcount_regs() resulting in a cleared
CS, however, AFAICT CS is uninitialized stack garbage.

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

* Re: [PATCH 2/3 v4] ftrace/x86: Allow for arguments to be passed in to ftrace_regs by default
  2020-11-09 11:10   ` Peter Zijlstra
@ 2020-11-09 23:16     ` Steven Rostedt
  2020-11-10  9:20       ` Peter Zijlstra
  0 siblings, 1 reply; 8+ messages in thread
From: Steven Rostedt @ 2020-11-09 23:16 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: linux-kernel, Ingo Molnar, Andrew Morton, Miroslav Benes,
	Thomas Gleixner, Masami Hiramatsu

On Mon, 9 Nov 2020 12:10:43 +0100
Peter Zijlstra <peterz@infradead.org> wrote:

> >  SYM_INNER_LABEL(ftrace_caller_op_ptr, SYM_L_GLOBAL)
> >  	/* Load the ftrace_ops into the 3rd parameter */
> >  	movq function_trace_op(%rip), %rdx
> >  
> > -	/* regs go into 4th parameter (but make it NULL) */
> > -	movq $0, %rcx
> > +	/* regs go into 4th parameter */
> > +	leaq (%rsp), %rcx
> > +
> > +	/* Only ops with REGS flag set should have CS register set */
> > +	movq $0, CS(%rsp)
> >  
> >  SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
> >  	call ftrace_stub  
> 
> You now seem to be relying on save_mcount_regs() resulting in a cleared
> CS, however, AFAICT CS is uninitialized stack garbage.

We have two trampolines that are also used to copy for other
trampolines that are dynamically created. There's this one
(ftrace_caller) and then there's the regs one (ftrace_regs_caller).
This one clears the CS in pt_regs to let the arch code know that the
ftrace_regs is not a full pt_regs and anyone trying to get it with
ftrace_get_regs() will get a NULL pointer. But the ftrace_regs_caller
loads the CS register with __KERNEL_CS, which is non zero, and the
ftrace_get_regs() will return the full pt_regs if it is set.

ftrace_caller:
	[..]
	movq $0, CS(%rsp) <- loads zero into pt_regs->cs for internal use only.
	[..]
	call callback

ftrace_regs_caller:
	[..]
	movq $__KERNEL_CS, %rcx
	movq %rcx, CS(%rsp)
	[..]
	call callback


Then in the callback we have:


callback(..., struct ftrace_regs *fregs)
{
	struct pt_regs *regs = ftrace_get_regs(fregs);
}

Where ftrace_get_regs is arch specific and returns for x86_64:

static __always_inline struct pt_regs *
arch_ftrace_get_regs(struct ftrace_regs *fregs)
{
	/* Only when FL_SAVE_REGS is set, cs will be non zero */
	if (!fregs->regs.cs)
		return NULL;
	return &fregs->regs;
}

What am I missing?

-- Steve

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

* Re: [PATCH 2/3 v4] ftrace/x86: Allow for arguments to be passed in to ftrace_regs by default
  2020-11-09 23:16     ` Steven Rostedt
@ 2020-11-10  9:20       ` Peter Zijlstra
  0 siblings, 0 replies; 8+ messages in thread
From: Peter Zijlstra @ 2020-11-10  9:20 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: linux-kernel, Ingo Molnar, Andrew Morton, Miroslav Benes,
	Thomas Gleixner, Masami Hiramatsu

On Mon, Nov 09, 2020 at 06:16:10PM -0500, Steven Rostedt wrote:
> ftrace_caller:
> 	[..]
> 	movq $0, CS(%rsp) <- loads zero into pt_regs->cs for internal use only.
> 	[..]

Argh, I missed that. I failed to spot it in the patch. No worries then.

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

end of thread, other threads:[~2020-11-10  9:20 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-06 21:42 [PATCH 0/3 v4] ftrace: Add access to function arguments for all callbacks Steven Rostedt
2020-11-06 21:42 ` [PATCH 1/3 v4] ftrace: Have the callbacks receive a struct ftrace_regs instead of pt_regs Steven Rostedt
2020-11-07  4:29   ` Masami Hiramatsu
2020-11-06 21:42 ` [PATCH 2/3 v4] ftrace/x86: Allow for arguments to be passed in to ftrace_regs by default Steven Rostedt
2020-11-09 11:10   ` Peter Zijlstra
2020-11-09 23:16     ` Steven Rostedt
2020-11-10  9:20       ` Peter Zijlstra
2020-11-06 21:42 ` [PATCH 3/3 v4] livepatch: Use the default ftrace_ops instead of REGS when ARGS is available 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).