diff mbox series

[7/8] ftrace: Add multi direct modify interface

Message ID 20211008091336.33616-8-jolsa@kernel.org (mailing list archive)
State Not Applicable
Delegated to: Netdev Maintainers
Headers show
Series x86/ftrace: Add direct batch interface | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Jiri Olsa Oct. 8, 2021, 9:13 a.m. UTC
Adding interface to modify registered direct function
for ftrace_ops. Adding following function:

   modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)

The function changes the currently registered direct
function for all attached functions.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 include/linux/ftrace.h |  6 ++++
 kernel/trace/ftrace.c  | 62 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 68 insertions(+)

Comments

Steven Rostedt Oct. 14, 2021, 8:28 p.m. UTC | #1
On Fri,  8 Oct 2021 11:13:35 +0200
Jiri Olsa <jolsa@redhat.com> wrote:

> +	/*
> +	 * Shutdown the ops, change 'direct' pointer for each
> +	 * ops entry in direct_functions hash and startup the
> +	 * ops back again.
> +	 *
> +	 * Note there is no callback called for @ops object after
> +	 * this ftrace_shutdown call until ftrace_startup is called
> +	 * later on.
> +	 */
> +	err = ftrace_shutdown(ops, 0);
> +	if (err)
> +		goto out_unlock;

I believe I said before that we can do this by adding a stub ops that match
all the functions with the direct ops being modified. This will cause the
loop function to be called, which will call the direct function helper,
which will then call the direct function that is found. That is, there is
no "pause" in calling the direct callers. Either the old direct is called,
or the new one. When the function returns, all are calling the new one.

That is, instead of:

[ Changing direct call from my_direct_1 to my_direct_2 ]

  <traced_func>:
     call my_direct_1

 ||||||||||||||||||||
 vvvvvvvvvvvvvvvvvvvv

  <traced_func>:
     nop

 ||||||||||||||||||||
 vvvvvvvvvvvvvvvvvvvv

  <traced_func>:
     call my_direct_2


We have it do:

  <traced_func>:
     call my_direct_1

 ||||||||||||||||||||
 vvvvvvvvvvvvvvvvvvvv

  <traced_func>:
     call ftrace_caller


  <ftrace_caller>:
    [..]
    call ftrace_ops_list_func


