티스토리 뷰

4.7.5 p442 슬랩 페이지 할당

slab 용으로 페이지를 할당받고, 슬랩 페이지로 사용할 수 있도록 초기화

4-125 p443 mm/slub.c new_slab()

  • 새 페이지 할당 요청
// allocate_slab 을 부를때 최초 생성할 수 있도록 플래그를 추가해서 요청
allocate_slab(s, flags & (GFP_RECLAIM_MASK | GFP_CONSTRAINT_MASK), node);

4-126 p444 mm/slub.c allocate_slab()

  • 페이지 획득시 권장 order 로 시도후 실패시 최소 order 로 시도
// kmem_cache 구조체의 order_objects 인 oo 에는, 권장 order 와 객체수가 담겨있다.
s->oo;

첫번째 요청시에는 warning 과 retry 플래그는 제거하고, 실패는 가능하도록 한다.

4-127 p444 mm/slub.c allocate_slab() 2

  • compound order 인 경우 별도 처리 추가 p447
  • pfmemalloc 으로 받은 페이지일 경우 active 설정
  • pfmemalloc -> 특정 상황에서 메모리 할당의 우선 순위를 조정하기 위해 사용됩니다. 특히, 시스템이 메모리 부족 상태에 빠졌을 때, 핵심적인 작업(critical tasks)이 필요한 메모리를 확보할 수 있도록 보장하기 위해 존재합니다.
  • SLAB_POISION flag : 디버깅 요청한 경우
//  받아온 page 에 slab object 의 전체 객체수를 지정
page->objects = oo_objects(oo);

// 페이지에 슬랩 정보를 지정
page->slab_cache = s

// 각 object 를 돌며 생성자와 free pointer 를 연결
for_each_object_idx(p, idx, s, start, page->objects) {
  setup_object(s, page, p);
  if (likely(idx < page->objects))
    set_freepointer(s, p, p + s->size);
  else
    set_freepointer(s, p, NULL);
}

page->freelist = fixup_red_left(s, start); // 첫 객체 연결
page->inuse = page->objects; // inuse 가 뭐지? p452 에서 다뤄짐 (초기화 중이어서, 모든 객체를 사용중이다?)
page->frozen = 1; // frozen 이 뭐지? p452 에서 다뤄짐 (초기화 중이어서 다른 쓰레드에서 못잡게 락을 거는 용도?)

order-object 관련

// 페이지 사이즈 에서 가질 수 있는 object 수
static inline int order_objects(int order, unsigned long size, int reserved)
{
    return ((PAGE_SIZE << order) - reserved) / size;
}

// order 랑 수를 같이 기록
static inline struct kmem_cache_order_objects oo_make(int order,
        unsigned long size, int reserved)
{
    struct kmem_cache_order_objects x = {
        (order << OO_SHIFT) + order_objects(order, size, reserved)
    };

    return x;
}

// 그래서 예에서 order 와 객체수를 가져올 수 있다!
struct kmem_cache_order_objects {
    unsigned int x; // order와 objects를 압축 저장
};

p447 compound_order

  • 물리적으로 2개 이상의 페이지가 하나의 유닛으로 포함된 페이지
  • 첫번째 페이지의 flag 에서 PG_HEAD 가 설정되어있다면 무조건 두번째 페이지가 존재함이 보장되고,
  • 두번째 페이지의 compound_order 값에 실제 order 가 적용된다.

p448 set_freepointer

s->offset 은 metadata 를 사용하는 경우 값이 들어가있기 때문에, 객체의 시작 주소에서 offset 을 더한 곳에 fp 를 지정한다.

static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
{
    *(void **)(object + s->offset) = fp;
}

4-128 p449 alloc_slab_page

  • memcg 의 통제하에 슬랩에서 사용할 페이지를 할당받아온다.
memcg_charge_slab - 특정 프로세스나 작업이 메모리를 사용하는 양을 추적하고 제한하는 메커니즘입니다. 

4.7.6 슬랩 객체 할당

  • freelist 에 객체가 남아있는 경우, 바로 꺼낸다.
  • 실패시 4단계의 slowpath 로 객체를 획득한다.
    • free list, cpu partial list, node partial list, buddy system 등 단계적으로 요청

frozned 슬랩 페이지

  • 슬랩 할당자는 per-cpu 로 관리
  • page->frozen 을 1로 설정하는 경우, freelist 를 잠가 현재 테스크가 직접 관리하도록 한다. 락을 잡을 필요 없게 하기 위함.
  • 다른 cpu 는 해제만 가능

