kmem_cache_init()

커널이 부트업 프로세스 과정에서 slub 메모리 할당자를 활성화 하기 위해 다음 그림과 같이 먼저 캐시 초기화 과정을 수행한다.

kmem_cache_init-1d

kmem_cache_init() – (slub)

mm/slub.c

void __init kmem_cache_init(void)
{
        static __initdata struct kmem_cache boot_kmem_cache,
                boot_kmem_cache_node;

        if (debug_guardpage_minorder())
                slub_max_order = 0;

        kmem_cache_node = &boot_kmem_cache_node;
        kmem_cache = &boot_kmem_cache;

        create_boot_cache(kmem_cache_node, "kmem_cache_node",
                sizeof(struct kmem_cache_node), SLAB_HWCACHE_ALIGN);

        register_hotmemory_notifier(&slab_memory_callback_nb);

        /* Able to allocate the per node structures */
        slab_state = PARTIAL;

        create_boot_cache(kmem_cache, "kmem_cache",
                        offsetof(struct kmem_cache, node) +
                                nr_node_ids * sizeof(struct kmem_cache_node *),
                       SLAB_HWCACHE_ALIGN);

        kmem_cache = bootstrap(&boot_kmem_cache);

        /*
         * Allocate kmem_cache_node properly from the kmem_cache slab.
         * kmem_cache_node is separately allocated so no need to
         * update any list pointers.
         */
        kmem_cache_node = bootstrap(&boot_kmem_cache_node);

        /* Now we can use the kmem_cache to allocate kmalloc slabs */
        create_kmalloc_caches(0);

#ifdef CONFIG_SMP
        register_cpu_notifier(&slab_notifier);
#endif

        pr_info("SLUB: HWalign=%d, Order=%d-%d, MinObjects=%d, CPUs=%d, Nodes=%d\n",
                cache_line_size(),
                slub_min_order, slub_max_order, slub_min_objects,
                nr_cpu_ids, nr_node_ids);
}

slub 캐시를 만들기 위해 사용할 kmem_cache와 kmem_cache_node 캐시를 먼저 구성한다.

  • static __initdata struct kmem_cache boot_kmem_cache, boot_kmem_cache_node;
    • kmem_cache 구조체인 boot_kmem_cache와 boot_kmem_cache_node 변수를 준비한다.
    • 이 변수는 kmem_cache와 kmem_cache_node 라는 이름의 캐시가 만들어지는 경우 대체되어 추후 사용되지 않게 된다.
  • if(debug_guardpage_minorder()) slub_max_order = 0;
    • “debug_guardpage_minorder=” 커널 파라메터를 사용하여 0~MAX_ORDER/2 범위의 값을 지정할 수 있고 이러한 경우 slub_max_order 값에 0을 대입한다.
  • kmem_cache_node = &boot_kmem_cache_node;
    • 전역 변수 kmem_cache_node에 boot_kmem_cache_node 초기 구조체 값을 대입한다.
  • kmem_cache = &boot_kmem_cache;
    • 전역 변수 kmem_cache에 boot_kmem_cache 초기 구조체 값을 대입한다.
  • create_boot_cache(kmem_cache_node, “kmem_cache_node”, sizeof(struct kmem_cache_node), SLAB_HWCACHE_ALIGN);
    • kmem_cache_node 캐시에서 사용하는 per cpu 캐시를 per-cpu 자료형으로 할당받고, per 노드에서 사용할 slub page를 할당받아 partial 리스트에 등록한다.
  • register_hotmemory_notifier(&slab_memory_callback_nb);
    • hotmemory notifier로 slab_memory_callback_nb를 등록한다.
  • slab_state = PARTIAL;
    • slab 상태를 DOWN에서 PARTIAL로 변경하여 조금 전에 만든 kmem_cache_node 캐시에서 할당 받을 수 있도록 한다.
    • PARTIAL 상태는 slub 캐시를 만들지는 못하지만 이미 존재하는 slub 캐시에서는 slub object를 할당 받을 수 있다.
  • create_boot_cache(kmem_cache, “kmem_cache”, offsetof(struct kmem_cache, node) + nr_node_ids * sizeof(struct kmem_cache_node *), SLAB_HWCACHE_ALIGN);
    • kmem_cache 캐시에서 사용하는 per cpu 캐시를 per-cpu 자료형으로 할당받고, per 노드에서 사용할 kmem_cache_node 구조체는 위에서 만든 kmem_cache_node 캐시에서 slub object를 할당받아 partial 리스트에 등록한다
  • kmem_cache = bootstrap(&boot_kmem_cache);
    • kmem_cache 캐시에서 object를 할당받아 임시로 사용하는 kmem_cache를 똑같이 복사한 후 사용한다.
  • kmem_cache_node = bootstrap(&boot_kmem_cache_node);
    • kmem_cache_node 캐시에서 object를 할당받아 임시로 사용하는 kmem_cache_node를 똑같이 복사한 후 사용한다.
  • create_kmalloc_caches(0);
    • kmalloc-<size> 이름의 slub 캐시를 사이즈별로 미리 생성한다.
      • size=8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192 이며, 조건에 따라 96, 192가 추가된다.
  • register_cpu_notifier(&slab_notifier);
    • cpu notifier로 slab_notifier를 등록한다.
  • pr_info(“SLUB: HWalign=%d, Order=%d-%d, MinObjects=%d, CPUs=%d, Nodes=%d\n”, cache_line_size(), slub_min_order, slub_max_order, slub_min_objects, nr_cpu_ids, nr_node_ids);
    • SLUB 정보를 출력한다.
      • rpi2 예)
        • SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1

 