ftrace_ops_list_func()
{
	ops->func() -> direct_helper -> set rax to my_direct_1 or my_direct_2
}

   call rax (to either my_direct_1 or my_direct_2

 ||||||||||||||||||||
 vvvvvvvvvvvvvvvvvvvv

  <traced_func>:
     call my_direct_2


I did this on top of this patch:

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 kernel/trace/ftrace.c | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 30120342176e..7ad1e8ae5855 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5561,8 +5561,12 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
  */
 int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
 {
-	struct ftrace_hash *hash = ops->func_hash->filter_hash;
+	struct ftrace_hash *hash;
 	struct ftrace_func_entry *entry, *iter;
+	static struct ftrace_ops tmp_ops = {
+		.func		= ftrace_stub,
+		.flags		= FTRACE_OPS_FL_STUB,
+	};
 	int i, size;
 	int err;
 
@@ -5572,21 +5576,22 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
 		return -EINVAL;
 
 	mutex_lock(&direct_mutex);
-	mutex_lock(&ftrace_lock);
+
+	/* Enable the tmp_ops to have the same functions as the direct ops */
+	ftrace_ops_init(&tmp_ops);
+	tmp_ops.func_hash = ops->func_hash;
+
+	err = register_ftrace_function(&tmp_ops);
+	if (err)
+		goto out_direct;
 
 	/*
-	 * Shutdown the ops, change 'direct' pointer for each
-	 * ops entry in direct_functions hash and startup the
-	 * ops back again.
-	 *
-	 * Note there is no callback called for @ops object after
-	 * this ftrace_shutdown call until ftrace_startup is called
-	 * later on.
+	 * Now the ftrace_ops_list_func() is called to do the direct callers.
+	 * We can safely change the direct functions attached to each entry.
 	 */
-	err = ftrace_shutdown(ops, 0);
-	if (err)
-		goto out_unlock;
+	mutex_lock(&ftrace_lock);
 
+	hash = ops->func_hash->filter_hash;
 	size = 1 << hash->size_bits;
 	for (i = 0; i < size; i++) {
 		hlist_for_each_entry(iter, &hash->buckets[i], hlist) {
@@ -5597,10 +5602,12 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
 		}
 	}
 
-	err = ftrace_startup(ops, 0);
+	/* Removing the tmp_ops will add the updated direct callers to the functions */
+	unregister_ftrace_function(&tmp_ops);
 
  out_unlock:
 	mutex_unlock(&ftrace_lock);
+ out_direct:
 	mutex_unlock(&direct_mutex);
 	return err;
 }
Jiri Olsa Oct. 15, 2021, 12:05 p.m. UTC | #2
On Thu, Oct 14, 2021 at 04:28:19PM -0400, Steven Rostedt wrote:
> On Fri,  8 Oct 2021 11:13:35 +0200
> Jiri Olsa <jolsa@redhat.com> wrote:
> 
> > +	/*
> > +	 * Shutdown the ops, change 'direct' pointer for each
> > +	 * ops entry in direct_functions hash and startup the
> > +	 * ops back again.
> > +	 *
> > +	 * Note there is no callback called for @ops object after
> > +	 * this ftrace_shutdown call until ftrace_startup is called
> > +	 * later on.
> > +	 */
> > +	err = ftrace_shutdown(ops, 0);
> > +	if (err)
> > +		goto out_unlock;
> 
> I believe I said before that we can do this by adding a stub ops that match
> all the functions with the direct ops being modified. This will cause the
> loop function to be called, which will call the direct function helper,
> which will then call the direct function that is found. That is, there is
> no "pause" in calling the direct callers. Either the old direct is called,
> or the new one. When the function returns, all are calling the new one.
> 
> That is, instead of:
> 
> [ Changing direct call from my_direct_1 to my_direct_2 ]
> 
>   <traced_func>:
>      call my_direct_1
> 
>  ||||||||||||||||||||
>  vvvvvvvvvvvvvvvvvvvv
> 
>   <traced_func>:
>      nop
> 
>  ||||||||||||||||||||
>  vvvvvvvvvvvvvvvvvvvv
> 
>   <traced_func>:
>      call my_direct_2
> 
> 
> We have it do:
> 
>   <traced_func>:
>      call my_direct_1
> 
>  ||||||||||||||||||||
>  vvvvvvvvvvvvvvvvvvvv
> 
>   <traced_func>:
>      call ftrace_caller
> 
> 
>   <ftrace_caller>:
>     [..]
>     call ftrace_ops_list_func
> 
> 
> ftrace_ops_list_func()
> {
> 	ops->func() -> direct_helper -> set rax to my_direct_1 or my_direct_2
> }
> 
>    call rax (to either my_direct_1 or my_direct_2

nice! :) I did not see that as a problem and something that can be
done later, thanks for doing this

> 
>  ||||||||||||||||||||
>  vvvvvvvvvvvvvvvvvvvv
> 
>   <traced_func>:
>      call my_direct_2
> 
> 
> I did this on top of this patch:

ATM I'm bit stuck on the bpf side of this whole change, I'll test
it with my other changes when I unstuck myself ;-)

thanks,
jirka

> 
> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> ---
>  kernel/trace/ftrace.c | 33 ++++++++++++++++++++-------------
>  1 file changed, 20 insertions(+), 13 deletions(-)
> 
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 30120342176e..7ad1e8ae5855 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -5561,8 +5561,12 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
>   */
>  int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
>  {
> -	struct ftrace_hash *hash = ops->func_hash->filter_hash;
> +	struct ftrace_hash *hash;
>  	struct ftrace_func_entry *entry, *iter;
> +	static struct ftrace_ops tmp_ops = {
> +		.func		= ftrace_stub,
> +		.flags		= FTRACE_OPS_FL_STUB,
> +	};
>  	int i, size;
>  	int err;
>  
> @@ -5572,21 +5576,22 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
>  		return -EINVAL;
>  
>  	mutex_lock(&direct_mutex);
> -	mutex_lock(&ftrace_lock);
> +
> +	/* Enable the tmp_ops to have the same functions as the direct ops */
> +	ftrace_ops_init(&tmp_ops);
> +	tmp_ops.func_hash = ops->func_hash;
> +
> +	err = register_ftrace_function(&tmp_ops);
> +	if (err)
> +		goto out_direct;
>  
>  	/*
> -	 * Shutdown the ops, change 'direct' pointer for each
> -	 * ops entry in direct_functions hash and startup the
> -	 * ops back again.
> -	 *
> -	 * Note there is no callback called for @ops object after
> -	 * this ftrace_shutdown call until ftrace_startup is called
> -	 * later on.
> +	 * Now the ftrace_ops_list_func() is called to do the direct callers.
> +	 * We can safely change the direct functions attached to each entry.
>  	 */
> -	err = ftrace_shutdown(ops, 0);
> -	if (err)
> -		goto out_unlock;
> +	mutex_lock(&ftrace_lock);
>  
> +	hash = ops->func_hash->filter_hash;
>  	size = 1 << hash->size_bits;
>  	for (i = 0; i < size; i++) {
>  		hlist_for_each_entry(iter, &hash->buckets[i], hlist) {
> @@ -5597,10 +5602,12 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
>  		}
>  	}
>  
> -	err = ftrace_startup(ops, 0);
> +	/* Removing the tmp_ops will add the updated direct callers to the functions */
> +	unregister_ftrace_function(&tmp_ops);
>  
>   out_unlock:
>  	mutex_unlock(&ftrace_lock);
> + out_direct:
>  	mutex_unlock(&direct_mutex);
>  	return err;
>  }
> -- 
> 2.31.1
>
Steven Rostedt Oct. 15, 2021, 2:05 p.m. UTC | #3
On Fri, 15 Oct 2021 14:05:25 +0200
Jiri Olsa <jolsa@redhat.com> wrote:

