From: "Matt Sealey" <matt@genesi-usa.com>
To: Grant Erickson <gerickson@nuovations.com>
Cc: linuxppc-dev@ozlabs.org, Stefan Roese <sr@denx.de>,
Wolfgang Denx <wd@denx.de>,
linux-embedded@vger.kernel.org
Subject: Re: [PATCH/RFC] Add Alternative Log Buffer Support for printk Messages
Date: Tue, 25 Nov 2008 12:53:12 -0600 [thread overview]
Message-ID: <b5e2fc790811251053w507792a1r3673ee2dc7581379@mail.gmail.com> (raw)
In-Reply-To: <1227638045-12862-1-git-send-email-gerickson@nuovations.com>
[-- Attachment #1.1: Type: text/plain, Size: 18409 bytes --]
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@genesi-usa.com>
Genesi, Manager, Developer Relations
On Tue, Nov 25, 2008 at 12:34 PM, Grant Erickson
<gerickson@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@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@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@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>
[-- Attachment #1.2: Type: text/html, Size: 24906 bytes --]
[-- Attachment #2: Type: text/plain, Size: 146 bytes --]
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev
next prev parent reply other threads:[~2008-11-25 18:53 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-11-25 18:34 [PATCH/RFC] Add Alternative Log Buffer Support for printk Messages Grant Erickson
2008-11-25 18:53 ` Matt Sealey [this message]
2008-11-25 18:55 ` Josh Boyer
2008-11-25 19:01 ` Matt Sealey
2008-11-25 19:04 ` Grant Erickson
2008-11-25 19:31 ` Matt Sealey
2008-11-25 19:51 ` Bill Gatliff
2008-11-25 20:07 ` Matt Sealey
2008-11-25 20:17 ` Grant Likely
2008-11-25 20:46 ` Matt Sealey
2008-11-25 20:19 ` Bill Gatliff
2008-11-25 20:54 ` David VomLehn
2008-11-25 21:45 ` David Brownell
2008-11-26 20:57 ` Matt Sealey
2008-11-25 20:14 ` Grant Likely
2008-11-25 21:05 ` Wolfgang Denk
2008-11-26 1:23 ` Mike Frysinger
2009-01-07 0:04 ` Benjamin Herrenschmidt
2009-01-07 2:11 ` Grant Erickson
2009-01-07 2:11 ` Grant Erickson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=b5e2fc790811251053w507792a1r3673ee2dc7581379@mail.gmail.com \
--to=matt@genesi-usa.com \
--cc=gerickson@nuovations.com \
--cc=linux-embedded@vger.kernel.org \
--cc=linuxppc-dev@ozlabs.org \
--cc=sr@denx.de \
--cc=wd@denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).