다음 그림은 kmem_cache_node와 kmem_cache라는 이름으로 두 개의 kmem_cache 구조체에 대입되는 내용을 보여준다.

kmem_cache_init-2

다음 그림은 kmem_cache_init() 함수에서 2 개의 bootstrap 과정이 동작하기 전 단계까지의 처리되는 과정을 보여준다.

kmem_cache_init-3

 

다음 그림은 kmem_cache_init() 함수에서 2 개의 bootstrap 과정이 처리되는 과정을 보여준다.

kmem_cache_init-4

 

create_boot_cache() – (slab, slub)

mm/slab_common.c

#ifndef CONFIG_SLOB             
/* Create a cache during boot when no slab services are available yet */
void __init create_boot_cache(struct kmem_cache *s, const char *name, size_t size,
                unsigned long flags)
{
        int err;

        s->name = name;
        s->size = s->object_size = size;
        s->align = calculate_alignment(flags, ARCH_KMALLOC_MINALIGN, size);

        slab_init_memcg_params(s);

        err = __kmem_cache_create(s, flags);

        if (err)
                panic("Creation of kmalloc slab %s size=%zu failed. Reason %d\n",
                                        name, size, err);

        s->refcount = -1;       /* Exempt from merging for now */
}
#endif

요청한 slub 캐시를 생성한다.

  • s->align = calculate_alignment(flags, ARCH_KMALLOC_MINALIGN, size);
    • 인수 flags, align, size로 최종 사용할 align을 구한다.
  • slab_init_memcg_params(s);
    • memcg에 대한 파라메터들을 초기화한다.
  • err = __kmem_cache_create(s, flags);
    • 요청한 slub 캐시를 생성한다.
  • s->refcount = -1
    • 아직 완전히 구성되지 않아서 레퍼런스 카운터 값에 -1을 대입한다.

 

/*
 * Some archs want to perform DMA into kmalloc caches and need a guaranteed
 * alignment larger than the alignment of a 64-bit integer.
 * Setting ARCH_KMALLOC_MINALIGN in arch headers allows that.
 */
#if defined(ARCH_DMA_MINALIGN) && ARCH_DMA_MINALIGN > 8
#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN
#define KMALLOC_MIN_SIZE ARCH_DMA_MINALIGN
#define KMALLOC_SHIFT_LOW ilog2(ARCH_DMA_MINALIGN)
#else
#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long)
#endif
  • ARCH_KMALLOC_MINALIGN
    • kmalloc()에 의해 리턴되는 메모리는 DMA를 위해 사용되므로 이런 할당은 cache align되어야 한다.
    • cpu 아키텍처가 지원하는 L1 d-cache line size 까지로 설정하되 최하 최하 8바이트 이상이다.
      • rpi2: 64 bytes

 

