From patchwork Tue Jan 13 10:26:55 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Thompson X-Patchwork-Id: 42985 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-wg0-f70.google.com (mail-wg0-f70.google.com [74.125.82.70]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 111E820DE8 for ; Tue, 13 Jan 2015 10:27:35 +0000 (UTC) Received: by mail-wg0-f70.google.com with SMTP id y19sf1206215wgg.1 for ; Tue, 13 Jan 2015 02:27:34 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=ExDM9MeRn3i08tGyH7fNLYFL87o+iW6/zbgfazd3n6s=; b=Uk+4quPT2rWC5yxqrF3CVTM4Xed2SywtXBSIJdNiN2h1xcGTdVz2Nwad87YhXEp5Er BgTVkZLvwt4dbsbwjNKCaXyDgEyK4C3y73wGxDXqI09qh5dG25h785IOjyeNXB0bKLlT 3dtlW8aiEXdabrbHC2CIcfzrywsJq2tpi8BpBmGH9dJ4pbTR4Jf5t2boh65zui88A+Y6 Y30ksNDQvPPFjzpr8pk4Guz8OuKhjWZ/i1YG0bmqIh9/DMIK5ma9IzXVOWbQULKZwMRL B/OYIhzbgKLcevgJt6g1vLpzZyx7O2XVvUnxFNjraTPvzRXsCuzbMuouXuORVSJlTjAx MllQ== X-Gm-Message-State: ALoCoQkfdnzJbHyMMYNBNTFRIBwmbm1Nx+gM0MV6DBC2kyP1rdVT+MVqAwLR98IFTHbZQ4TdQXaO X-Received: by 10.152.10.233 with SMTP id l9mr4344155lab.0.1421144854304; Tue, 13 Jan 2015 02:27:34 -0800 (PST) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.27.202 with SMTP id v10ls651078lag.77.gmail; Tue, 13 Jan 2015 02:27:34 -0800 (PST) X-Received: by 10.152.36.37 with SMTP id n5mr40535750laj.27.1421144854069; Tue, 13 Jan 2015 02:27:34 -0800 (PST) Received: from mail-lb0-f182.google.com (mail-lb0-f182.google.com. [209.85.217.182]) by mx.google.com with ESMTPS id o5si2430708lae.100.2015.01.13.02.27.33 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 13 Jan 2015 02:27:33 -0800 (PST) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.182 as permitted sender) client-ip=209.85.217.182; Received: by mail-lb0-f182.google.com with SMTP id u10so1758641lbd.13 for ; Tue, 13 Jan 2015 02:27:33 -0800 (PST) X-Received: by 10.112.104.4 with SMTP id ga4mr41345490lbb.24.1421144853661; Tue, 13 Jan 2015 02:27:33 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.112.9.200 with SMTP id c8csp1337869lbb; Tue, 13 Jan 2015 02:27:33 -0800 (PST) X-Received: by 10.180.19.193 with SMTP id h1mr4187618wie.10.1421144851141; Tue, 13 Jan 2015 02:27:31 -0800 (PST) Received: from mail-we0-f182.google.com (mail-we0-f182.google.com. [74.125.82.182]) by mx.google.com with ESMTPS id hf3si3157568wjc.42.2015.01.13.02.27.30 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 13 Jan 2015 02:27:31 -0800 (PST) Received-SPF: pass (google.com: domain of daniel.thompson@linaro.org designates 74.125.82.182 as permitted sender) client-ip=74.125.82.182; Received: by mail-we0-f182.google.com with SMTP id w62so1959043wes.13 for ; Tue, 13 Jan 2015 02:27:30 -0800 (PST) X-Received: by 10.180.107.228 with SMTP id hf4mr39200288wib.47.1421144850806; Tue, 13 Jan 2015 02:27:30 -0800 (PST) Received: from sundance.lan (cpc4-aztw19-0-0-cust157.18-1.cable.virginm.net. [82.33.25.158]) by mx.google.com with ESMTPSA id u18sm25111297wjq.42.2015.01.13.02.27.28 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 13 Jan 2015 02:27:29 -0800 (PST) From: Daniel Thompson To: Thomas Gleixner , Jason Cooper Cc: Daniel Thompson , Russell King , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, patches@linaro.org, linaro-kernel@lists.linaro.org, John Stultz , Sumit Semwal , Dirk Behme , Daniel Drake , Dmitry Pervushin , Tim Sander , Stephen Boyd , Steven Rostedt Subject: [PATCH 3.19-rc2 v14 4/7] printk: Simple implementation for NMI backtracing Date: Tue, 13 Jan 2015 10:26:55 +0000 Message-Id: <1421144818-14036-5-git-send-email-daniel.thompson@linaro.org> X-Mailer: git-send-email 1.9.3 In-Reply-To: <1421144818-14036-1-git-send-email-daniel.thompson@linaro.org> References: <1415968543-29469-1-git-send-email-daniel.thompson@linaro.org> <1421144818-14036-1-git-send-email-daniel.thompson@linaro.org> X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: daniel.thompson@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.182 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Currently there is a quite a pile of code sitting in arch/x86/kernel/apic/hw_nmi.c to support safe all-cpu backtracing from NMI. The code is inaccessible to backtrace implementations for other architectures, which is a shame because they would probably like to be safe too. Copy this code into printk. We'll port the x86 NMI backtrace to it in a later patch. Incidentally, technically I think it might be safe to call prepare_nmi_printk() from NMI, providing care were taken to honour the return code. complete_nmi_printk() cannot be called from NMI but could be scheduled using irq_work_queue(). However honouring the return code means sometimes it is impossible to get the message out so I'd say using this code in such a way should probably attract sympathy and/or derision rather than admiration. Signed-off-by: Daniel Thompson Cc: Steven Rostedt --- arch/Kconfig | 3 ++ include/linux/printk.h | 22 +++++++++ kernel/printk/printk.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) diff --git a/arch/Kconfig b/arch/Kconfig index 05d7a8a458d5..50c9412a77d0 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -309,6 +309,9 @@ config ARCH_WANT_OLD_COMPAT_IPC select ARCH_WANT_COMPAT_IPC_PARSE_VERSION bool +config ARCH_WANT_NMI_PRINTK + bool + config HAVE_ARCH_SECCOMP_FILTER bool help diff --git a/include/linux/printk.h b/include/linux/printk.h index c8f170324e64..539ea5a8f219 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -219,6 +219,28 @@ static inline void show_regs_print_info(const char *log_lvl) } #endif +#ifdef CONFIG_ARCH_WANT_NMI_PRINTK +extern __printf(1, 0) int nmi_vprintk(const char *fmt, va_list args); + +struct cpumask; +extern int prepare_nmi_printk(struct cpumask *cpus); +extern void complete_nmi_printk(struct cpumask *cpus); + +/* + * Replace printk to write into the NMI seq. + * + * To avoid include hell this is a macro rather than an inline function + * (printk_func is not declared in this header file). + */ +#define this_cpu_begin_nmi_printk() ({ \ + printk_func_t orig = this_cpu_read(printk_func); \ + this_cpu_write(printk_func, nmi_vprintk); \ + orig; \ +}) +#define this_cpu_end_nmi_printk(fn) this_cpu_write(printk_func, fn) + +#endif + extern asmlinkage void dump_stack(void) __cold; #ifndef pr_fmt diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 02d6b6d28796..774119e27e0b 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -1805,6 +1805,127 @@ asmlinkage int printk_emit(int facility, int level, } EXPORT_SYMBOL(printk_emit); +#ifdef CONFIG_ARCH_WANT_NMI_PRINTK + +#define NMI_BUF_SIZE 4096 + +struct nmi_seq_buf { + unsigned char buffer[NMI_BUF_SIZE]; + struct seq_buf seq; +}; + +/* Safe printing in NMI context */ +static DEFINE_PER_CPU(struct nmi_seq_buf, nmi_print_seq); + +/* "in progress" flag of NMI printing */ +static unsigned long nmi_print_flag; + +/* + * It is not safe to call printk() directly from NMI handlers. + * It may be fine if the NMI detected a lock up and we have no choice + * but to do so, but doing a NMI on all other CPUs to get a back trace + * can be done with a sysrq-l. We don't want that to lock up, which + * can happen if the NMI interrupts a printk in progress. + * + * Instead, we redirect the vprintk() to this nmi_vprintk() that writes + * the content into a per cpu seq_buf buffer. Then when the NMIs are + * all done, we can safely dump the contents of the seq_buf to a printk() + * from a non NMI context. + * + * This is not a generic printk() implementation and must be used with + * great care. In particular there is a static limit on the quantity of + * data that may be emitted during NMI, only one client can be active at + * one time (arbitrated by the return value of begin_nmi_printk() and + * it is required that something at task or interrupt context be scheduled + * to issue the output. + */ +int nmi_vprintk(const char *fmt, va_list args) +{ + struct nmi_seq_buf *s = this_cpu_ptr(&nmi_print_seq); + unsigned int len = seq_buf_used(&s->seq); + + seq_buf_vprintf(&s->seq, fmt, args); + return seq_buf_used(&s->seq) - len; +} +EXPORT_SYMBOL_GPL(nmi_vprintk); + +/* + * Check for concurrent usage and set up per_cpu seq_buf buffers that the NMIs + * running on the other CPUs will write to. Provides the mask of CPUs it is + * safe to write from (i.e. a copy of the online mask). + */ +int prepare_nmi_printk(struct cpumask *cpus) +{ + struct nmi_seq_buf *s; + int cpu; + + if (test_and_set_bit(0, &nmi_print_flag)) { + /* + * If something is already using the NMI print facility we + * can't allow a second one... + */ + return -EBUSY; + } + + cpumask_copy(cpus, cpu_online_mask); + + for_each_cpu(cpu, cpus) { + s = &per_cpu(nmi_print_seq, cpu); + seq_buf_init(&s->seq, s->buffer, NMI_BUF_SIZE); + } + + return 0; +} +EXPORT_SYMBOL_GPL(prepare_nmi_printk); + +static void print_seq_line(struct nmi_seq_buf *s, int start, int end) +{ + const char *buf = s->buffer + start; + + printk("%.*s", (end - start) + 1, buf); +} + +void complete_nmi_printk(struct cpumask *cpus) +{ + struct nmi_seq_buf *s; + int len; + int cpu; + int i; + + /* + * Now that all the NMIs have triggered, we can dump out their + * back traces safely to the console. + */ + for_each_cpu(cpu, cpus) { + int last_i = 0; + + s = &per_cpu(nmi_print_seq, cpu); + + len = seq_buf_used(&s->seq); + if (!len) + continue; + + /* Print line by line. */ + for (i = 0; i < len; i++) { + if (s->buffer[i] == '\n') { + print_seq_line(s, last_i, i); + last_i = i + 1; + } + } + /* Check if there was a partial line. */ + if (last_i < len) { + print_seq_line(s, last_i, len - 1); + pr_cont("\n"); + } + } + + clear_bit(0, &nmi_print_flag); + smp_mb__after_atomic(); +} +EXPORT_SYMBOL_GPL(complete_nmi_printk); + +#endif /* CONFIG_ARCH_WANT_NMI_PRINTK */ + int vprintk_default(const char *fmt, va_list args) { int r; @@ -1829,6 +1950,7 @@ EXPORT_SYMBOL_GPL(vprintk_default); */ DEFINE_PER_CPU(printk_func_t, printk_func) = vprintk_default; + /** * printk - print a kernel message * @fmt: format string