On Wed, Jun 05, 2024 at 02:03:36PM -0400, Steven Rostedt wrote:
> From: "Steven Rostedt (Google)" <rost...@goodmis.org>
> 
> While adding comments to the function __ftrace_hash_rec_update() and
> trying to describe in detail what the parameter for "ftrace_hash" does, I
> realized that it basically does exactly the same thing (but differently)
> if it is set or not!

Typo: "ftrace_hash" should be "filter_hash", and likewise in the commit
title.

> If it is set, the idea was the ops->filter_hash was being updated, and the
> code should focus on the functions that are in the ops->filter_hash and
> add them. But it still had to pay attention to the functions in the
> ops->notrace_hash, to ignore them.
> 
> If it was cleared, it focused on the ops->notrace_hash, and would add
> functions that were not in the ops->notrace_hash but would still keep
> functions in the "ops->filter_hash". Basically doing the same thing.
> 
> In reality, the __ftrace_hash_rec_update() only needs to either remove the
> functions associated to the give ops (if "inc" is set) or remove them (if
> "inc" is cleared). It has to pay attention to both the filter_hash and
> notrace_hash regardless.

AFAICT, we actually remove a latent bug here, but that bug wasn't
reachable because we never call __ftrace_hash_rec_update() with
"filter_hash" clear.

Before this patch, if we did call __ftrace_hash_rec_update() with
"filter_hash" clear, we'd setup:

        all = false;
        ...
        if (filter_hash) {
                ...
        } else {
                hash = ops->func_hash->notrace_hash;
                other_hash = ops->func_hash->filter_hash;
        }

... and then at the tail of the loop where we do:

        count++;

        [...] 

        /* Shortcut, if we handled all records, we are done. */
        if (!all && count == hash->count) {
                pr_info("HARK: stopping after %d recs\n", count);
                return update;
        }

... we'd be checking whether we've updated notrace_hash->count entries,
when that could be smaller than the number of entries we actually need
to update (e.g. if the notrace hash contains a single entry, but the
filter_hash contained more).

As above, we never actually hit that because we never call with
"filter_hash" clear, so it might be good to explicitly say that before
this patch we never actually call __ftrace_hash_rec_update() with
"filter_hash" clear, and this is removing dead (and potentially broken)
code.

> Remove the "filter_hash" parameter from __filter_hash_rec_update() and
> comment the function for what it really is doing.
> 
> Signed-off-by: Steven Rostedt (Google) <rost...@goodmis.org>

FWIW, this looks good to me, and works in testing, so:

Reviewed-by: Mark Rutland <mark.rutl...@arm.com>
Tested-by: Mark Rutland <mark.rutl...@arm.com>

I have one comment below, but the above stands regardless.

[...]

