2014년 7월 26일 토요일

[Linux Kernel] 64주차(2014.07.26)

ARM10C 64주차 후기

일시 : 2014.07.26 (64주차)
모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
장소 : 토즈 타워점
장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
참여인원 : 5명

스터디 진도 :

  • mm_init()을 계속 분석합니다.
  • start_kernel()-> mm_init()-> kmem_cache_init() 완료
  • start_kernel()-> mm_init()-> percpu_init_late() 완료
  • start_kernel()-> mm_init()-> pgtable_cache_init() 완료
  • slub이 partial에서 up으로 활성화 되었습니다.
  • 다음 주 (8월 3일)은 여름휴가 입니다.
  • 8월 9일에 65주차 분석 스터디를 계속합니다.
  • 다음 스터디는 vmalloc_init(); 부터 시작합니다.

main.c::mm_init()

static void __init mm_init(void)
{
    page_cgroup_init_flatmem(); // null function
    mem_init();
    // bootmem으로 관리하던 메모리를 buddy로 이관.
    // 각 section 메모리 크기를 출력.

    // mm/Makefile 에서 CONFIG_SLUB 설정으로 slub.c 로 jump
    kmem_cache_init();
// mm/Makefile 에서 CONFIG_SLUB 설정으로 slub.c 로 jump
kmem_cache_init();

slub.c::kmem_cache_init()

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

    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);
    // 할당받은 pcpu 들의 16 byte 공간 (&boot_kmem_cache_node)->cpu_slab 에
    // 각 cpu에 사용하는 kmem_cache_cpu의 tid 맵버를 설정
...
    slab_state = PARTIAL;
    // slab_state 의미:
    // slab을 초기화한 단계를 나타냄, PARTIAL은 kmem_cache_node 만 사용이 가능함

    create_boot_cache(kmem_cache, "kmem_cache",
            offsetof(struct kmem_cache, node) +
                nr_node_ids * sizeof(struct kmem_cache_node *),
               SLAB_HWCACHE_ALIGN);
    // 할당받은 pcpu 들의 16 byte 공간 (&boot_kmem_cache)->cpu_slab 에
    // 각 cpu에 사용하는 kmem_cache_cpu의 tid 맵버를 설정

    kmem_cache = bootstrap(&boot_kmem_cache);
    // slab_caches 의 list에 (UNMOVABLE인 page (boot_kmem_cache)의 object의 시작 virtual address)->list를 등록

    kmem_cache_node = bootstrap(&boot_kmem_cache_node);
    // slab_caches 의 list에 (UNMOVABLE인 page (boot_kmem_cache)의 시작 virtual address + 3968)->list를 등록    

    create_kmalloc_caches(0);
create_kmalloc_caches(0);

slab_common.c::create_kmalloc_caches()

