From: 黄涛 <huangtao@rock-chips.com>
Date: Tue, 7 Jun 2011 01:44:02 +0000 (+0800)
Subject: rk29: last_log: support save the last kernel log on /proc/last_log
X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=ed29925c0bc5687896f74831923fdf8c79397ccb;p=firefly-linux-kernel-4.4.55.git

rk29: last_log: support save the last kernel log on /proc/last_log
---

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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#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)