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 = &current->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


Reply via email to