mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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(&current->unwind_info.timestamp)))
+		local64_set(&current->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 = &current->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



  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®