#include <linux/sysfs.h>
 
 #include <asm/stacktrace.h>
-
+#include <asm/unwind.h>
 
 int panic_on_unrecovered_nmi;
 int panic_on_io_nmi;
 }
 EXPORT_SYMBOL_GPL(print_context_stack_bp);
 
-static int print_trace_stack(void *data, const char *name)
+void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
+                       unsigned long *stack, char *log_lvl)
 {
-       printk("%s <%s> ", (char *)data, name);
-       return 0;
-}
+       struct unwind_state state;
+       struct stack_info stack_info = {0};
+       unsigned long visit_mask = 0;
+       int graph_idx = 0;
 
-/*
- * Print one address/symbol entries per line.
- */
-static int print_trace_address(void *data, unsigned long addr, int reliable)
-{
-       printk_stack_address(addr, reliable, data);
-       return 0;
-}
+       printk("%sCall Trace:\n", log_lvl);
 
-static const struct stacktrace_ops print_trace_ops = {
-       .stack                  = print_trace_stack,
-       .address                = print_trace_address,
-       .walk_stack             = print_context_stack,
-};
+       unwind_start(&state, task, regs, stack);
 
-void
-show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
-               unsigned long *stack, unsigned long bp, char *log_lvl)
-{
-       printk("%sCall Trace:\n", log_lvl);
-       dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
+       /*
+        * Iterate through the stacks, starting with the current stack pointer.
+        * Each stack has a pointer to the next one.
+        *
+        * x86-64 can have several stacks:
+        * - task stack
+        * - interrupt stack
+        * - HW exception stacks (double fault, nmi, debug, mce)
+        *
+        * x86-32 can have up to three stacks:
+        * - task stack
+        * - softirq stack
+        * - hardirq stack
+        */
+       for (; stack; stack = stack_info.next_sp) {
+               const char *str_begin, *str_end;
+
+               /*
+                * If we overflowed the task stack into a guard page, jump back
+                * to the bottom of the usable stack.
+                */
+               if (task_stack_page(task) - (void *)stack < PAGE_SIZE)
+                       stack = task_stack_page(task);
+
+               if (get_stack_info(stack, task, &stack_info, &visit_mask))
+                       break;
+
+               stack_type_str(stack_info.type, &str_begin, &str_end);
+               if (str_begin)
+                       printk("%s <%s> ", log_lvl, str_begin);
+
+               /*
+                * Scan the stack, printing any text addresses we find.  At the
+                * same time, follow proper stack frames with the unwinder.
+                *
+                * Addresses found during the scan which are not reported by
+                * the unwinder are considered to be additional clues which are
+                * sometimes useful for debugging and are prefixed with '?'.
+                * This also serves as a failsafe option in case the unwinder
+                * goes off in the weeds.
+                */
+               for (; stack < stack_info.end; stack++) {
+                       unsigned long real_addr;
+                       int reliable = 0;
+                       unsigned long addr = *stack;
+                       unsigned long *ret_addr_p =
+                               unwind_get_return_address_ptr(&state);
+
+                       if (!__kernel_text_address(addr))
+                               continue;
+
+                       if (stack == ret_addr_p)
+                               reliable = 1;
+
+                       /*
+                        * When function graph tracing is enabled for a
+                        * function, its return address on the stack is
+                        * replaced with the address of an ftrace handler
+                        * (return_to_handler).  In that case, before printing
+                        * the "real" address, we want to print the handler
+                        * address as an "unreliable" hint that function graph
+                        * tracing was involved.
+                        */
+                       real_addr = ftrace_graph_ret_addr(task, &graph_idx,
+                                                         addr, stack);
+                       if (real_addr != addr)
+                               printk_stack_address(addr, 0, log_lvl);
+                       printk_stack_address(real_addr, reliable, log_lvl);
+
+                       if (!reliable)
+                               continue;
+
+                       /*
+                        * Get the next frame from the unwinder.  No need to
+                        * check for an error: if anything goes wrong, the rest
+                        * of the addresses will just be printed as unreliable.
+                        */
+                       unwind_next_frame(&state);
+               }
+
+               if (str_end)
+                       printk("%s <%s> ", log_lvl, str_end);
+       }
 }
 
 void show_stack(struct task_struct *task, unsigned long *sp)
 {
-       unsigned long bp = 0;
-
        task = task ? : current;
 
        /*
         * Stack frames below this one aren't interesting.  Don't show them
         * if we're printing for %current.
         */
-       if (!sp && task == current) {
+       if (!sp && task == current)
                sp = get_stack_pointer(current, NULL);
-               bp = (unsigned long)get_frame_pointer(current, NULL);
-       }
 
-       show_stack_log_lvl(task, NULL, sp, bp, "");
+       show_stack_log_lvl(current, NULL, sp, "");
 }
 
 void show_stack_regs(struct pt_regs *regs)
 {
-       show_stack_log_lvl(current, regs, NULL, 0, "");
+       show_stack_log_lvl(current, regs, NULL, "");
 }
 
 static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED;