Miroslav Benes 39bf93
From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
Miroslav Benes 39bf93
Date: Thu, 29 Oct 2020 19:35:08 -0400
Miroslav Benes 39bf93
Subject: ftrace: Handle tracing when switching between context
Miroslav Benes 39bf93
Git-commit: 726b3d3f141fba6f841d715fc4d8a4a84f02c02a
Miroslav Benes 39bf93
Patch-mainline: v5.10-rc3
Miroslav Benes 39bf93
References: git-fixes
Miroslav Benes 39bf93
Miroslav Benes 39bf93
When an interrupt or NMI comes in and switches the context, there's a delay
Miroslav Benes 39bf93
from when the preempt_count() shows the update. As the preempt_count() is
Miroslav Benes 39bf93
used to detect recursion having each context have its own bit get set when
Miroslav Benes 39bf93
tracing starts, and if that bit is already set, it is considered a recursion
Miroslav Benes 39bf93
and the function exits. But if this happens in that section where context
Miroslav Benes 39bf93
has changed but preempt_count() has not been updated, this will be
Miroslav Benes 39bf93
incorrectly flagged as a recursion.
Miroslav Benes 39bf93
Miroslav Benes 39bf93
To handle this case, create another bit call TRANSITION and test it if the
Miroslav Benes 39bf93
current context bit is already set. Flag the call as a recursion if the
Miroslav Benes 39bf93
TRANSITION bit is already set, and if not, set it and continue. The
Miroslav Benes 39bf93
TRANSITION bit will be cleared normally on the return of the function that
Miroslav Benes 39bf93
set it, or if the current context bit is clear, set it and clear the
Miroslav Benes 39bf93
TRANSITION bit to allow for another transition between the current context
Miroslav Benes 39bf93
and an even higher one.
Miroslav Benes 39bf93
Miroslav Benes 39bf93
Cc: stable@vger.kernel.org
Miroslav Benes 39bf93
Fixes: edc15cafcbfa3 ("tracing: Avoid unnecessary multiple recursion checks")
Miroslav Benes 39bf93
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Miroslav Benes 39bf93
Acked-by: Miroslav Benes <mbenes@suse.cz>
Miroslav Benes 39bf93
---
Miroslav Benes 39bf93
 kernel/trace/trace.h          |   23 +++++++++++++++++++++--
Miroslav Benes 39bf93
 kernel/trace/trace_selftest.c |    9 +++++++--
Miroslav Benes 39bf93
 2 files changed, 28 insertions(+), 4 deletions(-)
Miroslav Benes 39bf93
Miroslav Benes 39bf93
--- a/kernel/trace/trace.h
Miroslav Benes 39bf93
+++ b/kernel/trace/trace.h
Miroslav Benes 39bf93
@@ -497,6 +497,12 @@ enum {
Miroslav Benes 39bf93
  * can only be modified by current, we can reuse trace_recursion.
Miroslav Benes 39bf93
  */
Miroslav Benes 39bf93
 	TRACE_IRQ_BIT,
Miroslav Benes 39bf93
+
Miroslav Benes 39bf93
+	/*
Miroslav Benes 39bf93
+	 * When transitioning between context, the preempt_count() may
Miroslav Benes 39bf93
+	 * not be correct. Allow for a single recursion to cover this case.
Miroslav Benes 39bf93
+	 */
Miroslav Benes 39bf93
+	TRACE_TRANSITION_BIT,
Miroslav Benes 39bf93
 };
Miroslav Benes 39bf93
 
Miroslav Benes 39bf93
 #define trace_recursion_set(bit)	do { (current)->trace_recursion |= (1<<(bit)); } while (0)
Miroslav Benes 39bf93
@@ -541,8 +547,21 @@ static __always_inline int trace_test_an
Miroslav Benes 39bf93
 		return 0;
Miroslav Benes 39bf93
 
Miroslav Benes 39bf93
 	bit = trace_get_context_bit() + start;
Miroslav Benes 39bf93
-	if (unlikely(val & (1 << bit)))
Miroslav Benes 39bf93
-		return -1;
Miroslav Benes 39bf93
+	if (unlikely(val & (1 << bit))) {
Miroslav Benes 39bf93
+		/*
Miroslav Benes 39bf93
+		 * It could be that preempt_count has not been updated during
Miroslav Benes 39bf93
+		 * a switch between contexts. Allow for a single recursion.
Miroslav Benes 39bf93
+		 */
Miroslav Benes 39bf93
+		bit = TRACE_TRANSITION_BIT;
Miroslav Benes 39bf93
+		if (trace_recursion_test(bit))
Miroslav Benes 39bf93
+			return -1;
Miroslav Benes 39bf93
+		trace_recursion_set(bit);
Miroslav Benes 39bf93
+		barrier();
Miroslav Benes 39bf93
+		return bit + 1;
Miroslav Benes 39bf93
+	}
Miroslav Benes 39bf93
+
Miroslav Benes 39bf93
+	/* Normal check passed, clear the transition to allow it again */
Miroslav Benes 39bf93
+	trace_recursion_clear(TRACE_TRANSITION_BIT);
Miroslav Benes 39bf93
 
Miroslav Benes 39bf93
 	val |= 1 << bit;
Miroslav Benes 39bf93
 	current->trace_recursion = val;
Miroslav Benes 39bf93
--- a/kernel/trace/trace_selftest.c
Miroslav Benes 39bf93
+++ b/kernel/trace/trace_selftest.c
Miroslav Benes 39bf93
@@ -491,8 +491,13 @@ trace_selftest_function_recursion(void)
Miroslav Benes 39bf93
 	unregister_ftrace_function(&test_rec_probe);
Miroslav Benes 39bf93
 
Miroslav Benes 39bf93
 	ret = -1;
Miroslav Benes 39bf93
-	if (trace_selftest_recursion_cnt != 1) {
Miroslav Benes 39bf93
-		pr_cont("*callback not called once (%d)* ",
Miroslav Benes 39bf93
+	/*
Miroslav Benes 39bf93
+	 * Recursion allows for transitions between context,
Miroslav Benes 39bf93
+	 * and may call the callback twice.
Miroslav Benes 39bf93
+	 */
Miroslav Benes 39bf93
+	if (trace_selftest_recursion_cnt != 1 &&
Miroslav Benes 39bf93
+	    trace_selftest_recursion_cnt != 2) {
Miroslav Benes 39bf93
+		pr_cont("*callback not called once (or twice) (%d)* ",
Miroslav Benes 39bf93
 			trace_selftest_recursion_cnt);
Miroslav Benes 39bf93
 		goto out;
Miroslav Benes 39bf93
 	}