From ed29925c0bc5687896f74831923fdf8c79397ccb Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=BB=84=E6=B6=9B?= Date: Tue, 7 Jun 2011 09:44:02 +0800 Subject: [PATCH] rk29: last_log: support save the last kernel log on /proc/last_log --- arch/arm/mach-rk29/Kconfig | 7 ++++ arch/arm/mach-rk29/Makefile | 1 + arch/arm/mach-rk29/last_log.c | 72 +++++++++++++++++++++++++++++++++++ kernel/printk.c | 15 ++++++++ 4 files changed, 95 insertions(+) create mode 100644 arch/arm/mach-rk29/last_log.c diff --git a/arch/arm/mach-rk29/Kconfig b/arch/arm/mach-rk29/Kconfig index fcca5caa484f..f52d25e83502 100755 --- a/arch/arm/mach-rk29/Kconfig +++ b/arch/arm/mach-rk29/Kconfig @@ -173,4 +173,11 @@ config RK29_JTAG This is an option for SDK board. Always enable JTAG clock, but consumes more power. +config RK29_LAST_LOG + bool "Save the last kernel log on /proc/last_log" + depends on DEBUG_KERNEL && PRINTK + default y + help + It is only intended for debugging. + endif diff --git a/arch/arm/mach-rk29/Makefile b/arch/arm/mach-rk29/Makefile index 8be7c6da339b..e2c0ed70b24f 100644 --- a/arch/arm/mach-rk29/Makefile +++ b/arch/arm/mach-rk29/Makefile @@ -4,6 +4,7 @@ obj-y += early_printk.o ifndef CONFIG_DEBUG_LL obj-y += ../kernel/debug.o endif +obj-$(CONFIG_RK29_LAST_LOG) += last_log.o obj-$(CONFIG_USB_GADGET) += usb_detect.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o diff --git a/arch/arm/mach-rk29/last_log.c b/arch/arm/mach-rk29/last_log.c new file mode 100644 index 000000000000..7096001eb377 --- /dev/null +++ b/arch/arm/mach-rk29/last_log.c @@ -0,0 +1,72 @@ +/* + * arch/arm/mach-rk29/last_log.c + * + * Copyright (C) 2011 ROCKCHIP, Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#define LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) +static char last_log_buf[LOG_BUF_LEN]; +extern void switch_log_buf(char *new_log_buf, unsigned size); + +static ssize_t last_log_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + loff_t pos = *offset; + ssize_t count; + + if (pos >= LOG_BUF_LEN) + return 0; + + count = min(len, (size_t)(LOG_BUF_LEN - pos)); + if (copy_to_user(buf, &last_log_buf[pos], count)) + return -EFAULT; + + *offset += count; + return count; +} + +static const struct file_operations last_log_file_ops = { + .owner = THIS_MODULE, + .read = last_log_read, +}; + +static int __init last_log_init(void) +{ + char *log_buf; + struct proc_dir_entry *entry; + + log_buf = (char *)__get_free_pages(GFP_KERNEL, CONFIG_LOG_BUF_SHIFT - PAGE_SHIFT); + if (!log_buf) { + printk(KERN_ERR "last_log: failed to __get_free_pages(%d)\n", CONFIG_LOG_BUF_SHIFT - PAGE_SHIFT); + return 0; + } + printk("last_log: 0x%p 0x%p\n", log_buf, last_log_buf); + + memcpy(last_log_buf, log_buf, LOG_BUF_LEN); + switch_log_buf(log_buf, LOG_BUF_LEN); + + entry = create_proc_entry("last_log", S_IFREG | S_IRUGO, NULL); + if (!entry) { + printk(KERN_ERR "last_log: failed to create proc entry\n"); + return 0; + } + + entry->proc_fops = &last_log_file_ops; + entry->size = LOG_BUF_LEN; + + return 0; +} + +postcore_initcall(last_log_init); + diff --git a/kernel/printk.c b/kernel/printk.c index 068c1c7147be..3522afcb8f2b 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -855,6 +855,21 @@ out_restore_irqs: EXPORT_SYMBOL(printk); EXPORT_SYMBOL(vprintk); +#ifdef CONFIG_RK29_LAST_LOG +void switch_log_buf(char *new_log_buf, int size) +{ + unsigned long flags; + + if (!new_log_buf || log_buf_len > size) + return; + + spin_lock_irqsave(&logbuf_lock, flags); + memcpy(new_log_buf, log_buf, min(log_buf_len, size)); + log_buf = new_log_buf; + log_buf_len = size; + spin_unlock_irqrestore(&logbuf_lock, flags); +} +#endif /* CONFIG_RK29_LAST_LOG */ #else static void call_console_drivers(unsigned start, unsigned end) -- 2.34.1