// ARM10C 20140719
// flags: 0
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
     */
    // KMALLOC_MIN_SIZE: 64
    BUILD_BUG_ON(KMALLOC_MIN_SIZE > 256 ||
        (KMALLOC_MIN_SIZE & (KMALLOC_MIN_SIZE - 1)));

    // KMALLOC_MIN_SIZE: 64
    for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) {
        // i: 8, size_index_elem(8): 0
        int elem = size_index_elem(i);
        // elem: 0

        // elem: 0, ARRAY_SIZE(size_index): 24
        if (elem >= ARRAY_SIZE(size_index))
            break;

        // elem: 0, KMALLOC_SHIFT_LOW: 6
        size_index[elem] = KMALLOC_SHIFT_LOW;
        // size_index[0]: 6
    }
    // 루프 수행 결과
    // size_index[0 .. 6]: 6

    // KMALLOC_MIN_SIZE: 64
    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)
            // i: 72, size_index_elem(72): 8
            size_index[size_index_elem(i)] = 7;
            // size_index[8]: 7

        // 루프 수행 결과
        // size_index[8 .. 11]: 7
    }

    // KMALLOC_MIN_SIZE: 64
    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_SHIFT_LOW: 6, KMALLOC_SHIFT_HIGH: 13
    for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {
        // i: 6, kmalloc_caches[6]: NULL
        // i: 7, kmalloc_caches[7]: NULL
        // i: 12, kmalloc_caches[12]: NULL
        if (!kmalloc_caches[i]) {

            // i: 6, flags: 0, create_kmalloc_cache(NULL, 64, 0): kmem_cache#2
            // i: 7, flags: 0, create_kmalloc_cache(NULL, 128, 0): kmem_cache#3
            // i: 12, flags: 0, create_kmalloc_cache(NULL, 4096, 0): kmem_cache#9
            kmalloc_caches[i] = create_kmalloc_cache(NULL,
                            1 << i, flags);
            // kmalloc_caches[6]: kmem_cache#2
            // kmalloc_caches[7]: kmem_cache#3
            // kmalloc_caches[12]: kmem_cache#9
        }

        /*
         * 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
         */
        // KMALLOC_MIN_SIZE: 64, i: 6, kmalloc_caches[1]: NULL
        // KMALLOC_MIN_SIZE: 64, i: 7, kmalloc_caches[1]: NULL
        // KMALLOC_MIN_SIZE: 64, i: 12, kmalloc_caches[1]: NULL
        if (KMALLOC_MIN_SIZE <= 32 && !kmalloc_caches[1] && i == 6)
            kmalloc_caches[1] = create_kmalloc_cache(NULL, 96, flags);

        // KMALLOC_MIN_SIZE: 64, i: 6, kmalloc_caches[1]: NULL
        // KMALLOC_MIN_SIZE: 64, i: 7, kmalloc_caches[2]: NULL
        // KMALLOC_MIN_SIZE: 64, i: 12, kmalloc_caches[2]: NULL
        if (KMALLOC_MIN_SIZE <= 64 && !kmalloc_caches[2] && i == 7)
            // i: 7, flags: 0, create_kmalloc_cache(NULL, 192, 0): kmem_cache#4
            kmalloc_caches[2] = create_kmalloc_cache(NULL, 192, flags);
            // kmalloc_caches[2]: kmem_cache#4

        // loop i = 8 9 10 11 13 수행 (skip)
    }

    // 위 loop 에서 한일:
    // kmem_cache object를 1개 할당받음
    // kmem_cache_node object를 1개 할당받음
    // kmem_cache 의 refcount 가 1로 set
    // slab_caches에 kmem_cache의 list 추가

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

    // KMALLOC_SHIFT_HIGH: 13
    for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {
        // i: 0, kmalloc_caches[0]: NULL
        // i: 2, kmalloc_caches[2]: kmem_cache#4
        struct kmem_cache *s = kmalloc_caches[i];
        // i: 0, s: NULL
        // i: 2, s: kmem_cache#4
        char *n;

        // i: 0, s: NULL
        // i: 2, s: kmem_cache#4
        if (s) {
            // GFP_NOWAIT: 0, i: 2, kmalloc_size(2): 192
            n = kasprintf(GFP_NOWAIT, "kmalloc-%d", kmalloc_size(i));
            // n: kmem_cache#2-o1

            // n: kmem_cache#2-o1
            BUG_ON(!n);

            // s->name: kmem_cache#2->name: NULL, n: kmem_cache#2-o1
            s->name = n;
            // s->name: kmem_cache#2->name: kmem_cache#2-o1: "kmalloc-192"
        }

        // loop i = 1 3 .. 13 수행 (skip)
    }

    // kmalloc_caches[0] kmalloc_caches[1], kmalloc_caches[3], kmalloc_caches[4], kmalloc_caches[5]
    // 는 값이 null 이므로 skip
    // kmalloc_caches[6]:  kmem_cache#2->name:  "kmalloc-64"
    // kmalloc_caches[7]:  kmem_cache#3->name:  "kmalloc-128"
    // kmalloc_caches[2]:  kmem_cache#4->name:  "kmalloc-192"
    // kmalloc_caches[8]:  kmem_cache#5->name:  "kmalloc-256"
    // kmalloc_caches[9]:  kmem_cache#6->name:  "kmalloc-512"
    // kmalloc_caches[10]: kmem_cache#7->name:  "kmalloc-1024"
    // kmalloc_caches[11]: kmem_cache#8->name:  "kmalloc-2048"
    // kmalloc_caches[12]: kmem_cache#9->name:  "kmalloc-4096"
    // kmalloc_caches[13]: kmem_cache#10->name: "kmalloc-8192"