> ATM I'm bit stuck on the bpf side of this whole change, I'll test
> it with my other changes when I unstuck myself ;-)

If you want, I'll apply this as a separate change on top of your patch set.
As I don't see anything wrong with your current code.

And when you are satisfied with this, just give me a "tested-by" and I'll
push it too.

-- Steve
Jiri Olsa Oct. 16, 2021, 11:39 a.m. UTC | #4
On Fri, Oct 15, 2021 at 10:05:09AM -0400, Steven Rostedt wrote:
> On Fri, 15 Oct 2021 14:05:25 +0200
> Jiri Olsa <jolsa@redhat.com> wrote:
> 
> > ATM I'm bit stuck on the bpf side of this whole change, I'll test
> > it with my other changes when I unstuck myself ;-)
> 
> If you want, I'll apply this as a separate change on top of your patch set.
> As I don't see anything wrong with your current code.
> 
> And when you are satisfied with this, just give me a "tested-by" and I'll
> push it too.

sounds great, thanks
jirka
Steven Rostedt Oct. 19, 2021, 2:10 a.m. UTC | #5
On Sat, 16 Oct 2021 13:39:55 +0200
Jiri Olsa <jolsa@redhat.com> wrote:

> On Fri, Oct 15, 2021 at 10:05:09AM -0400, Steven Rostedt wrote:
> > On Fri, 15 Oct 2021 14:05:25 +0200
> > Jiri Olsa <jolsa@redhat.com> wrote:
> >   
> > > ATM I'm bit stuck on the bpf side of this whole change, I'll test
> > > it with my other changes when I unstuck myself ;-)  
> > 
> > If you want, I'll apply this as a separate change on top of your patch set.
> > As I don't see anything wrong with your current code.
> > 
> > And when you are satisfied with this, just give me a "tested-by" and I'll
> > push it too.  
> 
> sounds great, thanks
> jirka

Would you want to ack/review this?

-- Steve

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
Subject: [PATCH] ftrace/direct: Do not disable when switching direct callers

Currently to switch a set of "multi" direct trampolines from one
trampoline to another, a full shutdown of the current set needs to be
done, followed by an update to what trampoline the direct callers would
call, and then re-enabling the callers. This leaves a time when the
functions will not be calling anything, and events may be missed.

Instead, use a trick to allow all the functions with direct trampolines
attached will always call either the new or old trampoline while the
switch is happening. To do this, first attach a "dummy" callback via
ftrace to all the functions that the current direct trampoline is attached
to. This will cause the functions to call the "list func" instead of the
direct trampoline. The list function will call the direct trampoline
"helper" that will set the function it should call as it returns back to
the ftrace trampoline.

At this moment, the direct caller descriptor can safely update the direct
call trampoline. The list function will pick either the new or old
function (depending on the memory coherency model of the architecture).

Now removing the dummy function from each of the locations of the direct
trampoline caller, will put back the direct call, but now to the new
trampoline.

A better visual is:

