From patchwork Fri Oct 16 07:46:01 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Simon Kagstrom X-Patchwork-Id: 36178 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [18.85.46.34]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 85DC5B7BB0 for ; Fri, 16 Oct 2009 18:48:44 +1100 (EST) Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux)) id 1MyhVu-0005gs-S6; Fri, 16 Oct 2009 07:46:14 +0000 Received: from ernst.netinsight.se ([194.16.221.21]) by bombadil.infradead.org with smtp (Exim 4.69 #1 (Red Hat Linux)) id 1MyhVm-0005Sn-Fz for linux-mtd@lists.infradead.org; Fri, 16 Oct 2009 07:46:11 +0000 Received: from marrow.netinsight.se (unverified [10.100.3.78]) by ernst.netinsight.se (EMWAC SMTPRS 0.83) with SMTP id ; Fri, 16 Oct 2009 09:46:00 +0200 Date: Fri, 16 Oct 2009 09:46:01 +0200 From: Simon Kagstrom To: Ingo Molnar , linux-mtd Subject: [PATCH v9 4/5] core: Add kernel message dumper to call on oopses and panics Message-ID: <20091016094601.4e2c2d3e@marrow.netinsight.se> In-Reply-To: <20091015154640.GA11408@elte.hu> References: <20091015094057.7298e0d7@marrow.netinsight.se> <20091015094805.754461fa@marrow.netinsight.se> <20091015093133.GF10546@elte.hu> <20091015161052.0752208e@marrow.netinsight.se> <20091015154640.GA11408@elte.hu> X-Mailer: Claws Mail 3.7.3 (GTK+ 2.16.1; i486-pc-linux-gnu) Mime-Version: 1.0 X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20091016_034606_846272_ED022F43 X-CRM114-Status: GOOD ( 34.43 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.2.5 on bombadil.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- _SUMMARY_ Cc: Artem Bityutskiy , Linus Torvalds , LKML , "Koskinen Aaro \(Nokia-D/Helsinki\)" , Andrew Morton , David Woodhouse , Alan Cox X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org The core functionality is implemented as per Linus suggestion from http://lists.infradead.org/pipermail/linux-mtd/2009-October/027620.html (with the kmsg_dump implementation by Linus). A struct kmsg_dumper has been added which contains a callback to dump the kernel log buffers on crashes. The kmsg_dump function gets called from oops_exit() and panic() and invokes this callbacks with the crash reason. Signed-off-by: Simon Kagstrom Reviewed-by: Anders Grafstrom Reviewed-by: Ingo Molnar --- ChangeLog: * (Arjan van de Ven): Use EXPORT_SYMBOL_GPL * (Ingo Molnar): Switch list/spinlock definition order * (Ingo Molnar): Set/check ->registered within spinlocked region * (Ingo Molnar): Return error from unregister and check that the dumper really is registered * (Ingo Molnar): Maximise constness and correct array check * (Ingo Molnar): Use _irqsave/_irqrestore also in kmsg_dump * (Ingo Molnar): Style fixes * (Anders Grafström): Correct kerneldoc names include/linux/kmsg_dump.h | 36 ++++++++++++++ kernel/panic.c | 3 + kernel/printk.c | 116 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 0 deletions(-) create mode 100644 include/linux/kmsg_dump.h diff --git a/include/linux/kmsg_dump.h b/include/linux/kmsg_dump.h new file mode 100644 index 0000000..64296f3 --- /dev/null +++ b/include/linux/kmsg_dump.h @@ -0,0 +1,36 @@ +/* + * linux/include/kmsg_dump.h + * + * Copyright (C) 2009 Net Insight AB + * + * Author: Simon Kagstrom + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ +#ifndef _LINUX_KMSG_DUMP_H +#define _LINUX_KMSG_DUMP_H + +#include + +enum kmsg_dump_reason { + KMSG_DUMP_OOPS, + KMSG_DUMP_PANIC, +}; + +struct kmsg_dumper { + void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason, + const char *s1, unsigned long l1, + const char *s2, unsigned long l2); + struct list_head list; + int registered; +}; + +void kmsg_dump(enum kmsg_dump_reason reason); + +int kmsg_dump_register(struct kmsg_dumper *dumper); + +int kmsg_dump_unregister(struct kmsg_dumper *dumper); + +#endif /* _LINUX_DUMP_DEVICE_H */ diff --git a/kernel/panic.c b/kernel/panic.c index c0b33b8..763296d 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -10,6 +10,7 @@ */ #include #include +#include #include #include #include @@ -76,6 +77,7 @@ NORET_TYPE void panic(const char * fmt, ...) dump_stack(); #endif + kmsg_dump(KMSG_DUMP_PANIC); /* * If we have crashed and we have a crash kernel loaded let it handle * everything else. @@ -341,6 +343,7 @@ void oops_exit(void) { do_oops_enter_exit(); print_oops_end_marker(); + kmsg_dump(KMSG_DUMP_OOPS); } #ifdef WANT_WARN_ON_SLOWPATH diff --git a/kernel/printk.c b/kernel/printk.c index f38b07f..d363306 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -1405,3 +1406,118 @@ bool printk_timed_ratelimit(unsigned long *caller_jiffies, } EXPORT_SYMBOL(printk_timed_ratelimit); #endif + +static DEFINE_SPINLOCK(dump_list_lock); +static LIST_HEAD(dump_list); + +/** + * kmsg_dump_register - register a kernel log dumper. + * @dump: pointer to the kmsg_dumper structure + * + * Adds a kernel log dumper to the system. The dump callback in the + * structure will be called when the kernel oopses or panics and must be + * set. Returns zero on success and -EINVAL or -EBUSY otherwise. + */ +int kmsg_dump_register(struct kmsg_dumper *dumper) +{ + unsigned long flags; + + /* The dump callback needs to be set */ + if (!dumper->dump) + return -EINVAL; + + spin_lock_irqsave(&dump_list_lock, flags); + + /* Don't allow registering multiple times */ + if (dumper->registered) { + spin_unlock_irqrestore(&dump_list_lock, flags); + + return -EBUSY; + } + + dumper->registered = 1; + list_add(&dumper->list, &dump_list); + spin_unlock_irqrestore(&dump_list_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(kmsg_dump_register); + +/** + * kmsg_dump_unregister - unregister a kmsg dumper. + * @dump: pointer to the kmsg_dumper structure + * + * Removes a dump device from the system. + */ +int kmsg_dump_unregister(struct kmsg_dumper *dumper) +{ + unsigned long flags; + + spin_lock_irqsave(&dump_list_lock, flags); + if (!dumper->registered) { + spin_unlock_irqrestore(&dump_list_lock, flags); + + return -EINVAL; + } + + dumper->registered = 0; + list_del(&dumper->list); + spin_unlock_irqrestore(&dump_list_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(kmsg_dump_unregister); + +static const char const *kmsg_reasons[] = { + [KMSG_DUMP_OOPS] = "oops", + [KMSG_DUMP_PANIC] = "panic", +}; + +static const char *kmsg_to_str(enum kmsg_dump_reason reason) +{ + if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0) + return "unknown"; + + return kmsg_reasons[reason]; +} + +/** + * kmsg_dump - dump kernel log to kernel message dumpers. + * @reason: the reason (oops, panic etc) for dumping + * + * Iterate through each of the dump devices and call the oops/panic + * callbacks with the log buffer. + */ +void kmsg_dump(enum kmsg_dump_reason reason) +{ + unsigned long len = ACCESS_ONCE(log_end); + struct kmsg_dumper *dumper; + const char *s1, *s2; + unsigned long l1, l2; + unsigned long flags; + + s1 = ""; + l1 = 0; + s2 = log_buf; + l2 = len; + + /* Have we rotated around the circular buffer? */ + if (len > log_buf_len) { + unsigned long pos = len & LOG_BUF_MASK; + + s1 = log_buf + pos; + l1 = log_buf_len - pos; + + s2 = log_buf; + l2 = pos; + } + + if (!spin_trylock_irqsave(&dump_list_lock, flags)) { + printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n", + kmsg_to_str(reason)); + return; + } + list_for_each_entry(dumper, &dump_list, list) + dumper->dump(dumper, reason, s1, l1, s2, l2); + spin_unlock_irqrestore(&dump_list_lock, flags); +}