#ifdef CONFIG_ZONE_DMA // CONFIG_ZONE_DMA=n
    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
}

create_kmalloc_caches()가 한일

// create_kmalloc_caches 가 한일:
// 배열 size_index[] 값을 변경, kmalloc_caches[] 값을 채워줌
//
// size_index[0 .. 6]: 6
// size_index[8 .. 11]: 7
//
// kmem_cache object를 1개 할당받음
// kmem_cache_node object를 1개 할당받음
// kmem_cache 의 refcount 가 1로 set
// slab_caches에 kmem_cache의 list 추가
//
// kmalloc_caches[6]:
// # order: 0, object size: 64
// kmem_cache#2
// - kmem_cache#2->allocflags: 0
// - kmem_cache#2->oo.x: 0x40
// - kmem_cache#2->min.x: 0x40
// - kmem_cache#2->max.x: 0x40
// - kmem_cache#2->min_partial: 5
// - kmem_cache#2->cpu_partial: 30
// - kmem_cache#2->name:  "kmalloc-64"
// kmem_cache_node#3
//
// kmalloc_caches[7]:
// # order: 0, object size: 128
// kmem_cache#3
// - kmem_cache#3->allocflags: 0
// - kmem_cache#3->oo.x: 0x20
// - kmem_cache#3->min.x: 0x20
// - kmem_cache#3->max.x: 0x20
// - kmem_cache#3->min_partial: 5
// - kmem_cache#3->cpu_partial: 30
// - kmem_cache#3->name:  "kmalloc-128"
// kmem_cache_node#4
//
// kmalloc_caches[2]:
// # order: 0, object size: 192
// kmem_cache#4
// - kmem_cache#4->allocflags: 0
// - kmem_cache#4->oo.x: 0x15
// - kmem_cache#4->min.x: 0x15
// - kmem_cache#4->max.x: 0x15
// - kmem_cache#4->min_partial: 5
// - kmem_cache#4->cpu_partial: 30
// - kmem_cache#4->name:  "kmalloc-192"
// kmem_cache_node#5
//
// kmalloc_caches[8]:
// # order: 0, object size: 256
// kmem_cache#5
// - kmem_cache#5->allocflags: 0
// - kmem_cache#5->oo.x: 0x10
// - kmem_cache#5->min.x: 0x10
// - kmem_cache#5->max.x: 0x10
// - kmem_cache#5->min_partial: 5
// - kmem_cache#5->cpu_partial: 13
// - kmem_cache#5->name:  "kmalloc-256"
// kmem_cache_node#6
//
// kmalloc_caches[9]:
// # order: 1, object size: 512
// kmem_cache#6
// - kmem_cache#6->allocflags: __GFP_COMP (0x4000)
// - kmem_cache#6->oo.x: 0x10010
// - kmem_cache#6->min.x: 0x8
// - kmem_cache#6->max.x: 0x10010
// - kmem_cache#6->min_partial: 5
// - kmem_cache#6->cpu_partial: 13
// - kmem_cache#6->name:  "kmalloc-512"
// kmem_cache_node#7
//
// kmalloc_caches[10]:
// # order: 2, object size: 1024
// kmem_cache#7
// - kmem_cache#7->allocflags: __GFP_COMP (0x4000)
// - kmem_cache#7->oo.x: 0x20020
// - kmem_cache#7->min.x: 0x4
// - kmem_cache#7->max.x: 0x20020
// - kmem_cache#7->min_partial: 5
// - kmem_cache#7->cpu_partial: 6
// - kmem_cache#7->name:  "kmalloc-1024"
// kmem_cache_node#8
//
// kmalloc_caches[11]:
// # order: 3, object size: 2048
// kmem_cache#8
// - kmem_cache#8->allocflags: __GFP_COMP (0x4000)
// - kmem_cache#8->oo.x: 0x30010
// - kmem_cache#8->min.x: 0x2
// - kmem_cache#8->max.x: 0x30010
// - kmem_cache#8->min_partial: 5
// - kmem_cache#8->cpu_partial: 6
// - kmem_cache#8->name:  "kmalloc-2048"
// kmem_cache_node#9
//
// kmalloc_caches[12]:
// # order: 3, object size: 4096
// kmem_cache#9
// - kmem_cache#9->allocflags: __GFP_COMP (0x4000)
// - kmem_cache#9->oo.x: 0x30008
// - kmem_cache#9->min.x: 0x10002
// - kmem_cache#9->max.x: 0x30008
// - kmem_cache#9->min_partial: 6
// - kmem_cache#9->cpu_partial: 2
// - kmem_cache#9->name:  "kmalloc-4096"
// kmem_cache_node#10
//
// kmalloc_caches[13]:
// # order: 3, object size: 8192
// kmem_cache#10
// - kmem_cache#10->allocflags: __GFP_COMP (0x4000)
// - kmem_cache#10->oo.x: 0x30004
// - kmem_cache#10->min.x: 0x10001
// - kmem_cache#10->max.x: 0x30004
// - kmem_cache#10->min_partial: 6
// - kmem_cache#10->cpu_partial: 2
// - kmem_cache#10->name:  "kmalloc-8192"
// kmem_cache_node#11
//
// slab_state: UP