[ Changing direct call from my_direct_1 to my_direct_2 ]

  <traced_func>:
     call my_direct_1

 ||||||||||||||||||||
 vvvvvvvvvvvvvvvvvvvv

  <traced_func>:
     call ftrace_caller

  <ftrace_caller>:
    [..]
    call ftrace_ops_list_func

	ftrace_ops_list_func()
	{
		ops->func() -> direct_helper -> set rax to my_direct_1 or my_direct_2
	}

   call rax (to either my_direct_1 or my_direct_2

 ||||||||||||||||||||
 vvvvvvvvvvvvvvvvvvvv

  <traced_func>:
     call my_direct_2

Link: https://lore.kernel.org/all/20211014162819.5c85618b@gandalf.local.home/

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 kernel/trace/ftrace.c | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 30120342176e..7ad1e8ae5855 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5561,8 +5561,12 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
  */
 int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
 {
-	struct ftrace_hash *hash = ops->func_hash->filter_hash;
+	struct ftrace_hash *hash;
 	struct ftrace_func_entry *entry, *iter;
+	static struct ftrace_ops tmp_ops = {
+		.func		= ftrace_stub,
+		.flags		= FTRACE_OPS_FL_STUB,
+	};
 	int i, size;
 	int err;
 
@@ -5572,21 +5576,22 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
 		return -EINVAL;
 
 	mutex_lock(&direct_mutex);
-	mutex_lock(&ftrace_lock);
+
+	/* Enable the tmp_ops to have the same functions as the direct ops */
+	ftrace_ops_init(&tmp_ops);
+	tmp_ops.func_hash = ops->func_hash;
+
+	err = register_ftrace_function(&tmp_ops);
+	if (err)
+		goto out_direct;
 
 	/*
-	 * Shutdown the ops, change 'direct' pointer for each
-	 * ops entry in direct_functions hash and startup the
-	 * ops back again.
-	 *
-	 * Note there is no callback called for @ops object after
-	 * this ftrace_shutdown call until ftrace_startup is called
-	 * later on.
+	 * Now the ftrace_ops_list_func() is called to do the direct callers.
+	 * We can safely change the direct functions attached to each entry.
 	 */
-	err = ftrace_shutdown(ops, 0);
-	if (err)
-		goto out_unlock;
+	mutex_lock(&ftrace_lock);
 
+	hash = ops->func_hash->filter_hash;
 	size = 1 << hash->size_bits;
 	for (i = 0; i < size; i++) {
 		hlist_for_each_entry(iter, &hash->buckets[i], hlist) {
@@ -5597,10 +5602,12 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
 		}
 	}
 
-	err = ftrace_startup(ops, 0);
+	/* Removing the tmp_ops will add the updated direct callers to the functions */
+	unregister_ftrace_function(&tmp_ops);
 
  out_unlock:
 	mutex_unlock(&ftrace_lock);
+ out_direct:
 	mutex_unlock(&direct_mutex);
 	return err;
 }
Jiri Olsa Oct. 19, 2021, 1:19 p.m. UTC | #6
On Mon, Oct 18, 2021 at 10:10:15PM -0400, Steven Rostedt wrote:
> On Sat, 16 Oct 2021 13:39:55 +0200
> Jiri Olsa <jolsa@redhat.com> wrote:
> 
> > On Fri, Oct 15, 2021 at 10:05:09AM -0400, Steven Rostedt wrote:
> > > On Fri, 15 Oct 2021 14:05:25 +0200
> > > Jiri Olsa <jolsa@redhat.com> wrote:
> > >   
> > > > ATM I'm bit stuck on the bpf side of this whole change, I'll test
> > > > it with my other changes when I unstuck myself ;-)  
> > > 
> > > If you want, I'll apply this as a separate change on top of your patch set.
> > > As I don't see anything wrong with your current code.
> > > 
> > > And when you are satisfied with this, just give me a "tested-by" and I'll
> > > push it too.  
> > 
> > sounds great, thanks
> > jirka
> 
> Would you want to ack/review this?

hum, do you have it in some branch already? I'm getting:

patching file kernel/trace/ftrace.c
Hunk #1 succeeded at 5521 with fuzz 1 (offset -40 lines).
Hunk #2 FAILED at 5576.
Hunk #3 succeeded at 5557 (offset -44 lines).
1 out of 3 hunks FAILED -- saving rejects to file kernel/trace/ftrace.c.rej


when trying to apply on top of my changes

thanks,
jirka

