[PATCH/RFC] Add Alternative Log Buffer Support for printk Messages
Matt Sealey
matt at genesi-usa.com
Wed Nov 26 05:53:12 EST 2008
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 <matt at genesi-usa.com>
Genesi, Manager, Developer Relations
On Tue, Nov 25, 2008 at 12:34 PM, Grant Erickson
<gerickson at nuovations.com>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 <gerickson at nuovations.com>
> ---
>
> 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, wd at denx.de.
> + *
> + * 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 at ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/linuxppc-dev/attachments/20081125/4d1ceece/attachment.htm>
More information about the Linuxppc-dev
mailing list