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