学习了下 n7100代码中对kernel log保存代码:
kernel log保存:
1. init.setup 段初始化:
sec_log有如下实现
__setup("sec_log=", sec_log_setup);
说明如下:
/*
* Example usage: sec_log=256K@0x45000000
* In above case, log_buf size is 256KB and its base address is
* 0x45000000 physically. Actually, *(int *)(base - 8) is log_magic and
* *(int *)(base - 4) is log_ptr. So we reserve (size + 8) bytes from
* (base - 8).
*/
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
在内核booting过程中如果对比发现str=a的cmd会调用对应的fn(a)
具体实现如下
vmlinux.lds.h
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
VMLINUX_SYMBOL(__setup_start) = .; \
*(.init.setup) \
VMLINUX_SYMBOL(__setup_end) = .;
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
INITCALLS \
VMLINUX_SYMBOL(__initcall_end) = .;
#define INIT_DATA_SECTION(initsetup_align) \
.init.data : AT(ADDR(.init.data) - LOAD_OFFSET) { \
INIT_DATA \
INIT_SETUP(initsetup_align) \
INIT_CALLS \
CON_INITCALL \
SECURITY_INITCALL \
INIT_RAM_FS \
}
对于__setup()定义的:
start_kernel()--> setup_arch(&command_line);-->parse_early_param();
对于moudle_init定义的:
start_kernel() --> rest_init(); --> kernel_init() --> do_basic_setup() --> do_init_calls()
其中kenrel_init中:
static int __init kernel_init(void * unused)
{
/*
* Wait until kthreadd is all set-up.
*/
wait_for_completion(&kthreadd_done);
/*
* init can allocate pages on any node
*/
set_mems_allowed(node_states[N_HIGH_MEMORY]);
/*
* init can run on any cpu.
*/
set_cpus_allowed_ptr(current, cpu_all_mask);
cad_pid = task_pid(current);
smp_prepare_cpus(setup_max_cpus);
do_pre_smp_initcalls();
lockup_detector_init();
smp_init(); // enable multi-core
sched_init_smp();
do_basic_setup(); //init_machine也在这里调用
......
init_post();//这里调用free_initmem将initmem释放回收:
totalram_pages += free_area(__phys_to_pfn(__pa(__init_begin)),
__phys_to_pfn(__pa(__init_end)),
"init");
}
2. 对于ringbuffer 循环写的实现:
static inline void emit_sec_log_char(char c)
{
if (sec_log_buf && sec_log_ptr) {
sec_log_buf[*sec_log_ptr & (sec_log_size - 1)] = c;
(*sec_log_ptr)++;
}
}
这里& (sec_log_size - 1) 保证了在sec_log_size范围内 循环写(这要保证sec_log_size为1左移整数位的大小)
3.对上一次kmsg的保存:
static void __init sec_log_save_old(void)
{
/* provide previous log as last_kmsg */
last_kmsg_size =
min((unsigned)(1 << CONFIG_LOG_BUF_SHIFT), *sec_log_ptr);
last_kmsg_buffer = (char *)alloc_bootmem(last_kmsg_size);
if (last_kmsg_size && last_kmsg_buffer) {
unsigned i;
for (i = 0; i < last_kmsg_size; i++)
last_kmsg_buffer[i] =
sec_log_buf[(*sec_log_ptr - last_kmsg_size +
i) & (sec_log_size - 1)];
pr_info("%s: saved old log at %d@%p\n",
__func__, last_kmsg_size, last_kmsg_buffer);
} else
pr_err("%s: failed saving old log %d@%p\n",
__func__, last_kmsg_size, last_kmsg_buffer);
}
这里CONFIG_LOG_BUF_SHIFT是系统默认的比如 17 那么系统kmsg buffer默认为128K
不管sec_log_size多大,我们可以只保存lastmsg最大128K,其余的丢弃。
last_kmsg_size =
min((unsigned)(1 << CONFIG_LOG_BUF_SHIFT), *sec_log_ptr);
这样如果sec_log_ptr跑的长度大于128K 我们只要128K 如果有效长度小于128K 我们取有效长度。