Egloos | Log-in
F/OSS study
F/OSS study
[Linux] per-CPU 메모리 관리 (2)
Linux: 2.6.35
arch: x86_64

이전 글 보기:


이번에는 실제로 percpu 영역이 어떻게 할당되고 관리되는지를 살펴보기로 한다.

2.6.30 버전에서부터 동적 percpu 영역의 할당과 정적 percpu 영역의 할당 방식이 통합되어
동일한 인터페이스를 통해 이용할 수 있게 되었다.
percpu 영역은 내부적으로 chunk라는 단위로 관리되며
각 chunk는 하나의 CPU마다 부여된 unit으로 이루어진다.
하지만 NUMA 시스템의 경우 각 CPU는 서로 다른 노드에 속할 수 있으므로
같은 노드에 속한 CPU (unit)들은 다시 group으로 묶이게 된다.

percpu에서 사용되는 pcpu_chunk 구조체는 다음과 같이 정의되어 있다.

mm/percpu.c:
struct pcpu_chunk {
    struct list_head    list;           /* linked to pcpu_slot lists */
    int                 free_size;      /* free bytes in the chunk */
    int                 contig_hint;    /* max contiguous size hint */
    void                *base_addr;     /* base address of this chunk */
    int                 map_used;       /* # of map entries used */
    int                 map_alloc;      /* # of map entries allocated */
    int                 *map;           /* allocation map */
    void                *data;          /* chunk data */
    bool                immutable;      /* no [de]population allowed */
    unsigned long       populated[];    /* populated bitmap */
};

list는 시스템 내의 chunk들을 관리하기 위한 용도로 사용되는 리스트이다.
free_size는 chunk 내의 여유 공간의 합이며, contig_hint는 그 중 가장 큰 연속된 공간의 크기이다.
chunk가 할당된 시작 주소는 base_addr 필드에 기록되며 이는 (NUMA의 경우) 0번 unit의 시작 주소와 다를 수도 있다.
chunk 즉, unit 내의 여유 공간은 map 배열로 관리되는데 map은 실행 중에 동적으로 확장되는 가변 배열로
unit 내에서 사용 중이거나 비어있는 공간의 크기를 나타내는 역할을 한다.
이 때 양수 값은 비어있는 공간의 크기를, 음수 값은 사용 중인 공간의 크기를 의미한다.

즉 unit에 1024 바이트를 사용 중이고 그 뒤에 64 바이트의 여유 공간이 있으며,
그 뒤에 다시 128 바이트를 사용하고 나머지 공간이 비어있다면 map은 다음과 같이 구성되어 있을 것이다.

map[0] = -1024; map[1] = 64; map[2] = -128;
map[3] = (전체 unit 크기 - 1024 - 64 - 128);
map[4 ... map_alloc-1] = 0;

map_used는 현재 map 배열 중에서 사용된 항목의 갯수를 나타내며
map_alloc은 할당된 map 배열의 항목 수를 나태내고 기본 값은 16이다.

data는 chunk 관리에 필요한 부가적인 데이터이며 일반적으로 chunk가 할당된 페이지들을 가리키는
page 구조체 포인터의 배열의 주소가 저장된다.
immutable은 해당 chunk가 변경될 수 있는지 여부로 해당 chunk에 새 페이지를 할당하거나
해제할 수 있는지를 결정한다. (기본적으로 페이지 할당은 해당 영역이 요청될 때까지 미루어진다.)
마지막으로 populated 배열은 chunk 내에서 페이지 할당이 이루어진 영역과 아닌 영역을 관리하기 위한 것이다.

시스템 부팅 시에 정적으로 할당된 percpu 영역을 위해 첫 번째 chunk가 만들어지며
이 후 실행 시에 필요에 따라 새로운 chunk가 만들어지거나 제거될 수 있다.
시스템 내에 존재하는 모든 chunk들은 pcpu_slot 리스트에 연결되어 관리하며
chunk 내의 여유 공간의 크기 순으로 정렬하여 chunk의 사용률을 최대한 높일 수 있도록 한다.
(즉, 가장 여유 공간이 적은 chunk에서부터 할당을 시도한다.)

첫 번째 chunk는 각 group 및 unit의 배치를 결정하는 중요한 역할을 하는 chunk이므로 특별히 관리하는데
크게 embed 방식과 page 방식 중의 하나를 선택하여 할당할 수 있다.
이는 부팅 시 percpu_alloc 옵션으로 이 둘 중 하나를 지정하여 선택할 수 있으며
기본적으로는 먼저 embed 방식을 시도해 본 후 실패한 경우 page 방식을 이용하도록 한다.

embed 방식은 x86의 PSE 기능을 이용하여 group 별로 2MB 크기의 페이지(huge page)를 할당한 후 사용하는 것이다.
이는 TLB의 overhead를 줄일 수 있으므로 상당히 매력적인 방식이 아닐 수 없다.
group 내의 unit들은 할당된 페이지들을 최대한 효율적으로 사용할 수 있도록 크기를 조정한다.
이 경우 first chunk는 vmalloc 영역이 아닌 low memory 영역에 존재하게 되는데
NUMA 시스템의 경우 node 당, 즉 percpu group 당 page의 시작 오프셋이 많이 차이날 수 있으므로
이러한 차이가 vmalloc으로 이용할 수 있는 공간의 3/4 이상 나는 경우 경고를 보여준다.
(이러한 group offset은 이후의 chunk들에서도 동일하게 유지되어야 한다.
vmalloc의 주소 공간이 적은 32비트 머신의 경우 이는 심각한 문제가 될 여지가 있다)