> 
> -- Steve
> 
> From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
> Subject: [PATCH] ftrace/direct: Do not disable when switching direct callers
> 
> Currently to switch a set of "multi" direct trampolines from one
> trampoline to another, a full shutdown of the current set needs to be
> done, followed by an update to what trampoline the direct callers would
> call, and then re-enabling the callers. This leaves a time when the
> functions will not be calling anything, and events may be missed.
> 
> Instead, use a trick to allow all the functions with direct trampolines
> attached will always call either the new or old trampoline while the
> switch is happening. To do this, first attach a "dummy" callback via
> ftrace to all the functions that the current direct trampoline is attached
> to. This will cause the functions to call the "list func" instead of the
> direct trampoline. The list function will call the direct trampoline
> "helper" that will set the function it should call as it returns back to
> the ftrace trampoline.
> 
> At this moment, the direct caller descriptor can safely update the direct
> call trampoline. The list function will pick either the new or old
> function (depending on the memory coherency model of the architecture).
> 
> Now removing the dummy function from each of the locations of the direct
> trampoline caller, will put back the direct call, but now to the new
> trampoline.
> 
> A better visual is:
> 
> [ Changing direct call from my_direct_1 to my_direct_2 ]
> 
>   <traced_func>:
>      call my_direct_1
> 
>  ||||||||||||||||||||
>  vvvvvvvvvvvvvvvvvvvv
> 
>   <traced_func>:
>      call ftrace_caller
> 
>   <ftrace_caller>:
>     [..]
>     call ftrace_ops_list_func
> 
> 	ftrace_ops_list_func()
> 	{
> 		ops->func() -> direct_helper -> set rax to my_direct_1 or my_direct_2
> 	}
> 
>    call rax (to either my_direct_1 or my_direct_2
> 
>  ||||||||||||||||||||
>  vvvvvvvvvvvvvvvvvvvv
> 
>   <traced_func>:
>      call my_direct_2
> 
> Link: https://lore.kernel.org/all/20211014162819.5c85618b@gandalf.local.home/
> 
> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> ---
>  kernel/trace/ftrace.c | 33 ++++++++++++++++++++-------------
>  1 file changed, 20 insertions(+), 13 deletions(-)
> 
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 30120342176e..7ad1e8ae5855 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -5561,8 +5561,12 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
>   */
>  int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
>  {
> -	struct ftrace_hash *hash = ops->func_hash->filter_hash;
> +	struct ftrace_hash *hash;
>  	struct ftrace_func_entry *entry, *iter;
> +	static struct ftrace_ops tmp_ops = {
> +		.func		= ftrace_stub,
> +		.flags		= FTRACE_OPS_FL_STUB,
> +	};
>  	int i, size;
>  	int err;
>  
> @@ -5572,21 +5576,22 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
>  		return -EINVAL;
>  
>  	mutex_lock(&direct_mutex);
> -	mutex_lock(&ftrace_lock);
> +
> +	/* Enable the tmp_ops to have the same functions as the direct ops */
> +	ftrace_ops_init(&tmp_ops);
> +	tmp_ops.func_hash = ops->func_hash;
> +
> +	err = register_ftrace_function(&tmp_ops);
> +	if (err)
> +		goto out_direct;
>  
>  	/*
> -	 * Shutdown the ops, change 'direct' pointer for each
> -	 * ops entry in direct_functions hash and startup the
> -	 * ops back again.
> -	 *
> -	 * Note there is no callback called for @ops object after
> -	 * this ftrace_shutdown call until ftrace_startup is called
> -	 * later on.
> +	 * Now the ftrace_ops_list_func() is called to do the direct callers.
> +	 * We can safely change the direct functions attached to each entry.
>  	 */
> -	err = ftrace_shutdown(ops, 0);
> -	if (err)
> -		goto out_unlock;
> +	mutex_lock(&ftrace_lock);
>  
> +	hash = ops->func_hash->filter_hash;
>  	size = 1 << hash->size_bits;
>  	for (i = 0; i < size; i++) {
>  		hlist_for_each_entry(iter, &hash->buckets[i], hlist) {
> @@ -5597,10 +5602,12 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
>  		}
>  	}
>  
> -	err = ftrace_startup(ops, 0);
> +	/* Removing the tmp_ops will add the updated direct callers to the functions */
> +	unregister_ftrace_function(&tmp_ops);
>  
>   out_unlock:
>  	mutex_unlock(&ftrace_lock);
> + out_direct:
>  	mutex_unlock(&direct_mutex);
>  	return err;
>  }
> -- 
> 2.31.1
>
Jiri Olsa Oct. 19, 2021, 1:26 p.m. UTC | #7
On Tue, Oct 19, 2021 at 03:19:48PM +0200, Jiri Olsa wrote:
> On Mon, Oct 18, 2021 at 10:10:15PM -0400, Steven Rostedt wrote:
> > On Sat, 16 Oct 2021 13:39:55 +0200
> > Jiri Olsa <jolsa@redhat.com> wrote:
> > 
> > > On Fri, Oct 15, 2021 at 10:05:09AM -0400, Steven Rostedt wrote:
> > > > On Fri, 15 Oct 2021 14:05:25 +0200
> > > > Jiri Olsa <jolsa@redhat.com> wrote:
> > > >   
> > > > > ATM I'm bit stuck on the bpf side of this whole change, I'll test
> > > > > it with my other changes when I unstuck myself ;-)  
> > > > 
> > > > If you want, I'll apply this as a separate change on top of your patch set.
> > > > As I don't see anything wrong with your current code.
> > > > 
> > > > And when you are satisfied with this, just give me a "tested-by" and I'll
> > > > push it too.  
> > > 
> > > sounds great, thanks
> > > jirka
> > 
> > Would you want to ack/review this?
> 
> hum, do you have it in some branch already? I'm getting:
> 
> patching file kernel/trace/ftrace.c
> Hunk #1 succeeded at 5521 with fuzz 1 (offset -40 lines).
> Hunk #2 FAILED at 5576.
> Hunk #3 succeeded at 5557 (offset -44 lines).
> 1 out of 3 hunks FAILED -- saving rejects to file kernel/trace/ftrace.c.rej
> 
> 
> when trying to apply on top of my changes

