Commit 162a7e7500f9664636e649ba59defe541b7c2c60
Committed by
Linus Torvalds
1 parent
95dde50190
Exists in
master
and in
4 other branches
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>
Showing 4 changed files with 68 additions and 29 deletions Side-by-side Diff
arch/x86/kernel/setup.c
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 | |
... | ... | @@ -113,6 +115,7 @@ |
113 | 115 | extern int kptr_restrict; |
114 | 116 | |
115 | 117 | void log_buf_kexec_setup(void); |
118 | +void __init setup_log_buf(int early); | |
116 | 119 | #else |
117 | 120 | static inline __attribute__ ((format (printf, 1, 0))) |
118 | 121 | int vprintk(const char *s, va_list args) |
... | ... | @@ -135,6 +138,10 @@ |
135 | 138 | } |
136 | 139 | |
137 | 140 | static inline void log_buf_kexec_setup(void) |
141 | +{ | |
142 | +} | |
143 | + | |
144 | +static inline void setup_log_buf(int early) | |
138 | 145 | { |
139 | 146 | } |
140 | 147 | #endif |
init/main.c
kernel/printk.c
... | ... | @@ -31,6 +31,7 @@ |
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> |
37 | 38 | |
38 | 39 | |
39 | 40 | |
40 | 41 | |
41 | 42 | |
42 | 43 | |
43 | 44 | |
44 | 45 | |
... | ... | @@ -167,46 +168,74 @@ |
167 | 168 | } |
168 | 169 | #endif |
169 | 170 | |
171 | +/* requested log_buf_len from kernel cmdline */ | |
172 | +static unsigned long __initdata new_log_buf_len; | |
173 | + | |
174 | +/* save requested log_buf_len since it's too early to process it */ | |
170 | 175 | static int __init log_buf_len_setup(char *str) |
171 | 176 | { |
172 | 177 | unsigned size = memparse(str, &str); |
173 | - unsigned long flags; | |
174 | 178 | |
175 | 179 | if (size) |
176 | 180 | size = roundup_pow_of_two(size); |
177 | - if (size > log_buf_len) { | |
178 | - unsigned start, dest_idx, offset; | |
179 | - char *new_log_buf; | |
181 | + if (size > log_buf_len) | |
182 | + new_log_buf_len = size; | |
180 | 183 | |
181 | - new_log_buf = alloc_bootmem(size); | |
182 | - if (!new_log_buf) { | |
183 | - printk(KERN_WARNING "log_buf_len: allocation failed\n"); | |
184 | - goto out; | |
185 | - } | |
184 | + return 0; | |
185 | +} | |
186 | +early_param("log_buf_len", log_buf_len_setup); | |
186 | 187 | |
187 | - spin_lock_irqsave(&logbuf_lock, flags); | |
188 | - log_buf_len = size; | |
189 | - log_buf = new_log_buf; | |
188 | +void __init setup_log_buf(int early) | |
189 | +{ | |
190 | + unsigned long flags; | |
191 | + unsigned start, dest_idx, offset; | |
192 | + char *new_log_buf; | |
193 | + int free; | |
190 | 194 | |
191 | - offset = start = min(con_start, log_start); | |
192 | - dest_idx = 0; | |
193 | - while (start != log_end) { | |
194 | - log_buf[dest_idx] = __log_buf[start & (__LOG_BUF_LEN - 1)]; | |
195 | - start++; | |
196 | - dest_idx++; | |
197 | - } | |
198 | - log_start -= offset; | |
199 | - con_start -= offset; | |
200 | - log_end -= offset; | |
201 | - spin_unlock_irqrestore(&logbuf_lock, flags); | |
195 | + if (!new_log_buf_len) | |
196 | + return; | |
202 | 197 | |
203 | - printk(KERN_NOTICE "log_buf_len: %d\n", log_buf_len); | |
198 | + if (early) { | |
199 | + unsigned long mem; | |
200 | + | |
201 | + mem = memblock_alloc(new_log_buf_len, PAGE_SIZE); | |
202 | + if (mem == MEMBLOCK_ERROR) | |
203 | + return; | |
204 | + new_log_buf = __va(mem); | |
205 | + } else { | |
206 | + new_log_buf = alloc_bootmem_nopanic(new_log_buf_len); | |
204 | 207 | } |
205 | -out: | |
206 | - return 1; | |
207 | -} | |
208 | 208 | |
209 | -__setup("log_buf_len=", log_buf_len_setup); | |
209 | + if (unlikely(!new_log_buf)) { | |
210 | + pr_err("log_buf_len: %ld bytes not available\n", | |
211 | + new_log_buf_len); | |
212 | + return; | |
213 | + } | |
214 | + | |
215 | + spin_lock_irqsave(&logbuf_lock, flags); | |
216 | + log_buf_len = new_log_buf_len; | |
217 | + log_buf = new_log_buf; | |
218 | + new_log_buf_len = 0; | |
219 | + free = __LOG_BUF_LEN - log_end; | |
220 | + | |
221 | + offset = start = min(con_start, log_start); | |
222 | + dest_idx = 0; | |
223 | + while (start != log_end) { | |
224 | + unsigned log_idx_mask = start & (__LOG_BUF_LEN - 1); | |
225 | + | |
226 | + log_buf[dest_idx] = __log_buf[log_idx_mask]; | |
227 | + start++; | |
228 | + dest_idx++; | |
229 | + } | |
230 | + log_start -= offset; | |
231 | + con_start -= offset; | |
232 | + log_end -= offset; | |
233 | + spin_unlock_irqrestore(&logbuf_lock, flags); | |
234 | + | |
235 | + pr_info("log_buf_len: %d\n", log_buf_len); | |
236 | + pr_info("early log buf free: %d(%d%%)\n", | |
237 | + free, (free * 100) / __LOG_BUF_LEN); | |
238 | +} | |
210 | 239 | |
211 | 240 | #ifdef CONFIG_BOOT_PRINTK_DELAY |
212 | 241 |