page 방식은 위와 같은 embed 방식을 이용할 수 없는 경우 각각을 일반 페이지 크기로 할당하여
(물론 페이지 할당 자체는 아직 메모리 할당자가 초기화되지 않은 상태이므로 low memory 영역에서 이루어진다)
vmalloc 공간에 매핑시킨 후 사용한다. 이 경우에는 가상 주소를 자유롭게 할당할 수 있으므로
각 group/unit 별로 선형적인 오프셋을 가질 수 있고 따라서 주소 공간을 효율적으로 사용할 수 있다.
(단 각 페이지 별로 PTE (page table entry)가 필요하므로 TLB의 효율은 나빠질 것이다.)

first chunk을 제외한 나머지 chunk들은 이후 vmalloc 주소 공간에 배치되는데
위에서 말한 바와 같이 group 간의 offset을 관리하기 위해 공간을 비효율적으로 사용할 가능성이 있으므로
다른 vmalloc 접근 함수와 구분하여 vmalloc 영역의 뒤쪽에서부터 할당을 시작하게 된다.
또한 이러한 chunk들은 생성된 후에도 해당 영역에 대한 실제 할당 요청이 들어오기 전까지는
page frame을 할당하지 않은 채로 단지 주소 공간 만을 차지하는 demand paging 방식으로 구현된다.

first chunk가 할당되고 나면 pcpu_setup_first_chunk() 함수가 호출되어
다음과 같은 percpu에 관련된 정보들을 모두 저장해 두고 이 후의 할당 시에 참조하도록 한다.
  • pcpu_base_addr: first chunk가 할당된 가상 주소
  • pcpu_nr_groups: percpu group의 수 (NUMA node의 수)
  • pcpu_group_offsets: 각 group의 시작 offset을 저장한 테이블
  • pcpu_group_sizes: 각 group의 크기를 저장한 테이블
  • pcpu_nr_units: percpu unit의 수 (CPU의 수)
  • pcpu_unit_map: cpu 번호를 통해 unit 번호를 알아내기 위한 테이블
  • pcpu_unit_offsets: 각 unit의 시작 offset을 저장한 테이블
  • pcpu_unit_size: unit의 크기
  • pcpu_atom_size: page 할당을 위한 최소 단위
  • pcpu_nr_slots: chunk 관리를 위한 slot의 수

percpu 주소와 실제 할당된 가상 주소 간의 변환을 위해 다음과 같은 매크로가 사용된다.
(각 아키텍처는 필요한 경우 이들을 재정의하여 별도로 구현할 수 있도록 하고 있다)

/* default addr <-> pcpu_ptr mapping, override in asm/percpu.h if necessary */
#ifndef __addr_to_pcpu_ptr
#define __addr_to_pcpu_ptr(addr)                     \
    (void __percpu *)((unsigned long)(addr) -        \
              (unsigned long)pcpu_base_addr +        \
              (unsigned long)__per_cpu_start)
#endif
#ifndef __pcpu_ptr_to_addr
#define __pcpu_ptr_to_addr(ptr)                      \
    (void __force *)((unsigned long)(ptr) +          \
             (unsigned long)pcpu_base_addr -         \
             (unsigned long)__per_cpu_start)
#endif

즉 이 변환에서 pcpu_base_addr와 __per_cpu_start 주소가 매번 사용되므로
이를 __per_cpu_offset 배열 (per_cpu_offset 매크로를 통해 참조)에 저장해두고
이전 글에서 보았던 per_cpu 등의 매크로에서 이를 참조하여 쉽게 주소를 변환한다.

by namhyung | 2010/08/13 01:06 | Kernel | 트랙백 | 핑백(1) | 덧글(0)
트랙백 주소 : http://studyfoss.egloos.com/tb/5377666
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Linked at per-cpu &#8211; .. at 2016/02/11 15:06

... Per-CPU Variables robust per_cpu allocation for modules | LWN.net per-CPU 메모리 관리 (1) | F/OSS per-CPU 메모리 관리 (2) | F/OSS Documentation/preempt-locking.txt Better per-CPU variables | LWN.net What every ... more

:         :

:

비공개 덧글

◀ 이전 페이지 다음 페이지 ▶

카테고리
General
Application
System
Kernel
Book
Tips
태그
CAaQA3 gcc emacs synchronization memory CARM kernel bash awk script build binutils C compiler computer-architecture sed scm git glibc SMP documentation linux blktrace block-layer x86 elf algorithm perf patch vcs
전체보기
이글루 파인더

최근 등록된 덧글
informsi yang bagus dan be..
by pordanaia at 08/05
Informsi yang bagus dan b..
by jamalsu at 08/01
as inforasi yang menarik http..
by jamalsu at 07/22
최근 등록된 트랙백
Tod's Ferrari Homme
by Tods Pas Cher,Kodak did ..
Mocassin Femme
by Mocassins Homme, I got so..
natural garcinia cambogia
by
rss

skin by jiinny


X