I updated my ftrace/direct branch, it actually still had the previous
version.. sorry, perhaps this is the cause of fuzz

jirka

> 
> thanks,
> jirka
> 
> > 
> > -- Steve
> > 
> > From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
> > Subject: [PATCH] ftrace/direct: Do not disable when switching direct callers
> > 
> > Currently to switch a set of "multi" direct trampolines from one
> > trampoline to another, a full shutdown of the current set needs to be
> > done, followed by an update to what trampoline the direct callers would
> > call, and then re-enabling the callers. This leaves a time when the
> > functions will not be calling anything, and events may be missed.
> > 
> > Instead, use a trick to allow all the functions with direct trampolines
> > attached will always call either the new or old trampoline while the
> > switch is happening. To do this, first attach a "dummy" callback via
> > ftrace to all the functions that the current direct trampoline is attached
> > to. This will cause the functions to call the "list func" instead of the
> > direct trampoline. The list function will call the direct trampoline
> > "helper" that will set the function it should call as it returns back to
> > the ftrace trampoline.
> > 
> > At this moment, the direct caller descriptor can safely update the direct
> > call trampoline. The list function will pick either the new or old
> > function (depending on the memory coherency model of the architecture).
> > 
> > Now removing the dummy function from each of the locations of the direct
> > trampoline caller, will put back the direct call, but now to the new
> > trampoline.
> > 
> > A better visual is:
> > 
> > [ Changing direct call from my_direct_1 to my_direct_2 ]
> > 
> >   <traced_func>:
> >      call my_direct_1
> > 
> >  ||||||||||||||||||||
> >  vvvvvvvvvvvvvvvvvvvv
> > 
> >   <traced_func>:
> >      call ftrace_caller
> > 
> >   <ftrace_caller>:
> >     [..]
> >     call ftrace_ops_list_func
> > 
> > 	ftrace_ops_list_func()
> > 	{
> > 		ops->func() -> direct_helper -> set rax to my_direct_1 or my_direct_2
> > 	}
> > 
> >    call rax (to either my_direct_1 or my_direct_2
> > 
> >  ||||||||||||||||||||
> >  vvvvvvvvvvvvvvvvvvvv
> > 
> >   <traced_func>:
> >      call my_direct_2
> > 
> > Link: https://lore.kernel.org/all/20211014162819.5c85618b@gandalf.local.home/
> > 
> > Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> > ---
> >  kernel/trace/ftrace.c | 33 ++++++++++++++++++++-------------
> >  1 file changed, 20 insertions(+), 13 deletions(-)
> > 
> > diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> > index 30120342176e..7ad1e8ae5855 100644
> > --- a/kernel/trace/ftrace.c
> > +++ b/kernel/trace/ftrace.c
> > @@ -5561,8 +5561,12 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
> >   */
> >  int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
> >  {
> > -	struct ftrace_hash *hash = ops->func_hash->filter_hash;
> > +	struct ftrace_hash *hash;
> >  	struct ftrace_func_entry *entry, *iter;
> > +	static struct ftrace_ops tmp_ops = {
> > +		.func		= ftrace_stub,
> > +		.flags		= FTRACE_OPS_FL_STUB,
> > +	};
> >  	int i, size;
> >  	int err;
> >  
> > @@ -5572,21 +5576,22 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
> >  		return -EINVAL;
> >  
> >  	mutex_lock(&direct_mutex);
> > -	mutex_lock(&ftrace_lock);
> > +
> > +	/* Enable the tmp_ops to have the same functions as the direct ops */
> > +	ftrace_ops_init(&tmp_ops);
> > +	tmp_ops.func_hash = ops->func_hash;
> > +
> > +	err = register_ftrace_function(&tmp_ops);
> > +	if (err)
> > +		goto out_direct;
> >  
> >  	/*
> > -	 * Shutdown the ops, change 'direct' pointer for each
> > -	 * ops entry in direct_functions hash and startup the
> > -	 * ops back again.
> > -	 *
> > -	 * Note there is no callback called for @ops object after
> > -	 * this ftrace_shutdown call until ftrace_startup is called
> > -	 * later on.
> > +	 * Now the ftrace_ops_list_func() is called to do the direct callers.
> > +	 * We can safely change the direct functions attached to each entry.
> >  	 */
> > -	err = ftrace_shutdown(ops, 0);
> > -	if (err)
> > -		goto out_unlock;
> > +	mutex_lock(&ftrace_lock);
> >  
> > +	hash = ops->func_hash->filter_hash;
> >  	size = 1 << hash->size_bits;
> >  	for (i = 0; i < size; i++) {
> >  		hlist_for_each_entry(iter, &hash->buckets[i], hlist) {
> > @@ -5597,10 +5602,12 @@ int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
> >  		}
> >  	}
> >  
> > -	err = ftrace_startup(ops, 0);
> > +	/* Removing the tmp_ops will add the updated direct callers to the functions */
> > +	unregister_ftrace_function(&tmp_ops);
> >  
> >   out_unlock:
> >  	mutex_unlock(&ftrace_lock);
> > + out_direct:
> >  	mutex_unlock(&direct_mutex);
> >  	return err;
> >  }
> > -- 
> > 2.31.1
> >
Steven Rostedt Oct. 19, 2021, 1:32 p.m. UTC | #8
On Tue, 19 Oct 2021 15:26:21 +0200
Jiri Olsa <jolsa@redhat.com> wrote:

> > when trying to apply on top of my changes  
> 
> I updated my ftrace/direct branch, it actually still had the previous
> version.. sorry, perhaps this is the cause of fuzz

I just pushed it (including your patches) here:

  git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git

  ftrace/core


This is where I keep my WIP code. It should not be used to base anything
off of, as I rebase it constantly. But it has the current version I plan on
testing.

You can make sure the patches in there have your latest version, as you can
review my patch. I'll update the tags if you give me one.

-- Steve
Jiri Olsa Oct. 19, 2021, 2:03 p.m. UTC | #9
On Tue, Oct 19, 2021 at 09:32:16AM -0400, Steven Rostedt wrote:
> On Tue, 19 Oct 2021 15:26:21 +0200
> Jiri Olsa <jolsa@redhat.com> wrote:
> 
> > > when trying to apply on top of my changes  
> > 
> > I updated my ftrace/direct branch, it actually still had the previous
> > version.. sorry, perhaps this is the cause of fuzz
> 
> I just pushed it (including your patches) here:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
> 
>   ftrace/core
> 
> 
> This is where I keep my WIP code. It should not be used to base anything
> off of, as I rebase it constantly. But it has the current version I plan on
> testing.
> 
> You can make sure the patches in there have your latest version, as you can
> review my patch. I'll update the tags if you give me one.

I'm getting error when compiling:

  CC      kernel/trace/ftrace.o
kernel/trace/ftrace.c: In function ‘modify_ftrace_direct_multi’:
kernel/trace/ftrace.c:5608:2: error: label ‘out_unlock’ defined but not used [-Werror=unused-label]
 5608 |  out_unlock:
      |  ^~~~~~~~~~

looks like out_unlock is nolonger needed, I removed it

jirka
Steven Rostedt Oct. 19, 2021, 2:44 p.m. UTC | #10
On Tue, 19 Oct 2021 16:03:03 +0200
Jiri Olsa <jolsa@redhat.com> wrote:

> > You can make sure the patches in there have your latest version, as you can
> > review my patch. I'll update the tags if you give me one.  
> 
> I'm getting error when compiling:
> 
>   CC      kernel/trace/ftrace.o
> kernel/trace/ftrace.c: In function ‘modify_ftrace_direct_multi’:
> kernel/trace/ftrace.c:5608:2: error: label ‘out_unlock’ defined but not used [-Werror=unused-label]

Ah, I don't think I've been hit by the "-Werror" yet ;-)