p445 frozned 슬랩 페이지의 inuse 객체 관리

  • cpu_slab 의 freelist 에서 관리되고 있는 page 의 수를 inuse 로 관리.
  • 다른 cpu 에서 객체를 할당 해제 하는 경우 cpu_slab 의 freelist 가 아닌, page 의 freelist 로 해제되기 때문에 카운터 감소

p446 kmem_cache_alloc

  • kmem 캐시 객체 할당하기

4-129 p453 slab_alloc_node

  • NUMA 시스템을 사용중일때 kmem_cache_alloc 에서 호출됨
// slab 을 획득할 수 있는 캐시. s 가 그대로 나올수도 있고, 다른곳이 나올 수도 있는 것 같다 (__memcg_kmem_get_cache)
s = slab_pre_alloc_hook(s, gfpflags);

다른 태스크에서 선점이 됐을 수도 있으니, cpu 와 slab 캐시가 동일하게 획득했음을 보장. 

4-130 p455 slab_alloc_node 2

  • freelist 에 없는경우 slowpath 를 진행한다.
  • fastpath 에서 성공한 경우, 리스트를 갱신하는데 이때 실패하면 다시 시도한다. (다른 cpu 가 침범하는 경우 실패 가능성)
__cmpxchg_double(p1, p2, o1, o2, n1, n2)
cas 를 두번 실행. p 의 값이 o 인경우 n 으로 변경을 두개 atomic 으로 진행

4-131 p456 __slab_alloc

  • slowpath 로 슬랩 객체 할당을 진행한다.
  • fastpath 와는 다르게 cpu 경쟁이 발생한다. 왜일까?
c = this_cpu_ptr(s->cpu_slab); // preempt 커널에서 동작하는 경우 kmem_cache_cpu  가 변경됐을수 있다? 전혀 모르겠네..

4-132 p457 ___slab_alloc 1

  • 태스크가 다시 실행될때는 보통 원래의 cpu 나 노드의 cpu 에서 실행되어야 하는데, 모든 cpu 가 바쁜 경우 다른 노드의 cpu 에서 실행 될 수 있다.
  • kmem_cache, kmem_cache_cpu, cache_cpu->page 의 의미를 명확하게 알아야 할것 같다. 아직 어렵다
kmem_cache: 슬래브 캐시의 메타데이터를 관리하는 주요 구조체. 각 캐시의 이름, 크기, 플래그 등을 포함.
kmem_cache_cpu: 각 CPU가 자신의 캐시를 관리하는 구조체. 로컬 캐시를 통해 각 CPU에서 할당 성능을 최적화.
page: 메모리 페이지를 나타내는 구조체. 각 페이지에 포함된 객체들을 관리하고 할당 상태를 추적.

kmem_cache_cpu->freelist vs (slub_)page->freelist

  • cpu 캐시를 보통 먼저 찾아보고 slub 을 찾아서 할당을 해오기 때문에, 할당 해제시에는 cpu 캐시로 들어가게 된다.
  • 하지만 cpu 캐시가 아주 커지면 slub 으로 다시 돌아갈지도 모르겠다.

4-133 p458 ___slab_alloc 2

  • kmem_cache_cpu 의 freelist 는 여러개의 페이지중 하나의 free object 를 가리키고 있고, page 도 새로 객체를 할당할 page 를 가리킨다.
  • slub 에서 사용중인 page 의 freelist 는 해당 페이지 내에서 free object 를 가리킨다.
struct kmem_cache_cpu {
    void **freelist;    /* Pointer to next available object */
    unsigned long tid;    /* Globally unique transaction id */
    struct page *page;    /* The slab from which we are allocating */
    struct page *partial;    /* Partially allocated frozen slabs */
};

4-134 p459 ___slab_alloc 3

  • kmem_cache_cpu 의 tid 위 구조체에 있든 transcation id 로 관리
  • kmem_cache_cpu->page 의 모든 객체가 사용중이면, 하나라도 해제되기 전까지 kmem_cache 의 관리에서 벗어나며 unfrozen 된다.
  • partial 을 kmem_cache_cpu->page 로 올리고 cpu->freelist 도 연결한다.

4-135 p462 get_freelist

  • page 의 freelist 를 반환하고, page 의 freelist 를 NULL 로 설정.
  • get_freepointer 와는 다른 함수 -> 얘는 이전에 다뤘던 object 에서 offset 더한녀석
union {
  unsigned long counters;
  struct {
    unsigned inuse:16;
    unsigned objects:15;
    unsigned frozen:1;
  };
};

new.counters = counters;
// counter 값을 설정할때 inuse, objects, frozen 이 같이 들어가짐. union 이기 때문

new.inuse = slab->objects;
new.frozen = freelist != NULL;
댓글