Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Mike Travis and committed by
Linus Torvalds
162a7e75 95dde501

+70 -31
+2
arch/x86/kernel/setup.c
··· 946 946 if (init_ohci1394_dma_early) 947 947 init_ohci1394_dma_on_all_controllers(); 948 948 #endif 949 + /* Allocate bigger log buffer */ 950 + setup_log_buf(1); 949 951 950 952 reserve_initrd(); 951 953
+7
include/linux/printk.h
··· 1 1 #ifndef __KERNEL_PRINTK__ 2 2 #define __KERNEL_PRINTK__ 3 3 4 + #include <linux/init.h> 5 + 4 6 extern const char linux_banner[]; 5 7 extern const char linux_proc_banner[]; 6 8 ··· 115 113 extern int kptr_restrict; 116 114 117 115 void log_buf_kexec_setup(void); 116 + void __init setup_log_buf(int early); 118 117 #else 119 118 static inline __attribute__ ((format (printf, 1, 0))) 120 119 int vprintk(const char *s, va_list args) ··· 138 135 } 139 136 140 137 static inline void log_buf_kexec_setup(void) 138 + { 139 + } 140 + 141 + static inline void setup_log_buf(int early) 141 142 { 142 143 } 143 144 #endif
+1
init/main.c
··· 504 504 * These use large bootmem allocations and must precede 505 505 * kmem_cache_init() 506 506 */ 507 + setup_log_buf(0); 507 508 pidhash_init(); 508 509 vfs_caches_init_early(); 509 510 sort_main_extable();
+60 -31
kernel/printk.c
··· 31 31 #include <linux/smp.h> 32 32 #include <linux/security.h> 33 33 #include <linux/bootmem.h> 34 + #include <linux/memblock.h> 34 35 #include <linux/syscalls.h> 35 36 #include <linux/kexec.h> 36 37 #include <linux/kdb.h> ··· 168 167 } 169 168 #endif 170 169 170 + /* requested log_buf_len from kernel cmdline */ 171 + static unsigned long __initdata new_log_buf_len; 172 + 173 + /* save requested log_buf_len since it's too early to process it */ 171 174 static int __init log_buf_len_setup(char *str) 172 175 { 173 176 unsigned size = memparse(str, &str); 174 - unsigned long flags; 175 177 176 178 if (size) 177 179 size = roundup_pow_of_two(size); 178 - if (size > log_buf_len) { 179 - unsigned start, dest_idx, offset; 180 - char *new_log_buf; 180 + if (size > log_buf_len) 181 + new_log_buf_len = size; 181 182 182 - new_log_buf = alloc_bootmem(size); 183 - if (!new_log_buf) { 184 - printk(KERN_WARNING "log_buf_len: allocation failed\n"); 185 - goto out; 186 - } 187 - 188 - spin_lock_irqsave(&logbuf_lock, flags); 189 - log_buf_len = size; 190 - log_buf = new_log_buf; 191 - 192 - offset = start = min(con_start, log_start); 193 - dest_idx = 0; 194 - while (start != log_end) { 195 - log_buf[dest_idx] = __log_buf[start & (__LOG_BUF_LEN - 1)]; 196 - start++; 197 - dest_idx++; 198 - } 199 - log_start -= offset; 200 - con_start -= offset; 201 - log_end -= offset; 202 - spin_unlock_irqrestore(&logbuf_lock, flags); 203 - 204 - printk(KERN_NOTICE "log_buf_len: %d\n", log_buf_len); 205 - } 206 - out: 207 - return 1; 183 + return 0; 208 184 } 185 + early_param("log_buf_len", log_buf_len_setup); 209 186 210 - __setup("log_buf_len=", log_buf_len_setup); 187 + void __init setup_log_buf(int early) 188 + { 189 + unsigned long flags; 190 + unsigned start, dest_idx, offset; 191 + char *new_log_buf; 192 + int free; 193 + 194 + if (!new_log_buf_len) 195 + return; 196 + 197 + if (early) { 198 + unsigned long mem; 199 + 200 + mem = memblock_alloc(new_log_buf_len, PAGE_SIZE); 201 + if (mem == MEMBLOCK_ERROR) 202 + return; 203 + new_log_buf = __va(mem); 204 + } else { 205 + new_log_buf = alloc_bootmem_nopanic(new_log_buf_len); 206 + } 207 + 208 + if (unlikely(!new_log_buf)) { 209 + pr_err("log_buf_len: %ld bytes not available\n", 210 + new_log_buf_len); 211 + return; 212 + } 213 + 214 + spin_lock_irqsave(&logbuf_lock, flags); 215 + log_buf_len = new_log_buf_len; 216 + log_buf = new_log_buf; 217 + new_log_buf_len = 0; 218 + free = __LOG_BUF_LEN - log_end; 219 + 220 + offset = start = min(con_start, log_start); 221 + dest_idx = 0; 222 + while (start != log_end) { 223 + unsigned log_idx_mask = start & (__LOG_BUF_LEN - 1); 224 + 225 + log_buf[dest_idx] = __log_buf[log_idx_mask]; 226 + start++; 227 + dest_idx++; 228 + } 229 + log_start -= offset; 230 + con_start -= offset; 231 + log_end -= offset; 232 + spin_unlock_irqrestore(&logbuf_lock, flags); 233 + 234 + pr_info("log_buf_len: %d\n", log_buf_len); 235 + pr_info("early log buf free: %d(%d%%)\n", 236 + free, (free * 100) / __LOG_BUF_LEN); 237 + } 211 238 212 239 #ifdef CONFIG_BOOT_PRINTK_DELAY 213 240