x86/unwind: Dump all stacks in unwind_dump()
authorJosh Poimboeuf <jpoimboe@redhat.com>
Wed, 26 Apr 2017 01:48:52 +0000 (20:48 -0500)
committerIngo Molnar <mingo@kernel.org>
Wed, 26 Apr 2017 06:19:05 +0000 (08:19 +0200)
Currently unwind_dump() dumps only the most recently accessed stack.
But it has a few issues.

In some cases, 'first_sp' can get out of sync with 'stack_info', causing
unwind_dump() to start from the wrong address, flood the printk buffer,
and eventually read a bad address.

In other cases, dumping only the most recently accessed stack doesn't
give enough data to diagnose the error.

Fix both issues by dumping *all* stacks involved in the trace, not just
the last one.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: 8b5e99f02264 ("x86/unwind: Dump stack data on warnings")
Link: http://lkml.kernel.org/r/016d6a9810d7d1bfc87ef8c0e6ee041c6744c909.1493171120.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/kernel/unwind_frame.c

index ae0821f6c3a599b18d0ea6c1da01ca8b788faef6..fec70fe3b1ec637920c758a34a17e2f6cecbb082 100644 (file)
@@ -30,6 +30,8 @@ static void unwind_dump(struct unwind_state *state)
        static bool dumped_before = false;
        bool prev_zero, zero = false;
        unsigned long word, *sp;
+       struct stack_info stack_info = {0};
+       unsigned long visit_mask = 0;
 
        if (dumped_before)
                return;
@@ -40,21 +42,27 @@ static void unwind_dump(struct unwind_state *state)
                        state->stack_info.type, state->stack_info.next_sp,
                        state->stack_mask, state->graph_idx);
 
-       for (sp = state->orig_sp; sp < state->stack_info.end; sp++) {
-               word = READ_ONCE_NOCHECK(*sp);
+       for (sp = state->orig_sp; sp; sp = PTR_ALIGN(stack_info.next_sp, sizeof(long))) {
+               if (get_stack_info(sp, state->task, &stack_info, &visit_mask))
+                       break;
 
-               prev_zero = zero;
-               zero = word == 0;
+               for (; sp < stack_info.end; sp++) {
 
-               if (zero) {
-                       if (!prev_zero)
-                               printk_deferred("%p: %0*x ...\n",
-                                               sp, BITS_PER_LONG/4, 0);
-                       continue;
-               }
+                       word = READ_ONCE_NOCHECK(*sp);
+
+                       prev_zero = zero;
+                       zero = word == 0;
 
-               printk_deferred("%p: %0*lx (%pB)\n",
-                               sp, BITS_PER_LONG/4, word, (void *)word);
+                       if (zero) {
+                               if (!prev_zero)
+                                       printk_deferred("%p: %0*x ...\n",
+                                                       sp, BITS_PER_LONG/4, 0);
+                               continue;
+                       }
+
+                       printk_deferred("%p: %0*lx (%pB)\n",
+                                       sp, BITS_PER_LONG/4, word, (void *)word);
+               }
        }
 }
 
@@ -216,7 +224,7 @@ static bool update_stack_state(struct unwind_state *state,
        }
 
        /* Save the original stack pointer for unwind_dump(): */
-       if (!state->orig_sp || info->type != prev_type)
+       if (!state->orig_sp)
                state->orig_sp = frame;
 
        return true;