boot_cpu_init()

CPU 상태를 관리하는 몇 개의 전역 변수에 현재 CPU의 상태를 설정한다.

  • CPU 상태는 4개의 비트맵 변수를 사용하여 Bitmap Operations 관련 함수로 관리된다.
    • online
    • active
    • present
    • possible
  • CPU 상태 관련한 비트맵 전역 변수들은 배열로 관리하기 때문에 최대 수와 관련 없이 지원 가능하다.
  • 점점 지원 CPU 최대 수가 늘어나고 있으며 현재 linux kernel 4.4 기준 아키텍처별 최대 CPU 수
    • arm: 32개
    • arm64: 64, 4096개
    • x86: 8(32bit), 32(32bit), 512, 8192(64bit)
    • mips: 4, 8, 16, 32, 64, 256, 8192
    • powerpc: 128, 2048
    • ia64: 4096개
    • spark: 32(32bit), 64(64bit), 1024(64bit)

boot_cpu_init_a

CPU 상태

  • CPU 마다 4 가지의 상태 플래그를 가지고 있고 리눅스는 CPU hotplug 를 지원한다. (ARM linux는 아직 지원하지 않고 있음)
    •  online
      • 현재 online 상태이고 스케쥴되는 상태
    • active
      • cpu up/down에 사용
      • CPU가 hotplug되어 준비된 상태(present=true, active=false)에서 cpu up 하면 스케쥴링 상태(active=true, online=true)로 변경됨.
      • 스케쥴링 상태(online=true, active=true)에서 cpu down 하면 두 단계로 나뉘어 상태가 바뀐다.
        • 더 이상 스케쥴링 하지 않는 상태(active=false, online=true)
        • 이어서 조만간 완전히 기동을 멈춘 상태(active=false, online=false)로 바뀜
      • runqueue migration 등에 사용됨.
    • present 
      • 장착되어 cpu가 인식된 상태
      • 현재 시스템에서 CPU를 인식한 경우에만 true이며, possible의 부분집합
      • 처음 hotplug되기 전 상태는 online과 active는 둘 다 false 상태임.
      • 시스템에 CPU의 허용치 이내(possible=true)에서 CPU 카드를 hotplug한 경우 CPU가 정상 인식된 경우 가용 가능한 CPU 자원으로 만든다.(present=true)
    • possible
      • 현재 동작 또는 hot-plug 가능한 상태
      • 현재 동작하거나 확장할 수 있는 상태비트로 부팅 시 고정되는 비트로 앞으로 상태를 바꿀 수 없다.
      • 향후 hotplug될 카드의 CPU 수 만큼 미리 설정해 놓아야한다.
      • 시스템에 CPU 카드를 허용치 이상 hotplug 시킨 경우(해당 CPU의 possible 상태가 false) present로 바뀌지 않는다.
      • NR_CPUS 를 초과할 수 없음

 

CPU Hotplug

  • kernel 3.8 에 x86 generic이 올라가서 x86 프로세서에서 처음 적용되었다.
  • 대부분의 64비트 CPU 아키텍처에 탑재되었으나 ARM 32비트에는 아직 적용되지 않았다.
  • CPU 다운을 시키는 경우 먼저 active가 false가 되고 태스크의 수행이 완료되는 대로 스케줄러가 runqueue에 있는 태스크를 제거하여 online 마저 false가 된다.

cpu_hotplug

 

CPU hotplug 시나리오 (CPU: 4 → 8)

  • 최대 CPU를 8개로 설정(NR_CPUS=8, 최대 8개 CPU까지 확장 가능하다는 의미)
  • 각 CPU 상태 비트들은 좌측부터 우측으로 8개가 사용된다. (bit0 ~ bit7)
  • 부팅 시 4개 CPU가 동작하고 hotplug로 4개의 CPU를 추가하는 시나리오
 *                      possible        present         active          online
 *                      --------------------------------------------------------
 * 1. 첫 부팅 후:       11111111        11110000        11110000        11110000
 * 2. CPU 카드 추가:    11111111        11111111        11110000        11110000
 * 3. CPU 업 요청:      11111111        11111111        11111111        11111111
 * 4. CPU 다운 요청:    11111111        11111111        11110000        11111111
 *     -시간차-         11111111        11111111        11110000        11110000
 * 5. CPU 카드 제거:    11111111        11110000        11110000        11110000

 

명령으로 CPU 동작 상태 변경

 현재 상태 확인

  • Intel Core-i5 노트북에서 확인 시
    • 2개의 코어 x 2개의 스레드(하이퍼 스레드 기능)= total 4개의 CPU가 동작하는 것으로 나타남.
    • kernel_max는 NR_CPUS-1과 같다.
      • 현재 NR_CPUS=128, HOTPLUG_CPU=y
    • 예상치와 다른 점
      • offline이 4-127로 나올것을 기대했지만 출력이 되지 않았다.
      • possible이 0-127로 나올것을 기대했엇는데 0-3으로 출력됨.
    • 특이하게 NR_CPUS가 256으로 되어 있지만 possible 값은 0~3까지만 출력이 되었다.