> +/*
> + * This is the main engine to the ftrace updates to the dyn_ftrace records.
> + *
> + * It will iterate through all the available ftrace functions
> + * (the ones that ftrace can have callbacks to) and set the flags
> + * in the associated dyn_ftrace records.
> + *
> + * @inc: If true, the functions associated to @ops are added to
> + *       the dyn_ftrace records, otherwise they are removed.
> + */
>  static bool __ftrace_hash_rec_update(struct ftrace_ops *ops,
> -                                  int filter_hash,
>                                    bool inc)
>  {
>       struct ftrace_hash *hash;
> -     struct ftrace_hash *other_hash;
> +     struct ftrace_hash *notrace_hash;
>       struct ftrace_page *pg;
>       struct dyn_ftrace *rec;
>       bool update = false;
> @@ -1718,35 +1725,16 @@ static bool __ftrace_hash_rec_update(struct 
> ftrace_ops *ops,
>               return false;
>  
>       /*
> -      * In the filter_hash case:
>        *   If the count is zero, we update all records.
>        *   Otherwise we just update the items in the hash.
> -      *
> -      * In the notrace_hash case:
> -      *   We enable the update in the hash.
> -      *   As disabling notrace means enabling the tracing,
> -      *   and enabling notrace means disabling, the inc variable
> -      *   gets inversed.
>        */
> -     if (filter_hash) {
> -             hash = ops->func_hash->filter_hash;
> -             other_hash = ops->func_hash->notrace_hash;
> -             if (ftrace_hash_empty(hash))
> -                     all = true;
> -     } else {
> -             inc = !inc;
> -             hash = ops->func_hash->notrace_hash;
> -             other_hash = ops->func_hash->filter_hash;
> -             /*
> -              * If the notrace hash has no items,
> -              * then there's nothing to do.
> -              */
> -             if (ftrace_hash_empty(hash))
> -                     return false;
> -     }
> +     hash = ops->func_hash->filter_hash;
> +     notrace_hash = ops->func_hash->notrace_hash;
> +     if (ftrace_hash_empty(hash))
> +             all = true;
>  
>       do_for_each_ftrace_rec(pg, rec) {
> -             int in_other_hash = 0;
> +             int in_notrace_hash = 0;
>               int in_hash = 0;
>               int match = 0;
>  
> @@ -1758,26 +1746,17 @@ static bool __ftrace_hash_rec_update(struct 
> ftrace_ops *ops,
>                        * Only the filter_hash affects all records.
>                        * Update if the record is not in the notrace hash.
>                        */
> -                     if (!other_hash || !ftrace_lookup_ip(other_hash, 
> rec->ip))
> +                     if (!notrace_hash || !ftrace_lookup_ip(notrace_hash, 
> rec->ip))
>                               match = 1;
>               } else {
>                       in_hash = !!ftrace_lookup_ip(hash, rec->ip);
> -                     in_other_hash = !!ftrace_lookup_ip(other_hash, rec->ip);
> +                     in_notrace_hash = !!ftrace_lookup_ip(notrace_hash, 
> rec->ip);
>  
>                       /*
> -                      * If filter_hash is set, we want to match all functions
> -                      * that are in the hash but not in the other hash.
> -                      *
> -                      * If filter_hash is not set, then we are decrementing.
> -                      * That means we match anything that is in the hash
> -                      * and also in the other_hash. That is, we need to turn
> -                      * off functions in the other hash because they are 
> disabled
> -                      * by this hash.
> +                      * We want to match all functions that are in the hash 
> but
> +                      * not in the other hash.
>                        */
> -                     if (filter_hash && in_hash && !in_other_hash)
> -                             match = 1;
> -                     else if (!filter_hash && in_hash &&
> -                              (in_other_hash || 
> ftrace_hash_empty(other_hash)))
> +                     if (in_hash && !in_notrace_hash)
>                               match = 1;
>               }
>               if (!match)

I wonder how much the (subsequent) shortcut for count == hash->count
matters in practice, because if we were happy to get rid of that, we
could get rid of 'all', 'count', 'in_hash', 'in_notrace_hash', and
simplify the above down to:

        do_for_each_ftrace_rec(pg, rec) {
                if (skip_record(rec))
                        continue;

                /*
                 * When the hash is empty we update all records.
                 * Otherwise we just update the items in the hash.
                 */
                if (!ftrace_hash_empty(hash) &&
                    !ftrace_lookup_ip(hash, rec->ip))
                        continue;

                if (!ftrace_lookup_ip(notrace_hash, rec->ip))
                        continue;

                [...]
                        /* do the actual updates here */
                [...]

        } while_for_each_ftrace_rec();

... which'd be easier to follow.

FWIW, diff atop this patch below. It passes the selftests in my local
testing, but I understand if we'd rather keep the shortcut.

Mark.

---->8----
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index f1aab82fa465..165e8dd4f894 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1714,49 +1714,27 @@ static bool __ftrace_hash_rec_update(struct ftrace_ops 
*ops,
        struct ftrace_page *pg;
        struct dyn_ftrace *rec;
        bool update = false;
-       int count = 0;
-       int all = false;
 
        /* Only update if the ops has been registered */
        if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
                return false;
 
-       /*
-        *   If the count is zero, we update all records.
-        *   Otherwise we just update the items in the hash.
-        */
        hash = ops->func_hash->filter_hash;
        notrace_hash = ops->func_hash->notrace_hash;
-       if (ftrace_hash_empty(hash))
-               all = true;
 
        do_for_each_ftrace_rec(pg, rec) {
-               int in_notrace_hash = 0;
-               int in_hash = 0;
-               int match = 0;
-
                if (skip_record(rec))
                        continue;
 
-               if (all) {
-                       /*
-                        * Only the filter_hash affects all records.
-                        * Update if the record is not in the notrace hash.
-                        */
-                       if (!notrace_hash || !ftrace_lookup_ip(notrace_hash, 
rec->ip))
-                               match = 1;
-               } else {
-                       in_hash = !!ftrace_lookup_ip(hash, rec->ip);
-                       in_notrace_hash = !!ftrace_lookup_ip(notrace_hash, 
rec->ip);
+               /*
+                * If the hash is empty, we update all records.
+                * Otherwise we just update the items in the hash.
+                */
+               if (!ftrace_hash_empty(hash) &&
+                   !ftrace_lookup_ip(hash, rec->ip))
+                       continue;
 
-                       /*
-                        * We want to match all functions that are in the hash 
but
-                        * not in the other hash.
-                        */
-                       if (in_hash && !in_notrace_hash)
-                               match = 1;
-               }
-               if (!match)
+               if (ftrace_lookup_ip(notrace_hash, rec->ip))
                        continue;
 
                if (inc) {
@@ -1846,14 +1824,9 @@ static bool __ftrace_hash_rec_update(struct ftrace_ops 
*ops,
                else
                        rec->flags &= ~FTRACE_FL_CALL_OPS;
 
-               count++;
-
                /* Must match FTRACE_UPDATE_CALLS in ftrace_modify_all_code() */
                update |= ftrace_test_record(rec, true) != FTRACE_UPDATE_IGNORE;
 
-               /* Shortcut, if we handled all records, we are done. */
-               if (!all && count == hash->count)
-                       return update;
        } while_for_each_ftrace_rec();
 
        return update;



Reply via email to