From: Steven Rostedt <rostedt@goodmis.org>
To: linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
Cc: Masami Hiramatsu <mhiramat@kernel.org>,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
Josh Poimboeuf <jpoimboe@kernel.org>,
Peter Zijlstra <peterz@infradead.org>,
x86@kernel.org, Jiri Olsa <jolsa@kernel.org>,
Namhyung Kim <namhyung@kernel.org>
Subject: [PATCH v7 10/17] unwind_user/deferred: Make unwind deferral requests NMI-safe
Date: Fri, 02 May 2025 12:47:56 -0400 [thread overview]
Message-ID: <20250502165009.069806229@goodmis.org> (raw)
In-Reply-To: 20250502164746.178864972@goodmis.org
From: Josh Poimboeuf <jpoimboe@kernel.org>
Make unwind_deferred_request() NMI-safe so tracers in NMI context can
call it to get the cookie immediately rather than have to do the fragile
"schedule irq work and then call unwind_deferred_request()" dance.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Changes since v6: https://lore.kernel.org/20250424192612.669992559@goodmis.org
- Have unwind_deferred_request() return positive if already queued.
include/linux/unwind_deferred_types.h | 1 +
kernel/unwind/deferred.c | 100 ++++++++++++++++++++++----
2 files changed, 89 insertions(+), 12 deletions(-)
diff --git a/include/linux/unwind_deferred_types.h b/include/linux/unwind_deferred_types.h
index 33373c32c221..8f47d77ddda0 100644
--- a/include/linux/unwind_deferred_types.h
+++ b/include/linux/unwind_deferred_types.h
@@ -10,6 +10,7 @@ struct unwind_cache {
struct unwind_task_info {
struct unwind_cache cache;
u64 cookie;
+ u64 nmi_cookie;
struct callback_head work;
int pending;
};
diff --git a/kernel/unwind/deferred.c b/kernel/unwind/deferred.c
index b93ad97daf94..d86ea82a8915 100644
--- a/kernel/unwind/deferred.c
+++ b/kernel/unwind/deferred.c
@@ -47,23 +47,47 @@ static u64 ctx_to_cookie(u64 cpu, u64 ctx)
/*
* Read the task context cookie, first initializing it if this is the first
- * call to get_cookie() since the most recent entry from user.
+ * call to get_cookie() since the most recent entry from user. This has to be
+ * done carefully to coordinate with unwind_deferred_request_nmi().
*/
static u64 get_cookie(struct unwind_task_info *info)
{
u64 ctx_ctr;
u64 cookie;
- u64 cpu;
guard(irqsave)();
- cookie = info->cookie;
+ cookie = READ_ONCE(info->cookie);
if (cookie)
return cookie;
- cpu = raw_smp_processor_id();
- ctx_ctr = __this_cpu_inc_return(unwind_ctx_ctr);
- info->cookie = ctx_to_cookie(cpu, ctx_ctr);
+ ctx_ctr = __this_cpu_read(unwind_ctx_ctr);
+
+ /* Read ctx_ctr before info->nmi_cookie */
+ barrier();
+
+ cookie = READ_ONCE(info->nmi_cookie);
+ if (cookie) {
+ /*
+ * This is the first call to get_cookie() since an NMI handler
+ * first wrote it to info->nmi_cookie. Sync it.
+ */
+ WRITE_ONCE(info->cookie, cookie);
+ WRITE_ONCE(info->nmi_cookie, 0);
+ return cookie;
+ }
+
+ /*
+ * Write info->cookie. It's ok to race with an NMI here. The value of
+ * the cookie is based on ctx_ctr from before the NMI could have
+ * incremented it. The result will be the same even if cookie or
+ * ctx_ctr end up getting written twice.
+ */
+ cookie = ctx_to_cookie(raw_smp_processor_id(), ctx_ctr + 1);
+ WRITE_ONCE(info->cookie, cookie);
+ WRITE_ONCE(info->nmi_cookie, 0);
+ barrier();
+ __this_cpu_write(unwind_ctx_ctr, ctx_ctr + 1);
return info->cookie;
}
@@ -139,6 +163,51 @@ static void unwind_deferred_task_work(struct callback_head *head)
WRITE_ONCE(info->cookie, 0);
}
+static int unwind_deferred_request_nmi(struct unwind_work *work, u64 *cookie)
+{
+ struct unwind_task_info *info = ¤t->unwind_info;
+ bool inited_cookie = false;
+ int ret;
+
+ *cookie = info->cookie;
+ if (!*cookie) {
+ /*
+ * This is the first unwind request since the most recent entry
+ * from user. Initialize the task cookie.
+ *
+ * Don't write to info->cookie directly, otherwise it may get
+ * cleared if the NMI occurred in the kernel during early entry
+ * or late exit before the task work gets to run. Instead, use
+ * info->nmi_cookie which gets synced later by get_cookie().
+ */
+ if (!info->nmi_cookie) {
+ u64 cpu = raw_smp_processor_id();
+ u64 ctx_ctr;
+
+ ctx_ctr = __this_cpu_inc_return(unwind_ctx_ctr);
+ info->nmi_cookie = ctx_to_cookie(cpu, ctx_ctr);
+
+ inited_cookie = true;
+ }
+
+ *cookie = info->nmi_cookie;
+ }
+
+ if (info->pending)
+ return 1;
+
+ ret = task_work_add(current, &info->work, TWA_NMI_CURRENT);
+ if (ret) {
+ if (inited_cookie)
+ info->nmi_cookie = 0;
+ return ret;
+ }
+
+ info->pending = 1;
+
+ return 0;
+}
+
/*
* Schedule a user space unwind to be done in task work before exiting the
* kernel.
@@ -160,31 +229,38 @@ static void unwind_deferred_task_work(struct callback_head *head)
int unwind_deferred_request(struct unwind_work *work, u64 *cookie)
{
struct unwind_task_info *info = ¤t->unwind_info;
+ int pending;
int ret;
*cookie = 0;
- if (WARN_ON_ONCE(in_nmi()))
- return -EINVAL;
-
if ((current->flags & (PF_KTHREAD | PF_EXITING)) ||
!user_mode(task_pt_regs(current)))
return -EINVAL;
+ if (in_nmi())
+ return unwind_deferred_request_nmi(work, cookie);
+
guard(irqsave)();
*cookie = get_cookie(info);
/* callback already pending? */
- if (info->pending)
+ pending = READ_ONCE(info->pending);
+ if (pending)
+ return 1;
+
+ /* Claim the work unless an NMI just now swooped in to do so. */
+ if (!try_cmpxchg(&info->pending, &pending, 1))
return 1;
/* The work has been claimed, now schedule it. */
ret = task_work_add(current, &info->work, TWA_RESUME);
- if (WARN_ON_ONCE(ret))
+ if (WARN_ON_ONCE(ret)) {
+ WRITE_ONCE(info->pending, 0);
return ret;
+ }
- info->pending = 1;
return 0;
}
--
2.47.2
next prev parent reply other threads:[~2025-05-02 16:50 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-02 16:47 [PATCH v7 00/17] unwind_user: perf: x86: Deferred unwinding infrastructure Steven Rostedt
2025-05-02 16:47 ` [PATCH v7 01/17] unwind_user: Add user space unwinding API Steven Rostedt
2025-05-04 9:30 ` Ingo Molnar
2025-05-04 16:43 ` Steven Rostedt
2025-05-04 17:53 ` Josh Poimboeuf
2025-05-02 16:47 ` [PATCH v7 02/17] unwind_user: Add frame pointer support Steven Rostedt
2025-05-02 16:47 ` [PATCH v7 03/17] unwind_user/x86: Enable frame pointer unwinding on x86 Steven Rostedt
2025-05-02 16:47 ` [PATCH v7 04/17] perf/x86: Rename and move get_segment_base() and make it global Steven Rostedt
2025-05-02 16:47 ` [PATCH v7 05/17] unwind_user: Add compat mode frame pointer support Steven Rostedt
2025-05-02 16:47 ` [PATCH v7 06/17] unwind_user/x86: Enable compat mode frame pointer unwinding on x86 Steven Rostedt
2025-05-02 16:47 ` [PATCH v7 07/17] unwind_user/deferred: Add unwind_deferred_trace() Steven Rostedt
2025-05-02 16:47 ` [PATCH v7 08/17] unwind_user/deferred: Add unwind cache Steven Rostedt
2025-05-04 9:37 ` Ingo Molnar
2025-05-04 16:21 ` Steven Rostedt
2025-05-02 16:47 ` [PATCH v7 09/17] unwind_user/deferred: Add deferred unwinding interface Steven Rostedt
2025-05-02 16:47 ` Steven Rostedt [this message]
2025-05-02 16:47 ` [PATCH v7 11/17] unwind deferred: Use bitmask to determine which callbacks to call Steven Rostedt
2025-05-09 1:36 ` Steven Rostedt
2025-05-02 16:47 ` [PATCH v7 12/17] unwind deferred: Use SRCU unwind_deferred_task_work() Steven Rostedt
2025-05-02 16:47 ` [PATCH v7 13/17] perf: Remove get_perf_callchain() init_nr argument Steven Rostedt
2025-05-02 16:48 ` [PATCH v7 14/17] perf: Have get_perf_callchain() return NULL if crosstask and user are set Steven Rostedt
2025-05-02 16:48 ` [PATCH v7 15/17] perf: Use current->flags & PF_KTHREAD instead of current->mm == NULL Steven Rostedt
2025-05-02 16:48 ` [PATCH v7 16/17] perf: Simplify get_perf_callchain() user logic Steven Rostedt
2025-05-02 16:48 ` [PATCH v7 17/17] perf: Skip user unwind if the task is a kernel thread Steven Rostedt
2025-05-04 9:41 ` [PATCH v7 00/17] unwind_user: perf: x86: Deferred unwinding infrastructure Ingo Molnar
2025-05-04 16:32 ` Steven Rostedt
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=20250502165009.069806229@goodmis.org \
--to=rostedt@goodmis.org \
--cc=jolsa@kernel.org \
--cc=jpoimboe@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=mhiramat@kernel.org \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=x86@kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.