]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
s390/unwind: start unwinding from reliable state
authorVasily Gorbik <gor@linux.ibm.com>
Wed, 27 Nov 2019 16:37:51 +0000 (17:37 +0100)
committerVasily Gorbik <gor@linux.ibm.com>
Sat, 30 Nov 2019 09:52:48 +0000 (10:52 +0100)
A comment in arch/s390/include/asm/unwind.h says:
> If 'first_frame' is not zero unwind_start skips unwind frames until it
> reaches the specified stack pointer.
> The end of the unwinding is indicated with unwind_done, this can be true
> right after unwind_start, e.g. with first_frame!=0 that can not be found.
> unwind_next_frame skips to the next frame.
> Once the unwind is completed unwind_error() can be used to check if there
> has been a situation where the unwinder could not correctly understand
> the tasks call chain.

With this change backchain unwinder now comply with behaviour
described. As well as matches orc unwinder implementation.  Now unwinder
starts from reliable state, i.e. __unwind_start own stack frame is
taken or stack frame generated by __switch_to (ksp) - both known to be
valid. In case of pt_regs %r15 is better match for pt_regs psw, than
sometimes random "sp" caller passed.

Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/include/asm/unwind.h
arch/s390/kernel/unwind_bc.c

index 5d6c8fe7a271b6f885897cea476c20d8288158a6..de9006b0cfebbdc9c1f1f5451c12a448ab99b7e9 100644 (file)
@@ -58,11 +58,11 @@ static inline bool unwind_error(struct unwind_state *state)
 static inline void unwind_start(struct unwind_state *state,
                                struct task_struct *task,
                                struct pt_regs *regs,
-                               unsigned long sp)
+                               unsigned long first_frame)
 {
        task = task ?: current;
-       sp = sp ?: get_stack_pointer(task, regs);
-       __unwind_start(state, task, regs, sp);
+       first_frame = first_frame ?: get_stack_pointer(task, regs);
+       __unwind_start(state, task, regs, first_frame);
 }
 
 static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
index c5ebb8a4cdd6d676612c157364b57c5df29c181e..e1371cdf9fa5fedce48cdcda0672c48b6ecbec0c 100644 (file)
@@ -105,13 +105,11 @@ out_stop:
 EXPORT_SYMBOL_GPL(unwind_next_frame);
 
 void __unwind_start(struct unwind_state *state, struct task_struct *task,
-                   struct pt_regs *regs, unsigned long sp)
+                   struct pt_regs *regs, unsigned long first_frame)
 {
        struct stack_info *info = &state->stack_info;
-       unsigned long *mask = &state->stack_mask;
-       bool reliable;
        struct stack_frame *sf;
-       unsigned long ip;
+       unsigned long ip, sp;
 
        memset(state, 0, sizeof(*state));
        state->task = task;
@@ -123,23 +121,28 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
                return;
        }
 
+       /* Get the instruction pointer from pt_regs or the stack frame */
+       if (regs) {
+               ip = regs->psw.addr;
+               sp = regs->gprs[15];
+       } else if (task == current) {
+               sp = current_frame_address();
+       } else {
+               sp = task->thread.ksp;
+       }
+
        /* Get current stack pointer and initialize stack info */
-       if (get_stack_info(sp, task, info, mask) != 0 ||
-           !on_stack(info, sp, sizeof(struct stack_frame))) {
+       if (!update_stack_info(state, sp)) {
                /* Something is wrong with the stack pointer */
                info->type = STACK_TYPE_UNKNOWN;
                state->error = true;
                return;
        }
 
-       /* Get the instruction pointer from pt_regs or the stack frame */
-       if (regs) {
-               ip = READ_ONCE_NOCHECK(regs->psw.addr);
-               reliable = true;
-       } else {
-               sf = (struct stack_frame *) sp;
+       if (!regs) {
+               /* Stack frame is within valid stack */
+               sf = (struct stack_frame *)sp;
                ip = READ_ONCE_NOCHECK(sf->gprs[8]);
-               reliable = false;
        }
 
        ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL);
@@ -147,6 +150,17 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
        /* Update unwind state */
        state->sp = sp;
        state->ip = ip;
-       state->reliable = reliable;
+       state->reliable = true;
+
+       if (!first_frame)
+               return;
+       /* Skip through the call chain to the specified starting frame */
+       while (!unwind_done(state)) {
+               if (on_stack(&state->stack_info, first_frame, sizeof(struct stack_frame))) {
+                       if (state->sp >= first_frame)
+                               break;
+               }
+               unwind_next_frame(state);
+       }
 }
 EXPORT_SYMBOL_GPL(__unwind_start);