xref: /linux/kernel/unwind/deferred.c (revision b9c73524106e1c0c857006fb9ff2e5a510dc4021)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Deferred user space unwinding
4  */
5 #include <linux/kernel.h>
6 #include <linux/sched.h>
7 #include <linux/sizes.h>
8 #include <linux/slab.h>
9 #include <linux/unwind_deferred.h>
10 
11 /* Make the cache fit in a 4K page */
12 #define UNWIND_MAX_ENTRIES					\
13 	((SZ_4K - sizeof(struct unwind_cache)) / sizeof(long))
14 
15 /**
16  * unwind_user_faultable - Produce a user stacktrace in faultable context
17  * @trace: The descriptor that will store the user stacktrace
18  *
19  * This must be called in a known faultable context (usually when entering
20  * or exiting user space). Depending on the available implementations
21  * the @trace will be loaded with the addresses of the user space stacktrace
22  * if it can be found.
23  *
24  * Return: 0 on success and negative on error
25  *         On success @trace will contain the user space stacktrace
26  */
27 int unwind_user_faultable(struct unwind_stacktrace *trace)
28 {
29 	struct unwind_task_info *info = &current->unwind_info;
30 	struct unwind_cache *cache;
31 
32 	/* Should always be called from faultable context */
33 	might_fault();
34 
35 	if (current->flags & PF_EXITING)
36 		return -EINVAL;
37 
38 	if (!info->cache) {
39 		info->cache = kzalloc(struct_size(cache, entries, UNWIND_MAX_ENTRIES),
40 				      GFP_KERNEL);
41 		if (!info->cache)
42 			return -ENOMEM;
43 	}
44 
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 
57 	trace->nr = 0;
58 	unwind_user(trace, UNWIND_MAX_ENTRIES);
59 
60 	cache->nr_entries = trace->nr;
61 
62 	return 0;
63 }
64 
65 void unwind_task_init(struct task_struct *task)
66 {
67 	struct unwind_task_info *info = &task->unwind_info;
68 
69 	memset(info, 0, sizeof(*info));
70 }
71 
72 void unwind_task_free(struct task_struct *task)
73 {
74 	struct unwind_task_info *info = &task->unwind_info;
75 
76 	kfree(info->cache);
77 }
78