ARC: stack unwinding: don't assume non-current task is sleeping
[linux-2.6-microblaze.git] / arch / arc / kernel / stacktrace.c
index feba91c..b2557f5 100644 (file)
 
 #ifdef CONFIG_ARC_DW2_UNWIND
 
-static void seed_unwind_frame_info(struct task_struct *tsk,
-                                  struct pt_regs *regs,
-                                  struct unwind_frame_info *frame_info)
+static int
+seed_unwind_frame_info(struct task_struct *tsk, struct pt_regs *regs,
+                      struct unwind_frame_info *frame_info)
 {
        /*
         * synchronous unwinding (e.g. dump_stack)
         *  - uses current values of SP and friends
         */
-       if (tsk == NULL && regs == NULL) {
+       if (regs == NULL && (tsk == NULL || tsk == current)) {
                unsigned long fp, sp, blink, ret;
                frame_info->task = current;
 
@@ -65,11 +65,15 @@ static void seed_unwind_frame_info(struct task_struct *tsk,
                frame_info->call_frame = 0;
        } else if (regs == NULL) {
                /*
-                * Asynchronous unwinding of sleeping task
-                *  - Gets SP etc from task's pt_regs (saved bottom of kernel
-                *    mode stack of task)
+                * Asynchronous unwinding of a likely sleeping task
+                *  - first ensure it is actually sleeping
+                *  - if so, it will be in __switch_to, kernel mode SP of task
+                *    is safe-kept and BLINK at a well known location in there
                 */
 
+               if (tsk->state == TASK_RUNNING)
+                       return -1;
+
                frame_info->task = tsk;
 
                frame_info->regs.r27 = TSK_K_FP(tsk);
@@ -103,6 +107,8 @@ static void seed_unwind_frame_info(struct task_struct *tsk,
                frame_info->regs.r63 = regs->ret;
                frame_info->call_frame = 0;
        }
+
+       return 0;
 }
 
 #endif
@@ -112,11 +118,12 @@ arc_unwind_core(struct task_struct *tsk, struct pt_regs *regs,
                int (*consumer_fn) (unsigned int, void *), void *arg)
 {
 #ifdef CONFIG_ARC_DW2_UNWIND
-       int ret = 0;
+       int ret = 0, cnt = 0;
        unsigned int address;
        struct unwind_frame_info frame_info;
 
-       seed_unwind_frame_info(tsk, regs, &frame_info);
+       if (seed_unwind_frame_info(tsk, regs, &frame_info))
+               return 0;
 
        while (1) {
                address = UNW_PC(&frame_info);
@@ -132,6 +139,11 @@ arc_unwind_core(struct task_struct *tsk, struct pt_regs *regs,
                        break;
 
                frame_info.regs.r63 = frame_info.regs.r31;
+
+               if (cnt++ > 128) {
+                       printk("unwinder looping too long, aborting !\n");
+                       return 0;
+               }
        }
 
        return address;         /* return the last address it saw */