Skip to content

Commit b9c7352

Browse files
jpoimboerostedt
authored andcommitted
unwind_user/deferred: Add unwind cache
Cache the results of the unwind to ensure the unwind is only performed once, even when called by multiple tracers. The cache nr_entries gets cleared every time the task exits the kernel. When a stacktrace is requested, nr_entries gets set to the number of entries in the stacktrace. If another stacktrace is requested, if nr_entries is not zero, then it contains the same stacktrace that would be retrieved so it is not processed again and the entries is given to the caller. Cc: Masami Hiramatsu <[email protected]> Cc: Mathieu Desnoyers <[email protected]> Cc: Peter Zijlstra <[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: 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] Reviewed-by: Jens Remus <[email protected]> Reviewed-By: Indu Bhagat <[email protected]> Co-developed-by: Steven Rostedt (Google) <[email protected]> Signed-off-by: Josh Poimboeuf <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent 5e32d0f commit b9c7352

File tree

4 files changed

+40
-8
lines changed

4 files changed

+40
-8
lines changed

include/linux/entry-common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/resume_user_mode.h>
1313
#include <linux/tick.h>
1414
#include <linux/kmsan.h>
15+
#include <linux/unwind_deferred.h>
1516

1617
#include <asm/entry-common.h>
1718
#include <asm/syscall.h>
@@ -362,6 +363,7 @@ static __always_inline void exit_to_user_mode(void)
362363
lockdep_hardirqs_on_prepare();
363364
instrumentation_end();
364365

366+
unwind_reset_info();
365367
user_enter_irqoff();
366368
arch_exit_to_user_mode();
367369
lockdep_hardirqs_on(CALLER_ADDR0);

include/linux/unwind_deferred.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,21 @@ void unwind_task_free(struct task_struct *task);
1212

1313
int unwind_user_faultable(struct unwind_stacktrace *trace);
1414

15+
static __always_inline void unwind_reset_info(void)
16+
{
17+
if (unlikely(current->unwind_info.cache))
18+
current->unwind_info.cache->nr_entries = 0;
19+
}
20+
1521
#else /* !CONFIG_UNWIND_USER */
1622

1723
static inline void unwind_task_init(struct task_struct *task) {}
1824
static inline void unwind_task_free(struct task_struct *task) {}
1925

2026
static inline int unwind_user_faultable(struct unwind_stacktrace *trace) { return -ENOSYS; }
2127

28+
static inline void unwind_reset_info(void) {}
29+
2230
#endif /* !CONFIG_UNWIND_USER */
2331

2432
#endif /* _LINUX_UNWIND_USER_DEFERRED_H */

include/linux/unwind_deferred_types.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
#ifndef _LINUX_UNWIND_USER_DEFERRED_TYPES_H
33
#define _LINUX_UNWIND_USER_DEFERRED_TYPES_H
44

5+
struct unwind_cache {
6+
unsigned int nr_entries;
7+
unsigned long entries[];
8+
};
9+
510
struct unwind_task_info {
6-
unsigned long *entries;
11+
struct unwind_cache *cache;
712
};
813

914
#endif /* _LINUX_UNWIND_USER_DEFERRED_TYPES_H */

kernel/unwind/deferred.c

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
*/
55
#include <linux/kernel.h>
66
#include <linux/sched.h>
7+
#include <linux/sizes.h>
78
#include <linux/slab.h>
89
#include <linux/unwind_deferred.h>
910

10-
#define UNWIND_MAX_ENTRIES 512
11+
/* Make the cache fit in a 4K page */
12+
#define UNWIND_MAX_ENTRIES \
13+
((SZ_4K - sizeof(struct unwind_cache)) / sizeof(long))
1114

1215
/**
1316
* unwind_user_faultable - Produce a user stacktrace in faultable context
@@ -24,24 +27,38 @@
2427
int unwind_user_faultable(struct unwind_stacktrace *trace)
2528
{
2629
struct unwind_task_info *info = &current->unwind_info;
30+
struct unwind_cache *cache;
2731

2832
/* Should always be called from faultable context */
2933
might_fault();
3034

3135
if (current->flags & PF_EXITING)
3236
return -EINVAL;
3337

34-
if (!info->entries) {
35-
info->entries = kmalloc_array(UNWIND_MAX_ENTRIES, sizeof(long),
36-
GFP_KERNEL);
37-
if (!info->entries)
38+
if (!info->cache) {
39+
info->cache = kzalloc(struct_size(cache, entries, UNWIND_MAX_ENTRIES),
40+
GFP_KERNEL);
41+
if (!info->cache)
3842
return -ENOMEM;
3943
}
4044

45+
cache = info->cache;
46+
trace->entries = cache->entries;
47+
48+
if (cache->nr_entries) {
49+
/*
50+
* The user stack has already been previously unwound in this
51+
* entry context. Skip the unwind and use the cache.
52+
*/
53+
trace->nr = cache->nr_entries;
54+
return 0;
55+
}
56+
4157
trace->nr = 0;
42-
trace->entries = info->entries;
4358
unwind_user(trace, UNWIND_MAX_ENTRIES);
4459

60+
cache->nr_entries = trace->nr;
61+
4562
return 0;
4663
}
4764

@@ -56,5 +73,5 @@ void unwind_task_free(struct task_struct *task)
5673
{
5774
struct unwind_task_info *info = &task->unwind_info;
5875

59-
kfree(info->entries);
76+
kfree(info->cache);
6077
}

0 commit comments

Comments
 (0)