slub.c::kmem_cache_init()

void __init kmem_cache_init(void)
{
...
    create_kmalloc_caches(0);

#ifdef CONFIG_SMP // CONFIG_SMP=y
    register_cpu_notifier(&slab_notifier);
    // (&cpu_chain)->head: slab_notifier 포인터 대입
    // (&slab_notifier)->next은 (&page_alloc_cpu_notify_nb)->next로 대입
#endif

    // KERN_INFO: "\001" "6", cache_line_size(): 64
    // slub_min_order: 0, slub_max_order: 3, slub_min_objects: 0
    // nr_cpu_ids: 4, nr_node_ids: 1
    printk(KERN_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: HWalign=64, Order=0-3, MinObjects=0," " CPUs=4, Nodes=1"
}

main.c::mm_init()

static void __init mm_init(void)
{
...
    kmem_cache_init();
    // slub 을 활성화 시킴

    percpu_init_late();
percpu_init_late();

percpu.c::percpu_init_late()

// ARM10C 20140726
void __init percpu_init_late(void)
{
    // pcpu_first_chunk: pcpu_setup_first_chunk()함수에서 할당한 dchunk,
    // pcpu_reserved_chunk: pcpu_setup_first_chunk()함수에서 할당한 schunk
    struct pcpu_chunk *target_chunks[] =
        { pcpu_first_chunk, pcpu_reserved_chunk, NULL };
    struct pcpu_chunk *chunk;
    unsigned long flags;
    int i;

    for (i = 0; (chunk = target_chunks[i]); i++) {
        int *map;
        // PERCPU_DYNAMIC_EARLY_SLOTS: 128, sizeof(map[0]): 4
        const size_t size = PERCPU_DYNAMIC_EARLY_SLOTS * sizeof(map[0]);
        // size: 512

        // size: 512, PAGE_SIZE: 4096
        BUILD_BUG_ON(size > PAGE_SIZE);

        // size: 512
        map = pcpu_mem_zalloc(size);
        // map: kmem_cache#6-o1

        // map: kmem_cache#6-o1
        BUG_ON(!map);

        spin_lock_irqsave(&pcpu_lock, flags);
        // flags에 cpsr 저장 후 pcpu_lock 를 사용한 spinlock 획득

        // map: kmem_cache#6-o1, chunk->map: dchunk->map, size: 512
        memcpy(map, chunk->map, size);
        // dchunk로 할당 받은 pcpu 메모리 값들을 slab으로 카피하여 이관

        // map: kmem_cache#6-o1
        chunk->map = map;
        // chunk->map: dchunk->map: kmem_cache#6-o1

        spin_unlock_irqrestore(&pcpu_lock, flags);
        // flags에 저장된 cpsr을 원복하고 pcpu_lock 를 사용한 spinlock 해제
    }
}

main.c::mm_init()

static void __init mm_init(void)
{
...
    kmem_cache_init();
    // slub 을 활성화 시킴

    percpu_init_late();
    // dchunk로 할당 받은 pcpu 메모리 값들을 slab으로 카피하여 이관

    pgtable_cache_init(); // null function

2014년 7월 19일 토요일

[Linux Kernel] 63주차(2014.07.19)

ARM10C 63주차 후기

일시 : 2014.07.19 (63주차)
모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
장소 : 토즈 타워점
장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
참여인원 : 5명

스터디 진도 :

  • mm_init()을 계속 분석합니다.
  • start_kernel()-> mm_init()->kmem_cache_init()->bootstrap()분석중
  • bootstrap()은 두번 실행됩니다.
  • 1번째는 &boot_kmem_cache 매개변수를 가지고
  • /지난번 분석이 잘못되었습니다./
  • 2번째도 &boot_kmem_cache를 가지고 실행합니다.
  • 따라서 bootstrap()를 다시 분석합니다.

main.c::mm_init()

static void __init mm_init(void)
{
    page_cgroup_init_flatmem(); // null function
    mem_init();
    // bootmem으로 관리하던 메모리를 buddy로 이관.
    // 각 section 메모리 크기를 출력.

    // mm/Makefile 에서 CONFIG_SLUB 설정으로 slub.c 로 jump
    kmem_cache_init();
// mm/Makefile 에서 CONFIG_SLUB 설정으로 slub.c 로 jump
kmem_cache_init();

slub.c::kmem_cache_init()

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

    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);

    /* Able to allocate the per node structures */
    slab_state = PARTIAL;
    // slab_state 의미:
    // slab을 초기화한 단계를 나타냄, PARTIAL은 kmem_cache_node 만 사용이 가능함

    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);

    kmem_cache_node = bootstrap(&boot_kmem_cache_node);
kmem_cache_node = bootstrap(&boot_kmem_cache_study);

slub.c::bootstrap()

// ARM10C 20140705
// &boot_kmem_cache_node
static struct kmem_cache * __init bootstrap(struct kmem_cache *static_cache)
{
    int node;

    struct kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT);
  • kmem_cache: &boot_kmem_cache
  • 지난 주는 이것을 &boot_kmem_cache가 아닌 &boot_kmem_cache_node로 잘못 분석했다.
  • 따라서 이번주는 이것을 올바르게 수정해서 다시 분석을 한다.
  • GFP_NOWAIT: 0
  • 따라서 kmem_cache_zalloc()는 매개변수 (kmem_cache, GFP_NOWAIT)로 호출된다.

slab.h::kmem_cache_zalloc()

// ARM10C 20140719 // kmem_cache: UNMOVABLE인 page (boot_kmem_cache)의 object의 시작 virtual address, GFP_NOWAIT: 0
static inline void *kmem_cache_zalloc(struct kmem_cache *k, gfp_t flags)
{
    // k: UNMOVABLE인 page (boot_kmem_cache)의 object의 시작 virtual address, flags: GFP_NOWAIT: 0, __GFP_ZERO: 0x8000u
    // kmem_cache_alloc(UNMOVABLE인 page (boot_kmem_cache)의 object의 시작 virtual address, __GFP_ZERO: 0x8000u):
    // UNMOVABLE인 page (boot_kmem_cache)의 시작 virtual address + 128
    return kmem_cache_alloc(k, flags | __GFP_ZERO);
    // return UNMOVABLE인 page (boot_kmem_cache)의 시작 virtual address + 128
}

study log

boot_kmem_cache를 가지고 bootstrap()을 다시 분석했습니다.
cd7d188..cc0ab55  master     -> origin/master
Updating cd7d188..cc0ab55
Fast-forward
arch/arm/include/asm/processor.h |    2 -
include/linux/fault-inject.h     |    1 +
include/linux/gfp.h              |    1 +
include/linux/kernel.h           |    4 +-
include/linux/memcontrol.h       |   10 +-
include/linux/percpu.h           |   98 ---
include/linux/slab.h             |   23 +-
include/linux/slub_def.h         |    1 +
include/linux/topology.h         |    1 +
mm/slab_common.c                 |   43 ++
mm/slub.c                        | 1369 +++++++++++++++++++++++++++-----------
11 files changed, 1041 insertions(+), 512 deletions(-)