Cache the results of the unwind to ensure the unwind is only performed once, even when called by multiple tracers.
Signed-off-by: Josh Poimboeuf <jpoim...@kernel.org> --- include/linux/unwind_deferred_types.h | 8 +++++++- kernel/unwind/deferred.c | 26 ++++++++++++++++++++------ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/include/linux/unwind_deferred_types.h b/include/linux/unwind_deferred_types.h index 9749824aea09..6f71a06329fb 100644 --- a/include/linux/unwind_deferred_types.h +++ b/include/linux/unwind_deferred_types.h @@ -2,8 +2,14 @@ #ifndef _LINUX_UNWIND_USER_DEFERRED_TYPES_H #define _LINUX_UNWIND_USER_DEFERRED_TYPES_H -struct unwind_task_info { +struct unwind_cache { unsigned long *entries; + unsigned int nr_entries; + u64 cookie; +}; + +struct unwind_task_info { + struct unwind_cache cache; u64 cookie; }; diff --git a/kernel/unwind/deferred.c b/kernel/unwind/deferred.c index f0dbe4069247..2f38055cce48 100644 --- a/kernel/unwind/deferred.c +++ b/kernel/unwind/deferred.c @@ -56,6 +56,7 @@ static void unwind_deferred_task_work(struct callback_head *head) { struct unwind_work *work = container_of(head, struct unwind_work, work); struct unwind_task_info *info = ¤t->unwind_info; + struct unwind_cache *cache = &info->cache; struct unwind_stacktrace trace; u64 cookie; @@ -73,17 +74,30 @@ static void unwind_deferred_task_work(struct callback_head *head) if (!current->mm) goto do_callback; - if (!info->entries) { - info->entries = kmalloc(UNWIND_MAX_ENTRIES * sizeof(long), - GFP_KERNEL); - if (!info->entries) + if (!cache->entries) { + cache->entries = kmalloc(UNWIND_MAX_ENTRIES * sizeof(long), + GFP_KERNEL); + if (!cache->entries) goto do_callback; } - trace.entries = info->entries; + trace.entries = cache->entries; + + if (cookie == cache->cookie) { + /* + * The user stack has already been previously unwound in this + * entry context. Skip the unwind and use the cache. + */ + trace.nr = cache->nr_entries; + goto do_callback; + } + trace.nr = 0; unwind_user(&trace, UNWIND_MAX_ENTRIES); + cache->cookie = cookie; + cache->nr_entries = trace.nr; + do_callback: work->func(work, &trace, cookie); work->pending = 0; @@ -174,5 +188,5 @@ void unwind_task_free(struct task_struct *task) { struct unwind_task_info *info = &task->unwind_info; - kfree(info->entries); + kfree(info->cache.entries); } -- 2.48.1