Slub Memory Allocator -9- (Slub 해제)

다음 그림은 해제할 slub page가 free_slab() 함수를 통해 버디 시스템으로 돌아가기까지의 흐름을 보여준다.

free_slab-1

 

free_slab()

mm/slub.c

static void free_slab(struct kmem_cache *s, struct page *page)
{
        if (unlikely(s->flags & SLAB_DESTROY_BY_RCU)) {
                struct rcu_head *head;

                if (need_reserve_slab_rcu) {
                        int order = compound_order(page);
                        int offset = (PAGE_SIZE << order) - s->reserved;

                        VM_BUG_ON(s->reserved != sizeof(*head));
                        head = page_address(page) + offset;
                } else {
                        /*
                         * RCU free overloads the RCU head over the LRU
                         */
                        head = (void *)&page->lru;
                }

                call_rcu(head, rcu_free_slab);
        } else
                __free_slab(s, page);
}

slub page를 해제하는데 rcu를 사용한 해제와 사용하지 않는 경우의 구현이 다르다.

  • if (unlikely(s->flags & SLAB_DESTROY_BY_RCU)) {
    • 적은 확률로 slab(slub) 삭제에 rcu를 사용한 방법인 경우
  • if (need_reserve_slab_rcu) {
    • slab(slub)용 rcu가 reserve 영역을 사용하는 경우
  • int order = compound_order(page); int offset = (PAGE_SIZE << order) – s->reserved; head = page_address(page) + offset;
    • rcu head 위치로 slub page의 reserved 영역 주소를 알아온다.
  • } else { head = (void *)&page->lru; }
    • rcu head 위치는 slub page의 lru를 사용한다.
  • call_rcu(head, rcu_free_slab);
    • rcu의 grace period를 넘긴 후 slub page의 해제 처리를 요청한다.
      • 이 slub 페이지를 참조하는 모든 태스크의 스케쥴 타임이 모두 소모된 경우 rcu_free_slab을 호출하여 slub page를 해지하게 한다.
  • lock을 사용한 방법
    • lock을 사용하여 slub page의 링크를 끊는다.

 

rcu_free_slab()

mm/slub.c

static void rcu_free_slab(struct rcu_head *h)
{
        struct page *page;

        if (need_reserve_slab_rcu)
                page = virt_to_head_page(h);
        else
                page = container_of((struct list_head *)h, struct page, lru);

        __free_slab(page->slab_cache, page);
}

slub 페이지를 해제한다.

  • if (need_reserve_slab_rcu) page = virt_to_head_page(h);
    • slub page의 reserved 영역을 사용하여 인수로 전달 받은 rcu_head를 사용하여 첫 페이지를 알아온다.
  • else page = container_of((struct list_head *)h, struct page, lru);
    • page->lru 영역을 사용하여 인수로 전달 받은 rcu_head를 사용하여 page를 알아온다.
      • page->lru 영역을 사용하는 방법은 항상 첫 페이지의 lru 영역을 사용한다.
  • __free_slab(page->slab_cache, page);
    • slub 페이지를 해제한다.

 

다음 그림은 slub page의 빠른 해제에 rcu를 사용하는 경우 rcu head를 저장하는 위치를 보여준다.

free_slab-1a

 

__free_slab()

mm/slub.c

static void __free_slab(struct kmem_cache *s, struct page *page)
{
        int order = compound_order(page);
        int pages = 1 << order;

        if (kmem_cache_debug(s)) {
                void *p;

                slab_pad_check(s, page);
                for_each_object(p, s, page_address(page),
                                                page->objects)
                        check_object(s, page, p, SLUB_RED_INACTIVE);
        }

        kmemcheck_free_shadow(page, compound_order(page));

        mod_zone_page_state(page_zone(page),
                (s->flags & SLAB_RECLAIM_ACCOUNT) ?
                NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE,
                -pages);

        __ClearPageSlabPfmemalloc(page);
        __ClearPageSlab(page);

        page_mapcount_reset(page);
        if (current->reclaim_state)
                current->reclaim_state->reclaimed_slab += pages;
        __free_pages(page, order);
        memcg_uncharge_slab(s, order);
}

slub page를 해제시켜 버디 시스템으로 돌려준다.

  • int order = compound_order(page); int pages = 1 << order;
    • slub page의 order 값을 구하고 pages를 2의 order 차수로 대입한다.
      • compound page가 아닌 경우 0이다.
  • mod_zone_page_state(page_zone(page), (s->flags & SLAB_RECLAIM_ACCOUNT) ? NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE, -pages);
    • 캐시가 SLAB_RECLAIM_ACCOUNT 플래그를 사용하면 zone의 NR_SLAB_RECLAIMABLE stat을, 사용하지 않으면 NR_SLAB_UNRECLAIMABLE stat을 pages 만큼 감소시킨다.
  • __ClearPageSlabPfmemalloc(page);
    • PG_Active 플래그 비트를 클리어한다.
  • __ClearPageSlab(page);
    • PG_Slab 플래그 비트를 클리어한다.
  • page_mapcount_reset(page);
    • _mapcount를 -1로 리셋한다.
  • if (current->reclaim_state) current->reclaim_state->reclaimed_slab += pages;
    • 현재 태스크의 회수 상태가 설정된 경우 reclaimed_slab을 pages 수 만큼 더한다.
  • __free_pages(page, order);
    • 버디 시스템으로 page를 order 만큼 돌려준다.
  • memcg_uncharge_slab(s, order);
    • memory cgroup으로 slab 페이지가 회수되었다는 것을 알려 counting에서 감소시키게 한다.

 

 

memcg_uncharge_slab()

mm/slab.h

static __always_inline void memcg_uncharge_slab(struct kmem_cache *s, int order)
{
        if (!memcg_kmem_enabled())
                return;
        if (is_root_cache(s))
                return;
        memcg_uncharge_kmem(s->memcg_params.memcg, 1 << order);
}

CONFIG_MEMCG_KMEM 커널 옵션을 사용하면서 memcg_kmem이 활성화된 경우 memory cgroup으로 slab 페이지가 회수되었다는 것을 알려 counting에서 감소시키게 한다. 단 루트 캐시인 경우 처리하지 않고 루틴을 빠져나간다.

 

참고

 

답글 남기기

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