>  5608 |  out_unlock:
>       |  ^~~~~~~~~~
> 
> looks like out_unlock is nolonger needed, I removed it

My tests would have found this, as it has a check for "new warnings".

Anyway, was this in your latest patch, or did I pull in and older one?

That is, should I expect a v2 from you?

-- Steve
Jiri Olsa Oct. 19, 2021, 2:47 p.m. UTC | #11
On Tue, Oct 19, 2021 at 10:44:11AM -0400, Steven Rostedt wrote:
> On Tue, 19 Oct 2021 16:03:03 +0200
> Jiri Olsa <jolsa@redhat.com> wrote:
> 
> > > You can make sure the patches in there have your latest version, as you can
> > > review my patch. I'll update the tags if you give me one.  
> > 
> > I'm getting error when compiling:
> > 
> >   CC      kernel/trace/ftrace.o
> > kernel/trace/ftrace.c: In function ‘modify_ftrace_direct_multi’:
> > kernel/trace/ftrace.c:5608:2: error: label ‘out_unlock’ defined but not used [-Werror=unused-label]
> 
> Ah, I don't think I've been hit by the "-Werror" yet ;-)
> 
> 
> >  5608 |  out_unlock:
> >       |  ^~~~~~~~~~
> > 
> > looks like out_unlock is nolonger needed, I removed it
> 
> My tests would have found this, as it has a check for "new warnings".
> 
> Anyway, was this in your latest patch, or did I pull in and older one?
> 
> That is, should I expect a v2 from you?

