s390/unwind: introduce stack unwind API
[linux-2.6-block.git] / arch / s390 / kernel / dumpstack.c
index cb7f55bbe06e87eeb16a6e3f38d54fdc9be63824..9e87b68be21c776ffac7a8400c1e74b1a3a55d56 100644 (file)
 #include <asm/debug.h>
 #include <asm/dis.h>
 #include <asm/ipl.h>
+#include <asm/unwind.h>
 
-/*
- * For dump_trace we have tree different stack to consider:
- *   - the panic stack which is used if the kernel stack has overflown
- *   - the asynchronous interrupt stack (cpu related)
- *   - the synchronous kernel stack (process related)
- * The stack trace can start at any of the three stacks and can potentially
- * touch all of them. The order is: panic stack, async stack, sync stack.
- */
-static unsigned long __no_sanitize_address
-__dump_trace(dump_trace_func_t func, void *data, unsigned long sp,
-            unsigned long low, unsigned long high)
+const char *stack_type_name(enum stack_type type)
 {
-       struct stack_frame *sf;
-       struct pt_regs *regs;
-
-       while (1) {
-               if (sp < low || sp > high - sizeof(*sf))
-                       return sp;
-               sf = (struct stack_frame *) sp;
-               if (func(data, sf->gprs[8], 0))
-                       return sp;
-               /* Follow the backchain. */
-               while (1) {
-                       low = sp;
-                       sp = sf->back_chain;
-                       if (!sp)
-                               break;
-                       if (sp <= low || sp > high - sizeof(*sf))
-                               return sp;
-                       sf = (struct stack_frame *) sp;
-                       if (func(data, sf->gprs[8], 1))
-                               return sp;
-               }
-               /* Zero backchain detected, check for interrupt frame. */
-               sp = (unsigned long) (sf + 1);
-               if (sp <= low || sp > high - sizeof(*regs))
-                       return sp;
-               regs = (struct pt_regs *) sp;
-               if (!user_mode(regs)) {
-                       if (func(data, regs->psw.addr, 1))
-                               return sp;
-               }
-               low = sp;
-               sp = regs->gprs[15];
+       switch (type) {
+       case STACK_TYPE_TASK:
+               return "task";
+       case STACK_TYPE_IRQ:
+               return "irq";
+       case STACK_TYPE_NODAT:
+               return "nodat";
+       case STACK_TYPE_RESTART:
+               return "restart";
+       default:
+               return "unknown";
        }
 }
 
-void dump_trace(dump_trace_func_t func, void *data, struct task_struct *task,
-               unsigned long sp)
+static inline bool in_stack(unsigned long sp, struct stack_info *info,
+                           enum stack_type type, unsigned long low,
+                           unsigned long high)
+{
+       if (sp < low || sp >= high)
+               return false;
+       info->type = type;
+       info->begin = low;
+       info->end = high;
+       return true;
+}
+
+static bool in_task_stack(unsigned long sp, struct task_struct *task,
+                         struct stack_info *info)
+{
+       unsigned long stack;
+
+       stack = (unsigned long) task_stack_page(task);
+       return in_stack(sp, info, STACK_TYPE_TASK, stack, stack + THREAD_SIZE);
+}
+
+static bool in_irq_stack(unsigned long sp, struct stack_info *info)
 {
-       unsigned long frame_size;
+       unsigned long frame_size, top;
 
        frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
-#ifdef CONFIG_CHECK_STACK
-       sp = __dump_trace(func, data, sp,
-                         S390_lowcore.nodat_stack + frame_size - THREAD_SIZE,
-                         S390_lowcore.nodat_stack + frame_size);
-#endif
-       sp = __dump_trace(func, data, sp,
-                         S390_lowcore.async_stack + frame_size - THREAD_SIZE,
-                         S390_lowcore.async_stack + frame_size);
-       task = task ?: current;
-       __dump_trace(func, data, sp,
-                    (unsigned long)task_stack_page(task),
-                    (unsigned long)task_stack_page(task) + THREAD_SIZE);
+       top = S390_lowcore.async_stack + frame_size;
+       return in_stack(sp, info, STACK_TYPE_IRQ, top - THREAD_SIZE, top);
+}
+
+static bool in_nodat_stack(unsigned long sp, struct stack_info *info)
+{
+       unsigned long frame_size, top;
+
+       frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
+       top = S390_lowcore.nodat_stack + frame_size;
+       return in_stack(sp, info, STACK_TYPE_NODAT, top - THREAD_SIZE, top);
 }
-EXPORT_SYMBOL_GPL(dump_trace);
 
-static int show_address(void *data, unsigned long address, int reliable)
+static bool in_restart_stack(unsigned long sp, struct stack_info *info)
 {
-       if (reliable)
-               printk(" [<%016lx>] %pSR \n", address, (void *)address);
-       else
-               printk("([<%016lx>] %pSR)\n", address, (void *)address);
+       unsigned long frame_size, top;
+
+       frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
+       top = S390_lowcore.restart_stack + frame_size;
+       return in_stack(sp, info, STACK_TYPE_RESTART, top - THREAD_SIZE, top);
+}
+
+int get_stack_info(unsigned long sp, struct task_struct *task,
+                  struct stack_info *info, unsigned long *visit_mask)
+{
+       if (!sp)
+               goto unknown;
+
+       task = task ? : current;
+
+       /* Check per-task stack */
+       if (in_task_stack(sp, task, info))
+               goto recursion_check;
+
+       if (task != current)
+               goto unknown;
+
+       /* Check per-cpu stacks */
+       if (!in_irq_stack(sp, info) &&
+           !in_nodat_stack(sp, info) &&
+           !in_restart_stack(sp, info))
+               goto unknown;
+
+recursion_check:
+       /*
+        * Make sure we don't iterate through any given stack more than once.
+        * If it comes up a second time then there's something wrong going on:
+        * just break out and report an unknown stack type.
+        */
+       if (*visit_mask & (1UL << info->type)) {
+               printk_deferred_once(KERN_WARNING
+                       "WARNING: stack recursion on stack type %d\n",
+                       info->type);
+               goto unknown;
+       }
+       *visit_mask |= 1UL << info->type;
        return 0;
+unknown:
+       info->type = STACK_TYPE_UNKNOWN;
+       return -EINVAL;
 }
 
 void show_stack(struct task_struct *task, unsigned long *stack)
 {
-       unsigned long sp = (unsigned long) stack;
+       struct unwind_state state;
 
-       if (!sp)
-               sp = task ? task->thread.ksp : current_stack_pointer();
        printk("Call Trace:\n");
-       dump_trace(show_address, NULL, task, sp);
        if (!task)
                task = current;
-       debug_show_held_locks(task);
+       unwind_for_each_frame(&state, task, NULL, (unsigned long) stack)
+               printk(state.reliable ? " [<%016lx>] %pSR \n" :
+                                       "([<%016lx>] %pSR)\n",
+                      state.ip, (void *) state.ip);
+       debug_show_held_locks(task ? : current);
 }
 
 static void show_last_breaking_event(struct pt_regs *regs)