$ cd /sys/devices/system/cpu
$ cat kernel_max
255
$ cat offline

$ cat online
0-3
$ cat present
0-3
$ cat possible
0-3
$ lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                4
On-line CPU(s) list:   0-3
Thread(s) per core:    2
Core(s) per socket:    2
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 61
Model name:            Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz
Stepping:              4
CPU MHz:               2200.257
CPU max MHz:           2700.0000
CPU min MHz:           500.0000
BogoMIPS:              4390.00
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              3072K
NUMA node0 CPU(s):     0-3

 

  • 라즈베리파이2에서 확인 시
    • ARM Cortex A7이 4 코어가 동작
    • NR_CPUS=4, HOTPLUG_CPU=n
$ cd /sys/devices/system/cpu
$ cat kernel_max
3 
$ cat offline

$ cat online
0-3
$ cat present
0-3
$ cat possible
0-3
$ lscpu
Architecture:          armv7l
Byte Order:            Little Endian
CPU(s):                4
On-line CPU(s) list:   0-3
Thread(s) per core:    1
Core(s) per socket:    4
Socket(s):             1

 

특정 CPU 코어 기동 정지 및 기동

  • CPU2를 offline 상태로 변경
echo 0 > /sys/devices/system/cpu/cpu2/online
  • CPU 상태 확인
$ cd /sys/devices/system/cpu
$ cat offline
2
$ cat online
0-1,3
$ cat possible
0-3
$ cat present
0-3
$ lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                4
On-line CPU(s) list:   0,1,3
Off-line CPU(s) list:  2
Thread(s) per core:    1
Core(s) per socket:    2
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 61
Model name:            Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz
Stepping:              4
CPU MHz:               2200.000
CPU max MHz:           2700.0000
CPU min MHz:           500.0000
BogoMIPS:              4390.00
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              3072K
NUMA node0 CPU(s):     0,1,3
  • CPU2를 online 상태로 변경
echo 1 > /sys/devices/system/cpu/cpu2/online

 

boot_cpu_init()

/*
 *      Activate the first processor.
 */

static void __init boot_cpu_init(void)
{
        int cpu = smp_processor_id();

        /* Mark the boot cpu "present", "online" etc for SMP and UP case */
        set_cpu_online(cpu, true);
        set_cpu_active(cpu, true);
        set_cpu_present(cpu, true);
        set_cpu_possible(cpu, true);
}
  • CPU 상태를 관리하는 몇 개의 비트맵 전역 변수에 현재 CPU의 상태를 설정한다.
  • int cpu = smp_processor_id();
    • 현재 동작하는 프로세서 번호가 리턴 (0 부터~~)
  • 4 가지 cpu 상태를 true로 설정하는데 유형이 모두 같으므로 set_cpu_online() 함수를 위주로 분석한다.

 

set_cpu_online()

  • 아래 소스는 몇 개의 CPU 상태 조작 함수 중 CPU를 online 또는 offline 상태로 변경하는 함수이다.
  • 인수로 사용하는 cpu는 변경하고자 하는 cpu 번호(보통 0~31)를 지정한다.

kernel/cpu.c

void set_cpu_online(unsigned int cpu, bool online)
{
        if (online) {
                cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
                cpumask_set_cpu(cpu, to_cpumask(cpu_active_bits));
        } else {
                cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
        }
}

 

각 CPU 상태 비트맵 전역 변수

  •  아래 소스와 같이 CPU 상태는 5개의 비트맵으로 관리되는 전역변수로 정의되어 있다.
    • unsigned long cpu_all_bits[]
    • unsigned long cpu_possible_bits[]
    • unsigned long cpu_online_mask[]
    • unsigned long cpu_present_mask[]
    • unsigned long cpu_active_mask[]

kernel/cpu.c

const DECLARE_BITMAP(cpu_all_bits, NR_CPUS) = CPU_BITS_ALL;
EXPORT_SYMBOL(cpu_all_bits);

#ifdef CONFIG_INIT_ALL_POSSIBLE
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly
        = CPU_BITS_ALL;
#else
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly;
#endif
const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);
EXPORT_SYMBOL(cpu_possible_mask);

static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits);
EXPORT_SYMBOL(cpu_online_mask);

static DECLARE_BITMAP(cpu_present_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_present_mask = to_cpumask(cpu_present_bits);
EXPORT_SYMBOL(cpu_present_mask);

static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits);
EXPORT_SYMBOL(cpu_active_mask);

 

