Skip to content

Commit

Permalink
printk: allocate kernel log buffer earlier
Browse files Browse the repository at this point in the history
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.

[[email protected]: coding-style fixes]
[[email protected]: fix CONFIG_PRINTK=n build]
Signed-off-by: Mike Travis <[email protected]>
Cc: Yinghai Lu <[email protected]>
Cc: "H. Peter Anvin" <[email protected]>
Cc: Jack Steiner <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Mike Travis authored and torvalds committed May 25, 2011
1 parent 95dde50 commit 162a7e7
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 29 deletions.
2 changes: 2 additions & 0 deletions arch/x86/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
7 changes: 7 additions & 0 deletions include/linux/printk.h
Original file line number Diff line number Diff line change
@@ -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[];

Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions init/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
87 changes: 58 additions & 29 deletions kernel/printk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
}
return 0;
}
early_param("log_buf_len", log_buf_len_setup);

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);
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;

printk(KERN_NOTICE "log_buf_len: %d\n", log_buf_len);
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);
}
out:
return 1;
}

__setup("log_buf_len=", log_buf_len_setup);
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

Expand Down

0 comments on commit 162a7e7

Please sign in to comment.