calculate_alignment()

mm/slab_common.c

/*
 * Figure out what the alignment of the objects will be given a set of
 * flags, a user specified alignment and the size of the objects.
 */
unsigned long calculate_alignment(unsigned long flags,
                unsigned long align, unsigned long size)
{
        /*
         * If the user wants hardware cache aligned objects then follow that
         * suggestion if the object is sufficiently large.
         *
         * The hardware cache alignment cannot override the specified
         * alignment though. If that is greater then use it.
         */
        if (flags & SLAB_HWCACHE_ALIGN) {
                unsigned long ralign = cache_line_size();
                while (size <= ralign / 2)
                        ralign /= 2;
                align = max(align, ralign);
        }

        if (align < ARCH_SLAB_MINALIGN)
                align = ARCH_SLAB_MINALIGN;

        return ALIGN(align, sizeof(void *));
}

지정한 객체에 대한 인수 size와 align 및 하드웨어 캐시 정렬 요청 플래그로 최종 사용할 align을 구한다. 반환되는 값은 최소 8부터이다.

  • rpi2:
    • size=0~8
      • s->align=8
    • size=9~16
      • s->align=16
    • size=17~32
      • s->align=32
    • size=33~64
      • s->align=64
    • size=65~
      • size 값을 4 단위로 round up
      • 68, 72, 76, 80, …

 

  • if (flags & SLAB_HWCACHE_ALIGN) {
    • slab에 대해 하드웨어 캐시 정렬 요청이 있는 경우
  • unsigned long ralign = cache_line_size();
    • CONFIG_ARCH_HAS_CACHE_LINE_SIZE 커널 옵션이 제공되지 않는 경우 default로 L1_CACHE_BYTES이 사용된다.
      • rpi2: 64 bytes
  • while (size <= ralign / 2) ralign /= 2;
    • size를 초과하는 2의 차수 값
      • 예) size=30, ralign=64
        • ralign=32
  • align = max(align, ralign);
    • 요청한 align이 최소한 size를 초과하는 2의 차수 값 이상이 되게 한다.
  • if (align < ARCH_SLAB_MINALIGN) align = ARCH_SLAB_MINALIGN;
    • align이 최소 8 이상이 되도록 조정한다.
    • ARCH_SLAB_MINALIGN
      • arm=8
  • return ALIGN(align, sizeof(void *));
    • 리턴되는 align 값은 void * 자료형 사이즈 단위로 round up한다.
      • 32bit arm 예) align=66
        • return 68;

 

slab_init_memcg_params()

mm/slab_common.c

#ifdef CONFIG_MEMCG_KMEM
void slab_init_memcg_params(struct kmem_cache *s)
{
        s->memcg_params.is_root_cache = true;
        INIT_LIST_HEAD(&s->memcg_params.list);
        RCU_INIT_POINTER(s->memcg_params.memcg_caches, NULL);
}
#endif

Memory Control Group에 대한 파라메터들을 초기화한다.

 

memcg_propagate_slab_attrs()

mm/slub.c

static void memcg_propagate_slab_attrs(struct kmem_cache *s)
{
#ifdef CONFIG_MEMCG_KMEM
        int i;
        char *buffer = NULL;
        struct kmem_cache *root_cache;

        if (is_root_cache(s))
                return;

        root_cache = s->memcg_params.root_cache;

        /*
         * This mean this cache had no attribute written. Therefore, no point
         * in copying default values around
         */
        if (!root_cache->max_attr_size)
                return;

        for (i = 0; i < ARRAY_SIZE(slab_attrs); i++) {
                char mbuf[64];
                char *buf;
                struct slab_attribute *attr = to_slab_attr(slab_attrs[i]);

                if (!attr || !attr->store || !attr->show)
                        continue;

                /*
                 * It is really bad that we have to allocate here, so we will
                 * do it only as a fallback. If we actually allocate, though,
                 * we can just use the allocated buffer until the end.
                 *
                 * Most of the slub attributes will tend to be very small in
                 * size, but sysfs allows buffers up to a page, so they can
                 * theoretically happen.
                 */
                if (buffer)
                        buf = buffer;
                else if (root_cache->max_attr_size < ARRAY_SIZE(mbuf))
                        buf = mbuf;
                else {
                        buffer = (char *) get_zeroed_page(GFP_KERNEL);
                        if (WARN_ON(!buffer))
                                continue;
                        buf = buffer;
                }

                attr->show(root_cache, buf);
                attr->store(s, buf, strlen(buf));
        }

        if (buffer)
                free_page((unsigned long)buffer);
#endif
}

 

