From: Steven Rostedt <rostedt@xxxxxxxxxxx> On do_exit() when a task is exiting, if a unwind is requested and the deferred user stacktrace is deferred via the task_work, the task_work callback is called after exit_mm() is called in do_exit(). This means that the user stack trace will not be retrieved and an empty stack is created. Instead, add a function unwind_deferred_task_exit() and call it just before exit_mm() so that the unwinder can call the requested callbacks with the user space stack. Signed-off-by: Steven Rostedt (Google) <rostedt@xxxxxxxxxxx> --- include/linux/unwind_deferred.h | 4 +++- kernel/exit.c | 2 ++ kernel/unwind/deferred.c | 23 ++++++++++++++++++++--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/include/linux/unwind_deferred.h b/include/linux/unwind_deferred.h index 426e21457606..bf0cc0477b2e 100644 --- a/include/linux/unwind_deferred.h +++ b/include/linux/unwind_deferred.h @@ -35,6 +35,8 @@ int unwind_deferred_init(struct unwind_work *work, unwind_callback_t func); int unwind_deferred_request(struct unwind_work *work, u64 *timestamp); void unwind_deferred_cancel(struct unwind_work *work); +void unwind_deferred_task_exit(struct task_struct *task); + static __always_inline void unwind_exit_to_user_mode(void) { struct unwind_task_info *info = ¤t->unwind_info; @@ -65,7 +67,7 @@ static inline int unwind_deferred_trace(struct unwind_stacktrace *trace) { retur static inline int unwind_deferred_init(struct unwind_work *work, unwind_callback_t func) { return -ENOSYS; } static inline int unwind_deferred_request(struct unwind_work *work, u64 *timestamp) { return -ENOSYS; } static inline void unwind_deferred_cancel(struct unwind_work *work) {} - +static inline void unwind_deferred_task_exit(struct task_struct *task) {} static inline void unwind_exit_to_user_mode(void) {} #endif /* !CONFIG_UNWIND_USER */ diff --git a/kernel/exit.c b/kernel/exit.c index bd743900354c..6599f9518436 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -68,6 +68,7 @@ #include <linux/rethook.h> #include <linux/sysfs.h> #include <linux/user_events.h> +#include <linux/unwind_deferred.h> #include <linux/uaccess.h> #include <linux/pidfs.h> @@ -938,6 +939,7 @@ void __noreturn do_exit(long code) tsk->exit_code = code; taskstats_exit(tsk, group_dead); + unwind_deferred_task_exit(tsk); trace_sched_process_exit(tsk, group_dead); exit_mm(); diff --git a/kernel/unwind/deferred.c b/kernel/unwind/deferred.c index 8a6caaae04d3..6c95f484568e 100644 --- a/kernel/unwind/deferred.c +++ b/kernel/unwind/deferred.c @@ -77,7 +77,7 @@ int unwind_deferred_trace(struct unwind_stacktrace *trace) /* Should always be called from faultable context */ might_fault(); - if (current->flags & PF_EXITING) + if (!current->mm) return -EINVAL; if (!info->cache) { @@ -107,9 +107,9 @@ int unwind_deferred_trace(struct unwind_stacktrace *trace) return 0; } -static void unwind_deferred_task_work(struct callback_head *head) +static void process_unwind_deferred(struct task_struct *task) { - struct unwind_task_info *info = container_of(head, struct unwind_task_info, work); + struct unwind_task_info *info = &task->unwind_info; struct unwind_stacktrace trace; struct unwind_work *work; unsigned long bits; @@ -151,6 +151,23 @@ static void unwind_deferred_task_work(struct callback_head *head) srcu_read_unlock(&unwind_srcu, idx); } +static void unwind_deferred_task_work(struct callback_head *head) +{ + process_unwind_deferred(current); +} + +void unwind_deferred_task_exit(struct task_struct *task) +{ + struct unwind_task_info *info = ¤t->unwind_info; + + if (!unwind_pending(info)) + return; + + process_unwind_deferred(task); + + task_work_cancel(task, &info->work); +} + static int unwind_deferred_request_nmi(struct unwind_work *work, u64 *timestamp) { struct unwind_task_info *info = ¤t->unwind_info; -- 2.47.2