From patchwork Thu Apr 28 20:44:37 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Poimboeuf X-Patchwork-Id: 616413 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3qwpxR31c7z9sDX for ; Fri, 29 Apr 2016 06:55:07 +1000 (AEST) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3qwpxR1trzzDr8k for ; Fri, 29 Apr 2016 06:55:07 +1000 (AEST) X-Original-To: linuxppc-dev@lists.ozlabs.org Delivered-To: linuxppc-dev@lists.ozlabs.org Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3qwpk74bTWzDq62 for ; Fri, 29 Apr 2016 06:45:19 +1000 (AEST) Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id CE5B93DD47; Thu, 28 Apr 2016 20:45:17 +0000 (UTC) Received: from treble.redhat.com (ovpn-113-171.phx2.redhat.com [10.3.113.171]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u3SKj3lx008776; Thu, 28 Apr 2016 16:45:15 -0400 From: Josh Poimboeuf To: Jessica Yu , Jiri Kosina , Miroslav Benes , Ingo Molnar , Peter Zijlstra , Michael Ellerman , Heiko Carstens Subject: [RFC PATCH v2 06/18] x86: dump_trace() error handling Date: Thu, 28 Apr 2016 15:44:37 -0500 Message-Id: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.68 on 10.5.11.27 X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-s390@vger.kernel.org, Vojtech Pavlik , Petr Mladek , Jiri Slaby , x86@kernel.org, linux-kernel@vger.kernel.org, Andy Lutomirski , live-patching@vger.kernel.org, linuxppc-dev@lists.ozlabs.org, Chris J Arges MIME-Version: 1.0 Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" In preparation for being able to determine whether a given stack trace is reliable, allow the stacktrace_ops functions to propagate errors to dump_trace(). Signed-off-by: Josh Poimboeuf --- arch/x86/include/asm/stacktrace.h | 36 +++++++++++++++----------- arch/x86/kernel/dumpstack.c | 31 +++++++++++------------ arch/x86/kernel/dumpstack_32.c | 22 ++++++++++------ arch/x86/kernel/dumpstack_64.c | 53 ++++++++++++++++++++++++++------------- 4 files changed, 87 insertions(+), 55 deletions(-) diff --git a/arch/x86/include/asm/stacktrace.h b/arch/x86/include/asm/stacktrace.h index 7c247e7..a64523f3 100644 --- a/arch/x86/include/asm/stacktrace.h +++ b/arch/x86/include/asm/stacktrace.h @@ -14,26 +14,32 @@ extern int kstack_depth_to_print; struct thread_info; struct stacktrace_ops; -typedef unsigned long (*walk_stack_t)(struct thread_info *tinfo, - unsigned long *stack, - unsigned long bp, - const struct stacktrace_ops *ops, - void *data, - unsigned long *end, - int *graph); - -extern unsigned long +typedef int (*walk_stack_t)(struct thread_info *tinfo, + unsigned long *stack, + unsigned long *bp, + const struct stacktrace_ops *ops, + void *data, + unsigned long *end, + int *graph); + +extern int print_context_stack(struct thread_info *tinfo, - unsigned long *stack, unsigned long bp, + unsigned long *stack, unsigned long *bp, const struct stacktrace_ops *ops, void *data, unsigned long *end, int *graph); -extern unsigned long +extern int print_context_stack_bp(struct thread_info *tinfo, - unsigned long *stack, unsigned long bp, + unsigned long *stack, unsigned long *bp, const struct stacktrace_ops *ops, void *data, unsigned long *end, int *graph); +extern int +print_context_stack_reliable(struct thread_info *tinfo, + unsigned long *stack, unsigned long *bp, + const struct stacktrace_ops *ops, void *data, + unsigned long *end, int *graph); + /* Generic stack tracer with callbacks */ struct stacktrace_ops { @@ -43,9 +49,9 @@ struct stacktrace_ops { walk_stack_t walk_stack; }; -void dump_trace(struct task_struct *tsk, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data); +int dump_trace(struct task_struct *tsk, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data); #ifdef CONFIG_X86_32 #define STACKSLOTS_PER_LINE 8 diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 2bb25c3..13d240c 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -92,23 +92,22 @@ static inline int valid_stack_ptr(struct thread_info *tinfo, return p > t && p < t + THREAD_SIZE - size; } -unsigned long -print_context_stack(struct thread_info *tinfo, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data, - unsigned long *end, int *graph) +int print_context_stack(struct thread_info *tinfo, + unsigned long *stack, unsigned long *bp, + const struct stacktrace_ops *ops, void *data, + unsigned long *end, int *graph) { - struct stack_frame *frame = (struct stack_frame *)bp; + struct stack_frame *frame = (struct stack_frame *)*bp; while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) { unsigned long addr; addr = *stack; if (__kernel_text_address(addr)) { - if ((unsigned long) stack == bp + sizeof(long)) { + if ((unsigned long) stack == *bp + sizeof(long)) { ops->address(data, addr, 1); frame = frame->next_frame; - bp = (unsigned long) frame; + *bp = (unsigned long) frame; } else { ops->address(data, addr, 0); } @@ -116,17 +115,16 @@ print_context_stack(struct thread_info *tinfo, } stack++; } - return bp; + return 0; } EXPORT_SYMBOL_GPL(print_context_stack); -unsigned long -print_context_stack_bp(struct thread_info *tinfo, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data, - unsigned long *end, int *graph) +int print_context_stack_bp(struct thread_info *tinfo, + unsigned long *stack, unsigned long *bp, + const struct stacktrace_ops *ops, void *data, + unsigned long *end, int *graph) { - struct stack_frame *frame = (struct stack_frame *)bp; + struct stack_frame *frame = (struct stack_frame *)*bp; unsigned long *ret_addr = &frame->return_address; while (valid_stack_ptr(tinfo, ret_addr, sizeof(*ret_addr), end)) { @@ -142,7 +140,8 @@ print_context_stack_bp(struct thread_info *tinfo, print_ftrace_graph_addr(addr, data, ops, tinfo, graph); } - return (unsigned long)frame; + *bp = (unsigned long)frame; + return 0; } EXPORT_SYMBOL_GPL(print_context_stack_bp); diff --git a/arch/x86/kernel/dumpstack_32.c b/arch/x86/kernel/dumpstack_32.c index 464ffd6..e710bab 100644 --- a/arch/x86/kernel/dumpstack_32.c +++ b/arch/x86/kernel/dumpstack_32.c @@ -38,13 +38,14 @@ static void *is_softirq_stack(unsigned long *stack, int cpu) return is_irq_stack(stack, irq); } -void dump_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data) +int dump_trace(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data) { const unsigned cpu = get_cpu(); int graph = 0; u32 *prev_esp; + int ret; if (!task) task = current; @@ -69,8 +70,10 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, end_stack = is_softirq_stack(stack, cpu); context = task_thread_info(task); - bp = ops->walk_stack(context, stack, bp, ops, data, - end_stack, &graph); + ret = ops->walk_stack(context, stack, &bp, ops, data, + end_stack, &graph); + if (ret) + goto out; /* Stop if not on irq stack */ if (!end_stack) @@ -82,11 +85,16 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, if (!stack) break; - if (ops->stack(data, "IRQ") < 0) - break; + ret = ops->stack(data, "IRQ"); + if (ret) + goto out; + touch_nmi_watchdog(); } + +out: put_cpu(); + return ret; } EXPORT_SYMBOL(dump_trace); diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c index 5f1c626..0c810ba 100644 --- a/arch/x86/kernel/dumpstack_64.c +++ b/arch/x86/kernel/dumpstack_64.c @@ -148,9 +148,9 @@ analyze_stack(int cpu, struct task_struct *task, unsigned long *stack, * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack */ -void dump_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data) +int dump_trace(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data) { const unsigned cpu = get_cpu(); struct thread_info *tinfo; @@ -159,6 +159,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, unsigned used = 0; int graph = 0; int done = 0; + int ret; if (!task) task = current; @@ -198,13 +199,18 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, break; case STACK_IS_EXCEPTION: - - if (ops->stack(data, id) < 0) - break; - - bp = ops->walk_stack(tinfo, stack, bp, ops, - data, stack_end, &graph); - ops->stack(data, ""); + ret = ops->stack(data, id); + if (ret) + goto out; + + ret = ops->walk_stack(tinfo, stack, &bp, ops, data, + stack_end, &graph); + if (ret) + goto out; + + ret = ops->stack(data, ""); + if (ret) + goto out; /* * We link to the next stack via the * second-to-last pointer (index -2 to end) in the @@ -215,11 +221,15 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, break; case STACK_IS_IRQ: + ret = ops->stack(data, "IRQ"); + if (ret) + goto out; + + ret = ops->walk_stack(tinfo, stack, &bp, ops, data, + stack_end, &graph); + if (ret) + goto out; - if (ops->stack(data, "IRQ") < 0) - break; - bp = ops->walk_stack(tinfo, stack, bp, - ops, data, stack_end, &graph); /* * We link to the next stack (which would be * the process stack normally) the last @@ -227,12 +237,18 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, */ stack = (unsigned long *) (stack_end[-1]); irq_stack = NULL; - ops->stack(data, "EOI"); + + ret = ops->stack(data, "EOI"); + if (ret) + goto out; + done = 0; break; case STACK_IS_UNKNOWN: - ops->stack(data, "UNK"); + ret = ops->stack(data, "UNK"); + if (ret) + goto out; break; } } @@ -240,8 +256,11 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, /* * This handles the process stack: */ - bp = ops->walk_stack(tinfo, stack, bp, ops, data, NULL, &graph); + ret = ops->walk_stack(tinfo, stack, &bp, ops, data, NULL, &graph); + +out: put_cpu(); + return ret; } EXPORT_SYMBOL(dump_trace);