<kernel v5.0>
많은 수의 CPU를 사용하는 SMP 시스템에서 로그 버퍼가 부족해지는 경우가 있어 그러한 시스템에서 더 커진 로그 버퍼를 할당한다.
setup_log_buf()
kernel/printk/printk.c
void __init setup_log_buf(int early)
{
unsigned long flags;
char *new_log_buf;
unsigned int free;
if (log_buf != __log_buf)
return;
if (!early && !new_log_buf_len)
log_buf_add_cpu();
if (!new_log_buf_len)
return;
if (early) {
new_log_buf =
memblock_alloc(new_log_buf_len, LOG_ALIGN);
} else {
new_log_buf = memblock_alloc_nopanic(new_log_buf_len,
LOG_ALIGN);
}
if (unlikely(!new_log_buf)) {
pr_err("log_buf_len: %lu bytes not available\n",
new_log_buf_len);
return;
}
logbuf_lock_irqsave(flags);
log_buf_len = new_log_buf_len;
log_buf = new_log_buf;
new_log_buf_len = 0;
free = __LOG_BUF_LEN - log_next_idx;
memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
logbuf_unlock_irqrestore(flags);
pr_info("log_buf_len: %u bytes\n", log_buf_len);
pr_info("early log buf free: %u(%u%%)\n",
free, (free * 100) / __LOG_BUF_LEN);
}
cpu 수가 적정 수(arm=17개, x86=33)를 초과하는 시스템의 경우 그 cpu 수 만큼 더 커진 새 로그 버퍼를 할당한다.
- 코드 라인 7~8에서 이미 log_buf가 기본 버퍼를 사용하지 않고 재 설정된 경우 함수를 빠져나간다.
- 코드 라인 10~11에서 early 호출이 아니면서 new_log_buf_len이 0으로 되어 있는 경우 new_log_buf_len를 재조정한다.
- 보다 빠르게 로그 버퍼가 필요한 x86시스템에서만 setup_arch() 함수 내부에서 early=1로 하여 호출한다.
- 정규 호출은 start_kernel()에서 각 아키텍처의 setup_arch() 함수가 완료된 후에 호출한다.
- 코드 라인 13~14에서 로그 버퍼로 더 추가할 필요가 없는 경우 new_log_buf_len이 여전히 0이며 이 때 함수를 빠져나간다.
- 코드 라인 16~18에서 early 호출인 경우 new_log_buf를 new_log_buf_len 만큼 할당한다.
- early 호출 시 memblock 할당이 안되는 경우 panic()이 호출된다.
- 코드 라인 19~22에서 early 호출이 아닌 경우 new_log_buf를 new_log_buf_len 만큼 할당한다
- memblock 할당이 안되는 경우에도 panic()이 호출되지 않는다.
- 코드 라인 34~28에서 적은 확률로 new_log_buf가 설정되지 않으면 에러 메시지를 출력하고 함수를 빠져나간다.
- 코드 라인 31~33에서 log 버퍼를 새롭게 할당 받은 버퍼로 설정한다.
- 코드 라인 35에서 원래 로그 버퍼 전체를 새 로그 버퍼로 복사한다.
- 코드 라인 38~40에서 로그 버퍼 길이 정보를 출력한다.
kernel/printk/printk.c
/* record buffer */ #define LOG_ALIGN __alignof__(struct printk_log) #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) #define LOG_BUF_LEN_MAX (u32)(1 << 31) static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN); static char *log_buf = __log_buf; static u32 log_buf_len = __LOG_BUF_LEN;
kernel/printk/printk.c
/* requested log_buf_len from kernel cmdline */ static unsigned long __initdata new_log_buf_len;
log_buf_add_cpu()
kernel/printk/printk.c
static void __init log_buf_add_cpu(void)
{
unsigned int cpu_extra;
/*
* archs should set up cpu_possible_bits properly with
* set_cpu_possible() after setup_arch() but just in
* case lets ensure this is valid.
*/
if (num_possible_cpus() == 1)
return;
cpu_extra = (num_possible_cpus() - 1) * __LOG_CPU_MAX_BUF_LEN;
/* by default this will only continue through for large > 64 CPUs */
if (cpu_extra <= __LOG_BUF_LEN / 2)
return;
pr_info("log_buf_len individual max cpu contribution: %d bytes\n",
__LOG_CPU_MAX_BUF_LEN);
pr_info("log_buf_len total cpu_extra contributions: %d bytes\n",
cpu_extra);
pr_info("log_buf_len min size: %d bytes\n", __LOG_BUF_LEN);
log_buf_len_update(cpu_extra + __LOG_BUF_LEN);
}
default 설정된 로그 버퍼 기준으로 possible cpu 수가 적정 수(arm=17, x86=33)를 초과하는 경우에만 추가 cpu 버퍼를 준비한다.
- 코드 라인 10~11에서 cpu가 1개일 경우 추가 로그 버퍼를 만들지 않고 함수를 빠져나간다.
- 코드 라인 13에서 possible cpu-1 만큼의 추가 로그 버퍼를 산출한다.
- __LOG_CPU_MAX_BUF_LEN
- default로 4K (2^12)
- powerpc 아키텍처는 8K(2^13)
- __LOG_CPU_MAX_BUF_LEN
- 코드 라인 16~17에서 추가 로그 버퍼가 기존 로그 버퍼의 절반 보다 작은 경우 추가 버퍼를 만들지 않고 함수를 빠져나간다.
- __LOG_BUF_LEN
- arm 아키텍처는 SoC 마다 8K(2^13) ~ 512K(2^19)를 사용한다.
- arm64 아키텍처는 128K(2^17)을 사용한다.
- x86 아키텍처는 256K(2^18)을 사용한다.
- __LOG_BUF_LEN
- 코드 라인 19~23에서 추가 버퍼 정보를 출력한다.
- 코드 라인 25에서 로그 버퍼를 기본 버퍼 + cpu_extra 버퍼 만큼 설정한다.
kernel/printk/printk.c
#define __LOG_CPU_MAX_BUF_LEN (1 << CONFIG_LOG_CPU_MAX_BUF_SHIFT)
- CONFIG_LOG_CPU_MAX_BUF_SHIFT
- default=12
kernel/printk/printk.c
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
- CONFIG_LOG_BUF_SHIFT
- default=17
num_possible_cpus()
include/linux/cpumask.h
#define num_possible_cpus() cpumask_weight(cpu_possible_mask)
possible cpu 수를 알아온다.
cpumask_weight()
include/linux/cpumask.h
/**
* cpumask_weight - Count of bits in *srcp
* @srcp: the cpumask to count bits (< nr_cpu_ids) in.
*/
static inline unsigned int cpumask_weight(const struct cpumask *srcp)
{
return bitmap_weight(cpumask_bits(srcp), nr_cpumask_bits);
}
cpu 관련 비트맵 srcp에서 nr_cpumask_bits 만큼의 비트에서 1로 설정된 비트 수를 알아온다.
nr_cpumask_bits
include/linux/cpumask.h
#ifdef CONFIG_CPUMASK_OFFSTACK /* Assuming NR_CPUS is huge, a runtime limit is more efficient. Also, * not all bits may be allocated. */ #define nr_cpumask_bits nr_cpu_ids #else #define nr_cpumask_bits NR_CPUS #endif
log_buf_len_update()
kernel/printk/printk.c
/* we practice scaling the ring buffer by powers of 2 */
static void __init log_buf_len_update(unsigned size)
{
if (size > (u64)LOG_BUF_LEN_MAX) {
size = (u64)LOG_BUF_LEN_MAX;
pr_err("log_buf over 2G is not supported.\n");
}
if (size)
size = roundup_pow_of_two(size);
if (size > log_buf_len)
new_log_buf_len = size;
}
인수로 지정된 size 값을 가까운 2의 n승에 맞게 round up한 후 new_log_buf_len 보다 큰 경우에만 update 한다. 최대 2G로 제한된다.
- 예) size=636K
- 1M
/**
* roundup_pow_of_two - round the given value up to nearest power of two
* @n - parameter
*
* round the given value up to the nearest power of two
* - the result is undefined when n == 0
* - this can be used to initialise global variables from constant data
*/
#define roundup_pow_of_two(n) \
( \
__builtin_constant_p(n) ? ( \
(n == 1) ? 1 : \
(1UL << (ilog2((n) - 1) + 1)) \
) : \
__roundup_pow_of_two(n) \
)
n 값을 가까운 2의 n승에 맞게 round up한 후 리턴한다.
- 예) 100
- 128
/*
* round up to nearest power of two
*/
static inline __attribute__((const))
unsigned long __roundup_pow_of_two(unsigned long n)
{
return 1UL << fls_long(n - 1);
}
n 값을 가까운 2의 n승에 맞게 round up한 후 리턴한다.
