smp_setup_processor_id()

리눅스는 물리 cpu 번호(id)를 사용하지 않고 로지컬 cpu 번호(id)를 사용하여 관리한다. 현재 부트된 물리 cpu는 로지컬 cpu id 0번으로 배치하여 사용한다.

  • DTB를 사용하는 경우 setup_arch() -> arm_dt_init_cpu_maps() 함수에서 로지컬 cpu id가 재조정된다.

 

Flow

smp_setup_processor_id

smp_setup_processor_id()

u32 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = MPIDR_INVALID };

void __init smp_setup_processor_id(void)
{
        int i;

        u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0; 
        u32 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);

        cpu_logical_map(0) = cpu;
        for (i = 1; i < nr_cpu_ids; ++i)
                cpu_logical_map(i) = i == cpu ? 0 : i;

        /*
         * clear __my_cpu_offset on boot CPU to avoid hang caused by
         * using percpu variable early, for example, lockdep will
         * access percpu variable inside lock_release
         */
        set_my_cpu_offset(0);

        pr_info("Booting Linux on physical CPU 0x%x\n", mpidr);
}
  • MPIDR의 하위 3바이트(Aff0, Aff1, Aff2)를 가져오기 위해 비트마스킹을 한다.
    • CPU Affinity는 여러 개의 CPU core 중에서 각각의 cpu가 가지는 고유 번호같은 것.
    • Affinity는 계층적으로 표현된다.
  • u32 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
    • 읽어온 mpidr에서 해당 affinity level의 값을 추출한다.
    • cpu에는 MPIDR.Affinity0의 값이 들어간다.
      • rpi2: 0~3까지의 값이 들어갈 수 있다.
#define MPIDR_AFFINITY_LEVEL(mpidr, level) \
        ((mpidr >> (MPIDR_LEVEL_BITS * level)) & MPIDR_LEVEL_MASK)
#define MPIDR_LEVEL_BITS 8
#define MPIDR_LEVEL_MASK ((1 << MPIDR_LEVEL_BITS) - 1)
  • MPIDR_LEVEL_BITS
    • 하나의 affinity level 값이 몇 비트로 이루어져 있는지 나타내고 8로 설정되어 있다 (1바이트).
  • MPIDR_LEVEL_MASK
    • 하위 MPIDR_LEVEL_BITS개의 비트만 1이고 나머지는 0인 값이다.
  • cpu_logical_map(0) = cpu;
    • 현재 사용하고 있는 cpu의 번호를 cpu_logical_map의 첫 번째로 설정한다.
  • cpu_logical_map(i) = i == cpu ? 0 : i;
    • cpu_logical_map은 커널이 사용할 cpu의 logical 번호를 physical cpu 번호로 매핑한다
    • 부팅 cpu가 physical cpu 2번이라면 physical cpu 0번은 logical cpu 2번이 된다.
      * 나머지는 physical 번호와 logical 번호가 같은 값을 가진다
  • set_my_cpu_offset(0);
    • percpu가 사용되려면 cpu_offset를 먼저 지정해놓아야 한다.
  • pr_info(“Booting Linux on physical CPU 0x%x\n”, mpidr);
    • 어떤 물리 CPU로 리눅스 부팅이 되었는지 안내하는 정보 출력

 

__cpu_logical_map[]

u32 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = MPIDR_INVALID };
  • Designated Initializers 라고 불리는 배열 초기화 방법.
    • 참고: http://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html
  • NR_CPUS: 가능한 CPU의 최대 개수이고 configuration 할 때 정해지는 값.
    • 2 ~ 32의 값이고 default는 4이다.
  • MPIDR_INVALID:
    • 하위 세 바이트는 모두 0이고 상위 바이트는 0xFF인 값. 따라서 __cpu_logical_map은 모두 invalid mpidr 값을 가지도록 초기화된다.

 

set_my_cpu_offset()

/*
 * Same as asm-generic/percpu.h, except that we store the per cpu offset 
 * in the TPIDRPRW. TPIDRPRW only exists on V6K and V7
 */
#if defined(CONFIG_SMP) && !defined(CONFIG_CPU_V6)
static inline void set_my_cpu_offset(unsigned long off)
{
        /* Set TPIDRPRW */

        asm volatile("mcr p15, 0, %0, c13, c0, 4" : : "r" (off) : "memory");
}
  • per-cpu 자료구조에서 사용되는 cpu마다 개별적으로 가지는 offset 값.
  • ARMv7에서는 속도 향상을 위해서 TPIDRPRW register를 사용함.
    • ARMv7 이전 아키텍처에서는 이 레지스터를 사용하지 않고 메모리를 사용하여 연산하느라 메모리에 대한 접근이 2번 필요하여 느렸었고 이를 극복하기 위해 본래의 목적으로 사용하지 않는 레지스터인 TPIDRPRW를 사용하여 메모리 접근을 1번으로 줄이기 위해 사용한다.
    • 참고: ARM: implement optimized percpu variable | LWN.net

 

MPIDR

mpidr

U(Uniprocessor):

0=Multiprocessor, 1=Uniprocessor

MT(Multi-Thread):

멀티스레딩 타입 접근으로 구현된 논리 프로세서의 밀결합 최소 레벨

0=최소 affinity 레벨에서의 프로세스 성능이 최대 독립적
1=최소 affinity 레벨에서의 프로세스 성능이 최대 결합적

ARM CPU Topology:

구현에 따라 보통 2개 중 하나를 사용한다. (rpi2: 2단계를 사용한다)

  • 3단계 affinity 사용: cluster -> cpu core -> virtual core
    • affinity 2: cluster id
    • affinity 1: cpu core id
    • affinity 0: virtual core id
  • 2단계 affinity 사용: cluster -> cpu core
    • affinity 2: reserved id
    • affinity 1: cluster id
    • affinity 0: cpu core id

프로세스 친화력:

프로세스 스케쥴링시 한 번 배정되었던 프로세스를 어떤 CPU 코어를 사용하게 할 지 결정하기 위해 필요. (리눅스는 가능하면 캐시 데이터 재활용을 위해 같은 코어에 배정)

TPIDRPRW (Thread ID-R)

  • Multiprocessor Extension에서 사용하며 PL1 이상에서만 사용가능하다.
  • Security Extension에서는 레지스터는 뱅크된다.
  • TPID(스레드 ID) 정보가 기록된 레지스터
    • 현재 리눅스에서는 TPID를 저장하는 목적으로 사용하지 않고 각 cpu의 per-cpu offset를 저장하여 더 빠른 per-cpu data의 access를 위해 사용된다.

 

참고

 

답글 남기기

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