lkml.org 
[lkml]   [2020]   [Nov]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.19 43/71] ftrace: Handle tracing when switching between context
    Date
    From: Steven Rostedt (VMware) <rostedt@goodmis.org>

    commit 726b3d3f141fba6f841d715fc4d8a4a84f02c02a upstream.

    When an interrupt or NMI comes in and switches the context, there's a delay
    from when the preempt_count() shows the update. As the preempt_count() is
    used to detect recursion having each context have its own bit get set when
    tracing starts, and if that bit is already set, it is considered a recursion
    and the function exits. But if this happens in that section where context
    has changed but preempt_count() has not been updated, this will be
    incorrectly flagged as a recursion.

    To handle this case, create another bit call TRANSITION and test it if the
    current context bit is already set. Flag the call as a recursion if the
    TRANSITION bit is already set, and if not, set it and continue. The
    TRANSITION bit will be cleared normally on the return of the function that
    set it, or if the current context bit is clear, set it and clear the
    TRANSITION bit to allow for another transition between the current context
    and an even higher one.

    Cc: stable@vger.kernel.org
    Fixes: edc15cafcbfa3 ("tracing: Avoid unnecessary multiple recursion checks")
    Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    kernel/trace/trace.h | 23 +++++++++++++++++++++--
    kernel/trace/trace_selftest.c | 9 +++++++--
    2 files changed, 28 insertions(+), 4 deletions(-)

    --- a/kernel/trace/trace.h
    +++ b/kernel/trace/trace.h
    @@ -534,6 +534,12 @@ enum {

    TRACE_GRAPH_DEPTH_START_BIT,
    TRACE_GRAPH_DEPTH_END_BIT,
    +
    + /*
    + * When transitioning between context, the preempt_count() may
    + * not be correct. Allow for a single recursion to cover this case.
    + */
    + TRACE_TRANSITION_BIT,
    };

    #define trace_recursion_set(bit) do { (current)->trace_recursion |= (1<<(bit)); } while (0)
    @@ -588,8 +594,21 @@ static __always_inline int trace_test_an
    return 0;

    bit = trace_get_context_bit() + start;
    - if (unlikely(val & (1 << bit)))
    - return -1;
    + if (unlikely(val & (1 << bit))) {
    + /*
    + * It could be that preempt_count has not been updated during
    + * a switch between contexts. Allow for a single recursion.
    + */
    + bit = TRACE_TRANSITION_BIT;
    + if (trace_recursion_test(bit))
    + return -1;
    + trace_recursion_set(bit);
    + barrier();
    + return bit + 1;
    + }
    +
    + /* Normal check passed, clear the transition to allow it again */
    + trace_recursion_clear(TRACE_TRANSITION_BIT);

    val |= 1 << bit;
    current->trace_recursion = val;
    --- a/kernel/trace/trace_selftest.c
    +++ b/kernel/trace/trace_selftest.c
    @@ -492,8 +492,13 @@ trace_selftest_function_recursion(void)
    unregister_ftrace_function(&test_rec_probe);

    ret = -1;
    - if (trace_selftest_recursion_cnt != 1) {
    - pr_cont("*callback not called once (%d)* ",
    + /*
    + * Recursion allows for transitions between context,
    + * and may call the callback twice.
    + */
    + if (trace_selftest_recursion_cnt != 1 &&
    + trace_selftest_recursion_cnt != 2) {
    + pr_cont("*callback not called once (or twice) (%d)* ",
    trace_selftest_recursion_cnt);
    goto out;
    }

    \
     
     \ /
      Last update: 2020-11-09 14:10    [W:4.896 / U:0.064 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site