setup_log_buf()

많은 수의 CPU를 사용하는 SMP 시스템에서 로그 버퍼가 부족해지는 경우가 있어 그러한 시스템에서 더 커진 로그 버퍼를 할당한다.

setup_log_buf-1

 

setup_log_buf()

kernel/printk/printk.c

void __init setup_log_buf(int early)
{
        unsigned long flags;
        char *new_log_buf;
        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_virt_alloc(new_log_buf_len, LOG_ALIGN);
        } else {
                new_log_buf = memblock_virt_alloc_nopanic(new_log_buf_len,
                                                          LOG_ALIGN);
        }

        if (unlikely(!new_log_buf)) {
                pr_err("log_buf_len: %ld bytes not available\n",
                        new_log_buf_len);
                return;
        }

        raw_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_next_idx;
        memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
        raw_spin_unlock_irqrestore(&logbuf_lock, flags);

        pr_info("log_buf_len: %d bytes\n", log_buf_len);
        pr_info("early log buf free: %d(%d%%)\n",
                free, (free * 100) / __LOG_BUF_LEN);
}

cpu 수가 적정 수(arm=17개, x86=33)를 초과하는 시스템의 경우 그 cpu 수 만큼 더 커진 새 로그 버퍼를 할당한다.

  • if (log_buf != __log_buf) return;
    • 이미 log_buf가 기본 버퍼를 사용하지 않고 재 설정된 경우 함수를 빠져나간다.
  • if (!early && !new_log_buf_len) log_buf_add_cpu();
    • early 호출이 아니면서 new_log_buf_len이 0으로 되어 있는 경우
    • 정규 호출은 start_kernel()에서 각 아키텍처의 setup_arch() 함수가 완료된 후에 호출한다.
    • 보다 빠르게 로그 버퍼가 필요한 시스템에서 early=1로 하여 호출한다.
      • 예)  x86 아키텍처의 setup_arch() 함수에서 호출한다.
  • if (!new_log_buf_len) return;
    • new_log_buf_len이 여전히 0인 경우 함수를 빠져나간다.
  • if (early) { new_log_buf = memblock_virt_alloc(new_log_buf_len, LOG_ALIGN);
    • early 호출인 경우 new_log_buf를 new_log_buf_len 만큼 할당한다.
    • early 호출 시 memblock 할당이 안되는 경우 panic()이 호출된다.
  • else { new_log_buf = memblock_virt_alloc_nopanic(new_log_buf_len, LOG_ALIGN); }
    • early 호출이 아닌 경우 new_log_buf를 new_log_buf_len 만큼 할당한다
    • memblock 할당이 안되는 경우에도 panic()이 호출되지 않는다.
  • if (unlikely(!new_log_buf)) {
    • 적은 확률로 new_log_buf가 설정되지 않으면 에러 메시지를 출력하고 함수를 빠져나간다.
  • log_buf_len = new_log_buf_len; log_buf = new_log_buf; new_log_buf_len = 0;
    • log 버퍼를 새롭게 할당 받은 버퍼로 설정한다.
  • free = __LOG_BUF_LEN – log_next_idx;
    • 원래 로그 버퍼에서 남은 공간 길이
  • memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
    • 원래 로그 버퍼 전체를 새 로그 버퍼로 복사한다.

 

kernel/printk/printk.c

/* record buffer */
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
#define LOG_ALIGN 4
#else
#define LOG_ALIGN __alignof__(struct printk_log)
#endif
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
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 설정된 로그 버퍼 기준으로 cpu 수가 적정 수(arm=17, x86=33)개를 초과하는 경우에만 추가 cpu 버퍼를 준비한다.

  • if (num_possible_cpus() == 1) return;
    • cpu가 1개일 경우 추가 로그 버퍼를 만들지 않고 리턴한다.
  • cpu_extra = (num_possible_cpus() – 1) * __LOG_CPU_MAX_BUF_LEN;
    • cpu 수 만큼의 로그 버퍼 길이
    • __LOG_CPU_MAX_BUF_LEN
      • default로 4K (2 ^ 12)
  • if (cpu_extra <= __LOG_BUF_LEN / 2) return;
    • __LOG_BUF_LEN
      • default로 128K (2 ^ 17)
    • arm에서 16개 이하의 cpu는 위의 조건으로 인하여 함수를 빠져나간다.
  •  log_buf_len_update(cpu_extra + __LOG_BUF_LEN);
    •  로그 버퍼를 기본 버퍼 + cpu_extra 버퍼 만큼 설정한다.
    • 예) cpu=128개
      • 508K + 128K = 636K 버퍼

 

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)
                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 한다.

  • 예) 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한 후 리턴한다.

 

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.