diff mbox

[02/15] xen: tracing: avoid checking tb_init_done multiple times.

Message ID 149633842869.12814.8051616219182929257.stgit@Solace.fritz.box (mailing list archive)
State New, archived
Headers show

Commit Message

Dario Faggioli June 1, 2017, 5:33 p.m. UTC
In fact, when calling __trace_var() directly, we can
assume that tb_init_done has been checked to be true,
and the if is hence redundant.

While there, also:
 - still in __trace_var(), move the check that the event
   is actually being traced up a little bit (to bail as
   soon as possible, if it is not);
 - make it explicit that tb_init_done is likely 0 in
   trace_will_trace_event(), as it is almost everywhere
   in the code.

Signed-off-by: Dario Faggioli <dario.faggioli@citrix.com>
---
Cc: George Dunlap <george.dunlap@eu.citrix.com>
---
 xen/common/trace.c |    8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

Comments

Andrew Cooper June 1, 2017, 5:53 p.m. UTC | #1
On 01/06/17 18:33, Dario Faggioli wrote:
> In fact, when calling __trace_var() directly, we can
> assume that tb_init_done has been checked to be true,
> and the if is hence redundant.
>
> While there, also:
>  - still in __trace_var(), move the check that the event
>    is actually being traced up a little bit (to bail as
>    soon as possible, if it is not);
>  - make it explicit that tb_init_done is likely 0 in
>    trace_will_trace_event(), as it is almost everywhere
>    in the code.
>
> Signed-off-by: Dario Faggioli <dario.faggioli@citrix.com>
> ---
> Cc: George Dunlap <george.dunlap@eu.citrix.com>
> ---
>  xen/common/trace.c |    8 +++-----
>  1 file changed, 3 insertions(+), 5 deletions(-)
>
> diff --git a/xen/common/trace.c b/xen/common/trace.c
> index 4fedc26..f29cd4c 100644
> --- a/xen/common/trace.c
> +++ b/xen/common/trace.c
> @@ -311,7 +311,7 @@ static int tb_set_size(unsigned int pages)
>  
>  int trace_will_trace_event(u32 event)
>  {
> -    if ( !tb_init_done )
> +    if ( likely(!tb_init_done) )
>          return 0;
>  
>      /*
> @@ -691,7 +691,8 @@ void __trace_var(u32 event, bool_t cycles, unsigned int extra,
>      unsigned int extra_word;
>      bool_t started_below_highwater;

You probably want an ASSERT(tb_init_done) here to keep callers in check.

Otherwise, Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>

>  
> -    if( !tb_init_done )
> +    /* If the event is not interesting, bail, as early as possible */
> +    if ( (tb_event_mask & event) == 0 )
>          return;
>  
>      /* Convert byte count into word count, rounding up */
> @@ -705,9 +706,6 @@ void __trace_var(u32 event, bool_t cycles, unsigned int extra,
>      /* Round size up to nearest word */
>      extra = extra_word * sizeof(u32);
>  
> -    if ( (tb_event_mask & event) == 0 )
> -        return;
> -
>      /* match class */
>      if ( ((tb_event_mask >> TRC_CLS_SHIFT) & (event >> TRC_CLS_SHIFT)) == 0 )
>          return;
>
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.xen.org
> https://lists.xen.org/xen-devel
Dario Faggioli June 1, 2017, 11:08 p.m. UTC | #2
On Thu, 2017-06-01 at 18:53 +0100, Andrew Cooper wrote:
> On 01/06/17 18:33, Dario Faggioli wrote:
> > diff --git a/xen/common/trace.c b/xen/common/trace.c
> > index 4fedc26..f29cd4c 100644
> > --- a/xen/common/trace.c
> > +++ b/xen/common/trace.c
> > @@ -691,7 +691,8 @@ void __trace_var(u32 event, bool_t cycles,
> > unsigned int extra,
> >      unsigned int extra_word;
> >      bool_t started_below_highwater;
> 
> You probably want an ASSERT(tb_init_done) here to keep callers in
> check.
> 
Indeed! And in fact, I did have one. But only to discover that it
triggers! :-O

After looking at this better, I figured out that the existing check
(the one that this patch kills) is not just redundant, but only a best
effort measure.

In fact, we still haven't acquired any locks here. If I put the
ASSERT() and, between when I call __trace_var() and when the ASSERT()
is reached, someone manages to set tb_init_done=0, the ASSERT(), as
said, fires.

However, that is no better in current code. In fact, it is very well
possible that someone manages to set tb_init_done=0 *right after*, here
in __trace_var(), we check it with the if.

Actually, I think having the check there is misleading, because it
makes one think that tb_init_done is guaranteed to be and stay 0 below
it, while that isn't true at all. And this is therefore just another
reason to get rid of it, IMO. But, sadly, I can't replace it with an
ASSERT(). :-(

> Otherwise, Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
> 
Thanks, let me know if this still stands.
> >  
> > -    if( !tb_init_done )
> > +    /* If the event is not interesting, bail, as early as possible
> > */
> > +    if ( (tb_event_mask & event) == 0 )
> >          return;
> >  
> >      /* Convert byte count into word count, rounding up */
>
Regards,
Dario
Jan Beulich June 7, 2017, 2:46 p.m. UTC | #3
>>> On 01.06.17 at 19:33, <dario.faggioli@citrix.com> wrote:
> In fact, when calling __trace_var() directly, we can
> assume that tb_init_done has been checked to be true,
> and the if is hence redundant.

The "assume" here worries me: What if there's a single place
somewhere that a grep can't easily find where no check is
present? Is it certain that ...

> @@ -691,7 +691,8 @@ void __trace_var(u32 event, bool_t cycles, unsigned int extra,
>      unsigned int extra_word;
>      bool_t started_below_highwater;
>  
> -    if( !tb_init_done )
> +    /* If the event is not interesting, bail, as early as possible */
> +    if ( (tb_event_mask & event) == 0 )
>          return;

... this check would always be false then (i.e. tb_event_mask is
always zero) in that case?

Jan
Dario Faggioli June 7, 2017, 3:55 p.m. UTC | #4
On Wed, 2017-06-07 at 08:46 -0600, Jan Beulich wrote:
> > > > On 01.06.17 at 19:33, <dario.faggioli@citrix.com> wrote:
> > 
> > In fact, when calling __trace_var() directly, we can
> > assume that tb_init_done has been checked to be true,
> > and the if is hence redundant.
> 
> The "assume" here worries me: What if there's a single place
> somewhere that a grep can't easily find where no check is
> present? Is it certain that ...
> 
> > @@ -691,7 +691,8 @@ void __trace_var(u32 event, bool_t cycles,
> > unsigned int extra,
> >      unsigned int extra_word;
> >      bool_t started_below_highwater;
> >  
> > -    if( !tb_init_done )
> > +    /* If the event is not interesting, bail, as early as possible
> > */
> > +    if ( (tb_event_mask & event) == 0 )
> >          return;
> 
> ... this check would always be false then (i.e. tb_event_mask is
> always zero) in that case?
> 
As said when replying to Andrew, I originally put an ASSERT() there.

That made me realize, though, that the existing if(!tb_init_done) is
ineffective and potentially racy (or, if you want to be kind "it's a
best effort kind of measure") already.

In fact, even right now, without my patches, we don't hold the tracing
lock when we execute that if. Nothing prevents tb_init_done to become 0
_right_after_ we saw it being 1 and decide to go ahead.

This, to me, looks like an even more compelling reason to remove it.
But I think I can improve the commit message so that it explains this
thing that I just said above too.

Thanks and Regards,
Dario
Jan Beulich June 7, 2017, 4:06 p.m. UTC | #5
>>> On 07.06.17 at 17:55, <dario.faggioli@citrix.com> wrote:
> On Wed, 2017-06-07 at 08:46 -0600, Jan Beulich wrote:
>> > > > On 01.06.17 at 19:33, <dario.faggioli@citrix.com> wrote:
>> > 
>> > In fact, when calling __trace_var() directly, we can
>> > assume that tb_init_done has been checked to be true,
>> > and the if is hence redundant.
>> 
>> The "assume" here worries me: What if there's a single place
>> somewhere that a grep can't easily find where no check is
>> present? Is it certain that ...
>> 
>> > @@ -691,7 +691,8 @@ void __trace_var(u32 event, bool_t cycles,
>> > unsigned int extra,
>> >      unsigned int extra_word;
>> >      bool_t started_below_highwater;
>> >  
>> > -    if( !tb_init_done )
>> > +    /* If the event is not interesting, bail, as early as possible
>> > */
>> > +    if ( (tb_event_mask & event) == 0 )
>> >          return;
>> 
>> ... this check would always be false then (i.e. tb_event_mask is
>> always zero) in that case?
>> 
> As said when replying to Andrew, I originally put an ASSERT() there.
> 
> That made me realize, though, that the existing if(!tb_init_done) is
> ineffective and potentially racy (or, if you want to be kind "it's a
> best effort kind of measure") already.
> 
> In fact, even right now, without my patches, we don't hold the tracing
> lock when we execute that if. Nothing prevents tb_init_done to become 0
> _right_after_ we saw it being 1 and decide to go ahead.
> 
> This, to me, looks like an even more compelling reason to remove it.
> But I think I can improve the commit message so that it explains this
> thing that I just said above too.

Well, my question wasn't about a possible race (as the code would
need to be able to deal with that no matter what change you do
here), but about the case where tb_init_done has never been set.
Would tb_event_mask be reliably zero in that case, no matter
what other operations may have been performed?

Jan
George Dunlap June 8, 2017, 2:34 p.m. UTC | #6
On 07/06/17 17:06, Jan Beulich wrote:
>>>> On 07.06.17 at 17:55, <dario.faggioli@citrix.com> wrote:
>> On Wed, 2017-06-07 at 08:46 -0600, Jan Beulich wrote:
>>>>>> On 01.06.17 at 19:33, <dario.faggioli@citrix.com> wrote:
>>>>
>>>> In fact, when calling __trace_var() directly, we can
>>>> assume that tb_init_done has been checked to be true,
>>>> and the if is hence redundant.
>>>
>>> The "assume" here worries me: What if there's a single place
>>> somewhere that a grep can't easily find where no check is
>>> present? Is it certain that ...
>>>
>>>> @@ -691,7 +691,8 @@ void __trace_var(u32 event, bool_t cycles,
>>>> unsigned int extra,
>>>>      unsigned int extra_word;
>>>>      bool_t started_below_highwater;
>>>>  
>>>> -    if( !tb_init_done )
>>>> +    /* If the event is not interesting, bail, as early as possible
>>>> */
>>>> +    if ( (tb_event_mask & event) == 0 )
>>>>          return;
>>>
>>> ... this check would always be false then (i.e. tb_event_mask is
>>> always zero) in that case?
>>>
>> As said when replying to Andrew, I originally put an ASSERT() there.
>>
>> That made me realize, though, that the existing if(!tb_init_done) is
>> ineffective and potentially racy (or, if you want to be kind "it's a
>> best effort kind of measure") already.
>>
>> In fact, even right now, without my patches, we don't hold the tracing
>> lock when we execute that if. Nothing prevents tb_init_done to become 0
>> _right_after_ we saw it being 1 and decide to go ahead.
>>
>> This, to me, looks like an even more compelling reason to remove it.
>> But I think I can improve the commit message so that it explains this
>> thing that I just said above too.
> 
> Well, my question wasn't about a possible race (as the code would
> need to be able to deal with that no matter what change you do
> here), but about the case where tb_init_done has never been set.
> Would tb_event_mask be reliably zero in that case, no matter
> what other operations may have been performed?

Looks like tb_event_mask can be set even if opt_tbuf_size == 0; so yes,
if someone enabled the event mask before allocating any buffers, *and*
there was a bug where __trace_var() was called without first checking
tb_init_done, then we might get past this point.

But it looks like that's still OK -- we'll then bail when our bit in
tb_cpu_mask isn't set, or finally bail when we find t_bufs to be zero.

 -George
George Dunlap June 8, 2017, 2:37 p.m. UTC | #7
On 01/06/17 18:33, Dario Faggioli wrote:
> In fact, when calling __trace_var() directly, we can
> assume that tb_init_done has been checked to be true,
> and the if is hence redundant.

You probably want to adjust the comment before the smp_rmb() that
mentions tb_init_done as well.

Other than that, looks good.

 -George
diff mbox

Patch

diff --git a/xen/common/trace.c b/xen/common/trace.c
index 4fedc26..f29cd4c 100644
--- a/xen/common/trace.c
+++ b/xen/common/trace.c
@@ -311,7 +311,7 @@  static int tb_set_size(unsigned int pages)
 
 int trace_will_trace_event(u32 event)
 {
-    if ( !tb_init_done )
+    if ( likely(!tb_init_done) )
         return 0;
 
     /*
@@ -691,7 +691,8 @@  void __trace_var(u32 event, bool_t cycles, unsigned int extra,
     unsigned int extra_word;
     bool_t started_below_highwater;
 
-    if( !tb_init_done )
+    /* If the event is not interesting, bail, as early as possible */
+    if ( (tb_event_mask & event) == 0 )
         return;
 
     /* Convert byte count into word count, rounding up */
@@ -705,9 +706,6 @@  void __trace_var(u32 event, bool_t cycles, unsigned int extra,
     /* Round size up to nearest word */
     extra = extra_word * sizeof(u32);
 
-    if ( (tb_event_mask & event) == 0 )
-        return;
-
     /* match class */
     if ( ((tb_event_mask >> TRC_CLS_SHIFT) & (event >> TRC_CLS_SHIFT)) == 0 )
         return;