From: Dragos Tatulea <[email protected]>
This patchset adds an interface for registering various clients (dumpers)
that can save info into pstore at crash time.
2 dumpers are added:
* task list dumper
* Android logs dumper
v3 changes:
Added Android log dumping.
v2 changes:
v1 patchset was corrupted.
Adrian Hunter (4):
pstore: add flags
pstore: add debugfs support for causing dumps and panics
pstore: add support for external writers
pstore: do not run timer while pstore is not mounted
Dragos Tatulea (4):
pstore: allow storing different type id's in ram backend
pstore: add task list dumper
pstore: make sure pstore_write exists on flush error
pstore: max out console log level during sysfs dump switch
fs/pstore/Kconfig | 12 ++++
fs/pstore/Makefile | 1 +
fs/pstore/dump_tasks.c | 107 ++++++++++++++++++++++++++++
fs/pstore/inode.c | 6 ++
fs/pstore/internal.h | 2 +
fs/pstore/platform.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++--
fs/pstore/ram.c | 18 +++--
include/linux/pstore.h | 46 ++++++++++--
8 files changed, 356 insertions(+), 20 deletions(-)
create mode 100644 fs/pstore/dump_tasks.c
--
1.7.9.5
From: Adrian Hunter <[email protected]>
There are 2 debugfs files:
pstore/dump writing a kmsg dump reason code
will cause a dump to persistent
storage
pstore/panic writing anything will cause
a panic
Signed-off-by: Adrian Hunter <[email protected]>
---
fs/pstore/platform.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index c4ea778..9d65723 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -33,6 +33,7 @@
#include <linux/hardirq.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
+#include <linux/debugfs.h>
#include "internal.h"
@@ -206,6 +207,54 @@ static int pstore_write_compat(enum pstore_type_id type,
return psi->write_buf(type, reason, id, part, psinfo->buf, size, psi);
}
+#ifdef CONFIG_DEBUG_FS
+
+static DEFINE_SPINLOCK(dbg_lock);
+
+static int dbg_dump(void *data, u64 val)
+{
+ unsigned long flags;
+
+ switch (val) {
+ case KMSG_DUMP_PANIC:
+ case KMSG_DUMP_OOPS:
+ case KMSG_DUMP_EMERG:
+ case KMSG_DUMP_RESTART:
+ case KMSG_DUMP_HALT:
+ case KMSG_DUMP_POWEROFF:
+ spin_lock_irqsave(&dbg_lock, flags);
+ kmsg_dump(val);
+ spin_unlock_irqrestore(&dbg_lock, flags);
+ return 0;
+ }
+ return -EINVAL;
+}
+DEFINE_SIMPLE_ATTRIBUTE(dbg_dump_fops, NULL, dbg_dump, "%llu\n");
+
+static int dbg_panic(void *data, u64 val)
+{
+ panic(KERN_EMERG "pstore debugging panic!\n");
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(dbg_panic_fops, NULL, dbg_panic, "%llu\n");
+
+static void pstore_debugfs_init(void)
+{
+ struct dentry *root;
+
+ root = debugfs_create_dir("pstore", NULL);
+ debugfs_create_file("dump", S_IWUSR, root, NULL, &dbg_dump_fops);
+ debugfs_create_file("panic", S_IWUSR, root, NULL, &dbg_panic_fops);
+}
+
+#else
+
+static inline void pstore_debugfs_init(void)
+{
+}
+
+#endif
+
/*
* platform specific persistent storage driver registers with
* us here. If pstore is already mounted, call the platform
@@ -257,6 +306,8 @@ int pstore_register(struct pstore_info *psi)
add_timer(&pstore_timer);
}
+ pstore_debugfs_init();
+
return 0;
}
EXPORT_SYMBOL_GPL(pstore_register);
--
1.7.9.5
From: Dragos Tatulea <[email protected]>
The task dumper can dump task information during a panic.
This is equivalent to a magic sysrq 't' command but
the result is captured from the console and written
to persistent storage. Note that this happens after
pstore dumps kernel messages because the task dump will
overwrite other kernel messages.
There is a single module parameter "enabled" which must
be used to enable task dumping.
Signed-off-by: Adrian Hunter <[email protected]>
Signed-off-by: Dragos Tatulea <[email protected]>
---
fs/pstore/Kconfig | 12 ++++++
fs/pstore/Makefile | 1 +
fs/pstore/dump_tasks.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++
fs/pstore/inode.c | 3 ++
include/linux/pstore.h | 1 +
5 files changed, 124 insertions(+)
create mode 100644 fs/pstore/dump_tasks.c
diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
index ca71db6..ee967c5 100644
--- a/fs/pstore/Kconfig
+++ b/fs/pstore/Kconfig
@@ -48,3 +48,15 @@ config PSTORE_RAM
"ramoops.ko".
For more information, see Documentation/ramoops.txt.
+
+config PSTORE_DUMP_TASKS
+ bool "Dump task information"
+ default n
+ depends on PSTORE
+ help
+ This option allows a dump of task information during a
+ panic. This is equivalent to a magic sysrq 't' command
+ but the result is captured from the console and written
+ to persistent storage. Note that this happens after
+ pstore dumps kernel messages because the task dump will
+ overwrite other kernel messages.
diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile
index 4c9095c..14b5c83 100644
--- a/fs/pstore/Makefile
+++ b/fs/pstore/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o
ramoops-objs += ram.o ram_core.o
obj-$(CONFIG_PSTORE_RAM) += ramoops.o
+obj-$(CONFIG_PSTORE_DUMP_TASKS) += dump_tasks.o
diff --git a/fs/pstore/dump_tasks.c b/fs/pstore/dump_tasks.c
new file mode 100644
index 0000000..b010b35
--- /dev/null
+++ b/fs/pstore/dump_tasks.c
@@ -0,0 +1,107 @@
+/*
+ * Persistent Storage task dumper
+ *
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/sched.h>
+#include <linux/hardirq.h>
+#include <linux/delay.h>
+#include <linux/pstore.h>
+
+static int enabled;
+
+static void pstore_dump_tasks(struct console *console, const char *s,
+ unsigned int count)
+{
+ pstore_write(PSTORE_TYPE_TASK_DUMP, s, count);
+}
+
+static struct console pstore_dump_tasks_console = {
+ .name = "dump_tasks",
+ .write = pstore_dump_tasks,
+ .flags = CON_ANYTIME | CON_ENABLED,
+ .index = -1,
+};
+
+static int pstore_notifier_cb(struct notifier_block *nb, unsigned long event,
+ void *_psinfo)
+{
+ struct pstore_info *psinfo = _psinfo;
+ int retry;
+
+ if (psinfo->ext_reason != KMSG_DUMP_PANIC || !enabled)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case PSTORE_DUMP:
+ pstore_dump_tasks_console.flags |= CON_ENABLED;
+ show_state();
+
+ /* Make sure data gets pushed to console drivers.
+ * Yes, can take a long time to write everything,
+ * shortening the length increases the chances of
+ * ending up with an incomplete log.
+ */
+ retry = 100;
+ while (retry) {
+ if (console_trylock()) {
+ console_unlock();
+ break;
+ } else {
+ mdelay(100);
+ retry--;
+ }
+ }
+
+ break;
+ case PSTORE_END:
+ pstore_dump_tasks_console.flags &= ~CON_ENABLED;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block pstore_notifier = {
+ .notifier_call = pstore_notifier_cb,
+ /* Leave other dumpers do their job. This one can take longer. */
+ .priority = -1,
+};
+
+static int __init pstore_dump_tasks_init(void)
+{
+ register_console(&pstore_dump_tasks_console);
+ console_stop(&pstore_dump_tasks_console);
+ pstore_notifier_register(&pstore_notifier);
+ return 0;
+}
+module_init(pstore_dump_tasks_init);
+
+static void __exit pstore_dump_tasks_exit(void)
+{
+ pstore_notifier_unregister(&pstore_notifier);
+ unregister_console(&pstore_dump_tasks_console);
+}
+module_exit(pstore_dump_tasks_exit);
+
+module_param(enabled, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(enabled, "set to 1 to enable task dump, 0 to disable (default 0)");
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_DESCRIPTION("Persistent Storage task dumper");
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 4ab572e..4800c09 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -321,6 +321,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
case PSTORE_TYPE_MCE:
sprintf(name, "mce-%s-%lld", psname, id);
break;
+ case PSTORE_TYPE_TASK_DUMP:
+ sprintf(name, "tasks-%s-%lld", psname, id);
+ break;
case PSTORE_TYPE_UNKNOWN:
sprintf(name, "unknown-%s-%lld", psname, id);
break;
diff --git a/include/linux/pstore.h b/include/linux/pstore.h
index 1bf7f4b..48dcafb 100644
--- a/include/linux/pstore.h
+++ b/include/linux/pstore.h
@@ -35,6 +35,7 @@ enum pstore_type_id {
PSTORE_TYPE_MCE = 1,
PSTORE_TYPE_CONSOLE = 2,
PSTORE_TYPE_FTRACE = 3,
+ PSTORE_TYPE_TASK_DUMP = 4,
PSTORE_TYPE_UNKNOWN = 255
};
--
1.7.9.5
From: Adrian Hunter <[email protected]>
Other modules that may wish to write to persistent storage
are supported by adding a notifier and write function.
The notifier has 3 events: PSTORE_BEGIN, PSTORE_DUMP and
PSTORE_END. External writers use the PSTORE_DUMP event
whereas the PSTORE_BEGIN and PSTORE_END can be used by
platform code to ensure the back end is powered up.
Signed-off-by: Adrian Hunter <[email protected]>
---
fs/pstore/platform.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/pstore.h | 43 +++++++++++++++++++----
2 files changed, 124 insertions(+), 7 deletions(-)
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 9d65723..63ff377 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -34,6 +34,7 @@
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
+#include <linux/notifier.h>
#include "internal.h"
@@ -74,6 +75,20 @@ void pstore_set_kmsg_bytes(int bytes)
kmsg_bytes = bytes;
}
+static ATOMIC_NOTIFIER_HEAD(pstore_notifiers);
+
+int pstore_notifier_register(struct notifier_block *n)
+{
+ return atomic_notifier_chain_register(&pstore_notifiers, n);
+}
+EXPORT_SYMBOL_GPL(pstore_notifier_register);
+
+int pstore_notifier_unregister(struct notifier_block *n)
+{
+ return atomic_notifier_chain_unregister(&pstore_notifiers, n);
+}
+EXPORT_SYMBOL_GPL(pstore_notifier_unregister);
+
/* Tag each group of saved records with a sequence number */
static int oopscount;
@@ -97,6 +112,26 @@ static const char *get_reason_str(enum kmsg_dump_reason reason)
}
}
+static int pstore_ext_flush(void)
+{
+ int ret;
+
+ if (!psinfo->ext_len)
+ return 0;
+
+ ret = psinfo->write(psinfo->ext_type, psinfo->ext_reason,
+ &psinfo->ext_id, psinfo->ext_part++,
+ psinfo->ext_len, psinfo);
+
+ if (ret == 0 && psinfo->ext_reason == KMSG_DUMP_OOPS &&
+ pstore_is_mounted())
+ pstore_new_entry = 1;
+
+ psinfo->ext_len = 0;
+
+ return ret;
+}
+
/*
* callback from kmsg_dump. (s2,l2) has the most recently
* written bytes, older bytes are in (s1,l1). Save as much
@@ -122,6 +157,15 @@ static void pstore_dump(struct kmsg_dumper *dumper,
} else
spin_lock_irqsave(&psinfo->buf_lock, flags);
oopscount++;
+
+ psinfo->ext_id = 0;
+ psinfo->ext_len = 0;
+ psinfo->ext_part = 0;
+ psinfo->ext_type = PSTORE_TYPE_UNKNOWN;
+ psinfo->ext_reason = reason;
+
+ atomic_notifier_call_chain(&pstore_notifiers, PSTORE_BEGIN, psinfo);
+
while (total < kmsg_bytes) {
char *dst;
unsigned long size;
@@ -148,6 +192,13 @@ static void pstore_dump(struct kmsg_dumper *dumper,
total += hsize + len;
part++;
}
+
+ atomic_notifier_call_chain(&pstore_notifiers, PSTORE_DUMP, psinfo);
+
+ pstore_ext_flush();
+
+ atomic_notifier_call_chain(&pstore_notifiers, PSTORE_END, psinfo);
+
if (in_nmi()) {
if (is_locked)
spin_unlock(&psinfo->buf_lock);
@@ -368,5 +419,42 @@ static void pstore_timefunc(unsigned long dummy)
mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms));
}
+/* pstore_write must only be called from PSTORE_DUMP notifier callbacks */
+int pstore_write(enum pstore_type_id type, const char *buf, size_t size)
+{
+ size_t len;
+ int err = 0, err2;
+
+ if (!psinfo)
+ return -ENODEV;
+
+ /*
+ * No locking is needed because pstore_write is called only from
+ * PSTORE_DUMP notifier callbacks.
+ */
+
+ if (type != psinfo->ext_type) {
+ err = pstore_ext_flush();
+ psinfo->ext_type = type;
+ psinfo->ext_part = 1;
+ }
+
+ while (size) {
+ len = min(size, psinfo->bufsize - psinfo->ext_len);
+ memcpy(psinfo->buf + psinfo->ext_len, buf, len);
+ psinfo->ext_len += len;
+ buf += len;
+ size -= len;
+ if (psinfo->ext_len == psinfo->bufsize) {
+ err2 = pstore_ext_flush();
+ if (err2 && !err)
+ err = err2;
+ }
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(pstore_write);
+
module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "Pstore backend to use");
diff --git a/include/linux/pstore.h b/include/linux/pstore.h
index 55ab23f..1bf7f4b 100644
--- a/include/linux/pstore.h
+++ b/include/linux/pstore.h
@@ -40,17 +40,27 @@ enum pstore_type_id {
struct module;
+/* Notifier events */
+#define PSTORE_BEGIN 1
+#define PSTORE_DUMP 2
+#define PSTORE_END 3
+
#define PSTORE_NO_HEADINGS BIT(0)
#define PSTORE_MAX_KMSG_BYTES BIT(1)
struct pstore_info {
- struct module *owner;
- char *name;
- unsigned int flags;
- spinlock_t buf_lock; /* serialize access to 'buf' */
- char *buf;
- size_t bufsize;
- struct mutex read_mutex; /* serialize open/read/close */
+ struct module *owner;
+ char *name;
+ unsigned int flags;
+ spinlock_t buf_lock; /* serialize access to 'buf' */
+ char *buf;
+ size_t bufsize;
+ struct mutex read_mutex; /* serialize open/read/close */
+ u64 ext_id;
+ size_t ext_len;
+ unsigned int ext_part;
+ enum pstore_type_id ext_type;
+ enum kmsg_dump_reason ext_reason;
int (*open)(struct pstore_info *psi);
int (*close)(struct pstore_info *psi);
ssize_t (*read)(u64 *id, enum pstore_type_id *type,
@@ -70,12 +80,31 @@ struct pstore_info {
#ifdef CONFIG_PSTORE
extern int pstore_register(struct pstore_info *);
+extern int pstore_notifier_register(struct notifier_block *n);
+extern int pstore_notifier_unregister(struct notifier_block *n);
+/* pstore_write must only be called from PSTORE_DUMP notifier callbacks */
+extern int pstore_write(enum pstore_type_id type, const char *buf, size_t size);
#else
static inline int
pstore_register(struct pstore_info *psi)
{
return -ENODEV;
}
+static inline int
+pstore_notifier_register(struct notifier_block *n)
+{
+ return 0;
+}
+static inline int
+pstore_notifier_unregister(struct notifier_block *n)
+{
+ return 0;
+}
+static inline int
+pstore_write(enum pstore_type_id type, const char *buf, size_t size)
+{
+ return 0;
+}
#endif
#endif /*_LINUX_PSTORE_H*/
--
1.7.9.5
From: Dragos Tatulea <[email protected]>
Otherwise we might miss out on some pstore dumpers that use
printks below current log level.
Signed-off-by: Dragos Tatulea <[email protected]>
---
fs/pstore/platform.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 25f59ed..e3ad13e 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -267,6 +267,7 @@ static DEFINE_SPINLOCK(dbg_lock);
static int dbg_dump(void *data, u64 val)
{
unsigned long flags;
+ int saved_loglevel;
switch (val) {
case KMSG_DUMP_PANIC:
@@ -276,7 +277,10 @@ static int dbg_dump(void *data, u64 val)
case KMSG_DUMP_HALT:
case KMSG_DUMP_POWEROFF:
spin_lock_irqsave(&dbg_lock, flags);
+ saved_loglevel = console_loglevel;
+ console_loglevel = 15;
kmsg_dump(val);
+ console_loglevel = saved_loglevel;
spin_unlock_irqrestore(&dbg_lock, flags);
return 0;
}
--
1.7.9.5
From: Dragos Tatulea <[email protected]>
Return error if flushing to backend failed.
Signed-off-by: Dragos Tatulea <[email protected]>
---
fs/pstore/platform.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 3a4c38f..25f59ed 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -445,7 +445,7 @@ void pstore_del_timer(void)
int pstore_write(enum pstore_type_id type, const char *buf, size_t size)
{
size_t len;
- int err = 0, err2;
+ int err = 0;
if (!psinfo)
return -ENODEV;
@@ -461,16 +461,14 @@ int pstore_write(enum pstore_type_id type, const char *buf, size_t size)
psinfo->ext_part = 1;
}
- while (size) {
+ while (size && !err) {
len = min(size, psinfo->bufsize - psinfo->ext_len);
memcpy(psinfo->buf + psinfo->ext_len, buf, len);
psinfo->ext_len += len;
buf += len;
size -= len;
if (psinfo->ext_len == psinfo->bufsize) {
- err2 = pstore_ext_flush();
- if (err2 && !err)
- err = err2;
+ err = pstore_ext_flush();
}
}
--
1.7.9.5
From: Dragos Tatulea <[email protected]>
Dump android binary logs to pstore on panic.
Change-Id: I8ba59d498eae252f4c3844ece8be2a9c4979ecd2
Signed-off-by: Dragos Tatulea <[email protected]>
---
drivers/staging/android/Kconfig | 12 ++++++
drivers/staging/android/logger.c | 87 ++++++++++++++++++++++++++++++++++++++
fs/pstore/inode.c | 12 ++++++
include/linux/pstore.h | 16 ++++---
4 files changed, 121 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index 0ce50d1..72b0de3 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -49,6 +49,18 @@ config ANDROID_INTF_ALARM_DEV
elapsed realtime, and a non-wakeup alarm on the monotonic clock.
Also exports the alarm interface to user-space.
+config ANDROID_LOGGER_PANIC_DUMP
+ bool "Panic dump to pstore"
+ default n
+ depends on ANDROID_LOGGER
+ depends on PSTORE
+ help
+ This options allows the panic dumping of log data to persistent
+ storage. If the 'enabled' parameter is non-zero, during a panic
+ each log will be copied in binary format to pstore and, after
+ the next boot, will appear as files in the pstore file system.
+
+
endif # if ANDROID
endmenu
diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c
index 1d5ed47..f7f9c09 100644
--- a/drivers/staging/android/logger.c
+++ b/drivers/staging/android/logger.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
+#include <linux/pstore.h>
#include "logger.h"
#include <asm/ioctls.h>
@@ -653,6 +654,88 @@ out_free_buffer:
return ret;
}
+static int panic_dump;
+module_param(panic_dump, int, S_IRUSR | S_IWUSR);
+
+#ifdef CONFIG_ANDROID_LOGGER_PANIC_DUMP
+
+static void logger_pstore_dump(struct pstore_info *psinfo,
+ enum pstore_type_id type,
+ struct logger_log *log)
+{
+ size_t len1, len2, offs, w_offs;
+
+ /* Ignoring buffer locks because:
+ * - we are in an oops and it won't make any difference
+ * - it's just a read access so no danger of corrupting
+ * anything
+ */
+ offs = log->head;
+ w_offs = log->w_off;
+
+ if (offs < w_offs) {
+ len1 = w_offs - offs;
+ len2 = 0;
+ } else {
+ len1 = log->size - offs;
+ len2 = w_offs;
+ }
+
+ pstore_write(type, log->buffer + offs, len1);
+ pstore_write(type, log->buffer, len2);
+}
+
+static enum pstore_type_id get_pstore_log_type(const struct logger_log *log)
+{
+ enum pstore_type_id type = PSTORE_TYPE_UNKNOWN;
+
+ if (!strcmp(log->misc.name, LOGGER_LOG_MAIN))
+ type = PSTORE_TYPE_ANDROID_LOG_MAIN;
+ else if (!strcmp(log->misc.name, LOGGER_LOG_EVENTS))
+ type = PSTORE_TYPE_ANDROID_LOG_EVENTS;
+ else if (!strcmp(log->misc.name, LOGGER_LOG_RADIO))
+ type = PSTORE_TYPE_ANDROID_LOG_RADIO;
+ else if (!strcmp(log->misc.name, LOGGER_LOG_SYSTEM))
+ type = PSTORE_TYPE_ANDROID_LOG_SYSTEM;
+
+ return type;
+}
+
+static int pstore_notifier_cb(struct notifier_block *nb, unsigned long event,
+ void *_psinfo)
+{
+ struct pstore_info *psinfo = _psinfo;
+
+ if (!panic_dump || psinfo->ext_reason != KMSG_DUMP_PANIC)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case PSTORE_DUMP: {
+ struct logger_log *log;
+ list_for_each_entry(log, &log_list, logs) {
+ logger_pstore_dump(psinfo,
+ get_pstore_log_type(log),
+ log);
+ }
+ break;
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block pstore_notifier = {
+ .notifier_call = pstore_notifier_cb,
+};
+
+MODULE_PARM_DESC(panic_dump, "set to 1 to enable panic dump, 0 to disable (default 0)");
+
+#else
+
+MODULE_PARM_DESC(panic_dump, "panic dump is not supported");
+
+#endif
+
static int __init logger_init(void)
{
int ret;
@@ -673,6 +756,10 @@ static int __init logger_init(void)
if (unlikely(ret))
goto out;
+#ifdef CONFIG_ANDROID_LOGGER_PANIC_DUMP
+ pstore_notifier_register(&pstore_notifier);
+#endif
+
out:
return ret;
}
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 5e20a8d..8b1179d 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -324,6 +324,18 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
case PSTORE_TYPE_TASK_DUMP:
sprintf(name, "tasks-%s-%lld", psname, id);
break;
+ case PSTORE_TYPE_ANDROID_LOG_MAIN:
+ sprintf(name, "android_log_main-%s-%lld", psname, id);
+ break;
+ case PSTORE_TYPE_ANDROID_LOG_EVENTS:
+ sprintf(name, "android_log_events-%s-%lld", psname, id);
+ break;
+ case PSTORE_TYPE_ANDROID_LOG_RADIO:
+ sprintf(name, "android_log_radio-%s-%lld", psname, id);
+ break;
+ case PSTORE_TYPE_ANDROID_LOG_SYSTEM:
+ sprintf(name, "android_log_system-%s-%lld", psname, id);
+ break;
case PSTORE_TYPE_UNKNOWN:
sprintf(name, "unknown-%s-%lld", psname, id);
break;
diff --git a/include/linux/pstore.h b/include/linux/pstore.h
index 48dcafb..08b496a 100644
--- a/include/linux/pstore.h
+++ b/include/linux/pstore.h
@@ -31,12 +31,16 @@
/* types */
enum pstore_type_id {
- PSTORE_TYPE_DMESG = 0,
- PSTORE_TYPE_MCE = 1,
- PSTORE_TYPE_CONSOLE = 2,
- PSTORE_TYPE_FTRACE = 3,
- PSTORE_TYPE_TASK_DUMP = 4,
- PSTORE_TYPE_UNKNOWN = 255
+ PSTORE_TYPE_DMESG = 0,
+ PSTORE_TYPE_MCE = 1,
+ PSTORE_TYPE_CONSOLE = 2,
+ PSTORE_TYPE_FTRACE = 3,
+ PSTORE_TYPE_TASK_DUMP = 4,
+ PSTORE_TYPE_ANDROID_LOG_MAIN = 5,
+ PSTORE_TYPE_ANDROID_LOG_EVENTS = 6,
+ PSTORE_TYPE_ANDROID_LOG_RADIO = 7,
+ PSTORE_TYPE_ANDROID_LOG_SYSTEM = 8,
+ PSTORE_TYPE_UNKNOWN = 255
};
struct module;
--
1.7.9.5
From: Dragos Tatulea <[email protected]>
Added pstore_type_id in message header when storing to ram.
On write, take into account the contents of this header and
set the type accordingly.
Signed-off-by: Dragos Tatulea <[email protected]>
---
fs/pstore/ram.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index 1a4f6da..e1684ef 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -157,23 +157,30 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
time->tv_nsec = 0;
size = persistent_ram_old_size(prz);
+ if (!size)
+ return 0;
*buf = kmalloc(size, GFP_KERNEL);
if (*buf == NULL)
return -ENOMEM;
memcpy(*buf, persistent_ram_old(prz), size);
+ /* Read header. */
+ sscanf(*buf, "%u" RAMOOPS_KERNMSG_HDR "%lu.%lu",
+ type, &time->tv_sec, &time->tv_nsec);
+ time->tv_nsec *= 1000;
return size;
}
-static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz)
+static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz,
+ enum pstore_type_id type)
{
char *hdr;
struct timeval timestamp;
size_t len;
do_gettimeofday(×tamp);
- hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
- (long)timestamp.tv_sec, (long)timestamp.tv_usec);
+ hdr = kasprintf(GFP_ATOMIC, "%u" RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
+ type, (long)timestamp.tv_sec, (long)timestamp.tv_usec);
WARN_ON_ONCE(!hdr);
len = hdr ? strlen(hdr) : 0;
persistent_ram_write(prz, hdr, len);
@@ -204,9 +211,6 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
return 0;
}
- if (type != PSTORE_TYPE_DMESG)
- return -EINVAL;
-
/* Out of the various dmesg dump types, ramoops is currently designed
* to only store crash logs, rather than storing general kernel logs.
*/
@@ -226,7 +230,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
if (part != 1)
return -ENOSPC;
- hlen = ramoops_write_kmsg_hdr(prz);
+ hlen = ramoops_write_kmsg_hdr(prz, type);
if (size + hlen > prz->buffer_size)
size = prz->buffer_size - hlen;
persistent_ram_write(prz, buf, size);
--
1.7.9.5
From: Adrian Hunter <[email protected]>
A timer is used to periodically check for new dumps.
The timer is not needed unless pstore is mounted,
so disable it otherwise.
Signed-off-by: Adrian Hunter <[email protected]>
---
fs/pstore/inode.c | 3 +++
fs/pstore/internal.h | 2 ++
fs/pstore/platform.c | 36 +++++++++++++++++++++++++++++-------
3 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 4800c09..5e20a8d 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -397,6 +397,8 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
pstore_get_records(0);
+ pstore_add_timer();
+
return 0;
}
@@ -408,6 +410,7 @@ static struct dentry *pstore_mount(struct file_system_type *fs_type,
static void pstore_kill_sb(struct super_block *sb)
{
+ pstore_del_timer();
kill_litter_super(sb);
pstore_sb = NULL;
}
diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h
index 4847f58..c7b8197 100644
--- a/fs/pstore/internal.h
+++ b/fs/pstore/internal.h
@@ -54,4 +54,6 @@ extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
struct timespec time, struct pstore_info *psi);
extern int pstore_is_mounted(void);
+extern void pstore_add_timer(void);
+extern void pstore_del_timer(void);
#endif
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 63ff377..3a4c38f 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -54,6 +54,8 @@ static int pstore_new_entry;
static void pstore_timefunc(unsigned long);
static DEFINE_TIMER(pstore_timer, pstore_timefunc, 0, 0);
+static DEFINE_SPINLOCK(pstore_timer_lock);
+static int pstore_timer_on;
static void pstore_dowork(struct work_struct *);
static DECLARE_WORK(pstore_work, pstore_dowork);
@@ -351,12 +353,6 @@ int pstore_register(struct pstore_info *psi)
pstore_register_console();
pstore_register_ftrace();
- if (pstore_update_ms >= 0) {
- pstore_timer.expires = jiffies +
- msecs_to_jiffies(pstore_update_ms);
- add_timer(&pstore_timer);
- }
-
pstore_debugfs_init();
return 0;
@@ -411,12 +407,38 @@ static void pstore_dowork(struct work_struct *work)
static void pstore_timefunc(unsigned long dummy)
{
+ unsigned long flags;
+
if (pstore_new_entry) {
pstore_new_entry = 0;
schedule_work(&pstore_work);
}
- mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms));
+ spin_lock_irqsave(&pstore_timer_lock, flags);
+ if (pstore_timer_on)
+ mod_timer(&pstore_timer,
+ jiffies + msecs_to_jiffies(pstore_update_ms));
+ spin_unlock_irqrestore(&pstore_timer_lock, flags);
+}
+
+void pstore_add_timer(void)
+{
+ pstore_timer_on = 1;
+ if (pstore_update_ms >= 0) {
+ pstore_timer.expires = jiffies +
+ msecs_to_jiffies(pstore_update_ms);
+ add_timer(&pstore_timer);
+ }
+}
+
+void pstore_del_timer(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pstore_timer_lock, flags);
+ pstore_timer_on = 0;
+ spin_unlock_irqrestore(&pstore_timer_lock, flags);
+ del_timer_sync(&pstore_timer);
}
/* pstore_write must only be called from PSTORE_DUMP notifier callbacks */
--
1.7.9.5
From: Adrian Hunter <[email protected]>
Let the back end tweak pstore behaviour. Flags added are:
PSTORE_NO_HEADINGS
Omit pstore heading lines from dumped data
PSTORE_MAX_KMSG_BYTES
Default kmsg_bytes to ULONG_MAX
Signed-off-by: Adrian Hunter <[email protected]>
---
fs/pstore/platform.c | 9 ++++++++-
include/linux/pstore.h | 4 ++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index a40da07..c4ea778 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -128,7 +128,11 @@ static void pstore_dump(struct kmsg_dumper *dumper,
size_t len;
dst = psinfo->buf;
- hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
+ if (psinfo->flags & PSTORE_NO_HEADINGS)
+ hsize = 0;
+ else
+ hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount,
+ part);
size = psinfo->bufsize - hsize;
dst += hsize;
@@ -237,6 +241,9 @@ int pstore_register(struct pstore_info *psi)
return -EINVAL;
}
+ if (psinfo->flags & PSTORE_MAX_KMSG_BYTES)
+ kmsg_bytes = ULONG_MAX;
+
if (pstore_is_mounted())
pstore_get_records(0);
diff --git a/include/linux/pstore.h b/include/linux/pstore.h
index ee3034a..55ab23f 100644
--- a/include/linux/pstore.h
+++ b/include/linux/pstore.h
@@ -40,9 +40,13 @@ enum pstore_type_id {
struct module;
+#define PSTORE_NO_HEADINGS BIT(0)
+#define PSTORE_MAX_KMSG_BYTES BIT(1)
+
struct pstore_info {
struct module *owner;
char *name;
+ unsigned int flags;
spinlock_t buf_lock; /* serialize access to 'buf' */
char *buf;
size_t bufsize;
--
1.7.9.5
On Thu, Oct 18, 2012 at 02:06:00PM +0300, [email protected] wrote:
> From: Adrian Hunter <[email protected]>
>
> There are 2 debugfs files:
>
> pstore/dump writing a kmsg dump reason code
> will cause a dump to persistent
> storage
>
> pstore/panic writing anything will cause
> a panic
>
> Signed-off-by: Adrian Hunter <[email protected]>
> ---
Um... how is different from /proc/sysrq-trigger?.. At least sysrq has
'panic', and I guess it would be better to extend it if we need something
more than this.
Thanks,
Anton.
On Thu, Oct 18, 2012 at 02:06:02PM +0300, [email protected] wrote:
> From: Dragos Tatulea <[email protected]>
>
> Added pstore_type_id in message header when storing to ram.
> On write, take into account the contents of this header and
> set the type accordingly.
>
> Signed-off-by: Dragos Tatulea <[email protected]>
> ---
[...]
> -static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz)
> +static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz,
> + enum pstore_type_id type)
> {
> char *hdr;
> struct timeval timestamp;
> size_t len;
>
> do_gettimeofday(×tamp);
> - hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
> - (long)timestamp.tv_sec, (long)timestamp.tv_usec);
> + hdr = kasprintf(GFP_ATOMIC, "%u" RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
> + type, (long)timestamp.tv_sec, (long)timestamp.tv_usec);
This will probably break scripts (and the main purpose for the header is
to make it somewhat scripts-parsable). It's safer to change it to
RAMOOPS_KERNMSG_HDR "%lu.%lu %u"
Thanks,
Anton.
On Thu, Oct 18, 2012 at 02:06:05PM +0300, [email protected] wrote:
> From: Dragos Tatulea <[email protected]>
>
> Return error if flushing to backend failed.
I think you can easily fold this into 'pstore: add support for external
writers'.
Thanks.
> Signed-off-by: Dragos Tatulea <[email protected]>
> ---
> fs/pstore/platform.c | 8 +++-----
> 1 file changed, 3 insertions(+), 5 deletions(-)
>
> diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
> index 3a4c38f..25f59ed 100644
> --- a/fs/pstore/platform.c
> +++ b/fs/pstore/platform.c
> @@ -445,7 +445,7 @@ void pstore_del_timer(void)
> int pstore_write(enum pstore_type_id type, const char *buf, size_t size)
> {
> size_t len;
> - int err = 0, err2;
> + int err = 0;
>
> if (!psinfo)
> return -ENODEV;
> @@ -461,16 +461,14 @@ int pstore_write(enum pstore_type_id type, const char *buf, size_t size)
> psinfo->ext_part = 1;
> }
>
> - while (size) {
> + while (size && !err) {
> len = min(size, psinfo->bufsize - psinfo->ext_len);
> memcpy(psinfo->buf + psinfo->ext_len, buf, len);
> psinfo->ext_len += len;
> buf += len;
> size -= len;
> if (psinfo->ext_len == psinfo->bufsize) {
> - err2 = pstore_ext_flush();
> - if (err2 && !err)
> - err = err2;
> + err = pstore_ext_flush();
> }
> }
>
> --
> 1.7.9.5
On Thu, Oct 18, 2012 at 02:06:06PM +0300, [email protected] wrote:
> From: Dragos Tatulea <[email protected]>
>
> Otherwise we might miss out on some pstore dumpers that use
> printks below current log level.
If anything, this should be folded into 'pstore: add debugfs support for
causing dumps and panics'. But I'm not sure about the debugfs stuff
anyway.
> Signed-off-by: Dragos Tatulea <[email protected]>
> ---
> fs/pstore/platform.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
> index 25f59ed..e3ad13e 100644
> --- a/fs/pstore/platform.c
> +++ b/fs/pstore/platform.c
> @@ -267,6 +267,7 @@ static DEFINE_SPINLOCK(dbg_lock);
> static int dbg_dump(void *data, u64 val)
> {
> unsigned long flags;
> + int saved_loglevel;
>
> switch (val) {
> case KMSG_DUMP_PANIC:
> @@ -276,7 +277,10 @@ static int dbg_dump(void *data, u64 val)
> case KMSG_DUMP_HALT:
> case KMSG_DUMP_POWEROFF:
> spin_lock_irqsave(&dbg_lock, flags);
> + saved_loglevel = console_loglevel;
> + console_loglevel = 15;
> kmsg_dump(val);
> + console_loglevel = saved_loglevel;
> spin_unlock_irqrestore(&dbg_lock, flags);
> return 0;
> }
> --
> 1.7.9.5
On Thu, Oct 18, 2012 at 02:06:03PM +0300, [email protected] wrote:
> From: Dragos Tatulea <[email protected]>
>
> The task dumper can dump task information during a panic.
> This is equivalent to a magic sysrq 't' command but
> the result is captured from the console and written
> to persistent storage. Note that this happens after
> pstore dumps kernel messages because the task dump will
> overwrite other kernel messages.
>
> There is a single module parameter "enabled" which must
> be used to enable task dumping.
This is a very very strange approach to accomplish this.
Plus, we have the console dumper already, so I think it should be done the
other way around: if you want a tasklist dumped during panic, introduce a
generic feature for the panic logging, i.e. just print tasks after/before
panic trace.
Or am I missing the point of the patch?
>
> Signed-off-by: Adrian Hunter <[email protected]>
> Signed-off-by: Dragos Tatulea <[email protected]>
> ---
> fs/pstore/Kconfig | 12 ++++++
> fs/pstore/Makefile | 1 +
> fs/pstore/dump_tasks.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++
> fs/pstore/inode.c | 3 ++
> include/linux/pstore.h | 1 +
> 5 files changed, 124 insertions(+)
> create mode 100644 fs/pstore/dump_tasks.c
>
> diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
> index ca71db6..ee967c5 100644
> --- a/fs/pstore/Kconfig
> +++ b/fs/pstore/Kconfig
> @@ -48,3 +48,15 @@ config PSTORE_RAM
> "ramoops.ko".
>
> For more information, see Documentation/ramoops.txt.
> +
> +config PSTORE_DUMP_TASKS
> + bool "Dump task information"
> + default n
'default n' is there by default. :) So no need for this line.
Thanks,
Anton.
On Thu, Oct 18, 2012 at 02:06:01PM +0300, [email protected] wrote:
> From: Adrian Hunter <[email protected]>
>
> Other modules that may wish to write to persistent storage
> are supported by adding a notifier and write function.
>
> The notifier has 3 events: PSTORE_BEGIN, PSTORE_DUMP and
> PSTORE_END. External writers use the PSTORE_DUMP event
> whereas the PSTORE_BEGIN and PSTORE_END can be used by
> platform code to ensure the back end is powered up.
>
> Signed-off-by: Adrian Hunter <[email protected]>
> ---
> fs/pstore/platform.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pstore.h | 43 +++++++++++++++++++----
> 2 files changed, 124 insertions(+), 7 deletions(-)
>
> diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
> index 9d65723..63ff377 100644
> --- a/fs/pstore/platform.c
> +++ b/fs/pstore/platform.c
[...]
> +/* pstore_write must only be called from PSTORE_DUMP notifier callbacks */
> +int pstore_write(enum pstore_type_id type, const char *buf, size_t size)
> +{
> + size_t len;
> + int err = 0, err2;
I guess there is no need for err2 here, you can place it in the while
loop, in the if block.
> +
> + if (!psinfo)
> + return -ENODEV;
> +
> + /*
> + * No locking is needed because pstore_write is called only from
> + * PSTORE_DUMP notifier callbacks.
> + */
> +
> + if (type != psinfo->ext_type) {
> + err = pstore_ext_flush();
> + psinfo->ext_type = type;
> + psinfo->ext_part = 1;
> + }
> +
> + while (size) {
> + len = min(size, psinfo->bufsize - psinfo->ext_len);
> + memcpy(psinfo->buf + psinfo->ext_len, buf, len);
> + psinfo->ext_len += len;
> + buf += len;
> + size -= len;
> + if (psinfo->ext_len == psinfo->bufsize) {
> + err2 = pstore_ext_flush();
> + if (err2 && !err)
> + err = err2;
> + }
> + }
> +
> + return err;
> +}
> +EXPORT_SYMBOL_GPL(pstore_write);
> +
> module_param(backend, charp, 0444);
> MODULE_PARM_DESC(backend, "Pstore backend to use");
> diff --git a/include/linux/pstore.h b/include/linux/pstore.h
> index 55ab23f..1bf7f4b 100644
> --- a/include/linux/pstore.h
> +++ b/include/linux/pstore.h
> @@ -40,17 +40,27 @@ enum pstore_type_id {
>
> struct module;
>
> +/* Notifier events */
> +#define PSTORE_BEGIN 1
> +#define PSTORE_DUMP 2
> +#define PSTORE_END 3
> +
> #define PSTORE_NO_HEADINGS BIT(0)
> #define PSTORE_MAX_KMSG_BYTES BIT(1)
>
> struct pstore_info {
> - struct module *owner;
> - char *name;
> - unsigned int flags;
> - spinlock_t buf_lock; /* serialize access to 'buf' */
> - char *buf;
> - size_t bufsize;
> - struct mutex read_mutex; /* serialize open/read/close */
That's why I don't like "pretty" formatting for structs. :) The change is
unrelated, so please don't do this.
> + struct module *owner;
> + char *name;
> + unsigned int flags;
> + spinlock_t buf_lock; /* serialize access to 'buf' */
> + char *buf;
> + size_t bufsize;
> + struct mutex read_mutex; /* serialize open/read/close */
> + u64 ext_id;
> + size_t ext_len;
> + unsigned int ext_part;
> + enum pstore_type_id ext_type;
> + enum kmsg_dump_reason ext_reason;
> int (*open)(struct pstore_info *psi);
> int (*close)(struct pstore_info *psi);
> ssize_t (*read)(u64 *id, enum pstore_type_id *type,
> @@ -70,12 +80,31 @@ struct pstore_info {
>
> #ifdef CONFIG_PSTORE
> extern int pstore_register(struct pstore_info *);
> +extern int pstore_notifier_register(struct notifier_block *n);
> +extern int pstore_notifier_unregister(struct notifier_block *n);
> +/* pstore_write must only be called from PSTORE_DUMP notifier callbacks */
> +extern int pstore_write(enum pstore_type_id type, const char *buf, size_t size);
> #else
> static inline int
> pstore_register(struct pstore_info *psi)
> {
> return -ENODEV;
> }
> +static inline int
> +pstore_notifier_register(struct notifier_block *n)
> +{
> + return 0;
> +}
> +static inline int
> +pstore_notifier_unregister(struct notifier_block *n)
> +{
> + return 0;
> +}
> +static inline int
> +pstore_write(enum pstore_type_id type, const char *buf, size_t size)
> +{
> + return 0;
> +}
> #endif
>
> #endif /*_LINUX_PSTORE_H*/
> --
> 1.7.9.5