Hi, Pstore can support ramoops with this patch. drivers/char/Kconfig - Add "depends on PSTORE" to CONFIG_RAMOOPS drivers/char/ramoops.c - Add pstore_info structure so that ramoops can call pstore_register() - Remove kmsg_dump_unregister because pstore doesn't support unregister operation - Remove do_gettimeofday() from kexec/panic path for avoiding dead lock due to logbuf_lock of WARN_ON() in getnstimeofday(). kernel/time/timekeeping.c - introduce get_useconds() which get "xtime.nsec / 1000" without taking lock TODO: - I couldn't find any Documentation about ramoops. Please let me know how to test my patch. - I haven't implemented reader/eraser callbacks supported by pstore. - If ramoops users would like to unload module, pstore needs to support unregister operation. Signed-off-by: Seiji Aguchi <seiji.aguchi at hds.com> --- drivers/char/Kconfig | 1 + drivers/char/ramoops.c | 114 ++++++++++++++++++++++++++++++--------------- include/linux/time.h | 1 + kernel/time/timekeeping.c | 6 ++ 4 files changed, 85 insertions(+), 37 deletions(-) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 423fd56..c805d1e 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -603,6 +603,7 @@ source "drivers/s390/char/Kconfig" config RAMOOPS tristate "Log panic/oops to a RAM buffer" depends on HAS_IOMEM + depends on PSTORE default n help This enables panic and oops messages to be logged to a circular diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c index 8092c31..41c82e3 100644 --- a/drivers/char/ramoops.c +++ b/drivers/char/ramoops.c @@ -24,6 +24,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/kmsg_dump.h> +#include <linux/pstore.h> #include <linux/time.h> #include <linux/io.h> #include <linux/ioport.h> @@ -34,6 +35,7 @@ #include <linux/debugfs.h> #define RAMOOPS_KERNMSG_HDR "====" +#define RAMOOPS_HDR_SIZE 44 /* 4 (RAMOOPS_KERN_MSG_HDR) + 40 (timestamp) */ #define MIN_MEM_SIZE 4096UL #define RAMOOPS_DIR "ramoops" #define RAMOOPS_NEXT "next" @@ -58,6 +60,43 @@ module_param(dump_oops, int, 0600); MODULE_PARM_DESC(dump_oops, "set to 1 to dump oopses, 0 to only dump panics (default 1)"); +static unsigned long total_size; +static int ramoops_open(struct pstore_info *psi); +static int ramoops_close(struct pstore_info *psi); +static ssize_t ramoops_reader(u64 *id, enum pstore_type_id *type, + struct timespec *time); +static u64 ramoops_dumper(enum pstore_type_id type, size_t size, + enum kmsg_dump_reason reason); +static int ramoops_eraser(u64 record_id); + +static struct pstore_info ramoops_info = { + .owner = THIS_MODULE, + .name = "ramoops", + .open = ramoops_open, + .close = ramoops_close, + .read = ramoops_reader, + .write = ramoops_dumper, + .erase = ramoops_eraser +}; + +static int ramoops_open(struct pstore_info *psi) +{ + return 0; +} + +static int ramoops_close(struct pstore_info *psi) +{ + return 0; +} + +static ssize_t ramoops_reader(u64 *id, enum pstore_type_id *type, + struct timespec *time) +{ + return -EINVAL; +} + + + static struct ramoops_context { struct kmsg_dumper dump; void *virt_addr; @@ -141,49 +180,51 @@ static const struct file_operations ramoops_next_fops = { .read = ramoops_read_next, }; -static void ramoops_do_dump(struct kmsg_dumper *dumper, - enum kmsg_dump_reason reason, const char *s1, unsigned long l1, - const char *s2, unsigned long l2) +static u64 ramoops_dumper(enum pstore_type_id type, size_t size, + enum kmsg_dump_reason reason) { - struct ramoops_context *cxt = container_of(dumper, - struct ramoops_context, dump); - unsigned long s1_start, s2_start; - unsigned long l1_cpy, l2_cpy; - int res, hdr_size; - char *buf, *buf_orig; + char *buf; struct timeval timestamp; + struct ramoops_context *cxt = &oops_cxt; if (reason != KMSG_DUMP_OOPS && - reason != KMSG_DUMP_PANIC) - return; + reason != KMSG_DUMP_PANIC && + reason != KMSG_DUMP_KEXEC) + return 0; /* Only dump oopses if dump_oops is set */ if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops) - return; + return 0; - mutex_lock(&ramoops_mutex); - buf = cxt->virt_addr + (cxt->count * cxt->record_size); - buf_orig = buf; + if (total_size >= record_size) + return 0; - memset(buf, '\0', cxt->record_size); - res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR); - buf += res; - do_gettimeofday(×tamp); - res = sprintf(buf, "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec); - buf += res; + if (reason == KMSG_DUMP_OOPS) { + mutex_lock(&ramoops_mutex); + do_gettimeofday(×tamp); + } else { + timestamp.tv_sec = get_seconds(); + timestamp.tv_usec = get_useconds(); + } - hdr_size = buf - buf_orig; - l2_cpy = min(l2, cxt->record_size - hdr_size); - l1_cpy = min(l1, cxt->record_size - hdr_size - l2_cpy); + buf = cxt->virt_addr + (cxt->count * record_size); + snprintf(buf, RAMOOPS_HDR_SIZE, "%s%lu.%lu\n", + RAMOOPS_KERNMSG_HDR, (long)timestamp.tv_sec, + (long)timestamp.tv_usec); + ramoops_info.buf += record_size; + total_size = total_size + size + RAMOOPS_HDR_SIZE; - s2_start = l2 - l2_cpy; - s1_start = l1 - l1_cpy; + cxt->count = (cxt->count + 1) % cxt->max_count; - memcpy(buf, s1 + s1_start, l1_cpy); - memcpy(buf + l1_cpy, s2 + s2_start, l2_cpy); + if (reason == KMSG_DUMP_OOPS) + mutex_unlock(&ramoops_mutex); - cxt->count = (cxt->count + 1) % cxt->max_count; - mutex_unlock(&ramoops_mutex); + return 0; +} + +static int ramoops_eraser(u64 record_id) +{ + return 0; } static int __init ramoops_probe(struct platform_device *pdev) @@ -233,11 +274,13 @@ static int __init ramoops_probe(struct platform_device *pdev) pr_err("ioremap failed\n"); goto fail2; } + memset(cxt->virt_addr, '\0', cxt->size); - cxt->dump.dump = ramoops_do_dump; - err = kmsg_dump_register(&cxt->dump); - if (err) { - pr_err("registering kmsg dumper failed\n"); + mutex_init(&ramoops_info.buf_mutex); + ramoops_info.buf = cxt->virt_addr + RAMOOPS_HDR_SIZE; + ramoops_info.bufsize = record_size - RAMOOPS_HDR_SIZE; + if (pstore_register(&ramoops_info)) { + printk(KERN_ERR "Could not register with persistent store\n"); goto fail1; } @@ -284,9 +327,6 @@ static int __exit ramoops_remove(struct platform_device *pdev) { struct ramoops_context *cxt = &oops_cxt; - if (kmsg_dump_unregister(&cxt->dump) < 0) - pr_warn("could not unregister kmsg_dumper\n"); - iounmap(cxt->virt_addr); release_mem_region(cxt->phys_addr, cxt->size); return 0; diff --git a/include/linux/time.h b/include/linux/time.h index b306178..cff6bb5 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -121,6 +121,7 @@ void timekeeping_init(void); extern int timekeeping_suspended; unsigned long get_seconds(void); +unsigned long get_useconds(void); struct timespec current_kernel_time(void); struct timespec __current_kernel_time(void); /* does not take xtime_lock */ struct timespec get_monotonic_coarse(void); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 342408c..1927edc 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1029,6 +1029,12 @@ unsigned long get_seconds(void) } EXPORT_SYMBOL(get_seconds); +unsigned long get_useconds(void) +{ + return xtime.tv_sec / 1000; +} +EXPORT_SYMBOL(get_useconds); + struct timespec __current_kernel_time(void) { return xtime; -- 1.7.1