register_hotmemory_notifier()

include/linux/memory.h

#define register_hotmemory_notifier(nb)         register_memory_notifier(nb)

hotmemory notifier로 nb를 등록한다.

 

register_memory_notifier()

drivers/base/memory.c

int register_memory_notifier(struct notifier_block *nb)
{
        return blocking_notifier_chain_register(&memory_chain, nb);
}
EXPORT_SYMBOL(register_memory_notifier);

memory notifier로 nb를 memory_chain 리스트에 추가한다.

 

static BLOCKING_NOTIFIER_HEAD(memory_chain);

 

bootstrap()

mm/slub.c

/*
 * Used for early kmem_cache structures that were allocated using
 * the page allocator. Allocate them properly then fix up the pointers
 * that may be pointing to the wrong kmem_cache structure.
 */

static struct kmem_cache * __init bootstrap(struct kmem_cache *static_cache)
{
        int node;
        struct kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT);
        struct kmem_cache_node *n;

        memcpy(s, static_cache, kmem_cache->object_size);

        /*
         * This runs very early, and only the boot processor is supposed to be
         * up.  Even if it weren't true, IRQs are not up so we couldn't fire
         * IPIs around.
         */
        __flush_cpu_slab(s, smp_processor_id());
        for_each_kmem_cache_node(s, node, n) {
                struct page *p;

                list_for_each_entry(p, &n->partial, lru)
                        p->slab_cache = s;

#ifdef CONFIG_SLUB_DEBUG
                list_for_each_entry(p, &n->full, lru)
                        p->slab_cache = s;
#endif 
        }
        slab_init_memcg_params(s);
        list_add(&s->list, &slab_caches);
        return s;
}

