From: Steven Rostedt <rostedt@goodmis.org>
To: Andrea Parri <parri.andrea@gmail.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
Subject: Re: [RFC PATCH] ftrace: Fix lost recursion records in ftrace_record_recursion()
Date: Wed, 14 Oct 2026 17:28:21 -0400 [thread overview]
Message-ID: <20261014172821.2b1fde91@gandalf.local.home> (raw)
In-Reply-To: <20260917090217.6738-1-parri.andrea@gmail.com>
On Thu, 17 Sep 2026 11:02:17 +0200
Andrea Parri <parri.andrea@gmail.com> wrote:
> cached_function is set to the current ip before the cmpxchg() that
> claims a slot in recursed_functions[]. When that cmpxchg() loses a
> race for the same slot, the code bumps index and retries via "goto
> again", but the retry immediately matches its own cached_function
> write and returns without ever reaching the bumped-index cmpxchg().
>
> Concretely, for two writers racing on the same index:
>
> CPU 0 (ip = A) CPU 1 (ip = B)
> -------------- --------------
> cmpxchg(&recursed_functions[index].ip,
> 0, B); // succeeds
> cached_function = A;
> cmpxchg(&recursed_functions[index].ip,
> 0, A);
> // fails, old == B
> index++;
> goto again;
> if (A == cached_function) // true: matches A's own store
> return; // A is dropped, not retried
>
> Once dropped this way, cached_function stays set to A, so every later
> recursion of A also hits the fast path and returns before reaching the
> retry, until some unrelated ip overwrites the cache.
>
> Only set cached_function once the record is confirmed present, either
> because a concurrent writer already added it or because this writer
> just claimed the slot. The retry path leaves it untouched, so it no
> longer matches against a value it just wrote for itself.
>
Good catch!
> Fixes: 773c16705058e ("ftrace: Add recording of functions that caused recursion")
> Assisted-by: LLM
> Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
> ---
> Can't claim to fully understand the logic behind ftrace_record_recursion().
> Notably, its smp_mb__after_atomic() calls and the lack of barrier comments
> both look suspicious to my LKMM-trained eyes. ;) Hence the RFC.
Well, the nr_records is updated via a cmpxchg() which itself is a full
memory barrier. The smp_mb__after_atomic() calls are to make sure the value
read is in sync with other CPUs before doing the compares. It also syncs
with the clearing function that is disabling and the writes, clears it, and
enables it (the -1 updates).
Otherwise, IIUC, the atomic_read() itself doesn't guarantee that what you
read is what is in memory without explicitly using the smp_mb__after_atomic().
> ---
> kernel/trace/trace_recursion_record.c | 19 +++++++++++--------
> 1 file changed, 11 insertions(+), 8 deletions(-)
>
> diff --git a/kernel/trace/trace_recursion_record.c b/kernel/trace/trace_recursion_record.c
> index bac4bc844ccd8..f42089c30f53c 100644
> --- a/kernel/trace/trace_recursion_record.c
> +++ b/kernel/trace/trace_recursion_record.c
> @@ -17,8 +17,8 @@ static struct recursed_functions recursed_functions[CONFIG_FTRACE_RECORD_RECURSI
> static atomic_t nr_records;
>
> /*
> - * Cache the last found function. Yes, updates to this is racey, but
> - * so is memory cache ;-)
> + * Cache the last function confirmed present in recursed_functions[].
> + * Updates to this are racy, but this is only a best-effort cache.
> */
> static unsigned long cached_function;
>
> @@ -67,24 +67,27 @@ void ftrace_record_recursion(unsigned long ip, unsigned long parent_ip)
> }
> }
>
> - cached_function = ip;
> -
> /*
> * We only want to add a function if it hasn't been added before.
> - * Add to the current location before incrementing the count.
> - * If it fails to add, then increment the index (save in i)
> - * and try again.
> + * Claim the current slot before incrementing the count. If the slot
> + * is occupied by another function, advance to the next slot and retry.
> + *
> + * Do not update cached_function until ip is known to be present;
> + * otherwise the retry would match its own cache update.
> */
> old = cmpxchg(&recursed_functions[index].ip, 0, ip);
> if (old != 0) {
> /* Did something else already added this for us? */
> - if (old == ip)
> + if (old == ip) {
> + cached_function = ip;
> return;
> + }
I think we do not need the above. If old == ip, then whatever set it before
should have updated the cached_function.
-- Steve
> /* Try the next location (use i for the next index) */
> index++;
> goto again;
> }
>
> + cached_function = ip;
> recursed_functions[index].parent_ip = parent_ip;
>
> /*
next prev parent reply other threads:[~2026-10-14 21:28 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-17 9:02 [RFC PATCH] ftrace: Fix lost recursion records in ftrace_record_recursion() Andrea Parri
2026-10-14 21:28 ` Steven Rostedt [this message]
2026-10-15 7:26 ` Andrea Parri
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261014172821.2b1fde91@gandalf.local.home \
--to=rostedt@goodmis.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=mhiramat@kernel.org \
--cc=parri.andrea@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox