Skip to content

Commit 055c706

Browse files
committed
unwind_user/deferred: Make unwind deferral requests NMI-safe
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 cmpxchg. If an architecture requests a deferred stack trace from NMI context that does not support a safe NMI cmpxchg, it will get an -EINVAL and trigger a warning. 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. Cc: Masami Hiramatsu <[email protected]> Cc: Mathieu Desnoyers <[email protected]> Cc: Josh Poimboeuf <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Andrii Nakryiko <[email protected]> Cc: Indu Bhagat <[email protected]> Cc: "Jose E. Marchesi" <[email protected]> Cc: Beau Belgrave <[email protected]> Cc: Jens Remus <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Jens Axboe <[email protected]> Cc: Florian Weimer <[email protected]> Cc: Sam James <[email protected]> Link: https://lore.kernel.org/[email protected] Suggested-by: Peter Zijlstra <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent 2dffa35 commit 055c706

File tree

1 file changed

+44
-8
lines changed

1 file changed

+44
-8
lines changed

kernel/unwind/deferred.c

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,31 @@
1212
#include <linux/slab.h>
1313
#include <linux/mm.h>
1414

15+
/*
16+
* For requesting a deferred user space stack trace from NMI context
17+
* the architecture must support a safe cmpxchg in NMI context.
18+
* For those architectures that do not have that, then it cannot ask
19+
* for a deferred user space stack trace from an NMI context. If it
20+
* does, then it will get -EINVAL.
21+
*/
22+
#if defined(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG)
23+
# define CAN_USE_IN_NMI 1
24+
static inline bool try_assign_cnt(struct unwind_task_info *info, u32 cnt)
25+
{
26+
u32 old = 0;
27+
28+
return try_cmpxchg(&info->id.cnt, &old, cnt);
29+
}
30+
#else
31+
# define CAN_USE_IN_NMI 0
32+
/* When NMIs are not allowed, this always succeeds */
33+
static inline bool try_assign_cnt(struct unwind_task_info *info, u32 cnt)
34+
{
35+
info->id.cnt = cnt;
36+
return true;
37+
}
38+
#endif
39+
1540
/* Make the cache fit in a 4K page */
1641
#define UNWIND_MAX_ENTRIES \
1742
((SZ_4K - sizeof(struct unwind_cache)) / sizeof(long))
@@ -42,14 +67,13 @@ static DEFINE_PER_CPU(u32, unwind_ctx_ctr);
4267
static u64 get_cookie(struct unwind_task_info *info)
4368
{
4469
u32 cnt = 1;
45-
u32 old = 0;
4670

4771
if (info->id.cpu)
4872
return info->id.id;
4973

5074
/* LSB is always set to ensure 0 is an invalid value */
5175
cnt |= __this_cpu_read(unwind_ctx_ctr) + 2;
52-
if (try_cmpxchg(&info->id.cnt, &old, cnt)) {
76+
if (try_assign_cnt(info, cnt)) {
5377
/* Update the per cpu counter */
5478
__this_cpu_write(unwind_ctx_ctr, cnt);
5579
}
@@ -167,31 +191,43 @@ static void unwind_deferred_task_work(struct callback_head *head)
167191
int unwind_deferred_request(struct unwind_work *work, u64 *cookie)
168192
{
169193
struct unwind_task_info *info = &current->unwind_info;
194+
long pending;
170195
int ret;
171196

172197
*cookie = 0;
173198

174-
if (WARN_ON_ONCE(in_nmi()))
175-
return -EINVAL;
176-
177199
if ((current->flags & (PF_KTHREAD | PF_EXITING)) ||
178200
!user_mode(task_pt_regs(current)))
179201
return -EINVAL;
180202

203+
/*
204+
* NMI requires having safe cmpxchg operations.
205+
* Trigger a warning to make it obvious that an architecture
206+
* is using this in NMI when it should not be.
207+
*/
208+
if (WARN_ON_ONCE(!CAN_USE_IN_NMI && in_nmi()))
209+
return -EINVAL;
210+
181211
guard(irqsave)();
182212

183213
*cookie = get_cookie(info);
184214

185215
/* callback already pending? */
186-
if (info->pending)
216+
pending = READ_ONCE(info->pending);
217+
if (pending)
218+
return 1;
219+
220+
/* Claim the work unless an NMI just now swooped in to do so. */
221+
if (!try_cmpxchg(&info->pending, &pending, 1))
187222
return 1;
188223

189224
/* The work has been claimed, now schedule it. */
190225
ret = task_work_add(current, &info->work, TWA_RESUME);
191-
if (WARN_ON_ONCE(ret))
226+
if (WARN_ON_ONCE(ret)) {
227+
WRITE_ONCE(info->pending, 0);
192228
return ret;
229+
}
193230

194-
info->pending = 1;
195231
return 0;
196232
}
197233

0 commit comments

Comments
 (0)