diff options
Diffstat (limited to 'kernel/printk.c')
| -rw-r--r-- | kernel/printk.c | 87 | 
1 files changed, 58 insertions, 29 deletions
| diff --git a/kernel/printk.c b/kernel/printk.c index da8ca817eae3..35185392173f 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -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; -		} +	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 | 