DECLARE_BITMAP()

  • 매크로 인수로 따라오는 name이라는 이름으로 unsigned long 타입의 배열을 필요한 크기만큼 선언한다.

linux/types.h

#define DECLARE_BITMAP(name,bits) \
        unsigned long name[BITS_TO_LONGS(bits)]

 

CPU_BITS_ALL 매크로

  • CPU가 unsigned long(32 or 64)을 초과하는 것도 배열로 관리할 수 있도록 설계되어 있다.

include/linux/cpumask.h

#if NR_CPUS <= BITS_PER_LONG
#define CPU_BITS_ALL                                            \
{                                                               \
        [BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD \
}

#else /* NR_CPUS > BITS_PER_LONG */

#define CPU_BITS_ALL                                            \
{                                                               \
        [0 ... BITS_TO_LONGS(NR_CPUS)-2] = ~0UL,                \
        [BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD         \
}
#endif /* NR_CPUS > BITS_PER_LONG */

 

 

cpumask_set_cpu()

  • 인수로 사용된 cpu는 비트 조작 하고자하는 cpu 번호를 지정한다.
  • dstp는 설정하고자(dest) 하는 비트맵 전역 변수를 가리키는 cpumask 구조체 포인터이다.
  • set_bit()라는 Bitmap Operations  함수를 호출하여 dstp 비트맵에서 cpu에 해당하는 비트를 1로 atomic 하게 설정한다.

include/linux/cpumask.h

static inline void cpumask_set_cpu(unsigned int cpu, struct cpumask *dstp)
{
        set_bit(cpumask_check(cpu), cpumask_bits(dstp));
}

 

cpumask_check()

  • cpu라는 비트맵 인수가 최대 CPU 처리 수를 넘는지 체크한다.

include/linux/cpumask.h

static inline unsigned int cpumask_check(unsigned int cpu)
{
#ifdef CONFIG_DEBUG_PER_CPU_MAPS
        WARN_ON_ONCE(cpu >= nr_cpumask_bits);
#endif /* CONFIG_DEBUG_PER_CPU_MAPS */
        return cpu;
}

 

cpumask_bits()

  • maskp는 cpumask 구조체 포인터이다.

include/linux/cpumask.h

#define cpumask_bits(maskp) ((maskp)->bits)

 

cpumask_t 구조체

include/linux/cpumask.h

typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;

#define CPU_MASK_ALL                                                    \
(cpumask_t) { {                                                         \
        [BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD                 \
} }

#define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS)

 

BITS_TO_LONGS()

  • 인수에 해당하는 비트맵이 long data 형으로 몇 개가 필요한지 리턴

include/linux/bitops.h

#define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

 

to_cpumask()

  • 인수를 cpumask 구조체 포인터로 변환하여 리턴 (타입 캐스트)
  • 항상 true인데 왜 false에 대한 루틴을 호출하게 놓았을까?
    • 실제 실행되지 않지만 컴파일 시 unsigned long * 타입이 아닌 경우 컴파일 오류 발생 목적

include/linux/cpumask.h

/**
 * to_cpumask - convert an NR_CPUS bitmap to a struct cpumask *
 * @bitmap: the bitmap
 *
 * There are a few places where cpumask_var_t isn't appropriate and
 * static cpumasks must be used (eg. very early boot), yet we don't
 * expose the definition of 'struct cpumask'.
 *
 * This does the conversion, and can be used as a constant initializer.
 */
#define to_cpumask(bitmap)                                              \
        ((struct cpumask *)(1 ? (bitmap)                                \
                            : (void *)sizeof(__check_is_bitmap(bitmap))))

static inline int __check_is_bitmap(const unsigned long *bitmap)
{
        return 1;
}

 

DIV_ROUND_UP()

include/linux/kernel.h

#define DIV_ROUND_UP(x, y)  (((x) + (y) - 1) / (y))

 

BITMAP_LAST_WORD_MASK()

  • 32비트 시스템에서 인수에 따른 결과는
    • nbits = 0: 0b11111111_11111111_11111111_11111111 (0이 아님에 주의)
    • nbits = 1: 0b00000000_00000000_00000000_00000001
    • nbits = 16: 0b00000000_00000000_11111111_11111111
    • nbits = 32: 0b11111111_11111111_11111111_11111111
    • nbits = 40: 0b11111111_11111111_11111111_11111111_11111111
      • 큰 상수 표시에 UL이 없어서컴파일 에러로 예측

include/linux/bitmap.h

#define BITMAP_LAST_WORD_MASK(nbits)                                    \
(                                                                       \
        ((nbits) % BITS_PER_LONG) ?                                     \
                (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL               \
)

 

참고

답글 남기기

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