kmem_cache object를 새로 할당 받은 주소에 인수로 전달 받은 static_cache를 obj_size 만큼 복사한다. 그런 후 이 캐시를 활성화 시키고 slub 캐시 운용을 위해 slab_caches 리스트에 추가 한다.

  • struct kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT);
    • kmem_cache object를 할당 받아 온다.
  • memcpy(s, static_cache, kmem_cache->object_size);
    • 임시로 사용하고 있는 kmem_cache 구조체 형태로 전달받은 인수 static_cache의 내용을 object_size 만큼 새로 할당 받은 캐시로 복사한다.
  • __flush_cpu_slab(s, smp_processor_id());
    • early 부트업 과정에 있으므로 실제 다른 cpu로의 IPI call을 할 수 없으므로 local cpu에 대해서만 flush를 하게된다.
  • for_each_kmem_cache_node(s, node, n) {
    • 캐시의 per 노드에 대해 루프를 돈다.
  • list_for_each_entry(p, &n->partial, lru)
    • per 노드의 partial 리스트의 모든 slub page에 대해 루프를 돈다.
  •  p->slab_cache = s;
    • 각 slub page의 slab_cache에 캐시를 지정한다.
  • slab_init_memcg_params(s);
    • memcg 파라메터들을 모두 초기화한다.
  •  list_add(&s->list, &slab_caches);
    • 전역 slab_caches에 할당 받은 캐시를 선두에 추가한다.

 

create_kmalloc_caches() – (slab, slub)

mm/slab_common.c

#ifndef CONFIG_SLOB
/*
 * Create the kmalloc array. Some of the regular kmalloc arrays
 * may already have been created because they were needed to
 * enable allocations for slab creation.
 */
void __init create_kmalloc_caches(unsigned long flags)
{
        int i;

        /*
         * Patch up the size_index table if we have strange large alignment
         * requirements for the kmalloc array. This is only the case for
         * MIPS it seems. The standard arches will not generate any code here.
         *
         * Largest permitted alignment is 256 bytes due to the way we
         * handle the index determination for the smaller caches.
         *
         * Make sure that nothing crazy happens if someone starts tinkering
         * around with ARCH_KMALLOC_MINALIGN
         */
        BUILD_BUG_ON(KMALLOC_MIN_SIZE > 256 ||
                (KMALLOC_MIN_SIZE & (KMALLOC_MIN_SIZE - 1)));

        for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) {
                int elem = size_index_elem(i);

                if (elem >= ARRAY_SIZE(size_index))
                        break;
                size_index[elem] = KMALLOC_SHIFT_LOW;
        }

        if (KMALLOC_MIN_SIZE >= 64) {
                /*
                 * The 96 byte size cache is not used if the alignment
                 * is 64 byte.
                 */
                for (i = 64 + 8; i <= 96; i += 8)
                        size_index[size_index_elem(i)] = 7;

        }

        if (KMALLOC_MIN_SIZE >= 128) {
                /*
                 * The 192 byte sized cache is not used if the alignment
                 * is 128 byte. Redirect kmalloc to use the 256 byte cache
                 * instead.
                 */
                for (i = 128 + 8; i <= 192; i += 8)
                        size_index[size_index_elem(i)] = 8;
        }

kmalloc-<size> 이름의 slub 캐시를 사이즈별로 미리 생성한다.

  • size=8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192 이며, 조건에 따라 96, 192가 추가된다.

 

사이즈 8~192를 커버하는 size_index[] 테이블을 구성한다. (예) rpi2: size_index[0~7]=6, size_index[8~11]=7, size_index[12~23]=8)

  • for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) { int elem = size_index_elem(i); if (elem >= ARRAY_SIZE(size_index)) break; size_index[elem] = KMALLOC_SHIFT_LOW; }
    • 8 ~ KMALLOC_MIN_SIZE까지 size_index[]에 KMALLOC_SHIFT_LOW 값을 대입한다.
      • KMALLOC_MIN_SIZE
        • rpi2: 64
          • L1 d-cacheline 바이트로 64를 사용한다.
      • KMALLOC_SHIFT_LOW
        • rpi2: 6
          • L1 d-cacheline 바이트로 64를 사용하고 필요한 비트 수는 6이다.
  • if (KMALLOC_MIN_SIZE >= 64) { for (i = 64 + 8; i <= 96; i += 8) size_index[size_index_elem(i)] = 7; }
    • KMALLOC_MIN_SIZE가  64 이상인 경우 64 ~ 96까지 size_index[]에 7을 대입한다.
  • if (KMALLOC_MIN_SIZE >= 128) { for (i = 128 + 8; i <= 192; i += 8) size_index[size_index_elem(i)] = 8; }
    • KMALLOC_MIN_SIZE가  128 이상인 경우 128 ~ 192까지 size_index[]에 8을 대입한다.

 

        for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {
                if (!kmalloc_caches[i]) {
                        kmalloc_caches[i] = create_kmalloc_cache(NULL,
                                                        1 << i, flags);
                }

                /*
                 * Caches that are not of the two-to-the-power-of size.
                 * These have to be created immediately after the
                 * earlier power of two caches
                 */
                if (KMALLOC_MIN_SIZE <= 32 && !kmalloc_caches[1] && i == 6)
                        kmalloc_caches[1] = create_kmalloc_cache(NULL, 96, flags);

                if (KMALLOC_MIN_SIZE <= 64 && !kmalloc_caches[2] && i == 7)
                        kmalloc_caches[2] = create_kmalloc_cache(NULL, 192, flags);
        }

        /* Kmalloc array is now usable */
        slab_state = UP;

        for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {
                struct kmem_cache *s = kmalloc_caches[i];
                char *n;

                if (s) {
                        n = kasprintf(GFP_NOWAIT, "kmalloc-%d", kmalloc_size(i));

                        BUG_ON(!n);
                        s->name = n;
                }
        }

