2020-02-01 07:29:19

by Amol Grover

[permalink] [raw]
Subject: [PATCH] tracing: Annotate ftrace_graph_hash pointer with __rcu

Fix following instances of sparse error
kernel/trace/ftrace.c:5664:29: error: incompatible types in comparison
kernel/trace/ftrace.c:5785:21: error: incompatible types in comparison
kernel/trace/ftrace.c:5864:36: error: incompatible types in comparison
kernel/trace/ftrace.c:5866:25: error: incompatible types in comparison

Use rcu_dereference_protected to access the __rcu annotated pointer.

Signed-off-by: Amol Grover <[email protected]>
---
kernel/trace/ftrace.c | 2 +-
kernel/trace/trace.h | 9 ++++++---
2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 9bf1f2cd515e..959ded08dc13 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5596,7 +5596,7 @@ static const struct file_operations ftrace_notrace_fops = {

static DEFINE_MUTEX(graph_lock);

-struct ftrace_hash *ftrace_graph_hash = EMPTY_HASH;
+struct ftrace_hash __rcu *ftrace_graph_hash = EMPTY_HASH;
struct ftrace_hash *ftrace_graph_notrace_hash = EMPTY_HASH;

enum graph_filter_type {
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 63bf60f79398..97dad3326020 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -950,22 +950,25 @@ extern void __trace_graph_return(struct trace_array *tr,
unsigned long flags, int pc);

#ifdef CONFIG_DYNAMIC_FTRACE
-extern struct ftrace_hash *ftrace_graph_hash;
+extern struct ftrace_hash __rcu *ftrace_graph_hash;
extern struct ftrace_hash *ftrace_graph_notrace_hash;

static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)
{
unsigned long addr = trace->func;
int ret = 0;
+ struct ftrace_hash *hash;

preempt_disable_notrace();

- if (ftrace_hash_empty(ftrace_graph_hash)) {
+ hash = rcu_dereference_protected(ftrace_graph_hash, !preemptible());
+
+ if (ftrace_hash_empty(hash)) {
ret = 1;
goto out;
}

- if (ftrace_lookup_ip(ftrace_graph_hash, addr)) {
+ if (ftrace_lookup_ip(hash, addr)) {

/*
* This needs to be cleared on the return functions
--
2.24.1


2020-02-03 18:44:56

by Joel Fernandes

[permalink] [raw]
Subject: Re: [PATCH] tracing: Annotate ftrace_graph_hash pointer with __rcu

On Sat, Feb 01, 2020 at 12:57:04PM +0530, Amol Grover wrote:
> Fix following instances of sparse error
> kernel/trace/ftrace.c:5664:29: error: incompatible types in comparison
> kernel/trace/ftrace.c:5785:21: error: incompatible types in comparison
> kernel/trace/ftrace.c:5864:36: error: incompatible types in comparison
> kernel/trace/ftrace.c:5866:25: error: incompatible types in comparison
>
> Use rcu_dereference_protected to access the __rcu annotated pointer.
>
> Signed-off-by: Amol Grover <[email protected]>
> ---
> kernel/trace/ftrace.c | 2 +-
> kernel/trace/trace.h | 9 ++++++---
> 2 files changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 9bf1f2cd515e..959ded08dc13 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -5596,7 +5596,7 @@ static const struct file_operations ftrace_notrace_fops = {
>
> static DEFINE_MUTEX(graph_lock);
>
> -struct ftrace_hash *ftrace_graph_hash = EMPTY_HASH;
> +struct ftrace_hash __rcu *ftrace_graph_hash = EMPTY_HASH;
> struct ftrace_hash *ftrace_graph_notrace_hash = EMPTY_HASH;
>
> enum graph_filter_type {
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index 63bf60f79398..97dad3326020 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -950,22 +950,25 @@ extern void __trace_graph_return(struct trace_array *tr,
> unsigned long flags, int pc);
>
> #ifdef CONFIG_DYNAMIC_FTRACE
> -extern struct ftrace_hash *ftrace_graph_hash;
> +extern struct ftrace_hash __rcu *ftrace_graph_hash;
> extern struct ftrace_hash *ftrace_graph_notrace_hash;
>
> static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)
> {
> unsigned long addr = trace->func;
> int ret = 0;
> + struct ftrace_hash *hash;
>
> preempt_disable_notrace();
>
> - if (ftrace_hash_empty(ftrace_graph_hash)) {
> + hash = rcu_dereference_protected(ftrace_graph_hash, !preemptible());

I think you can use rcu_dereference_sched() here? That way no need to pass
!preemptible.

A preempt-disabled section is an RCU "sched flavor" section. Flavors are
consolidated in the backend, but in the front end the dereference API still
do have flavors.

thanks,

- Joel


> +
> + if (ftrace_hash_empty(hash)) {
> ret = 1;
> goto out;
> }
>
> - if (ftrace_lookup_ip(ftrace_graph_hash, addr)) {
> + if (ftrace_lookup_ip(hash, addr)) {
>
> /*
> * This needs to be cleared on the return functions
> --
> 2.24.1
>

2020-02-04 10:04:11

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCH] tracing: Annotate ftrace_graph_hash pointer with __rcu

On Mon, 3 Feb 2020 11:33:01 -0500
Joel Fernandes <[email protected]> wrote:

> > preempt_disable_notrace();
> >
> > - if (ftrace_hash_empty(ftrace_graph_hash)) {
> > + hash = rcu_dereference_protected(ftrace_graph_hash, !preemptible());
>
> I think you can use rcu_dereference_sched() here? That way no need to pass
> !preemptible.
>
> A preempt-disabled section is an RCU "sched flavor" section. Flavors are
> consolidated in the backend, but in the front end the dereference API still
> do have flavors.

Agreed, Amol, can you send an update?

-- Steve

2020-02-05 01:06:01

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCH] tracing: Annotate ftrace_graph_hash pointer with __rcu

On Mon, 3 Feb 2020 11:33:01 -0500
Joel Fernandes <[email protected]> wrote:


> > --- a/kernel/trace/trace.h
> > +++ b/kernel/trace/trace.h
> > @@ -950,22 +950,25 @@ extern void __trace_graph_return(struct trace_array *tr,
> > unsigned long flags, int pc);
> >
> > #ifdef CONFIG_DYNAMIC_FTRACE
> > -extern struct ftrace_hash *ftrace_graph_hash;
> > +extern struct ftrace_hash __rcu *ftrace_graph_hash;
> > extern struct ftrace_hash *ftrace_graph_notrace_hash;
> >
> > static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)
> > {
> > unsigned long addr = trace->func;
> > int ret = 0;
> > + struct ftrace_hash *hash;
> >
> > preempt_disable_notrace();
> >
> > - if (ftrace_hash_empty(ftrace_graph_hash)) {
> > + hash = rcu_dereference_protected(ftrace_graph_hash, !preemptible());
>
> I think you can use rcu_dereference_sched() here? That way no need to pass
> !preemptible.
>
> A preempt-disabled section is an RCU "sched flavor" section. Flavors are
> consolidated in the backend, but in the front end the dereference API still
> do have flavors.

Unfortunately, doing it with rcu_dereference_sched() causes a lockdep
splat :-P. This is because ftrace can execute when rcu is not
"watching" and that will trigger a lockdep error. That means, this
origin patch *is* correct. I'm re-applying this one.

-- Steve

2020-02-05 05:29:34

by Amol Grover

[permalink] [raw]
Subject: Re: [PATCH] tracing: Annotate ftrace_graph_hash pointer with __rcu

On Tue, Feb 04, 2020 at 08:01:16PM -0500, Steven Rostedt wrote:
> On Mon, 3 Feb 2020 11:33:01 -0500
> Joel Fernandes <[email protected]> wrote:
>
>
> > > --- a/kernel/trace/trace.h
> > > +++ b/kernel/trace/trace.h
> > > @@ -950,22 +950,25 @@ extern void __trace_graph_return(struct trace_array *tr,
> > > unsigned long flags, int pc);
> > >
> > > #ifdef CONFIG_DYNAMIC_FTRACE
> > > -extern struct ftrace_hash *ftrace_graph_hash;
> > > +extern struct ftrace_hash __rcu *ftrace_graph_hash;
> > > extern struct ftrace_hash *ftrace_graph_notrace_hash;
> > >
> > > static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)
> > > {
> > > unsigned long addr = trace->func;
> > > int ret = 0;
> > > + struct ftrace_hash *hash;
> > >
> > > preempt_disable_notrace();
> > >
> > > - if (ftrace_hash_empty(ftrace_graph_hash)) {
> > > + hash = rcu_dereference_protected(ftrace_graph_hash, !preemptible());
> >
> > I think you can use rcu_dereference_sched() here? That way no need to pass
> > !preemptible.
> >
> > A preempt-disabled section is an RCU "sched flavor" section. Flavors are
> > consolidated in the backend, but in the front end the dereference API still
> > do have flavors.
>
> Unfortunately, doing it with rcu_dereference_sched() causes a lockdep
> splat :-P. This is because ftrace can execute when rcu is not
> "watching" and that will trigger a lockdep error. That means, this
> origin patch *is* correct. I'm re-applying this one.
>

Something new to learn, thank you for the information Steve!

Thanks
Amol

> -- Steve

2020-02-05 13:12:39

by Paul E. McKenney

[permalink] [raw]
Subject: Re: [PATCH] tracing: Annotate ftrace_graph_hash pointer with __rcu

On Tue, Feb 04, 2020 at 08:01:16PM -0500, Steven Rostedt wrote:
> On Mon, 3 Feb 2020 11:33:01 -0500
> Joel Fernandes <[email protected]> wrote:
>
>
> > > --- a/kernel/trace/trace.h
> > > +++ b/kernel/trace/trace.h
> > > @@ -950,22 +950,25 @@ extern void __trace_graph_return(struct trace_array *tr,
> > > unsigned long flags, int pc);
> > >
> > > #ifdef CONFIG_DYNAMIC_FTRACE
> > > -extern struct ftrace_hash *ftrace_graph_hash;
> > > +extern struct ftrace_hash __rcu *ftrace_graph_hash;
> > > extern struct ftrace_hash *ftrace_graph_notrace_hash;
> > >
> > > static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)
> > > {
> > > unsigned long addr = trace->func;
> > > int ret = 0;
> > > + struct ftrace_hash *hash;
> > >
> > > preempt_disable_notrace();
> > >
> > > - if (ftrace_hash_empty(ftrace_graph_hash)) {
> > > + hash = rcu_dereference_protected(ftrace_graph_hash, !preemptible());
> >
> > I think you can use rcu_dereference_sched() here? That way no need to pass
> > !preemptible.
> >
> > A preempt-disabled section is an RCU "sched flavor" section. Flavors are
> > consolidated in the backend, but in the front end the dereference API still
> > do have flavors.
>
> Unfortunately, doing it with rcu_dereference_sched() causes a lockdep
> splat :-P. This is because ftrace can execute when rcu is not
> "watching" and that will trigger a lockdep error. That means, this
> origin patch *is* correct. I'm re-applying this one.

I strongly recommend a comment stating why disabling preemption prevents
ftrace_graph_hash from going away. I see the synchronize_rcu() after
the rcu_assign_pointer() in ftrace_graph_release(), but I don't see
anything that waits on CPUs that RCU is not watching.

Of course, event tracing -makes- RCU watch when needed, but if that
was set up, then lockdep would not have complained.

So what am I missing?

Thanx, Paul

2020-02-05 13:17:28

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCH] tracing: Annotate ftrace_graph_hash pointer with __rcu

On Wed, 5 Feb 2020 05:11:10 -0800
"Paul E. McKenney" <[email protected]> wrote:

> I strongly recommend a comment stating why disabling preemption prevents
> ftrace_graph_hash from going away. I see the synchronize_rcu() after
> the rcu_assign_pointer() in ftrace_graph_release(), but I don't see
> anything that waits on CPUs that RCU is not watching.
>
> Of course, event tracing -makes- RCU watch when needed, but if that
> was set up, then lockdep would not have complained.
>
> So what am I missing?

Keep looking in your INBOX and look at the patch I asked you to ack or
complain about ;-)

-- Steve

2020-02-05 13:21:23

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCH] tracing: Annotate ftrace_graph_hash pointer with __rcu

On Wed, 5 Feb 2020 08:14:09 -0500
Steven Rostedt <[email protected]> wrote:

> On Wed, 5 Feb 2020 05:11:10 -0800
> "Paul E. McKenney" <[email protected]> wrote:
>
> > I strongly recommend a comment stating why disabling preemption prevents
> > ftrace_graph_hash from going away. I see the synchronize_rcu() after
> > the rcu_assign_pointer() in ftrace_graph_release(), but I don't see
> > anything that waits on CPUs that RCU is not watching.
> >
> > Of course, event tracing -makes- RCU watch when needed, but if that
> > was set up, then lockdep would not have complained.
> >
> > So what am I missing?
>
> Keep looking in your INBOX and look at the patch I asked you to ack or
> complain about ;-)


Actually, looking at the code myself, it appears to be missing the
ftrace_sync. Thus, this is a bug, and requires the ftrace sync, as
synchronize_rcu() is not strong enough here.

Patch in process!

-- Steve

2020-02-05 13:30:28

by Paul E. McKenney

[permalink] [raw]
Subject: Re: [PATCH] tracing: Annotate ftrace_graph_hash pointer with __rcu

On Wed, Feb 05, 2020 at 08:19:21AM -0500, Steven Rostedt wrote:
> On Wed, 5 Feb 2020 08:14:09 -0500
> Steven Rostedt <[email protected]> wrote:
>
> > On Wed, 5 Feb 2020 05:11:10 -0800
> > "Paul E. McKenney" <[email protected]> wrote:
> >
> > > I strongly recommend a comment stating why disabling preemption prevents
> > > ftrace_graph_hash from going away. I see the synchronize_rcu() after
> > > the rcu_assign_pointer() in ftrace_graph_release(), but I don't see
> > > anything that waits on CPUs that RCU is not watching.
> > >
> > > Of course, event tracing -makes- RCU watch when needed, but if that
> > > was set up, then lockdep would not have complained.
> > >
> > > So what am I missing?
> >
> > Keep looking in your INBOX and look at the patch I asked you to ack or
> > complain about ;-)

Not yet seeing it, but...

> Actually, looking at the code myself, it appears to be missing the
> ftrace_sync. Thus, this is a bug, and requires the ftrace sync, as
> synchronize_rcu() is not strong enough here.
>
> Patch in process!

Thank you for chasing it down!

Thanx, Paul

2020-02-05 22:07:26

by Joel Fernandes

[permalink] [raw]
Subject: Re: [PATCH] tracing: Annotate ftrace_graph_hash pointer with __rcu

On Sat, Feb 01, 2020 at 12:57:04PM +0530, Amol Grover wrote:
> Fix following instances of sparse error
> kernel/trace/ftrace.c:5664:29: error: incompatible types in comparison
> kernel/trace/ftrace.c:5785:21: error: incompatible types in comparison
> kernel/trace/ftrace.c:5864:36: error: incompatible types in comparison
> kernel/trace/ftrace.c:5866:25: error: incompatible types in comparison
>
> Use rcu_dereference_protected to access the __rcu annotated pointer.
>
> Signed-off-by: Amol Grover <[email protected]>
> ---
> kernel/trace/ftrace.c | 2 +-
> kernel/trace/trace.h | 9 ++++++---
> 2 files changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 9bf1f2cd515e..959ded08dc13 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -5596,7 +5596,7 @@ static const struct file_operations ftrace_notrace_fops = {
>
> static DEFINE_MUTEX(graph_lock);
>
> -struct ftrace_hash *ftrace_graph_hash = EMPTY_HASH;
> +struct ftrace_hash __rcu *ftrace_graph_hash = EMPTY_HASH;
> struct ftrace_hash *ftrace_graph_notrace_hash = EMPTY_HASH;
>
> enum graph_filter_type {
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index 63bf60f79398..97dad3326020 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -950,22 +950,25 @@ extern void __trace_graph_return(struct trace_array *tr,
> unsigned long flags, int pc);
>
> #ifdef CONFIG_DYNAMIC_FTRACE
> -extern struct ftrace_hash *ftrace_graph_hash;
> +extern struct ftrace_hash __rcu *ftrace_graph_hash;
> extern struct ftrace_hash *ftrace_graph_notrace_hash;
>
> static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)
> {
> unsigned long addr = trace->func;
> int ret = 0;
> + struct ftrace_hash *hash;
>
> preempt_disable_notrace();
>
> - if (ftrace_hash_empty(ftrace_graph_hash)) {
> + hash = rcu_dereference_protected(ftrace_graph_hash, !preemptible());

Reviewed-by: Joel Fernandes (Google) <[email protected]>

BTW I think set_ftrace_early_graph() should use rcu_assign_pointer() so that
the early writes to the hash are release-barrier'ed before the global pointer
is updated. I will send a patch for that.

thanks,

- Joel


> +
> + if (ftrace_hash_empty(hash)) {
> ret = 1;
> goto out;
> }
>
> - if (ftrace_lookup_ip(ftrace_graph_hash, addr)) {
> + if (ftrace_lookup_ip(hash, addr)) {
>
> /*
> * This needs to be cleared on the return functions
> --
> 2.24.1
>