2014년 6월 1일 일요일

[Linux Kernel] 56주차(2014.05.31) 후기

ARM10C 56주차 후기

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

스터디 진도 :

  • 지난 스터디에 이어서 mem_init()을 계속 분석합니다.
  • start_kernel()-> mm_init()->kmem_cache_init()->create_boot_cache() 분석중

slub.c::kmem_cache_init()

// ARM10C 20140419
// &boot_kmem_cache_node, “kmem_cache_node”, sizeof(struct kmem_cache_node): 44 byte,
// SLAB_HWCACHE_ALIGN: 0x00002000UL
create_boot_cache(kmem_cache_node, "kmem_cache_node", \
    sizeof(struct kmem_cache_node), SLAB_HWCACHE_ALIGN);

slab_common.c::create_boot_cache()

// ARM10C 20140419
// &boot_kmem_cache_node, “kmem_cache_node”, sizeof(struct kmem_cache_node): 44 byte,
// SLAB_HWCACHE_ALIGN: 0x00002000UL
void __init create_boot_cache(struct kmem_cache *s, const char *name, size_t size,
        unsigned long flags)
{
    int err;

    // s->name: boot_kmem_cache_node.name: NULL
    s->name = name;
    // s->name: boot_kmem_cache_node.name: "kmem_cache_node"

    // s->size: boot_kmem_cache_node.size: 0
    // s->object_size: boot_kmem_cache_node.object_size: 0
    s->size = s->object_size = size;
    // s->size: boot_kmem_cache_node.size: 44
    // s->object_size: boot_kmem_cache_node.object_size: 44

    // flags: SLAB_HWCACHE_ALIGN: 0x00002000UL, ARCH_KMALLOC_MINALIGN: 64, size: 44
    // s->align: boot_kmem_cache_node.align: 0
    s->align = calculate_alignment(flags, ARCH_KMALLOC_MINALIGN, size);
    // s->align: boot_kmem_cache_node.align: 64

    // s: &boot_kmem_cache_node, flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
    err = __kmem_cache_create(s, flags);

slub.c::__kmem_cache_create()

// ARM10C 20140419
// s: &boot_kmem_cache_node,
// flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
int __kmem_cache_create(struct kmem_cache *s, unsigned long flags)
{
    int err;

    // s: &boot_kmem_cache_node, flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
    err = kmem_cache_open(s, flags);

slub.c::kmem_cache_open()

// ARM10C 20140419
// s: &boot_kmem_cache_node,
// flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
static int kmem_cache_open(struct kmem_cache *s, unsigned long flags)
{
    // s->size: boot_kmem_cache_node.size: 44, flags: SLAB_HWCACHE_ALIGN: 0x00002000UL,
    // s->name: boot_kmem_cache_node.name: "kmem_cache_node, s->ctor: boot_kmem_cache_node.ctor: NULL
    s->flags = kmem_cache_flags(s->size, flags, s->name, s->ctor);
    // s->flags: boot_kmem_cache_node.flags: SLAB_HWCACHE_ALIGN: 0x00002000UL

    // s->reserved: boot_kmem_cache_node.reserved: 0
    s->reserved = 0;
    // s->reserved: boot_kmem_cache_node.reserved: 0

    // need_reserve_slab_rcu: 0 , s->flags: boot_kmem_cache_node.flags: SLAB_HWCACHE_ALIGN
    if (need_reserve_slab_rcu && (s->flags & SLAB_DESTROY_BY_RCU))
        s->reserved = sizeof(struct rcu_head);

    // s: &boot_kmem_cache_node, -1, calculate_sizes(&boot_kmem_cache_node, -1): 1
    if (!calculate_sizes(s, -1))
        goto error;

    // disable_higher_order_debug: 0
    if (disable_higher_order_debug) {
        /*
         * Disable debugging flags that store metadata if the min slab
         * order increased.
         */
        if (get_order(s->size) > get_order(s->object_size)) {
            s->flags &= ~DEBUG_METADATA_FLAGS;
            s->offset = 0;
            if (!calculate_sizes(s, -1))
                goto error;
        }
    }

// CONFIG_HAVE_CMPXCHG_DOUBLE=n, CONFIG_HAVE_ALIGNED_STRUCT_PAGE=n
#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \
    defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE)
    if (system_has_cmpxchg_double() && (s->flags & SLAB_DEBUG_FLAGS) == 0)
        /* Enable fast mode */
        s->flags |= __CMPXCHG_DOUBLE;
#endif

    /*
     * The larger the object size is, the more pages we want on the partial
     * list to avoid pounding the page allocator excessively.
     */
    // s->size: boot_kmem_cache_node.size: 64, ilog2(64): 6
    // s: &boot_kmem_cache_node, 3
    set_min_partial(s, ilog2(s->size) / 2);
    // boot_kmem_cache_node.min_partial: 5

    /*
     * cpu_partial determined the maximum number of objects kept in the
     * per cpu partial lists of a processor.
     *
     * Per cpu partial lists mainly contain slabs that just have one
     * object freed. If they are used for allocation then they can be
     * filled up again with minimal effort. The slab will never hit the
     * per node partial lists and therefore no locking will be required.
     *
     * This setting also determines
     *
     * A) The number of objects from per cpu partial slabs dumped to the
     *    per node list when we reach the limit.
     * B) The number of objects in cpu partial slabs to extract from the
     *    per node list when we run out of per cpu objects. We only fetch
     *    50% to keep some capacity around for frees.
     */

    // s: &boot_kmem_cache_node, kmem_cache_has_cpu_partial(&boot_kmem_cache_node): 1
    // s->size: boot_kmem_cache_node.size: 64, PAGE_SIZE: 0x1000
    if (!kmem_cache_has_cpu_partial(s))
        s->cpu_partial = 0;
    else if (s->size >= PAGE_SIZE)
        s->cpu_partial = 2;
    else if (s->size >= 1024)
        s->cpu_partial = 6;
    else if (s->size >= 256)
        s->cpu_partial = 13;
    else
        // s->cpu_partial: boot_kmem_cache_node.cpu_partial: 0
        s->cpu_partial = 30;
        // boot_kmem_cache_node.cpu_partial: 30

#ifdef CONFIG_NUMA // CONFIG_NUMA=n
    s->remote_node_defrag_ratio = 1000;
#endif
    // s: &boot_kmem_cache_node, init_kmem_cache_nodes(&boot_kmem_cache_node): 1
    if (!init_kmem_cache_nodes(s))
        goto error;

slub.c::init_kmem_cache_nodes()

// s: &boot_kmem_cache_node,
// return: init_kmem_cache_nodes(&boot_kmem_cache_node): 1
static int init_kmem_cache_nodes(struct kmem_cache *s)
{
    int node;

    // N_NORMAL_MEMORY: 2
    for_each_node_state(node, N_NORMAL_MEMORY) {
    // for ( (node) = 0; (node) == 0; (node) = 1)

        struct kmem_cache_node *n;

        // slab_state: DOWN: 1
        if (slab_state == DOWN) {
            // node: 0
            early_kmem_cache_node_alloc(node);
            continue;
        }

slub.c::early_kmem_cache_node_alloc()

// ARM10C 20140426
// node: 0
static void early_kmem_cache_node_alloc(int node)
{
    struct page *page;
    struct kmem_cache_node *n;

    // kmem_cache_node->size: boot_kmem_cache_node.size: 64, sizeof(struct kmem_cache_node): 44 bytes
    BUG_ON(kmem_cache_node->size < sizeof(struct kmem_cache_node));

    // kmem_cache_node: &boot_kmem_cache_node, GFP_NOWAIT: 0, node: 0
    page = new_slab(kmem_cache_node, GFP_NOWAIT, node);

slub.c::new_slab()

// ARM10C 20140426
// kmem_cache_node: &boot_kmem_cache_node, GFP_NOWAIT: 0, node: 0
static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node)
{
    struct page *page;
    void *start;
    void *last;
    void *p;
    int order;

    // flags: GFP_NOWAIT: 0, GFP_SLAB_BUG_MASK: 0xfe000005
    BUG_ON(flags & GFP_SLAB_BUG_MASK);

    // s: &boot_kmem_cache_node, flags: GFP_NOWAIT: 0, node: 0
    // GFP_RECLAIM_MASK: 0x13ef0, GFP_CONSTRAINT_MASK: 0x60000
    page = allocate_slab(s,
        flags & (GFP_RECLAIM_MASK | GFP_CONSTRAINT_MASK), node);
    // page: migratetype이 MIGRATE_UNMOVABLE인 page

    if (!page)
        goto out;

    // page: migratetype이 MIGRATE_UNMOVABLE인 page
    // compound_order(page): 0
    order = compound_order(page);
    // free_pages_check에서 page->flags의 NR_PAGEFLAGS 만큼의 하위 비트를 전부 지워줌
    // order: 0

mm.h::compound_order()

// ARM10C 20140524
// page: migratetype이 MIGRATE_UNMOVABLE인 page
static inline int compound_order(struct page *page)
{
    // PageHead(page): 0
    if (!PageHead(page))
        return 0;
    return (unsigned long)page[1].lru.prev;
}
order = compound_order(page);
// order: 0
// free_pages_check에서 page->flags의 NR_PAGEFLAGS 만큼의 하위 비트를 전부 지워줌
  • compound가 접착이라는 의미이므로 page가 붙여서 할당 받았는지 여부를 나타내는 것으로 추정된다.
  • 우리는 작은 것을 받았으므로 0일것이다. 하지만 의미는 분명하게 확인해 보자.
  • free_pages_check에서 버디를 활성화하기 전에 모두 0으로 초기화 하고 왔다
  • page->flags의 NR_PAGEFLAGS 만큼의 하위 비트를 전부 지워줌.

slub.c::new_slab()

// 2014/05/31 시작
// s: &boot_kmem_cache_node, page: migratetype이 MIGRATE_UNMOVABLE인 page
// page_to_nid(migratetype이 MIGRATE_UNMOVABLE인 page): 0, page->objects: 64
inc_slabs_node(s, page_to_nid(page), page->objects);

slub.c::inc_slabs_node()

// ARM10C 20140531
// s: &boot_kmem_cache_node, page: migratetype이 MIGRATE_UNMOVABLE인 page
// page_to_nid(migratetype이 MIGRATE_UNMOVABLE인 page): 0, page->objects: 64
// kmem_cache_node: &boot_kmem_cache_node, node: 0, page->objects: 64
static inline void inc_slabs_node(struct kmem_cache *s, int node, int objects)
{
    // s: &boot_kmem_cache_node, node:0
    // get_node(&boot_kmem_cache_node,0) : (&boot_kmem_cache_node)->node[0]
    struct kmem_cache_node *n = get_node(s, node);

slub.c::get_node(s,node)

// ARM10C 20140531
// s: &boot_kmem_cache_node, node: 0
static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node)
{
    // node: 0, s->node: (&boot_kmem_cache_node)->node[0]
    return s->node[node];
    // return (&boot_kmem_cache_node)->node[0]
}
// return (&boot_kmem_cache_node)->node[0]

slub.c::inc_slabs_node()

// get_node에서 (&boot_kmem_cache_node)->node[0]를 리턴받아 n에 할당함
    struct kmem_cache_node *n = get_node(s, node);
    // n: &(&boot_kmem_cache_node)->node[0]: NULL
    // n: &(&boot_kmem_cache_node)->node[0]: UNMOVABLE인 page 의 object의 시작 virtual address

    /*
     * May be called early in order to allocate a slab for the
     * kmem_cache_node structure. Solve the chicken-egg
     * dilemma by deferring the increment of the count during
     * bootstrap (see early_kmem_cache_node_alloc).
     */
    // n: &(&boot_kmem_cache_node)->node[0]: NULL
    if (likely(n)) {
        // n->slabs: 0
        atomic_long_inc(&n->nr_slabs);
        // n->slabs: 1

        // objects: 64, n->total_objects: 0
        atomic_long_add(objects, &n->total_objects);
        // n->total_objects: 64
    }
    // kmem_cache_node 가 완성된 이후에 nr_slabs, total_objects 가 증가될 것으로 예상됨
}
여기서 if(likely(n))에서 n의 값을 확인해 보자.
n은 n : &(&boot_kmem_cache_node)->node[0]이므로NULL이다.
아직 kmem_cache_node를 한번 들어왔고, 초기화만 한것이므로
inc_slabs_node 는 나중에 kmem_cache_node가 완성된 이후면 증가하는 것으로 예상한다.

slub.c::new_slab()

// inc_slabs_node에서 n은 &(&boot_kmem_cache_node)->node[0]이므로NULL이다.
// 초기화만 했고, kmem_cache_node가 완성된 이후 증가한다.
    inc_slabs_node(s, page_to_nid(page), page->objects);

    // s: &boot_kmem_cache_node
    // order: 0
    memcg_bind_pages(s, order);
    // null function

    // page : migratetype이 MIGRATE_UNMOVEABLE인 page
    // s : &boot_kmem_cache_node
    page->slab_cache = s;
    // page->slab_cache : &boot_kmem_cache_node

    // page: migratetype이 MIGRATE_UNMOVABLE인 page
    __SetPageSlab(page);

page-flags.h::__SetPageSlab(page)

__SetPageSlab은 매크로이다.
__PAGEFLAG(Slab, slab)
__SetPageSlab(strunct page *page) \
 {__set_bits(PG_slab, &page->flags);}
# define __SETPAGEFLAG(Slab,slab); \
static inline void _SetPageSlab(struct page *page) \
 { __set_bits(PG_slab, &page->flags);
// PG_slab : enum 7
page->flags의 7번째 bit를 set한다.

slub.c::new_slab()

__SetPageSlab(page) 는 page->flags의 7번째 bit를 set한다.
여기서 page는 migratetype이 MIGRATE_UNMOVABLE인 page이다.
    __SetPageSlab(page);
    // page->flags에 7 (PG_slab) bit를 set

    // page->pfmemalloc: 0
    if (page->pfmemalloc)
        SetPageSlabPfmemalloc(page);

    // page: migratetype이 MIGRATE_UNMOVABLE인 page
    //       이 코드 이후 부터는 UNMOVABLE인 page로 표기
    start = page_address(page);

    if (page->pfmemalloc)
    // page->pfmemalloc : 0
        SetPageSlabPfmemalloc(page);

    // page : &boot_kmem_cache_node
    start = page_address(page);
  • page->pfmemalloc : page_alloc.c : 2887에서 0으로 초기화 했다.
  • page를 migratetype이 MI GRATE_UNMOVEABLE 에서
  • UNMOVEABLE인 page로 쓰기로 하자.

highmem.c::page_address(page)

// ARM10C 20140531
// page: migratetype이 MIGRATE_UNMOVABLE인 page
void *page_address(const struct page *page)
{
    unsigned long flags;
    void *ret;
    struct page_address_slot *pas;

    // PageHighMem(page): 0
    if (!PageHighMem(page))
        return lowmem_page_address(page);
        // page의 virtual address 값을 리턴

    pas = page_slot(page);
    ret = NULL;
    spin_lock_irqsave(&pas->lock, flags);
    if (!list_empty(&pas->lh)) {
        struct page_address_map *pam;

        list_for_each_entry(pam, &pas->lh, list) {
            if (pam->page == page) {
                ret = pam->virtual;
                goto done;
            }
        }
    }
done:
    spin_unlock_irqrestore(&pas->lock, flags);
    return ret;
}
// start: UNMOVABLE인 page 의 virtual address

slub.c::new_slab()

    start = page_address(page);
    // start: UNMOVABLE인 page 의 virtual address

    // s->flags: boot_kmem_cache_node.flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
    // SLAB_POISON: 0x00000800UL
    if (unlikely(s->flags & SLAB_POISON))
        memset(start, POISON_INUSE, PAGE_SIZE << order);

    // start: UNMOVABLE인 page 의 virtual address
    last = start;
    // last: UNMOVABLE인 page 의 virtual address

    // s: &boot_kmem_cache_node,
    // start: UNMOVABLE인 page 의 virtual address
    // page->objects: 64
    for_each_object(p, s, start, page->objects) {
    // for (p = (UNMOVABLE인 page 의 virtual address);
    //      p < (UNMOVABLE인 page 의 virtual address) + (64) * (&boot_kmem_cache_node)->size;
    //    p += (&boot_kmem_cache_node)->size)

        // p: UNMOVABLE인 page 의 virtual address
        // (&boot_kmem_cache_node)->size: 64

        // s: &boot_kmem_cache_node, page: UNMOVABLE인 page
        // last: UNMOVABLE인 page 의 virtual address
        setup_object(s, page, last);

        // s: &boot_kmem_cache_node, last: UNMOVABLE인 page 의 virtual address
        // p: UNMOVABLE인 page 의 virtual address
        set_freepointer(s, last, p);
        // last에 p 주소를 mapping 함

        last = p;
    }

slub.c::setup_objects(s page last)

// s: &boot_kmem_cache_node, page: UNMOVABLE인 page
// last: UNMOVABLE인 page 의 virtual address
// setup_object(s, page, last);
static void setup_object(struct kmem_cache *s, struct page *page,
                void *object)
{
    // s: &boot_kmem_cache_node, page: UNMOVABLE인 page
    // object: UNMOVABLE인 page 의 virtual address
    setup_object_debug(s, page, object);

slub.c::setup_object_debug()

// ARM10C 20140531
// s: &boot_kmem_cache_node, page: UNMOVABLE인 page
// object: UNMOVABLE인 page 의 virtual address
static void setup_object_debug(struct kmem_cache *s, struct page *page,
                                void *object)
{
    // s->flags: boot_kmem_cache_node.flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
    // SLAB_STORE_USER: 0x00010000UL,
    // SLAB_RED_ZONE: 0x00000400UL,
    // __OBJECT_POISON: 0x80000000UL
    // 0x00010000 |  0x00000400 | 0x80000000: 0x80010400
    // s->flags & 0x80010400
    if (!(s->flags & (SLAB_STORE_USER|SLAB_RED_ZONE|__OBJECT_POISON)))
        return;
        // return 수행
if문에서 참값으로 return한다.

slub.c::setup_objects(s page last)

    setup_object_debug(s, page, object);

    // s->ctor: boot_kmem_cache_node.ctor: NULL
    if (unlikely(s->ctor))
        s->ctor(object);
}

slub.c::newslab()

    for_each_object(p, s, start, page->objects) {

        setup_object(s, page, last);

        // s: &boot_kmem_cache_node,
        // last : UNMOVERABLE인 page의 가상 주소
        // p: UNMOVERABLE인 page의 가상 주소
        set_freepointer(s, last, p);

slub.c::set_freepoint(s,page,last)

// s: &boot_kmem_cache_node,
// last : UNMOVERABLE인 page의 가상 주소
// p: UNMOVERABLE인 page의 가상 주소
static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
{
    // object : last : UNMOVERABLE인 page의 가상 주소
    // s->offset : &boot_kmem_cache_node->offset : 0
    // fp : UNMOVABLE인 page 의 virtual address
    *(void **)(object + s->offset) = fp;
    // object: UNMOVERABLE인 page의 가상 주소
}
  • (void *)(object + s->offset) = fp
  • object + s->offset를 더한 주소에 fp를 넣는다.
  • 즉 next를 가리키는 구조가 된다.
  • |0x10001000|data|0x10001040|data|0x10000080|data|…|null|data|화
  • 한 페이지당 60Byte * 60 = 3840 Byte, 사용가능.
  • last에 p주소를 매핑함.
  • for_each_object가 하는일:
    • 다음 object의 시작주소를 이전 object의 내부에 저장을 하는데.
    • 그 위치는 s->offset에 의해 결정되어 저장됨.

slub.c::new_slab()

    for_each_object(p, s, start, page->objects) {...}
    // for_each_object가 하는일:
    // 다음 object의 시작주소를 이전 object의 내부(freepointer) 에 저장을 하는데
    // 그 위치는 s->offset에 의해 결정되어 저장됨
    // 예시:
    // |0x10001000|data 60Byte |0x10001040|data 60Byte |0x10000080|data 0x60Byte |...|null|data|

    // s: &boot_kmem_cache_node
    // page : UNMOVEABLE인 page
    // last: UNMOVEABLE인 page의 가상 주소 + 0x1000 - 64
    setup_object(s, page, last);
    set_freepointer(s, last, NULL);

    // 마지막 object의 freepoint는 null 로 초기화

    // page->freelist: UNMOVEABLE인 page의 가상 주소.
    // page의 freelist멤버는 slab의 oject의 시작 주소를 가리킴.
    page->freelist = start;
    // page->objects : 64
    page->inuse = page->objects;
    // page->inuse : 64
    //
    // page의 object 멤버는 slab의 object의 총 갯수
    // page의 inuse 멤버는 slab의 free상태인 object의 총 갯수
    page->frozen = 1;
    // page->frozen : 1
out:
    return page;
}
  • net_slab()이 한일
  • migratype이 MIGRATE_UNMOVEABLE인 page 를 할당 받았고,
  • page->flags의 NR_PAGEFLAGS를 지우고 (PG_slab) bit를 set
  • slab 의 object들의 freepoint를 매핑 함.
  • kmem_cache_node가 완성된 이후에 nr_slabs, total_objects가 증가
  • page->freelist : UNMOVEABLE인 page의 가상주소,
  • page->inuse: 64로 설정함
  • page->frozen : 1로 설정함

slub.c::early_kmem_cache_node_alloc()

// node: 0
static void early_kmem_cache_node_alloc(int node)
{
...
    page = new_slab(kmem_cache_node, GFP_NOWAIT, node);

    // page: UNMOVABLE인 page
    BUG_ON(!page);

    // page: UNMOVABLE인 page, page_to_nid(UNMOVABLE인 page): 0, node: 0
    // if: false
    if (page_to_nid(page) != node) {
        printk(KERN_ERR "SLUB: Unable to allocate memory from "
                "node %d\n", node);
        printk(KERN_ERR "SLUB: Allocating a useless per node structure "
                "in order to be able to continue\n");
    }

    // page->freelist: UNMOVABLE인 page 의 object시작 가상주소
    n = page->freelist;
    // n: page->freelist: UNMOVABLE인 page 의 object시작 가상주소

    BUG_ON(!n);

    // kmem_cache_node : &boot_kmem_cache_node
    // n: page->freelist: UNMOVABLE인 page 의 object시작 가상주소
    page->freelist = get_freepointer(kmem_cache_node, n);

slub.c::get_freepointer()

// ARM10C 20140531
// kmem_cache_node: &boot_kmem_cache_node,
// n: UNMOVABLE인 page 의 object의 시작 virtual address
static inline void *get_freepointer(struct kmem_cache *s, void *object)
{
    // object: UNMOVABLE인 page 의 object의 시작 virtual address
    // s->offset: (&boot_kmem_cache_node)->offset: 0
    return *(void **)(object + s->offset);
    // object: UNMOVABLE인 page 의 object의 시작 virtual address
}
gett_freepointer가 하는일
  • 다음 object의 주소를 가져옴.
  • // 할당받았던 slub_object을 처음 주소에 값을 가지고 오기 때문에.
  • // 다음 위치(next)주고가 된다.
  • 다음 freepointer를 가리킨다.

slub.c::early_kmem_cache_node_alloc()

...
    page->freelist = get_freepointer(kmem_cache_node, n);
    // page->freelist: UNMOVABLE인 page 의 object의 시작 virtual address + 64

    // get_freepointer 하는일:
    // 다음 object의 주소를 가져옴

    // page->inuse : 64 
    page->inuse = 1;
    // page의 inuse맴버는 현재 사용하고 있는 object수를 의미함
    // (64)에서 1로 바뀜. (사용중인 slab 수)

    // page->frozen : 1
    page->frozen = 0;
    // page->frozen : 0

    // kmem_cache_node->node : (&boot_kmem_cache_node)->node[0]: NULL
    // n: UNMOVEABLE인 page의 시작 object의 가상 주소
    kmem_cache_node->node[node] = n;
    // kmem_cache_node->node: (&boot_kmem_cache_node)->node[0]:
    //        UNMOVABLE인 page 의 object의 시작 virtual address

#ifdef CONFIG_SLUB_DEBUG // y
     // kmem_cache_node->node[0]:
    // #define SLUB_RED_ACTIVE        0xcc
    init_object(kmem_cache_node, n, SLUB_RED_ACTIVE);

slub.c::init_object()

// ARM10C 20140531
// kmem_cache_node: &boot_kmem_cache_node,
// n: UNMOVABLE인 page 의 object의 시작 virtual address,
// SLUB_RED_ACTIVE: 0xcc
static void init_object(struct kmem_cache *s, void *object, u8 val)
{
    // object: UNMOVABLE인 page 의 object의 시작 virtual address
    u8 *p = object;
    // p: UNMOVABLE인 page 의 object의 시작 virtual address

    // s->flags: (&boot_kmem_cache_node)->flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
    // __OBJECT_POISON: 0x80000000UL
    if (s->flags & __OBJECT_POISON) {
        memset(p, POISON_FREE, s->object_size - 1);
        p[s->object_size - 1] = POISON_END;
    }

    // s->flags: (&boot_kmem_cache_node)->flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
    // SLAB_RED_ZONE: 0x00000400UL
    if (s->flags & SLAB_RED_ZONE)
        memset(p + s->object_size, val, s->inuse - s->object_size);
}

slub.c::early_kmem_cache_node_alloc()

    init_object(kmem_cache_node, n, SLUB_RED_ACTIVE);

    // kmem_cache_node: &boot_kmem_cache_node,
    // n: UNMOVABLE인 page 의 object의 시작 virtual address
    init_tracking(kmem_cache_node, n);

slub.c::init_tracking()

// ARM10C 20140531
// kmem_cache_node: &boot_kmem_cache_node,
// n: UNMOVABLE인 page 의 object의 시작 virtual address
static void init_tracking(struct kmem_cache *s, void *object)
{
    // s->flags: (&boot_kmem_cache_node)->flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
    // SLAB_STORE_USER: 0x00010000UL
    if (!(s->flags & SLAB_STORE_USER))
        return;
        // return 수행

    set_track(s, object, TRACK_FREE, 0UL);
    set_track(s, object, TRACK_ALLOC, 0UL);
}
if문에서 리턴함

slub.c::early_kmem_cache_node_alloc()

    init_tracking(kmem_cache_node, n);

    // n: UNMOVABLE인 page 의 object의 시작 virtual address
    init_kmem_cache_node(n);

slub.c::init_kmem_cache_node()

// ARM10C 20140531
// n: UNMOVEABLE의 page의 object의 시작 virtual address
여기서 할당 받았던 slab을 kmem_cache_node로 초기화를 여기서 하는 과정이다.
static void
init_kmem_cache_node(struct kmem_cache_node *n)
{
    // n: UNMOVABLE인 page 의 object의 시작 virtual address

    n->nr_partial = 0;
    // n->nr_partial: 0

    spin_lock_init(&n->list_lock);
    // kmem_cache_node에 spinlock 맴버 변수를 초기화

    // n->partial: 0
    INIT_LIST_HEAD(&n->partial);
    // n->partial: 리스트 초기화

#ifdef CONFIG_SLUB_DEBUG // CONFIG_SLUB_DEBUG=y
    atomic_long_set(&n->nr_slabs, 0);
    // n->nr_slabs: 0

    atomic_long_set(&n->total_objects, 0);
    // n->total_objects: 0

    INIT_LIST_HEAD(&n->full);
    // n->full: 리스트 초기화
#endif
}
참조 kmem_cache_node의 자료구조
struct kmem_cache_node {
    spinlock_t list_lock;

    unsigned long nr_partial;
    struct list_head partial;
    atomic_long_t nr_slabs;
    atomic_long_t total_objects;
    struct list_head full;
};

slub.c::early_kmem_cache_node_alloc()

    init_kmem_cache_node(n);
    // 할당받은 slab object를 kmem_cache_node로 사용하고,
    // kmem_cache_node의 맴버 필들를 초기화함.
    // n->nr_partial: 0
    // n->list_lock: spinlock 초기화 수행
    // n->partial: 리스트 초기화
    // n->nr_slabs: 0
    // n->total_objects: 0
    // n->full: 리스트 초기화
    // 할당받은 slab object를 kmem_cache_node 로 사용하고
    // kmem_cache_node의 멤버 필드를 초기화함

    // page: UNMOVABLE인 page
    // kmem_cache_node: &boot_kmem_cache_node, node: 0, page->objects: 64
    inc_slabs_node(kmem_cache_node, node, page->objects);

    init_kmem_cache_node(n);

    // kmem_cache_node: &boot_kmem_cache_node, node: 0, page->object:64
    inc_slabs_node(kmem_cache_node, node, page->objects);a

slub.c::inc_slabs_node()

이번에는 n이 값이 있으므로 likely를 실햄함.
// ARM10C 20140531
// s: &boot_kmem_cache_node, page: migratetype이 MIGRATE_UNMOVABLE인 page
// page_to_nid(migratetype이 MIGRATE_UNMOVABLE인 page): 0, page->objects: 64
// ARM10C 20140531
// kmem_cache_node: &boot_kmem_cache_node, node: 0, page->objects: 64
static inline void inc_slabs_node(struct kmem_cache *s, int node, int objects)
{
    // s: &boot_kmem_cache_node, node: 0
    // get_node(&boot_kmem_cache_node, 0): (&boot_kmem_cache_node)->node[0]: NULL
    // s: &boot_kmem_cache_node, node: 0
    // get_node(&boot_kmem_cache_node, 0): (&boot_kmem_cache_node)->node[0]:
    // UNMOVABLE인 page 의 object의 시작 virtual address
    struct kmem_cache_node *n = get_node(s, node);
    // n: &(&boot_kmem_cache_node)->node[0]: NULL
    // n: &(&boot_kmem_cache_node)->node[0]: UNMOVABLE인 page 의 object의 시작 virtual address

    /*
     * May be called early in order to allocate a slab for the
     * kmem_cache_node structure. Solve the chicken-egg
     * dilemma by deferring the increment of the count during
     * bootstrap (see early_kmem_cache_node_alloc).
     */
    // n: &(&boot_kmem_cache_node)->node[0]: NULL
    // n: &(&boot_kmem_cache_node)->node[0]: UNMOVABLE인 page 의 object의 시작 virtual address
    if (likely(n)) {
        // n->slabs: 0
        atomic_long_inc(&n->nr_slabs);
        // n->slabs: 1

        // objects: 64, n->total_objects: 0
        atomic_long_add(objects, &n->total_objects);
        // n->total_objects: 64
    }
    // kmem_cache_node 가 완성된 이후에 nr_slabs, total_objects 가 증가될 것으로 예상됨
    // kmem_cache_node가 완성되었기 때문에 예상대로 증가했음. 
}

slub.c::early_kmem_cache_node_alloc()

    // kmem_cache_node: &boot_kmem_cache_node, node: 0, page->object:64
    inc_slabs_node(kmem_cache_node, node, page->objects);a
    // n->slabs: 1, n->total_objects: 64 로 설정함
    // n: UNMOVABLE인 page 의 object의 시작 virtual address,
    // page: UNMOVABLE인 page, DEACTIVATE_TO_HEAD: 15

    //     15: enum DEACTIVATE_TO_HEAD, Cpu slab was moved to the head of partials 
    add_partial(n, page, DEACTIVATE_TO_HEAD);
}

slub.c::add_partial()

// ARM10C 20140531
// n: UNMOVABLE인 page 의 object의 시작 virtual address,
// page: UNMOVABLE인 page, DEACTIVATE_TO_HEAD: 15
static inline void add_partial(struct kmem_cache_node *n,
                struct page *page, int tail)
{
    // n: UNMOVABLE인 page 의 object의 시작 virtual address,

    // n->nr_partial: 0
    n->nr_partial++;
    // n->nr_partial: 1

    // tail: DEACTIVATE_TO_HEAD: 15, DEACTIVATE_TO_TAIL: 16
    if (tail == DEACTIVATE_TO_TAIL)
        list_add_tail(&page->lru, &n->partial);
    else
        // page->lru: (UNMOVABLE인 page)->lru, n->partial: NULL
        list_add(&page->lru, &n->partial);
        // n->partial에 (UNMOVABLE인 page)->lru 가 추가됨
}

slub.c::early_kmem_cache_node_alloc()

    add_partial(n, page, DEACTIVATE_TO_HEAD);
    // kmem_cache_node의 partial 맴버의 현재 page의 lru 리스트를 추가함
}

slub.c::init_kmem_cache_node()

// s: &boot_kmem_cache_node
static int init_kmem_cache_nodes(struct kmem_cache *s)
{
    int node;

    // N_NORMAL_MEMORY: 2
    for_each_node_state(node, N_NORMAL_MEMORY) {
    // for ( (node) = 0; (node) == 0; (node) = 1)

        struct kmem_cache_node *n;

        // slab_state: DOWN: 1
        if (slab_state == DOWN) {
            // node: 0
            early_kmem_cache_node_alloc(node);
            // 
            continue;
        }
        ...
    }
    return 1;
    // return 수행
}
  • early_kmem_cache_node_alloc이 한일
    • new_slab()
      • migratetype이 MIGRATE_UNMOVEABLE인 page를 할당 받음.
      • page->flags에 7(PG_slab) bit를 초기화
      • slab의 objects들을 freepointer를 맵핑함.
      • page->freelist: UNMOVEABLE인 page의 object의 시작 가상 주소 + 64
    • init_kmem_cache_node()
      • n->nr_partial : 1
      • n->list_lock : spinglock 초기화함
      • kmem_cache_node->slabs: 1, kmem_cache_node->total_objects: 64 로 세팀함
      • n->partial : 리스트 초기화
      • n->nr_slabs: 0
      • n->slabs: 1,
      • kmem_cache_node->total_objects: 64
      • kmem_cache_node->full: 리스트 초기화
      • kmem_cache_node의 partial 맴버에 현재 page의 lru 리스트를 추가함
  • init_kmem_cache_nodes가 한일.
    • kmem_cache_alloc_node로 할당 받고,
    • free_kmem_cache_nodes로 프리시킨다.

slub.c::kmem_cache_open()

    // s: &boot_kmem_cache_node, init_kmem_cache_nodes(&boot_kmem_cache_node): 1
    if (!init_kmem_cache_nodes(s))
        goto error;

    // s: &boot_kmem_cache_node
    if (alloc_kmem_cache_cpus(s))
        return 0;

slub.c::alloc_kmem_cache_cpus()

// s: &boot_kmem_cache_node
static inline int alloc_kmem_cache_cpus(struct kmem_cache *s)
{
    // PERCPU_DYNAMIC_EARLY_SIZE: 0x3000, KMALLOC_SHIFT_HIGH: 13
    // sizeof(struct kmem_cache_cpu): 16 bytes
    BUILD_BUG_ON(PERCPU_DYNAMIC_EARLY_SIZE <
            KMALLOC_SHIFT_HIGH * sizeof(struct kmem_cache_cpu));

    /*
     * Must align to double word boundary for the double cmpxchg
     * instructions to work; see __pcpu_double_call_return_bool().
     */
    // s->cpu_slab: (&boot_kmem_cache_node)->cpu_slab
    // sizeof(struct kmem_cache_cpu): 16 bytes
    // __alloc_percpu(16, 8)
    s->cpu_slab = __alloc_percpu(sizeof(struct kmem_cache_cpu),
                     2 * sizeof(void *));

percpu.c:__alloc_percpu()

// ARM10C 20140531
// __alloc_percpu(16, 8)
void __percpu *__alloc_percpu(size_t size, size_t align)
{
    // size: 16, align: 8
    return pcpu_alloc(size, align, false);
}

percpu.c::*pcpu_alloc()

// ARM10C 20140531
// size: 16, align: 8, false
static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
{
    static int warn_limit = 10;
    // warn_limit: 10
    struct pcpu_chunk *chunk;
    const char *err;
    int slot, off, new_alloc;
    unsigned long flags;
    void __percpu *ptr;

    // size: 16, align: 8, PCPU_MIN_UNIT_SIZE: 0x8000, PAGE_SIZE: 0x1000
    // if: false
    if (unlikely(!size || size > PCPU_MIN_UNIT_SIZE || align > PAGE_SIZE)) {
        WARN(true, "illegal size (%zu) or align (%zu) for "
             "percpu allocation\n", size, align);
        return NULL;
    }

    mutex_lock(&pcpu_alloc_mutex);
    // pcpu_alloc_mutex의 mutex lock을 수행

    spin_lock_irqsave(&pcpu_lock, flags);
    // pcpu_lock의 spiclock 을 수행하고 cpsr을 flags에 저장

    /* serve reserved allocations from the reserved chunk if available */
    // reserved: false, pcpu_reserved_chunk: pcpu_setup_first_chunk()함수에서 할당한 schunk
    // if: false
    if (reserved && pcpu_reserved_chunk) {
       ...
    }
    ...

restart:
    /* search through normal chunks */
    // size: 16, pcpu_size_to_slot(16): 1, pcpu_nr_slots: 15
    for (slot = pcpu_size_to_slot(size); slot < pcpu_nr_slots; slot++) {

percpu.c::pcpu_size_to_slot()

// ARM10C 20140301
// pcpu_size_to_slot(size: 0x3000)
// ARM10C 20140531
// size: 16
static int pcpu_size_to_slot(int size)
{
    // size: 0x3000, pcpu_unit_size : 0x8000
    // size: 16, pcpu_unit_size : 0x8000
    if (size == pcpu_unit_size)
        return pcpu_nr_slots - 1;

    // size: 0x3000
    // size: 16
    return __pcpu_size_to_slot(size);
    // __pcpu_size_to_slot(0x3000): 11
    // __pcpu_size_to_slot(16): 1
}

percpu.c::__pcpu_size_to_slot()

// pcpu_size_to_slot(size: 0x8000)
// 
pcpu_size_to_slot(size: 0x3000)
static int __pcpu_size_to_slot(int size)
{    
    // size: 0x8000
    // size: 0x3000
    int highbit = fls(size);    /* size is in bytes */
    // highbit = 16
    // highbit = 14

    // PCPU_SLOT_BASE_SHIFT: 5
    return max(highbit - PCPU_SLOT_BASE_SHIFT + 2, 1);
    // max(13, 1) = 13
    // max(11, 1) = 11
}

percpu.c::*pcpu_alloc()

restart:
    /* search through normal chunks */
    // size: 16, pcpu_size_to_slot(16): 1, pcpu_nr_slots: 15
    for (slot = pcpu_size_to_slot(size); slot < pcpu_nr_slots; slot++) {

        // slot: 1~10
        // list_for_each_entry 의 &chunk->list != (&pcpu_slot[slot]) 조건에 의해
        // 수행 되지 않음

        list_for_each_entry(chunk, &pcpu_slot[slot], list) {
  • list_for_each_entry(chunk, &pcpu_slot[slot], list) 를 정의를 풀어보자.
    // #define list_for_each_entry(pos, head, member) \
    // for (pos = list_first_entry(head, typeof(pos), member); \
    // &pos->member != (head); \
    // pos = list_next_entry(pos, member))
    //
    // for (chunk = list_first_entry(&pcpu_slot[1], typeof(
    chunk), list);
    // &chunk->list != (&pcpu_slot[1]); chunk = list_next_entry(chunk, list))
    //
    // list_first_entry(&pcpu_slot[1])
    //
    // #define list_first_entry(ptr, type, member) \
    // list_entry((ptr)->next, type, member)
    //
    // list_first_entry(&pcpu_slot[1], typeof(chunk), list):
    // list_entry(&pcpu_slot[1]->next, typeof(
    chunk), list)
    //
    // #define list_entry(ptr, type, member) \
    // container_of(ptr, type, member)
    //
    // container_of(&pcpu_slot[1]->next, typeof(*chunk), list)
  • list_for_each_entry(chunk, &pcpu_slot[slot], list) {
    // for (chunk = list_first_entry(&pcpu_slot[1], typeof(*chunk), list);
    // &chunk->list != (&pcpu_slot[1]); chunk = list_next_entry(chunk, list))
slot 1~10까지 list_for_each_entry는
&chunk->list != (&pcpu_slot[1])의 조건에 의해서
수행되지 않는다.
slot이 11일때 (dchunk로 값을 넣었음)
    for (slot = pcpu_size_to_slot(size); slot < pcpu_nr_slots; slot++) {
    // pcpu_size_to_slot: size:16
    // slot: 1
        list_for_each_entry(chunk, &pcpu_slot[slot], list) {
        // for (chunk = list_first_entry(&pcpu_slot[1], typeof(*chunk), list);
        //       &chunk->list != (&pcpu_slot[1]); chunk = list_next_entry(chunk, list))

            // chunk: &pcpu_slot[11]

            // size: 16, chunk->contig_hint: &pcpu_slot[11]->contig_hint : 0x3000
            // 
            if (size > chunk->contig_hint)
                continue;

            new_alloc = pcpu_need_to_extend(chunk);

percpu.c::pcpu_need_to_extend()

// ARM10C 20140531
// chuck: &pcpu_slot[11]: dchunk: 4K만큼 할당 받은 주소
static int pcpu_need_to_extend(struct pcpu_chunk *chunk)
{
    int new_alloc;

    // chunk->map_alloc: dchunk->map_alloc: 128,
    // chunk->map_used: dchunk->map_used: 2
    if (chunk->map_alloc >= chunk->map_used + 2)
        return 0;
        // return 0 수행

    new_alloc = PCPU_DFL_MAP_ALLOC;
    while (new_alloc < chunk->map_used + 2)
        new_alloc *= 2;

    return new_alloc;
}

percpu.c::*pcpu_alloc()

    for (slot = pcpu_size_to_slot(size); slot < pcpu_nr_slots; slot++) {

        // slot: 1~10
        // list_for_each_entry 의 &chunk->list != (&pcpu_slot[slot]) 조건에 의해
        // 수행 되지 않음

        list_for_each_entry(chunk, &pcpu_slot[slot], list) {
        // for (chunk = list_first_entry(&pcpu_slot[slot], typeof(*chunk), list);
        //      &chunk->list != (&pcpu_slot[slot]); chunk = list_next_entry(chunk, list))

            // chuck: &pcpu_slot[11]

            // size: 16, chunk->contig_hint: (&pcpu_slot[11])->contig_hint: 0x3000
            if (size > chunk->contig_hint)
                continue;

            // chuck: &pcpu_slot[11]: dchunk: 4K만큼 할당 받은 주소
            new_alloc = pcpu_need_to_extend(chunk);
            // new_alloc: 0

            if (new_alloc) {
                spin_unlock_irqrestore(&pcpu_lock, flags);
                if (pcpu_extend_area_map(chunk,
                             new_alloc) < 0) {
                    err = "failed to extend area map";
                    goto fail_unlock_mutex;
                }
                spin_lock_irqsave(&pcpu_lock, flags);
                /*
                 * pcpu_lock has been dropped, need to
                 * restart cpu_slot list walking.
                 */
                goto restart;
            }
            // chunk: & 
            off = pcpu_alloc_area(chunk, size, align);
            if (off >= 0)
                goto area_found;
        }
    }

percpu.c::pcpu_alloc_area()

// ARM10C 20140531
// chuck: &pcpu_slot[11]: dchunk: 4K만큼 할당 받은 주소, size: 16, align: 8
static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align)
{
    // chuck: &pcpu_slot[11]: dchunk: 4K만큼 할당 받은 주소
    // pcpu_chunk_slot(&pcpu_slot[11]): 11
    int oslot = pcpu_chunk_slot(chunk);
    // oslot: 11
    int max_contig = 0;
    // max_contig: 0
    int i, off;

    // chunk->map_used: dchunk->map_used: 2,
    // chunk->map[0]: dchunk->map[0]: -(__per_cpu 실제 할당한 size + 0x2000)
    for (i = 0, off = 0; i < chunk->map_used; off += abs(chunk->map[i++])) {
        // [loop 1] i: 0, chunk->map_used: dchunk->map_used: 2,
        // [loop 2] i: 1, chunk->map_used: dchunk->map_used: 2,
        bool is_last = i + 1 == chunk->map_used;
        // [loop 1] is_last: 0
        // [loop 2] is_last: 1
        int head, tail;

        /* extra for alignment requirement */
        // [loop 1] off: 0, align: 8
        // [loop 2] off: __per_cpu 실제 할당한 size + 0x2000, align: 8
        head = ALIGN(off, align) - off;
        // [loop 1] head: 0
        // [loop 2] head: 0

        // [loop 1] i: 0, head: 0
        // [loop 2] i: 0, head: 0
        BUG_ON(i == 0 && head != 0);

        // [loop 1] i: 0, chunk->map[0]: -(__per_cpu 실제 할당한 size + 0x2000)
        // [loop 2] i: 1, chunk->map[1]: 0x3000
        if (chunk->map[i] < 0)
            continue;

        // [loop 2] i: 1, chunk->map[1]: 0x3000, head: 0, size: 16
        if (chunk->map[i] < head + size) {
            max_contig = max(chunk->map[i], max_contig);
            continue;
        }

        /*
         * If head is small or the previous block is free,
         * merge'em.  Note that 'small' is defined as smaller
         * than sizeof(int), which is very small but isn't too
         * uncommon for percpu allocations.
         */
        // [loop 2] i: 1, chunk->map[0]: -(__per_cpu 실제 할당한 size + 0x2000),
        // [loop 2] head: 0, size: 16
        if (head && (head < sizeof(int) || chunk->map[i - 1] > 0)) {
            if (chunk->map[i - 1] > 0)
                chunk->map[i - 1] += head;
            else {
                chunk->map[i - 1] -= head;
                chunk->free_size -= head;
            }
            chunk->map[i] -= head;
            off += head;
            head = 0;
        }

        /* if tail is small, just keep it around */
        // [loop 2] i: 1, chunk->map[1]: 0x3000, head: 0, size: 16
        tail = chunk->map[i] - head - size;
        // [loop 2] tail: 0x2ff0

        // [loop 2] tail: 0x2ff0, sizeof(int): 4
        if (tail < sizeof(int))
            tail = 0;

// 2014/05/31 종료

git log

Updating c43e50c..ce36365
Fast-forward
include/asm-generic/atomic-long.h | 1 +
include/linux/compiler.h | 1 +
include/linux/kernel.h | 1 +
include/linux/list.h | 10 
include/linux/mm.h | 1 +
include/linux/page-flags.h | 10 

include/linux/percpu.h | 2 +
include/linux/poison.h | 1 +
include/linux/slab.h | 10 +-
include/linux/slub_def.h | 7 +-
mm/highmem.c | 2 +
mm/percpu.c | 83 ++++-
mm/slab.h | 4 +-
mm/slub.c | 255 +-
14 files changed, 378 insertions(+), 10 deletions(-)

댓글 없음:

댓글 쓰기