From patchwork Mon Jul 6 13:13:58 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Thompson X-Patchwork-Id: 6723741 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 0C1D29F38C for ; Mon, 6 Jul 2015 13:17:10 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id ED29D205DA for ; Mon, 6 Jul 2015 13:17:08 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8E6F4205D6 for ; Mon, 6 Jul 2015 13:17:03 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZC6ES-0007jI-Vv; Mon, 06 Jul 2015 13:14:49 +0000 Received: from mail-wi0-f176.google.com ([209.85.212.176]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZC6E9-0007SS-6b for linux-arm-kernel@lists.infradead.org; Mon, 06 Jul 2015 13:14:30 +0000 Received: by wiwl6 with SMTP id l6so284418175wiw.0 for ; Mon, 06 Jul 2015 06:14:12 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=ewEv2ax8eAnAKfNbUqWiOrQmTmxw2Gv1yxfHMYya8+k=; b=kydQOXUwn+HqcGpm4ECfigBWrY3hkhHBCKX6PgyXE7LnzCDBmGeV7trEXfYo8dpVxP 04ppdCuGAiZYhjg+ptikBCy+INWK2cN2W/C34cvsx+1HbgzqOuRRBfrhZA0Tbn+5DG5f UtoMf2x6tNuKm2s+n4ZNhSYt9PI11oclyQQZqiOrxm0yEby+RoAtUNyjhQ4hSzL9BXPK Zq20IiBWBFb/Yq1SeT3krFltX538JEVE/FRfKCQrQBSjXdVmMezir8g25BpXYsklKJ8w I5W1+Te+2O3EBz6p+AuBl02XCM9a50NxRtB8dazRBpdm3DsFEQenDzgmcqGM+vga7mdt wnhA== X-Gm-Message-State: ALoCoQnBuGmb4FNA7ht0sZfuPeJKHJECAUxMbHEi6XNy3VrY2SOw7UIwN+emSsj4oU+ss1B/i/r5 X-Received: by 10.180.188.48 with SMTP id fx16mr53513572wic.35.1436188452398; Mon, 06 Jul 2015 06:14:12 -0700 (PDT) Received: from wychelm.lan (cpc4-aztw19-0-0-cust71.18-1.cable.virginm.net. [82.33.25.72]) by mx.google.com with ESMTPSA id l14sm28092828wjq.21.2015.07.06.06.14.10 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 06 Jul 2015 06:14:11 -0700 (PDT) From: Daniel Thompson To: Thomas Gleixner , Jason Cooper Subject: [PATCH 4.2-rc1 v21 6/6] ARM: Add support for on-demand backtrace of other CPUs Date: Mon, 6 Jul 2015 14:13:58 +0100 Message-Id: <1436188438-9478-7-git-send-email-daniel.thompson@linaro.org> X-Mailer: git-send-email 2.4.3 In-Reply-To: <1436188438-9478-1-git-send-email-daniel.thompson@linaro.org> References: <1436188438-9478-1-git-send-email-daniel.thompson@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150706_061429_566554_67AB7004 X-CRM114-Status: GOOD ( 27.82 ) X-Spam-Score: -2.6 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Daniel Thompson , linaro-kernel@lists.linaro.org, Russell King , patches@linaro.org, Marc Zyngier , Stephen Boyd , Will Deacon , linux-kernel@vger.kernel.org, Steven Rostedt , Daniel Drake , Dmitry Pervushin , Dirk Behme , John Stultz , Tim Sander , Catalin Marinas , Sumit Semwal , linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Replicate the x86 code to trigger a backtrace using an NMI and hook it up to IPI on ARM. The code differs slightly from the code on x86 because, on ARM, we do now know at compile time whether a platform is capable of supporting FIQ. We must avoid using an IPI to request a backtrace from the CPU on which the backtrace was requested if interrupts are disabled and fall back to generating it directly. In addition the implementation of arch_trigger_all_cpu_backtrace() the patch also includes a few small items of plumbing that must be hooked up for the new code to work. Credit: Russell King provided the initial prototype implementing this feature for ARM. Today the patch has been reworked and, mostly, rewriten to keep it aligned with x86. However this patch does still include some code from Russell's original prototype. Signed-off-by: Daniel Thompson Cc: Russell King Cc: Steven Rostedt --- arch/arm/Kconfig | 1 + arch/arm/include/asm/hardirq.h | 2 +- arch/arm/include/asm/irq.h | 5 +++ arch/arm/include/asm/smp.h | 3 ++ arch/arm/kernel/smp.c | 82 ++++++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/traps.c | 4 +++ 6 files changed, 96 insertions(+), 1 deletion(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a750c1425c3a..8bf16a7438b7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -79,6 +79,7 @@ config ARM select OLD_SIGACTION select OLD_SIGSUSPEND3 select PERF_USE_VMALLOC + select PRINTK_NMI_BACKTRACE select RTC_LIB select SYS_SUPPORTS_APM_EMULATION # Above selects are sorted alphabetically; please add new ones diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h index fe3ea776dc34..5df33e30ae1b 100644 --- a/arch/arm/include/asm/hardirq.h +++ b/arch/arm/include/asm/hardirq.h @@ -5,7 +5,7 @@ #include #include -#define NR_IPI 8 +#define NR_IPI 9 typedef struct { unsigned int __softirq_pending; diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h index 53c15dec7af6..be1d07d59ee9 100644 --- a/arch/arm/include/asm/irq.h +++ b/arch/arm/include/asm/irq.h @@ -35,6 +35,11 @@ extern void (*handle_arch_irq)(struct pt_regs *); extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); #endif +#ifdef CONFIG_SMP +extern void arch_trigger_all_cpu_backtrace(bool); +#define arch_trigger_all_cpu_backtrace(x) arch_trigger_all_cpu_backtrace(x) +#endif + #endif #endif diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index 2f3ac1ba6fb4..652dde6a304c 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -18,6 +18,8 @@ # error " included in non-SMP build" #endif +#define SMP_IPI_FIQ_MASK 0x0100 + #define raw_smp_processor_id() (current_thread_info()->cpu) struct seq_file; @@ -80,6 +82,7 @@ extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); extern void arch_send_wakeup_ipi_mask(const struct cpumask *mask); +extern void ipi_cpu_backtrace(struct pt_regs *regs); extern int register_ipi_completion(struct completion *completion, int cpu); struct smp_operations { diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 90dfbedfbfb8..b28658989d73 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -72,6 +73,7 @@ enum ipi_msg_type { IPI_CPU_STOP, IPI_IRQ_WORK, IPI_COMPLETION, + IPI_CPU_BACKTRACE, }; static DECLARE_COMPLETION(cpu_running); @@ -463,6 +465,7 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = { S(IPI_CPU_STOP, "CPU stop interrupts"), S(IPI_IRQ_WORK, "IRQ work interrupts"), S(IPI_COMPLETION, "completion interrupts"), + S(IPI_CPU_BACKTRACE, "backtrace interrupts"), }; static void smp_cross_call(const struct cpumask *target, unsigned int ipinr) @@ -577,6 +580,8 @@ void handle_IPI(int ipinr, struct pt_regs *regs) unsigned int cpu = smp_processor_id(); struct pt_regs *old_regs = set_irq_regs(regs); + BUILD_BUG_ON(SMP_IPI_FIQ_MASK != BIT(IPI_CPU_BACKTRACE)); + if ((unsigned)ipinr < NR_IPI) { trace_ipi_entry(ipi_types[ipinr]); __inc_irq_stat(cpu, ipi_irqs[ipinr]); @@ -630,6 +635,12 @@ void handle_IPI(int ipinr, struct pt_regs *regs) irq_exit(); break; + case IPI_CPU_BACKTRACE: + irq_enter(); + ipi_cpu_backtrace(regs); + irq_exit(); + break; + default: pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); @@ -724,3 +735,74 @@ static int __init register_cpufreq_notifier(void) core_initcall(register_cpufreq_notifier); #endif + +/* For reliability, we're prepared to waste bits here. */ +static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; + +void arch_trigger_all_cpu_backtrace(bool include_self) +{ + int err, i; + int this_cpu = get_cpu(); + + err = printk_nmi_backtrace_prepare(); + if (err) { + /* + * If there is already an nmi printk sequence in + * progress then just give up... + */ + put_cpu(); + return; + } + + cpumask_copy(to_cpumask(backtrace_mask), cpu_online_mask); + + /* + * If irqs are disabled on the current processor and + * IPI_CPU_BACKTRACE is delivered using IRQ then we aren't be able to + * react to IPI_CPU_BACKTRACE until we leave this function. This + * would force us to get stuck and, eventually, timeout. We avoid + * the timeout (and the resulting failure to print useful information) + * by calling the backtrace logic directly whenever irqs are disabled. + */ + if (include_self && irqs_disabled()) { + ipi_cpu_backtrace(in_interrupt() ? get_irq_regs() : NULL); + include_self = false; + } + + if (!include_self) + cpumask_clear_cpu(this_cpu, to_cpumask(backtrace_mask)); + + if (!cpumask_empty(to_cpumask(backtrace_mask))) { + pr_info("Sending FIQ to %s CPUs:\n", + (include_self ? "all" : "other")); + smp_cross_call(to_cpumask(backtrace_mask), IPI_CPU_BACKTRACE); + } + + /* Wait for up to 10 seconds for all CPUs to do the backtrace */ + for (i = 0; i < 10 * 1000; i++) { + if (cpumask_empty(to_cpumask(backtrace_mask))) + break; + mdelay(1); + touch_softlockup_watchdog(); + } + + printk_nmi_backtrace_complete(); + put_cpu(); +} + +void ipi_cpu_backtrace(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + + if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) { + printk_nmi_backtrace_this_cpu_begin(); + pr_warn("FIQ backtrace for cpu %d\n", cpu); + if (regs != NULL) + show_regs(regs); + else + dump_stack(); + printk_nmi_backtrace_this_cpu_end(); + + cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask)); + } +} diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 5634823a39cf..c5fe42f345a9 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -488,6 +488,10 @@ asmlinkage void __exception_irq_entry handle_fiq_as_nmi(struct pt_regs *regs) */ handle_arch_irq(regs); +#ifdef CONFIG_SMP + ipi_cpu_backtrace(regs); +#endif + nmi_exit(); set_irq_regs(old_regs);