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>, Florian Weimer <fweimer@redhat.com>
Subject: [PATCH v12 07/14] unwind_user/deferred: Make unwind deferral requests NMI-safe
Date: Mon, 30 Jun 2025 20:53:28 -0400 [thread overview]
Message-ID: <20250701005451.737614486@goodmis.org> (raw)
In-Reply-To: 20250701005321.942306427@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>
---
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 c6548e8d64d1..73f6cac53530 100644
--- a/include/linux/unwind_deferred.h
+++ b/include/linux/unwind_deferred.h
@@ -28,8 +28,8 @@ void unwind_deferred_cancel(struct unwind_work *work);
static __always_inline void unwind_reset_info(void)
{
- if (unlikely(current->unwind_info.timestamp))
- current->unwind_info.timestamp = 0;
+ if (unlikely(local64_read(¤t->unwind_info.timestamp)))
+ local64_set(¤t->unwind_info.timestamp, 0);
/*
* As unwind_user_faultable() can be called directly and
* depends on nr_entries being cleared on exit to 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-07-01 0:54 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-01 0:53 [PATCH v12 00/14] unwind_user: x86: Deferred unwinding infrastructure Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 01/14] unwind_user: Add user space unwinding API Steven Rostedt
2025-07-04 17:58 ` Mathieu Desnoyers
2025-07-04 18:20 ` Mathieu Desnoyers
2025-07-07 19:42 ` Steven Rostedt
2025-07-07 21:01 ` Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 02/14] unwind_user: Add frame pointer support Steven Rostedt
2025-07-01 2:10 ` Linus Torvalds
2025-07-01 2:56 ` Steven Rostedt
2025-07-01 3:05 ` Steven Rostedt
2025-07-01 15:36 ` Jens Remus
2025-07-02 23:50 ` Steven Rostedt
2025-07-03 16:21 ` Jens Remus
2025-07-07 21:28 ` Steven Rostedt
2025-07-01 4:46 ` Florian Weimer
2025-07-01 12:14 ` Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 03/14] unwind_user: Add compat mode " Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 04/14] unwind_user/deferred: Add unwind_user_faultable() Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 05/14] unwind_user/deferred: Add unwind cache Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 06/14] unwind_user/deferred: Add deferred unwinding interface Steven Rostedt
2025-07-02 16:36 ` Peter Zijlstra
2025-07-02 16:42 ` Steven Rostedt
2025-07-02 16:56 ` Linus Torvalds
2025-07-02 17:26 ` Steven Rostedt
2025-07-02 17:48 ` Steven Rostedt
2025-07-02 18:21 ` Linus Torvalds
2025-07-02 18:47 ` Mathieu Desnoyers
2025-07-02 19:05 ` Steven Rostedt
2025-07-02 19:12 ` Mathieu Desnoyers
2025-07-02 19:21 ` Steven Rostedt
2025-07-02 19:36 ` Steven Rostedt
2025-07-02 19:40 ` Steven Rostedt
2025-07-02 19:48 ` Mathieu Desnoyers
2025-07-02 20:10 ` Steven Rostedt
2025-07-02 19:43 ` Mathieu Desnoyers
2025-07-02 19:51 ` Steven Rostedt
2025-07-02 18:57 ` Steven Rostedt
2025-07-01 0:53 ` Steven Rostedt [this message]
2025-07-02 15:53 ` [PATCH v12 07/14] unwind_user/deferred: Make unwind deferral requests NMI-safe Jens Remus
2025-07-02 19:11 ` Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 08/14] unwind deferred: Use bitmask to determine which callbacks to call Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 09/14] unwind deferred: Use SRCU unwind_deferred_task_work() Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 10/14] unwind: Clear unwind_mask on exit back to user space Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 11/14] unwind: Add USED bit to only have one conditional on way " Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 12/14] unwind: Finish up unwind when a task exits Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 13/14] unwind_user/x86: Enable frame pointer unwinding on x86 Steven Rostedt
2025-07-01 0:53 ` [PATCH v12 14/14] unwind_user/x86: Enable compat mode " Steven Rostedt
2025-07-01 2:06 ` [PATCH v12 00/14] unwind_user: x86: Deferred unwinding infrastructure Linus Torvalds
2025-07-01 2:45 ` Steven Rostedt
2025-07-01 22:49 ` Kees Cook
2025-07-01 23:26 ` Steven Rostedt
2025-07-02 14:56 ` Kees Cook
2025-07-02 16:20 ` Mathieu Desnoyers
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=20250701005451.737614486@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=fweimer@redhat.com \
--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 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.