it's on top of your ftrace/core, in your change:
  e62d91d8206e ftrace/direct: Do not disable when switching direct callers

just removing the label will fix it

also you can add my ack

Acked-by: Jiri Olsa <jolsa@redhat.com>

thanks,
jirka
diff mbox series

Patch

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index ba5d02ba8166..c15b767f39cf 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -318,6 +318,8 @@  int ftrace_modify_direct_caller(struct ftrace_func_entry *entry,
 unsigned long ftrace_find_rec_direct(unsigned long ip);
 int register_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
 int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
+int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr);
+
 #else
 struct ftrace_ops;
 # define ftrace_direct_func_count 0
@@ -357,6 +359,10 @@  static inline int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigne
 {
 	return -ENODEV;
 }
+static inline int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
+{
+	return -ENODEV;
+}
 #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
 
 #ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index f9df7bffb770..d92f2591c3fc 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5547,6 +5547,68 @@  int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
 	return err;
 }
 EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
+
+/**
+ * modify_ftrace_direct_multi - Modify an existing direct 'multi' call
+ * to call something else
+ * @ops: The address of the struct ftrace_ops object
+ * @addr: The address of the new trampoline to call at @ops functions
+ *
+ * This is used to unregister currently registered direct caller and
+ * register new one @addr on functions registered in @ops object.
+ *
+ * Note there's window between ftrace_shutdown and ftrace_startup calls
+ * where there will be no callbacks called.
+ *
+ * Returns: zero on success. Non zero on error, which includes:
+ *  -EINVAL - The @ops object was not properly registered.
+ */
+int modify_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
+{
+	struct ftrace_hash *hash = ops->func_hash->filter_hash;
+	struct ftrace_func_entry *entry, *iter;
+	int i, size;
+	int err;
+
+	if (check_direct_multi(ops))
+		return -EINVAL;
+	if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
+		return -EINVAL;
+
+	mutex_lock(&direct_mutex);
+	mutex_lock(&ftrace_lock);
+
+	/*
+	 * Shutdown the ops, change 'direct' pointer for each
+	 * ops entry in direct_functions hash and startup the
+	 * ops back again.
+	 *
+	 * Note there is no callback called for @ops object after
+	 * this ftrace_shutdown call until ftrace_startup is called
+	 * later on.
+	 */
+	err = ftrace_shutdown(ops, 0);
+	if (err)
+		goto out_unlock;
+
+	size = 1 << hash->size_bits;
+	for (i = 0; i < size; i++) {
+		hlist_for_each_entry(iter, &hash->buckets[i], hlist) {
+			entry = __ftrace_lookup_ip(direct_functions, iter->ip);
+			if (!entry)
+				continue;
+			entry->direct = addr;
+		}
+	}
+
+	err = ftrace_startup(ops, 0);
+
+ out_unlock:
+	mutex_unlock(&ftrace_lock);
+	mutex_unlock(&direct_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(modify_ftrace_direct_multi);
 #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
 
 /**