printk: allocate kernel log buffer earlier

On larger systems, because of the numerous ACPI, Bootmem and EFI messages,
the static log buffer overflows before the larger one specified by the
log_buf_len param is allocated.  Minimize the overflow by allocating the
new log buffer as soon as possible.

On kernels without memblock, a later call to setup_log_buf from
kernel/init.c is the fallback.

[akpm@linux-foundation.org: coding-style fixes]
[akpm@linux-foundation.org: fix CONFIG_PRINTK=n build]
Signed-off-by: Mike Travis <travis@sgi.com>
Cc: Yinghai Lu <yhlu.kernel@gmail.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Jack Steiner <steiner@sgi.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Mike Travis 2011-05-24 17:13:20 -07:00 committed by Linus Torvalds
parent 95dde50190
commit 162a7e7500
4 changed files with 70 additions and 31 deletions

View File

@ -946,6 +946,8 @@ void __init setup_arch(char **cmdline_p)
if (init_ohci1394_dma_early)
init_ohci1394_dma_on_all_controllers();
#endif
/* Allocate bigger log buffer */
setup_log_buf(1);
reserve_initrd();

View File

@ -1,6 +1,8 @@
#ifndef __KERNEL_PRINTK__
#define __KERNEL_PRINTK__
#include <linux/init.h>
extern const char linux_banner[];
extern const char linux_proc_banner[];
@ -113,6 +115,7 @@ extern int dmesg_restrict;
extern int kptr_restrict;
void log_buf_kexec_setup(void);
void __init setup_log_buf(int early);
#else
static inline __attribute__ ((format (printf, 1, 0)))
int vprintk(const char *s, va_list args)
@ -137,6 +140,10 @@ static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies,
static inline void log_buf_kexec_setup(void)
{
}
static inline void setup_log_buf(int early)
{
}
#endif
extern void dump_stack(void) __cold;

View File

@ -504,6 +504,7 @@ asmlinkage void __init start_kernel(void)
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
setup_log_buf(0);
pidhash_init();
vfs_caches_init_early();
sort_main_extable();

View File

@ -31,6 +31,7 @@
#include <linux/smp.h>
#include <linux/security.h>
#include <linux/bootmem.h>
#include <linux/memblock.h>
#include <linux/syscalls.h>
#include <linux/kexec.h>
#include <linux/kdb.h>
@ -167,46 +168,74 @@ void log_buf_kexec_setup(void)
}
#endif
/* requested log_buf_len from kernel cmdline */
static unsigned long __initdata new_log_buf_len;
/* save requested log_buf_len since it's too early to process it */
static int __init log_buf_len_setup(char *str)
{
unsigned size = memparse(str, &str);
unsigned long flags;
if (size)
size = roundup_pow_of_two(size);
if (size > log_buf_len) {
unsigned start, dest_idx, offset;
char *new_log_buf;
if (size > log_buf_len)
new_log_buf_len = size;
new_log_buf = alloc_bootmem(size);
if (!new_log_buf) {
printk(KERN_WARNING "log_buf_len: allocation failed\n");
goto out;
}
spin_lock_irqsave(&logbuf_lock, flags);
log_buf_len = size;
log_buf = new_log_buf;
offset = start = min(con_start, log_start);
dest_idx = 0;
while (start != log_end) {
log_buf[dest_idx] = __log_buf[start & (__LOG_BUF_LEN - 1)];
start++;
dest_idx++;
}
log_start -= offset;
con_start -= offset;
log_end -= offset;
spin_unlock_irqrestore(&logbuf_lock, flags);
printk(KERN_NOTICE "log_buf_len: %d\n", log_buf_len);
}
out:
return 1;
return 0;
}
early_param("log_buf_len", log_buf_len_setup);
__setup("log_buf_len=", log_buf_len_setup);
void __init setup_log_buf(int early)
{
unsigned long flags;
unsigned start, dest_idx, offset;
char *new_log_buf;
int free;
if (!new_log_buf_len)
return;
if (early) {
unsigned long mem;
mem = memblock_alloc(new_log_buf_len, PAGE_SIZE);
if (mem == MEMBLOCK_ERROR)
return;
new_log_buf = __va(mem);
} else {
new_log_buf = alloc_bootmem_nopanic(new_log_buf_len);
}
if (unlikely(!new_log_buf)) {
pr_err("log_buf_len: %ld bytes not available\n",
new_log_buf_len);
return;
}
spin_lock_irqsave(&logbuf_lock, flags);
log_buf_len = new_log_buf_len;
log_buf = new_log_buf;
new_log_buf_len = 0;
free = __LOG_BUF_LEN - log_end;
offset = start = min(con_start, log_start);
dest_idx = 0;
while (start != log_end) {
unsigned log_idx_mask = start & (__LOG_BUF_LEN - 1);
log_buf[dest_idx] = __log_buf[log_idx_mask];
start++;
dest_idx++;
}
log_start -= offset;
con_start -= offset;
log_end -= offset;
spin_unlock_irqrestore(&logbuf_lock, flags);
pr_info("log_buf_len: %d\n", log_buf_len);
pr_info("early log buf free: %d(%d%%)\n",
free, (free * 100) / __LOG_BUF_LEN);
}
#ifdef CONFIG_BOOT_PRINTK_DELAY