Hi, This patch adds static function calls so that both pstore and APEI storage backend can work reliably in kexec path. kernel/kexec.c - Add pstore_kmsg_dump_in_interrupt(KMSG_DUMP_KEXEC) just after machine_crash_shutdown() so that pstore can work with one cpu. kernel/printk.c - Introduce get_logbuf_nolock() so that pstore can get values of logbuf without taking lock. fs/pstore/platform.c - Introduce pstore_kmsg_dump_in_interrupt() so that pstore/APEI storage backend can output kernel messages without taking lock. pstore_dump() - Add error checks below because pstore_dump() is called from kmsg_dump(KMSG_DUMP_KEXEC) directly. - Skip if no driver is registered - Skip if there is a driver calling pstore_register()/pstore_unregister() - Remove mutex_lock from kexec path TODO: APEI storage backend will work with this patch. However, I don't have any access to servers capable of APEI storage backend. Please help to test my patch. Signed-off-by: Seiji Aguchi <seiji.aguchi at hds.com> --- fs/pstore/platform.c | 27 +++++++++++++++++++++++++-- include/linux/kmsg_dump.h | 1 + include/linux/pstore.h | 9 +++++++++ kernel/kexec.c | 3 +++ kernel/printk.c | 29 +++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index f2c3ff2..85e0a9c 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -74,7 +74,17 @@ static void pstore_dump(struct kmsg_dumper *dumper, else why = "Unknown"; - mutex_lock(&psinfo->buf_mutex); + switch (reason) { + case KMSG_DUMP_KEXEC: + /* Skip if there is no driver or there is a driver calling + pstore_register() */ + if (!psinfo || !spin_trylock(&pstore_lock)) + return; + break; + default: + mutex_lock(&psinfo->buf_mutex); + } + oopscount++; while (total < kmsg_bytes) { dst = psinfo->buf; @@ -103,7 +113,20 @@ static void pstore_dump(struct kmsg_dumper *dumper, l2 -= l2_cpy; total += l1_cpy + l2_cpy; } - mutex_unlock(&psinfo->buf_mutex); + + if (reason != KMSG_DUMP_KEXEC) + mutex_unlock(&psinfo->buf_mutex); +} + +void pstore_kmsg_dump_in_interrupt(enum kmsg_dump_reason reason) +{ + const char *s1, *s2; + unsigned long l1, l2; + + /* get logbuf values without spin_lock for avoiding dead lock */ + get_logbuf_nolock(&s1, &l1, &s2, &l2); + + pstore_dump(NULL, reason, s1, l1, s2, l2); } static struct kmsg_dumper pstore_dumper = { diff --git a/include/linux/kmsg_dump.h b/include/linux/kmsg_dump.h index fee6631..ee0c952 100644 --- a/include/linux/kmsg_dump.h +++ b/include/linux/kmsg_dump.h @@ -18,6 +18,7 @@ enum kmsg_dump_reason { KMSG_DUMP_OOPS, KMSG_DUMP_PANIC, + KMSG_DUMP_KEXEC, KMSG_DUMP_RESTART, KMSG_DUMP_HALT, KMSG_DUMP_POWEROFF, diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 2455ef2..5cf008d 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -22,6 +22,8 @@ #ifndef _LINUX_PSTORE_H #define _LINUX_PSTORE_H +#include<linux/kmsg_dump.h> + /* types */ enum pstore_type_id { PSTORE_TYPE_DMESG = 0, @@ -46,6 +48,9 @@ struct pstore_info { #ifdef CONFIG_PSTORE extern int pstore_register(struct pstore_info *); extern int pstore_write(enum pstore_type_id type, char *buf, size_t size); +extern void pstore_kmsg_dump_in_interrupt(enum kmsg_dump_reason reason); +extern void get_logbuf_nolock(const char **s1, unsigned long *l1, + const char **s2, unsigned long *l2); #else static inline int pstore_register(struct pstore_info *psi) @@ -57,6 +62,10 @@ pstore_write(enum pstore_type_id type, char *buf, size_t size) { return -ENODEV; } +static inline void +pstore_kmsg_dump_in_interrupt(enum kmsg_dump_reason reason) +{ +} #endif #endif /*_LINUX_PSTORE_H*/ diff --git a/kernel/kexec.c b/kernel/kexec.c index e24bc1b..8e2761a 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -33,6 +33,8 @@ #include <linux/vmalloc.h> #include <linux/swap.h> #include <linux/syscore_ops.h> +#include <linux/kmsg_dump.h> +#include <linux/pstore.h> #include <asm/page.h> #include <asm/uaccess.h> @@ -1081,6 +1083,7 @@ void crash_kexec(struct pt_regs *regs) crash_setup_regs(&fixed_regs, regs); crash_save_vmcoreinfo(); machine_crash_shutdown(&fixed_regs); + pstore_kmsg_dump_in_interrupt(KMSG_DUMP_KEXEC); machine_kexec(kexec_crash_image); } mutex_unlock(&kexec_mutex); diff --git a/kernel/printk.c b/kernel/printk.c index 37dff34..966a7d9 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1710,6 +1710,35 @@ int kmsg_dump_unregister(struct kmsg_dumper *dumper) } EXPORT_SYMBOL_GPL(kmsg_dump_unregister); + +void get_logbuf_nolock(const char **s1, unsigned long *l1, const char **s2, + unsigned long *l2) +{ + unsigned long end; + unsigned chars; + + /* Theoretically, the log could move on after we do this, but + there's not a lot we can do about that. The new messages + will overwrite the start of what we dump. */ + end = log_end & LOG_BUF_MASK; + chars = logged_chars; + + if (chars > end) { + *s1 = log_buf + log_buf_len - chars + end; + *l1 = chars - end; + + *s2 = log_buf; + *l2 = end; + } else { + *s1 = ""; + *l1 = 0; + + *s2 = log_buf + end - chars; + *l2 = chars; + } + +} + /** * kmsg_dump - dump kernel log to kernel message dumpers. * @reason: the reason (oops, panic etc) for dumping -- 1.7.1