#ifdef CONFIG_ZONE_DMA
        for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {
                struct kmem_cache *s = kmalloc_caches[i];

                if (s) {
                        int size = kmalloc_size(i);
                        char *n = kasprintf(GFP_NOWAIT,
                                 "dma-kmalloc-%d", size);

                        BUG_ON(!n);
                        kmalloc_dma_caches[i] = create_kmalloc_cache(n,
                                size, SLAB_CACHE_DMA | flags);
                }
        }
#endif
}
#endif /* !CONFIG_SLOB */

 

 

  • for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {
    • KMALLOC_SHIFT_LOW ~ KMALLOC_SHIFT_HIGH 까지 루프를 돈다.
      • rpi2: 6 ~ 13까지
  • if (!kmalloc_caches[i]) { kmalloc_caches[i] = create_kmalloc_cache(NULL, 1 << i, flags); }
    • 전역 kmalloc_caches[]에 캐시가 설정되지 않았으면 캐시를 2^i 사이즈 만큼 생성하여 대입한다.
      • rpi2: 64, 128, 256, 512, 1024, 2048, 4096, 8192
  • if (KMALLOC_MIN_SIZE <= 32 && !kmalloc_caches[1] && i == 6) kmalloc_caches[1] = create_kmalloc_cache(NULL, 96, flags);
    • KMALLOC_MIN_SIZE가 32 이하이면서 kmalloc_caches[1]이 설정되지 않았고 i가 6인 경우 kmalloc_caches[1]에 96바이트의 캐시를 생성하여 대입한다.
  • if (KMALLOC_MIN_SIZE <= 64 && !kmalloc_caches[2] && i == 7) kmalloc_caches[2] = create_kmalloc_cache(NULL, 192, flags);
    • KMALLOC_MIN_SIZE가 64 이하이면서 kmalloc_caches[2]가 설정되지 않았고 i가 7인 경우 kmalloc_caches[2]에 192바이트의 캐시를 생성하여 대입한다.
  • slab_state = UP;
    • 지금부터 kmalloc array 캐시가 사용될 수 있다.
  • for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) { struct kmem_cache *s = kmalloc_caches[i]; char *n; if (s) { n = kasprintf(GFP_NOWAIT, “kmalloc-%d”, kmalloc_size(i)); s->name = n; } }
    • 0 ~ KMALLOC_SHIFT_HIGH까지 kmalloc_caches[]에 등록된 캐시에 대해 “kmalloc-<size>”라는 이름을 부여한다.
      • rpi2: kmalloc_caches[0] ~ kmalloc_caches[22]까지 등록된 캐시는 다음과 같다.
      • kmalloc_caches[2]=”kmalloc-192″
      • kmalloc_caches[3]=”kmalloc-8″
      • kmalloc_caches[4]=”kmalloc-16″
      • kmalloc_caches[5]=”kmalloc-32″
      • kmalloc_caches[6]=”kmalloc-64″
      • kmalloc_caches[7]=”kmalloc-128″
      • kmalloc_caches[8]=”kmalloc-256″
      • kmalloc_caches[9]=”kmalloc-512″
      • kmalloc_caches[10]=”kmalloc-1024″
      • kmalloc_caches[11]=”kmalloc-2048″
      • kmalloc_caches[12]=”kmalloc-4096″
      • kmalloc_caches[13]=”kmalloc-8192″

 

아래 그림은 H/W 캐시라인 크기에 size_index[] 값이 바뀌는 모습을 보여준다.

size_index-1

 

아래 그림은 H/W 캐시라인이 64인 경우의 예를 들어 size_index[] 값이 설정된 모습을 보여준다.

size_index-2

 

size_index_elem()

mm/slab_common.c

static inline int size_index_elem(size_t bytes)
{
        return (bytes - 1) / 8;
}

인수 bytes에 대해 8바이트 단위로 사용되는 size_index[] 배열 엘레멘트 인덱스 값을 반환한다.

  • 예) 0~7=0, 8~15=1, 16~23=2, …

 

kmalloc_size()

include/linux/slab.h

/*      
 * Determine size used for the nth kmalloc cache.
 * return size or 0 if a kmalloc cache for that
 * size does not exist
 */             
