From: Steven Rostedt <rostedt@goodmis.org>
To: linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
bpf@vger.kernel.org, x86@kernel.org
Cc: Masami Hiramatsu <mhiramat@kernel.org>,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
Josh Poimboeuf <jpoimboe@kernel.org>,
Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@kernel.org>, Jiri Olsa <jolsa@kernel.org>,
Namhyung Kim <namhyung@kernel.org>,
Thomas Gleixner <tglx@linutronix.de>,
Andrii Nakryiko <andrii@kernel.org>,
Indu Bhagat <indu.bhagat@oracle.com>,
"Jose E. Marchesi" <jemarch@gnu.org>,
Beau Belgrave <beaub@linux.microsoft.com>,
Jens Remus <jremus@linux.ibm.com>,
Linus Torvalds <torvalds@linux-foundation.org>,
Andrew Morton <akpm@linux-foundation.org>,
Jens Axboe <axboe@kernel.dk>
Subject: [PATCH v11 07/14] unwind_user/deferred: Make unwind deferral requests NMI-safe
Date: Wed, 25 Jun 2025 18:56:07 -0400 [thread overview]
Message-ID: <20250625225716.004109448@goodmis.org> (raw)
In-Reply-To: 20250625225600.555017347@goodmis.org
From: Steven Rostedt <rostedt@goodmis.org>
Make unwind_deferred_request() NMI-safe so tracers in NMI context can
call it and safely request a user space stacktrace when the task exits.
Note, this is only allowed for architectures that implement a safe 64 bit
cmpxchg. Which rules out some 32bit architectures and even some 64 bit
ones. If an architecture requests a deferred stack trace from NMI context
that does not support a safe NMI 64 bit cmpxchg, it will get an -EINVAL.
For those architectures, they would need another method (perhaps an
irqwork), to request a deferred user space stack trace. That can be dealt
with later if one of theses architectures require this feature.
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Changse since v10: https://lore.kernel.org/20250611010428.938845449@goodmis.org
- Reworked to simply use a 64bit cmpxchg to update the timestamp.
- Removed Josh Poimboeuf's authorship as the commit is completely
rewritten.
- Switch timestamp to local64_t type and pending to local_t type.
include/linux/unwind_deferred.h | 4 +-
include/linux/unwind_deferred_types.h | 7 ++-
kernel/unwind/deferred.c | 74 ++++++++++++++++++++++-----
3 files changed, 69 insertions(+), 16 deletions(-)
diff --git a/include/linux/unwind_deferred.h b/include/linux/unwind_deferred.h
index 26011b413142..718637777649 100644
--- a/include/linux/unwind_deferred.h
+++ b/include/linux/unwind_deferred.h
@@ -29,12 +29,12 @@ void unwind_deferred_cancel(struct unwind_work *work);
static __always_inline void unwind_reset_info(void)
{
/* Exit out early if this was never used */
- if (likely(!current->unwind_info.timestamp))
+ if (likely(!local64_read(¤t->unwind_info.timestamp)))
return;
if (current->unwind_info.cache)
current->unwind_info.cache->nr_entries = 0;
- current->unwind_info.timestamp = 0;
+ local64_set(¤t->unwind_info.timestamp, 0);
}
#else /* !CONFIG_UNWIND_USER */
diff --git a/include/linux/unwind_deferred_types.h b/include/linux/unwind_deferred_types.h
index 5df264cf81ad..0d722e877473 100644
--- a/include/linux/unwind_deferred_types.h
+++ b/include/linux/unwind_deferred_types.h
@@ -2,6 +2,9 @@
#ifndef _LINUX_UNWIND_USER_DEFERRED_TYPES_H
#define _LINUX_UNWIND_USER_DEFERRED_TYPES_H
+#include <asm/local64.h>
+#include <asm/local.h>
+
struct unwind_cache {
unsigned int nr_entries;
unsigned long entries[];
@@ -10,8 +13,8 @@ struct unwind_cache {
struct unwind_task_info {
struct unwind_cache *cache;
struct callback_head work;
- u64 timestamp;
- int pending;
+ local64_t timestamp;
+ local_t pending;
};
#endif /* _LINUX_UNWIND_USER_DEFERRED_TYPES_H */
diff --git a/kernel/unwind/deferred.c b/kernel/unwind/deferred.c
index d5f2c004a5b0..dd36e58c8cad 100644
--- a/kernel/unwind/deferred.c
+++ b/kernel/unwind/deferred.c
@@ -12,6 +12,35 @@
#include <linux/slab.h>
#include <linux/mm.h>
+/*
+ * For requesting a deferred user space stack trace from NMI context
+ * the architecture must support a 64bit safe cmpxchg in NMI context.
+ * For those architectures that do not have that, then it cannot ask
+ * for a deferred user space stack trace from an NMI context. If it
+ * does, then it will get -EINVAL.
+ */
+#if defined(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) && \
+ !defined(CONFIG_GENERIC_ATOMIC64)
+# define CAN_USE_IN_NMI 1
+static inline u64 assign_timestamp(struct unwind_task_info *info,
+ u64 timestamp)
+{
+ u64 old = 0;
+ if (!local64_try_cmpxchg(&info->timestamp, &old, timestamp))
+ timestamp = old;
+ return timestamp;
+}
+#else
+# define CAN_USE_IN_NMI 0
+static inline u64 assign_timestamp(struct unwind_task_info *info,
+ u64 timestamp)
+{
+ /* For archs that do not allow NMI here */
+ local64_set(&info->timestamp, timestamp);
+ return timestamp;
+}
+#endif
+
/* Make the cache fit in a 4K page */
#define UNWIND_MAX_ENTRIES \
((SZ_4K - sizeof(struct unwind_cache)) / sizeof(long))
@@ -31,12 +60,21 @@ static LIST_HEAD(callbacks);
*/
static u64 get_timestamp(struct unwind_task_info *info)
{
+ u64 timestamp;
+
lockdep_assert_irqs_disabled();
- if (!info->timestamp)
- info->timestamp = local_clock();
+ /*
+ * Note, the timestamp is generated on the first request.
+ * If it exists here, then the timestamp is earlier than
+ * this request and it means that this request will be
+ * valid for the stracktrace.
+ */
+ timestamp = local64_read(&info->timestamp);
+ if (timestamp)
+ return timestamp;
- return info->timestamp;
+ return assign_timestamp(info, local_clock());
}
/**
@@ -96,11 +134,11 @@ static void unwind_deferred_task_work(struct callback_head *head)
struct unwind_work *work;
u64 timestamp;
- if (WARN_ON_ONCE(!info->pending))
+ if (WARN_ON_ONCE(!local_read(&info->pending)))
return;
/* Allow work to come in again */
- WRITE_ONCE(info->pending, 0);
+ local_set(&info->pending, 0);
/*
* From here on out, the callback must always be called, even if it's
@@ -111,7 +149,7 @@ static void unwind_deferred_task_work(struct callback_head *head)
unwind_user_faultable(&trace);
- timestamp = info->timestamp;
+ timestamp = local64_read(&info->timestamp);
guard(mutex)(&callback_mutex);
list_for_each_entry(work, &callbacks, list) {
@@ -150,31 +188,43 @@ static void unwind_deferred_task_work(struct callback_head *head)
int unwind_deferred_request(struct unwind_work *work, u64 *timestamp)
{
struct unwind_task_info *info = ¤t->unwind_info;
+ long pending;
int ret;
*timestamp = 0;
- if (WARN_ON_ONCE(in_nmi()))
- return -EINVAL;
-
if ((current->flags & (PF_KTHREAD | PF_EXITING)) ||
!user_mode(task_pt_regs(current)))
return -EINVAL;
+ /* NMI requires having safe 64 bit cmpxchg operations */
+ if (!CAN_USE_IN_NMI && in_nmi())
+ return -EINVAL;
+
guard(irqsave)();
*timestamp = get_timestamp(info);
/* callback already pending? */
- if (info->pending)
+ pending = local_read(&info->pending);
+ if (pending)
return 1;
+ if (CAN_USE_IN_NMI) {
+ /* Claim the work unless an NMI just now swooped in to do so. */
+ if (!local_try_cmpxchg(&info->pending, &pending, 1))
+ return 1;
+ } else {
+ local_set(&info->pending, 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)) {
+ local_set(&info->pending, 0);
return ret;
+ }
- info->pending = 1;
return 0;
}
--
2.47.2
next prev parent reply other threads:[~2025-06-25 22:57 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-25 22:56 [PATCH v11 00/14] unwind_user: x86: Deferred unwinding infrastructure Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 01/14] unwind_user: Add user space unwinding API Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 02/14] unwind_user: Add frame pointer support Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 03/14] unwind_user: Add compat mode " Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 04/14] unwind_user/deferred: Add unwind_user_faultable() Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 05/14] unwind_user/deferred: Add unwind cache Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 06/14] unwind_user/deferred: Add deferred unwinding interface Steven Rostedt
2025-06-26 16:48 ` Steven Rostedt
2025-06-26 20:34 ` Steven Rostedt
2025-06-25 22:56 ` Steven Rostedt [this message]
2025-06-25 22:56 ` [PATCH v11 08/14] unwind deferred: Use bitmask to determine which callbacks to call Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 09/14] unwind deferred: Use SRCU unwind_deferred_task_work() Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 10/14] unwind: Clear unwind_mask on exit back to user space Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 11/14] unwind: Finish up unwind when a task exits Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 12/14] unwind_user/x86: Enable frame pointer unwinding on x86 Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 13/14] perf/x86: Rename and move get_segment_base() and make it global Steven Rostedt
2025-06-26 8:44 ` Ingo Molnar
2025-06-26 12:13 ` Steven Rostedt
2025-06-26 13:07 ` Peter Zijlstra
2025-06-26 15:13 ` Steven Rostedt
2025-06-25 22:56 ` [PATCH v11 14/14] unwind_user/x86: Enable compat mode frame pointer unwinding on x86 Steven Rostedt
2025-06-26 8:33 ` Ingo Molnar
2025-06-26 12:12 ` Steven Rostedt
2025-06-27 14:01 ` Steven Rostedt
2025-06-27 16:23 ` Linus Torvalds
2025-06-30 12:50 ` [PATCH v11 00/14] unwind_user: x86: Deferred unwinding infrastructure Florian Weimer
2025-06-30 16:35 ` Namhyung Kim
2025-06-30 17:30 ` Steven Rostedt
2025-07-02 11:44 ` Sam James
2025-07-02 16:15 ` Steven Rostedt
2025-07-02 17:14 ` Namhyung Kim
2025-07-03 4:01 ` Sam James
2025-07-02 17:10 ` Namhyung Kim
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=20250625225716.004109448@goodmis.org \
--to=rostedt@goodmis.org \
--cc=akpm@linux-foundation.org \
--cc=andrii@kernel.org \
--cc=axboe@kernel.dk \
--cc=beaub@linux.microsoft.com \
--cc=bpf@vger.kernel.org \
--cc=indu.bhagat@oracle.com \
--cc=jemarch@gnu.org \
--cc=jolsa@kernel.org \
--cc=jpoimboe@kernel.org \
--cc=jremus@linux.ibm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=mhiramat@kernel.org \
--cc=mingo@kernel.org \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=tglx@linutronix.de \
--cc=torvalds@linux-foundation.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox