|
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 |
}
|