static __always_inline int kmalloc_size(int n)
{
#ifndef CONFIG_SLOB
        if (n > 2)      
                return 1 << n;
        
        if (n == 1 && KMALLOC_MIN_SIZE <= 32)
                return 96;
        
        if (n == 2 && KMALLOC_MIN_SIZE <= 64)
                return 192;
#endif          
        return 0;
}

인수 n 값에 대응하는 사이즈를 반환한다.

  • 예) rpi2: 0->0,  1->0, 2->192, 3->8, 4->16, 5->32, 6->64, 7->128, …

 

create_kmalloc_cache()

“mm/slab_common.c

struct kmem_cache *__init create_kmalloc_cache(const char *name, size_t size,
                                unsigned long flags)
{
        struct kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT);

        if (!s)
                panic("Out of memory when creating slab %s\n", name);

        create_boot_cache(s, name, size, flags);
        list_add(&s->list, &slab_caches);
        s->refcount = 1;
        return s;
}

인수로 주어진 name, size 및 flags로 slub 캐시를 생성한다.

  • struct kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT);
    • kmem_cache 캐시에서 slub object를 할당 받아올 때 슬립하지 않아야 한다.
  • create_boot_cache(s, name, size, flags);
    • kmem_cache 캐시에서 사용하는 per cpu 캐시를 per-cpu 자료형으로 할당 받고, per 노드에서 사용할 kmem_cache_node 구조체는 kmem_cache_node 캐시에서 slub object를 할당받아 partial 리스트에 등록한다
  • list_add(&s->list, &slab_caches);
    • 전역 slab_caches 리스트에 생성한 캐시를 추가한다.
  • s->refcount = 1;
    • 레퍼런스 카운터에 1을 대입한다.

 

struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1];
EXPORT_SYMBOL(kmalloc_caches);

 

#ifdef CONFIG_ZONE_DMA
struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1];
EXPORT_SYMBOL(kmalloc_dma_caches);
#endif

 

/*
 * Conversion table for small slabs sizes / 8 to the index in the
 * kmalloc array. This is necessary for slabs < 192 since we have non power
 * of two cache sizes there. The size of larger slabs can be determined using
 * fls.
 */
static s8 size_index[24] = {
        3,      /* 8 */
        4,      /* 16 */
        5,      /* 24 */
        5,      /* 32 */
        6,      /* 40 */
        6,      /* 48 */
        6,      /* 56 */
        6,      /* 64 */
        1,      /* 72 */
        1,      /* 80 */
        1,      /* 88 */
        1,      /* 96 */
        7,      /* 104 */
        7,      /* 112 */
        7,      /* 120 */
        7,      /* 128 */
        2,      /* 136 */
        2,      /* 144 */
        2,      /* 152 */
        2,      /* 160 */
        2,      /* 168 */
        2,      /* 176 */
        2,      /* 184 */
        2       /* 192 */
};

 

Slub 디버그 커널 옵션

slub_debug

        slub_debug[=options[,slabs]]    [MM, SLUB]
                        Enabling slub_debug allows one to determine the
                        culprit if slab objects become corrupted. Enabling
                        slub_debug can create guard zones around objects and
                        may poison objects when not in use. Also tracks the
                        last alloc / free. For more information see
                        Documentation/vm/slub.txt.

 

slub_max_order

        slub_max_order= [MM, SLUB]
                        Determines the maximum allowed order for slabs.
                        A high setting may cause OOMs due to memory
                        fragmentation. For more information see
                        Documentation/vm/slub.txt.

 

slub_mib_objects

        slub_min_objects=       [MM, SLUB]
                        The minimum number of objects per slab. SLUB will
                        increase the slab order up to slub_max_order to
                        generate a sufficiently large slab able to contain
                        the number of objects indicated. The higher the number
                        of objects the smaller the overhead of tracking slabs
                        and the less frequently locks need to be acquired.
                        For more information see Documentation/vm/slub.txt.

 

slub_min_order

        slub_min_order= [MM, SLUB]
                        Determines the minimum page order for slabs. Must be
                        lower than slub_max_order.
                        For more information see Documentation/vm/slub.txt.

 

slub_nomerge

        slub_nomerge    [MM, SLUB]
                        Same with slab_nomerge. This is supported for legacy.
                        See slab_nomerge for more information.

 

참고

답글 남기기

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