Nitpick, really.. shouldn't the logbuffer location(s) be some device tree property(ies), perhaps something in the<br>/chosen node that U-Boot etc. can then fill out?<br><br>-- <br>Matt Sealey <<a href="mailto:matt@genesi-usa.com">matt@genesi-usa.com</a>><br>
Genesi, Manager, Developer Relations<br><br><div class="gmail_quote">On Tue, Nov 25, 2008 at 12:34 PM, Grant Erickson <span dir="ltr"><<a href="mailto:gerickson@nuovations.com">gerickson@nuovations.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">This merges support for the previously DENX-only kernel feature of<br>
specifying an alternative, "external" buffer for kernel printk<br>
messages and their associated metadata. In addition, this ports<br>
architecture support for this feature from arch/ppc to arch/powerpc.<br>
<br>
Signed-off-by: Grant Erickson <<a href="mailto:gerickson@nuovations.com">gerickson@nuovations.com</a>><br>
---<br>
<br>
When this option is enabled, an architecture- or machine-specific log<br>
buffer is used for all printk messages. This allows entities such as<br>
boot loaders (e.g. U-Boot) to place printk-compatible messages into<br>
this buffer and for the kernel to coalesce them with its normal<br>
messages.<br>
<br>
The code has historically been used and proven to work on the LWMON5<br>
platform under arch/ppc and is now used (by me) successfully on the<br>
AMCC Haleakala and Kilauea platforms.<br>
<br>
As implemented for arch/powerpc, two suboptions for the alternative<br>
log buffer are supported. The buffer may be contiguous with the<br>
metadata and message data colocated or the metadata and message<br>
storage may be in discontiguous regions of memory (e.g. a set of<br>
scratch registers and an SRAM buffer). On Kilauea and Haleakala, I<br>
have used the former; whereas LWMON5 has traditionally used the latter.<br>
<br>
The code here is, more or less, as-is from the DENX GIT tree. Comments<br>
welcome.<br>
<br>
arch/powerpc/kernel/prom.c | 93 +++++++++++++++++++++++++++<br>
include/linux/logbuff.h | 56 ++++++++++++++++<br>
init/Kconfig | 25 +++++++<br>
init/main.c | 4 +<br>
kernel/printk.c | 149 +++++++++++++++++++++++++++++++++++++++++++-<br>
5 files changed, 324 insertions(+), 3 deletions(-)<br>
create mode 100644 include/linux/logbuff.h<br>
<br>
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c<br>
index 3a2dc7e..60282f1 100644<br>
--- a/arch/powerpc/kernel/prom.c<br>
+++ b/arch/powerpc/kernel/prom.c<br>
@@ -32,6 +32,7 @@<br>
#include <linux/debugfs.h><br>
#include <linux/irq.h><br>
#include <linux/lmb.h><br>
+#include <linux/logbuff.h><br>
<br>
#include <asm/prom.h><br>
#include <asm/rtas.h><br>
@@ -61,6 +62,15 @@<br>
#define DBG(fmt...)<br>
#endif<br>
<br>
+#ifdef CONFIG_LOGBUFFER<br>
+#ifdef CONFIG_ALT_LB_LOCATION<br>
+# if !defined(BOARD_ALT_LH_ADDR) || !defined(BOARD_ALT_LB_ADDR)<br>
+# error "Please specify BOARD_ALT_LH_ADDR & BOARD_ALT_LB_ADDR."<br>
+# endif<br>
+#else /* !CONFIG_ALT_LB_LOCATION */<br>
+static phys_addr_t ext_logbuff;<br>
+#endif /* CONFIG_ALT_LB_LOCATION */<br>
+#endif /* CONFIG_LOGBUFFER */<br>
<br>
static int __initdata dt_root_addr_cells;<br>
static int __initdata dt_root_size_cells;<br>
@@ -1018,6 +1028,85 @@ static int __init early_init_dt_scan_memory(unsigned long node,<br>
return 0;<br>
}<br>
<br>
+#ifdef CONFIG_LOGBUFFER<br>
+#ifdef CONFIG_ALT_LB_LOCATION<br>
+/* Alternative external log buffer mapping: log metadata header & the<br>
+ * character buffer are separated and allocated not in RAM but in some<br>
+ * other memory-mapped I/O region (e.g. log head in unused registers,<br>
+ * and log buffer in OCM memory)<br>
+ */<br>
+int __init setup_ext_logbuff_mem(volatile logbuff_t **lhead, char **lbuf)<br>
+{<br>
+ void *h, *b;<br>
+<br>
+ if (unlikely(!lhead) || unlikely(!lbuf))<br>
+ return -EINVAL;<br>
+<br>
+ /* map log head */<br>
+ h = ioremap(BOARD_ALT_LH_ADDR, sizeof(logbuff_t));<br>
+ if (unlikely(!h))<br>
+ return -EFAULT;<br>
+<br>
+ /* map log buffer */<br>
+ b = ioremap(BOARD_ALT_LB_ADDR, LOGBUFF_LEN);<br>
+ if (unlikely(!b)) {<br>
+ iounmap(h);<br>
+ return -EFAULT;<br>
+ }<br>
+<br>
+ *lhead = h;<br>
+ *lbuf = b;<br>
+<br>
+ return 0;<br>
+}<br>
+#else /* !CONFIG_ALT_LB_LOCATION */<br>
+/* Usual external log-buffer mapping: log metadata header & the character<br>
+ * buffer are both contiguous in system RAM.<br>
+ */<br>
+int __init setup_ext_logbuff_mem(logbuff_t **lhead, char **lbuf)<br>
+{<br>
+ void *p;<br>
+<br>
+ if (unlikely(!lhead) || unlikely(!lbuf))<br>
+ return -EINVAL;<br>
+<br>
+ if (unlikely(!ext_logbuff) || !lmb_is_reserved(ext_logbuff))<br>
+ return -EFAULT;<br>
+<br>
+ p = ioremap(ext_logbuff, LOGBUFF_RESERVE);<br>
+<br>
+ if (unlikely(!p))<br>
+ return -EFAULT;<br>
+<br>
+ *lhead = (logbuff_t *)(p + LOGBUFF_OVERHEAD -<br>
+ sizeof(logbuff_t) +<br>
+ sizeof(((logbuff_t *)0)->buf));<br>
+ *lbuf = (*lhead)->buf;<br>
+<br>
+ return 0;<br>
+}<br>
+<br>
+/* When the external log buffer configuration is used with the<br>
+ * non-alternate location, the log head metadata and character buffer<br>
+ * lie in the LOGBUFF_RESERVE bytes at the end of system RAM. Add this<br>
+ * block of memory to the reserved memory pool so that it is not<br>
+ * allocated for other purposes.<br>
+ */<br>
+static void __init reserve_ext_logbuff_mem(void)<br>
+{<br>
+ phys_addr_t top = lmb_end_of_DRAM();<br>
+ phys_addr_t size = LOGBUFF_RESERVE;<br>
+ phys_addr_t base = top - size;<br>
+<br>
+ if (top > base) {<br>
+ ext_logbuff = base;<br>
+ DBG("reserving: %x -> %x\n", base, size);<br>
+ lmb_reserve(base, size);<br>
+ }<br>
+}<br>
+#endif /* CONFIG_ALT_LB_LOCATION */<br>
+#endif /* CONFIG_LOGBUFFER */<br>
+<br>
static void __init early_reserve_mem(void)<br>
{<br>
u64 base, size;<br>
@@ -1033,6 +1122,10 @@ static void __init early_reserve_mem(void)<br>
self_size = initial_boot_params->totalsize;<br>
lmb_reserve(self_base, self_size);<br>
<br>
+#if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_LOCATION)<br>
+ reserve_ext_logbuff_mem();<br>
+#endif /* defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_LOCATION) */<br>
+<br>
#ifdef CONFIG_BLK_DEV_INITRD<br>
/* then reserve the initrd, if any */<br>
if (initrd_start && (initrd_end > initrd_start))<br>
diff --git a/include/linux/logbuff.h b/include/linux/logbuff.h<br>
new file mode 100644<br>
index 0000000..22a51c0<br>
--- /dev/null<br>
+++ b/include/linux/logbuff.h<br>
@@ -0,0 +1,56 @@<br>
+/*<br>
+ * (C) Copyright 2007<br>
+ * Wolfgang Denk, DENX Software Engineering, <a href="mailto:wd@denx.de">wd@denx.de</a>.<br>
+ *<br>
+ * See file CREDITS for list of people who contributed to this<br>
+ * project.<br>
+ *<br>
+ * This program is free software; you can redistribute it and/or<br>
+ * modify it under the terms of the GNU General Public License as<br>
+ * published by the Free Software Foundation; either version 2 of<br>
+ * the License, or (at your option) any later version.<br>
+ *<br>
+ * This program is distributed in the hope that it will be useful,<br>
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+ * GNU General Public License for more details.<br>
+ *<br>
+ * You should have received a copy of the GNU General Public License<br>
+ * along with this program; if not, write to the Free Software<br>
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,<br>
+ * MA 02111-1307 USA<br>
+ */<br>
+#ifndef _LOGBUFF_H_<br>
+#define _LOGBUFF_H_<br>
+<br>
+#ifdef CONFIG_LOGBUFFER<br>
+<br>
+#define LOGBUFF_MAGIC 0xc0de4ced<br>
+#define LOGBUFF_LEN 16384<br>
+#define LOGBUFF_OVERHEAD 4096<br>
+#define LOGBUFF_RESERVE (LOGBUFF_LEN + LOGBUFF_OVERHEAD)<br>
+<br>
+/* The mapping used here has to be the same as in logbuff_init_ptrs ()<br>
+ in u-boot/common/cmd_log.c */<br>
+<br>
+typedef struct {<br>
+ unsigned long tag;<br>
+ unsigned long start;<br>
+ unsigned long con; /* next char to be sent to consoles */<br>
+ unsigned long end;<br>
+ unsigned long chars;<br>
+ unsigned char buf[0];<br>
+} logbuff_t;<br>
+<br>
+#ifdef CONFIG_ALT_LB_LOCATION<br>
+# define LOGBUFF_VOLATILE volatile<br>
+#else<br>
+# define LOGBUFF_VOLATILE<br>
+#endif /* defined(CONFIG_ALT_LB_LOCATION) */<br>
+<br>
+extern void setup_ext_logbuff(void);<br>
+/* arch specific */<br>
+extern int setup_ext_logbuff_mem(LOGBUFF_VOLATILE logbuff_t **lhead, char **lbuf);<br>
+<br>
+#endif /* CONFIG_LOGBUFFER */<br>
+#endif /* _LOGBUFF_H_ */<br>
diff --git a/init/Kconfig b/init/Kconfig<br>
index f763762..e1a1b59 100644<br>
--- a/init/Kconfig<br>
+++ b/init/Kconfig<br>
@@ -619,6 +619,31 @@ config PRINTK<br>
very difficult to diagnose system problems, saying N here is<br>
strongly discouraged.<br>
<br>
+config LOGBUFFER<br>
+ bool "External logbuffer" if PRINTK<br>
+ default n<br>
+ help<br>
+ This option enables support for an alternative, "external"<br>
+ printk log buffer. When enabled, an architecture- or machine-<br>
+ specific log buffer is used for all printk messages. This<br>
+ allows entities such as boot loaders to place printk-compatible<br>
+ messages into this buffer and for the kernel to coalesce them<br>
+ with its normal messages.<br>
+<br>
+config ALT_LB_LOCATION<br>
+ bool "Alternative logbuffer" if LOGBUFFER<br>
+ default n<br>
+ help<br>
+ When using an alternative, "external" printk log buffer, an<br>
+ architecture- or machine-specific log buffer with contiguous<br>
+ metadata and message storage is used. This option enables<br>
+ support for discontiguous metadata and message storage<br>
+ memory (e.g. a set of scratch registers and an SRAM<br>
+ buffer). By saying Y here, you must also ensure your<br>
+ architecture- or machine-code specify BOARD_ALT_LH_ADDR and<br>
+ BOARD_ALT_LB_ADDR, for the metadata and message memory,<br>
+ respectively.<br>
+<br>
config BUG<br>
bool "BUG() support" if EMBEDDED<br>
default y<br>
diff --git a/init/main.c b/init/main.c<br>
index 7e117a2..5687b98 100644<br>
--- a/init/main.c<br>
+++ b/init/main.c<br>
@@ -61,6 +61,7 @@<br>
#include <linux/kthread.h><br>
#include <linux/sched.h><br>
#include <linux/signal.h><br>
+#include <linux/logbuff.h><br>
#include <linux/idr.h><br>
#include <linux/ftrace.h><br>
<br>
@@ -563,6 +564,9 @@ asmlinkage void __init start_kernel(void)<br>
* Interrupts are still disabled. Do necessary setups, then<br>
* enable them<br>
*/<br>
+#ifdef CONFIG_LOGBUFFER<br>
+ setup_ext_logbuff();<br>
+#endif<br>
lock_kernel();<br>
tick_init();<br>
boot_cpu_init();<br>
diff --git a/kernel/printk.c b/kernel/printk.c<br>
index f492f15..59884e2 100644<br>
--- a/kernel/printk.c<br>
+++ b/kernel/printk.c<br>
@@ -32,6 +32,7 @@<br>
#include <linux/security.h><br>
#include <linux/bootmem.h><br>
#include <linux/syscalls.h><br>
+#include <linux/logbuff.h><br>
<br>
#include <asm/uaccess.h><br>
<br>
@@ -101,9 +102,39 @@ static DEFINE_SPINLOCK(logbuf_lock);<br>
* The indices into log_buf are not constrained to log_buf_len - they<br>
* must be masked before subscripting<br>
*/<br>
+#ifdef CONFIG_LOGBUFFER<br>
+/* Indexes to the local log buffer */<br>
+static unsigned long _log_start;<br>
+static unsigned long _con_start;<br>
+static unsigned long _log_end;<br>
+static unsigned long _logged_chars;<br>
+/* These will be switched to the external log buffer */<br>
+#ifndef CONFIG_ALT_LB_LOCATION<br>
+/* usual logbuffer location */<br>
+static unsigned long *ext_log_start = &_log_start;<br>
+static unsigned long *ext_con_start = &_con_start;<br>
+static unsigned long *ext_log_end = &_log_end;<br>
+static unsigned long *ext_logged_chars = &_logged_chars;<br>
+#define log_start (*ext_log_start)<br>
+#define con_start (*ext_con_start)<br>
+#define log_end (*ext_log_end)<br>
+#define logged_chars (*ext_logged_chars)<br>
+#else /* defined(CONFIG_ALT_LB_LOCATION) */<br>
+/* alternative logbuffer location */<br>
+static volatile unsigned long *ext_log_start = &_log_start;<br>
+static volatile unsigned long *ext_con_start = &_con_start;<br>
+static volatile unsigned long *ext_log_end = &_log_end;<br>
+static volatile unsigned long *ext_logged_chars = &_logged_chars;<br>
+#define log_start (*((volatile u32 *)ext_log_start))<br>
+#define con_start (*((volatile u32 *)ext_con_start))<br>
+#define log_end (*((volatile u32 *)ext_log_end))<br>
+#define logged_chars (*((volatile u32 *)ext_logged_chars))<br>
+#endif /* !defined(CONFIG_ALT_LB_LOCATION) */<br>
+#else /* !defined(CONFIG_LOGBUFFER) */<br>
static unsigned log_start; /* Index into log_buf: next char to be read by syslog() */<br>
static unsigned con_start; /* Index into log_buf: next char to be sent to consoles */<br>
static unsigned log_end; /* Index into log_buf: most-recently-written-char + 1 */<br>
+#endif /* CONFIG_LOGBUFFER */<br>
<br>
/*<br>
* Array of consoles built from command line options (console=)<br>
@@ -134,10 +165,121 @@ static int console_may_schedule;<br>
static char __log_buf[__LOG_BUF_LEN];<br>
static char *log_buf = __log_buf;<br>
static int log_buf_len = __LOG_BUF_LEN;<br>
+#ifndef CONFIG_LOGBUFFER<br>
static unsigned logged_chars; /* Number of chars produced since last read+clear operation */<br>
+#endif /* !defined(CONFIG_LOGBUFFER) */<br>
+#ifdef CONFIG_LOGBUFFER<br>
+/* Sanity check the external log buffer metadata. When an the external<br>
+ * log buffer is enabled, the log metadata is effectively non-volatile<br>
+ * in that the values are preserved from reboot to reboot (until/unless<br>
+ * the system loses power).<br>
+ */<br>
+static void __init logbuff_check_metadata(LOGBUFF_VOLATILE logbuff_t *log)<br>
+{<br>
+ unsigned long chars;<br>
+<br>
+ /* Sanity check the producer and consumer indices. */<br>
+<br>
+ if (log->end - log->start > LOGBUFF_LEN)<br>
+ log->start = log->end - LOGBUFF_LEN;<br>
+<br>
+ if (log->end - log->con > LOGBUFF_LEN)<br>
+ log->con = log->end - LOGBUFF_LEN;<br>
+<br>
+ /* Occasionally, particularly following a reboot, the start<br>
+ * consumer index is not properly caught up to the console<br>
+ * consumer index. If this is the case, catch it up so that<br>
+ * the log buffer doesn't start with, for example,<br>
+ * "<0>Restarting system.\n" followed by the 'real' start of<br>
+ * the log.<br>
+ */<br>
+<br>
+ if (log->con > log->start)<br>
+ log->start = log->con;<br>
+<br>
+ /* Ensure that the number of characters logged reflects the<br>
+ * characters actually logged based on the producer and<br>
+ * consumer indices rather than all characters cumulatively<br>
+ * logged across all reboots since a power-loss event.<br>
+ */<br>
+<br>
+ chars = log->end - log->start;<br>
+<br>
+ if (log->chars > chars)<br>
+ log->chars = chars;<br>
+}<br>
+<br>
+/* Coalesce the current log bounded buffer and the external log<br>
+ * bounded buffer by appending the former to the latter. Precedence is<br>
+ * given to the external log buffer when there is more data to be<br>
+ * appended than space exists, so the current log buffer is truncated<br>
+ * instead of overwritting the external buffer.<br>
+ */<br>
+static void __init logbuff_coalesce_buffers(LOGBUFF_VOLATILE logbuff_t *log)<br>
+{<br>
+ unsigned long dspace, ssize, len;<br>
+<br>
+ dspace = LOGBUFF_LEN - (log->end - log->start);<br>
+ ssize = log_end - log_start;<br>
+ len = min(dspace, ssize);<br>
+<br>
+ while (len-- > 0) {<br>
+ log->buf[log->end++ & (LOGBUFF_LEN-1)] = LOG_BUF(log_start++);<br>
+ log->chars++;<br>
+ }<br>
+}<br>
<br>
+void __init setup_ext_logbuff(void)<br>
+{<br>
+ LOGBUFF_VOLATILE logbuff_t *log;<br>
+ char *ext_log_buf = NULL;<br>
+ unsigned long flags;<br>
+<br>
+ if (setup_ext_logbuff_mem(&log, &ext_log_buf) < 0) {<br>
+ printk(KERN_WARNING<br>
+ "Failed to setup external logbuffer - ignoring it\n");<br>
+ return;<br>
+ }<br>
+<br>
+ /* When no properly setup buffer is found, reset pointers */<br>
+ if (log->tag != LOGBUFF_MAGIC) {<br>
+ printk(KERN_WARNING<br>
+ "Unexpected external log buffer magic number. "<br>
+ "Got %08lx, expected %08x. Resetting pointers and using "<br>
+ "the buffer anyway.\n",<br>
+ log->tag, LOGBUFF_MAGIC);<br>
+ log->tag = LOGBUFF_MAGIC;<br>
+ log->start = log->end = log->con = log->chars = 0;<br>
+ }<br>
+<br>
+ spin_lock_irqsave(&logbuf_lock, flags);<br>
+<br>
+ logbuff_check_metadata(log);<br>
+ logbuff_coalesce_buffers(log);<br>
+<br>
+ /* Switch to the external log buffer */<br>
+ ext_log_start = &log->start;<br>
+ ext_con_start = &log->con;<br>
+ ext_log_end = &log->end;<br>
+ ext_logged_chars = &log->chars;<br>
+<br>
+ log_buf = ext_log_buf;<br>
+ log_buf_len = LOGBUFF_LEN;<br>
+<br>
+ spin_unlock_irqrestore(&logbuf_lock, flags);<br>
+<br>
+ printk(KERN_NOTICE "log_buf=%p\n", log_buf);<br>
+}<br>
+#endif /* CONFIG_LOGBUFFER */<br>
static int __init log_buf_len_setup(char *str)<br>
{<br>
+#ifdef CONFIG_LOGBUFFER<br>
+ /* Log buffer size is LOGBUFF_LEN bytes */<br>
+ printk(KERN_NOTICE<br>
+ "External log buffer configured; "<br>
+ "ignoring log_buf_len param.\n");<br>
+ return 1;<br>
+#else<br>
unsigned size = memparse(str, &str);<br>
unsigned long flags;<br>
<br>
@@ -173,6 +315,7 @@ static int __init log_buf_len_setup(char *str)<br>
}<br>
out:<br>
return 1;<br>
+#endif /* CONFIG_LOGBUFFER */<br>
}<br>
<br>
__setup("log_buf_len=", log_buf_len_setup);<br>
@@ -230,7 +373,7 @@ static void boot_delay_msec(void)<br>
static inline void boot_delay_msec(void)<br>
{<br>
}<br>
-#endif<br>
+#endif /* CONFIG_BOOT_PRINTK_DELAY */<br>
<br>
/*<br>
* Commands to do_syslog:<br>
@@ -740,7 +883,7 @@ out_restore_irqs:<br>
EXPORT_SYMBOL(printk);<br>
EXPORT_SYMBOL(vprintk);<br>
<br>
-#else<br>
+#else /* !CONFIG_PRINTK */<br>
<br>
asmlinkage long sys_syslog(int type, char __user *buf, int len)<br>
{<br>
@@ -751,7 +894,7 @@ static void call_console_drivers(unsigned start, unsigned end)<br>
{<br>
}<br>
<br>
-#endif<br>
+#endif /* CONFIG_PRINTK */<br>
<br>
static int __add_preferred_console(char *name, int idx, char *options,<br>
char *brl_options)<br>
<font color="#888888">--<br>
<a href="http://1.6.0.4" target="_blank">1.6.0.4</a><br>
<br>
_______________________________________________<br>
Linuxppc-dev mailing list<br>
<a href="mailto:Linuxppc-dev@ozlabs.org">Linuxppc-dev@ozlabs.org</a><br>
<a href="https://ozlabs.org/mailman/listinfo/linuxppc-dev" target="_blank">https://ozlabs.org/mailman/listinfo/linuxppc-dev</a><br>
</font></blockquote></div><br><br clear="all"><br>