[커널 17차] 77 ~ 80주차

2022.03.13 16:36

ㅇㅇㅇ 조회 수:102

kmem_cache_open()
- L4203 부터 계속
- slab_state가 현재는 DOWN이므로 freelist randomize는 여기서 수행하지 않는다
- 함수 init_kmem_cache_nodes()으로 node partial list 구조를 생성한다
- 함수 alloc_kmem_cache_cpus() cpu freelist, page, partial list 구조를 생성한다
- 만약 init_kmem_cache_nodes() 함수가 실패하여 node partial list 생성이 안되면 함수 __kmem_cache_release()로 할당된 메모리를 해제하고 에러 리턴하는 것으로 보임

 

init_kmem_cache_nodes()
- 인자로 kmem_cache 구조체를 받는다
- 모든 slab_nodes에 대해서 루프를 돌면서 각 노드에 대해 다음을 수행한다
- 현재는 slab_state == DOWN이므로 함수 early_kmem_cache_node_alloc()를 수행해서 각 노드에게 partial list를 만들어 준다
- 함수 early_kmem_cache_node_alloc()는 buddy allocator를 이용하여 node partial list를 생성한다
- 함수 early_kmem_cache_node_alloc()는 인자로 들어온 kmem_cache 구조체를 사용하지 않고 각 노드만 인자로 받아서 처리한다
- 왜냐하면 slab page를 위한 자료구조는 전역 변수 kmem_cache_node를 사용하기 때문이다
- 모든 노드에 대해 함수 early_kmem_cache_node_alloc()를 수행했으면 1을 리턴하고 종료한다

 

early_kmem_cache_node_alloc()
- 아직 node partial list가 없을 때 최초의 node partial list를 생성하기 위한 함수이다
- Buddy system을 이용해서 각 노드의 node partial list를 만들어 준다
- 전역 변수 kmem_cache_node->size (object size)가 구조체 kmem_cache_node 크기보다 작으면 버그
- 함수 new_slab()로 buddy system으로 부터 kmem_cache_node를 위한 메모리를 page로 할당 받는다
- 할당 받은 kmem_cache_node의 slab page를 이용해서 현재 노드의 kmem_cache_node slab cache의 node partial list를 구성한다
- 먼저 page->freelist에서 첫 번째 object를 n으로 가져오고, 함수 init_object(), init_tracking()으로 초기화한다
- kasan_slab_alloc()으로 kasan 관련 처리를 한다
- page->freelist는 두 번째 object를 가리키게 하고, 첫 번째 object는 바로 사용할 예정이다
- page->inuse = 1로 하여 1개 object를 사용하고 있음을 설정한다
- page->frozen = 0으로 하여 현재 slab page는 node partial list에 달려 있음을 설정한다
- kmem_cache_node[node] = n으로 첫 번째 object n을 달아준다
- 함수 init_kmem_cache_node()로 kmem_cache_node 구조체 n의 nr_partial = 0, list_lock 초기화, n->partial list 초기화를 수행한다. CONFIG_SLUB_DEBUG = y이면 nr_slabs, total_objects, n->full list head도 초기화 해준다
- 함수 inc_slabs_node()로 n->nr_slabs를 1 증가시키고, n->total_objects를 page->objects 만큼 증가시킨다. 즉 현재 node partial list는 slab page 1개가 달려 있고, object의 개수는 page->object 만큼 존재한다
- 함수 __add_partial()로 n->nr_partial을 1 증가시키고 n->partial에 slab page를 달아준다
- 리턴한다

 

new_slab()
- 인자로 kmem_cache 구조체, flags, node를 받는다
- 인자로 들어온 flags에 DMA32, HIGHMEM이 있으면 함수 kmalloc_fix_flags()로 해당 플래그를 지워 주고 stack dump를 수행한다
- constructor가 있는데 flags에 GFP_ZERO가 있으면 경고를 출력한다
- 함수 allocate_slab()으로 flags 중 GFP_RECLAIM_MASK와 GFP_CONSTRAINT_MASK만 넘겨주고 slab page를 메모리로 할당받는다

 

allocate_slab()
- 인자로 kmem_cache 구조체 s, flags, node를 받는다
- 인자로 들어온 flags에서 __GFP_RECLAIM, __GFP_IO, __GFP_FS 플래그는 지워준다
- flags에 s->allocflags의 플래그들을 추가한다
- 우선 high order allocation을 시도한다. 이 때 플래그를 __GFP_NOWARN, __GFP_NORETRY를 추가하고 __GFP_NOFAIL은 지워준다 (실패 가능하고 재시도 필요없음)
- 또한 플래그에 __GFP_DIRECT_RECLAIM이 있고 object order가 s->min order보다 크면 __GFP_RECLAIM을 지우고 __GFP_NOMEMALLOC을 추가한다 (__GFP_MEMALLOC : 곧 대량의 메모리를 해제할 것이니 모든 reserve에 접근 가능하도록 요청하는 것)
- 이 플래그를 이용하여 함수 alloc_slab_page()로 메모리 할당 요청한다
- 즉 우선 high order allocation을 실패 가능하도록 설정하여 한 번 시도하는 것이다
- 실패할 경우 object order를 s->min으로 낮추고 플래그도 원래 flags로 원복시킨 다음 다시 함수 alloc_slab_page()로 메모리 할당 요청한다. 이 때 실패하면 NULL 리턴한다
- 할당 성공하면 page 변수로 메모리 공간이 할당된다
- page->objects에 object 수를 기록한다
- 함수 account_slab_page()로 메모리 할당과 관련된 memcg cgroup 등의 처리를 하고, 함수 mod_node_page_state()를 이용해서 pcpu stat 업데이트를 수행한다
- page->slab_cache에 인자 kmem_cache s를 기록한다
- 함수 __SetPageSlab()으로 page flag 중 Slab flag를 set한다
- 함수 page_is_pfmemalloc()으로 현재 page가 pfmemalloc page인지 체크하고 맞으면 함수 SetPageSlabPfmemalloc()으로 page flag의 active flag에 1을 세팅한다. pfmemalloc page는 emergency 영역을 의미한다. 또한 page flag 중 active flag가 pfmemalloc을 의미한다 (slab에서는 active를 쓰지 않는다)
- 함수 kasan_poison_slab()로 KASAN을 사용하는 경우 kasan 영역에 값을 채워준다
- 함수 page_address()로 할당 받은 slab page의 시작 주소 (linear mapping 가상 주소)를 받아온다.
- 함수 setup_page_debug()로 SLAB_POSION이 플래그에 설정되어 있는 경우 slab page 전체에 POISON_INUSE를 poisoning한다
- 함수 shuffle_freelist()로 해당 페이지의 freelist 순서를 randomize하고 그 결과를 shuffle로 리턴한다. 현재는 randomize하지 않으므로 shuffle = NULL이다
- L1938에서 shuffle == NULL이므로 if 문이 수행된다
- 함수 fixup_red_left()로 left redzone 고려해서 object 시작주소를 가져온다
- 함수 setup_object()로 redzone, object, user tracking 영역에 poisoning 값 등을 채워준다
- page->freelist를 object의 free pointer를 이용해서 첫 object부터 마지막 object까지 연결해준다
- 일단 현재 할당받은 slab page는 cpu로 할당될 것을 예상하고 page->inuse는 최대값인 page->objects로 할당하고, page->frozen도 1로 할당한다
- page == NULL이면 에러 리턴
- 함수 inc_slabs_node()를 호출하지만 현재는 아무것도 하지 않는다. 이는 아직 kmem_cache->node == NULL이기 때문이다
- 할당 받은 page를 리턴한다

 

alloc_slab_page()
- 현재 node 명시된 상태에서 호출했으므로 함수 __alloc_pages_node()를 불러서 버디 시스템에서 메모리를 요청한다
- __alloc_pages_node()는 __alloc_pages()를 호출해서 버디 시스템에서 메모리를 요청한다
- __alloc_pages()는 버디 시스템의 핵심 함수로 메모리 할당을 수행한다

 

mod_node_page_state()
- 함수 mod_node_state()를 호출하여 pcpu 스탯을 업데이트 한다

 

mod_node_state()
- pcpu stat 정보를 delta 만큼 업데이트하고, 만약 threshold 보다 pcpu 값이 커지면 글로벌 변수에 값을 더한다
- 이 함수는 delta 값이 page 단위로 처리되는데, L605에서 delta 단위가 byte 단위로 입력되는 경우에는 page 단위로 다시 바꿔 준다. 즉 delta는 byte 단위이지만 pcpu 및 전역 변수는 page 단위로 기록된다.
- Delta가 byte 단위로 입력되는 통계는 두 가지다. NR_SLAB_RECLAIMABLE_B와 NR_SLAB_UNRECLAIMABLE_B이다.
- 경우에 따라 overstep으로 추가적으로 업데이트하는 경우도 있는데 이 경우에도 전역 변수와 pcpu 변수의 합은 일정하게 유지된다

 

setup_object()
- 함수 setup_object_debug()로 object, redzone, user tracking 영역에 값을 채워준다
- constructor가 설정되어 있으면 constructor를 불러주고 object를 리턴한다

 

setup_object_debug()
- 함수 init_object()로 redzone, object 값을 posioning한다
- 또한 함수 init_tracking()으로 user tracking 영역을 모두 0으로 채워준다

 

init_object()
- left redzone, right redzone 값을 redzone 설정값으로 채워준다
- 또한 __OBJECT_POISON = true인 경우에는 object 본체를 POSION_FREE 값으로 채우고 object 마지막 바이트에는 POISON_END를 채워준다

 

alloc_kmem_cache_cpus()
- PERCPU_DYNAMIC_EARLY_SIZE가 kmem_cache_cpu x (PAGE_SHIFT + 1) 보다 작으면 빌드 버그
- 함수 __alloc_percpu()로 kmem_cache_cpu 크기만큼 16B align으로 percpu 영역을 s->cpu_slab에 할당받는다
- 할당 실패하면 0 리턴
- 성공하면 함수 init_kmem_cache_cpus()로 각 cpu에 대해서 c->tid를 cpu값으로 초기화한다
- 1을 리턴한다

 

__kmem_cache_create()
- L4883부터
- slab_state == DOWN이므로 0을 리턴한다

 

create_boot_cache()
- L662부터
- s->refcount = -1로 하고 리턴

 

kmem_cache_init()
- L4817부터
- 함수 register_hotmemory_notifier()로 notifier block slab_memory_callback_nb을 등록한다. 여기에는 함수 slab_memory_callback()가 콜백으로 달려있다
- slab_state를 PARTIAL로 세팅한다
- 함수 create_boot_cache()로 kmem_cache에 대한 kmem_cache를 생성한다 <— 여기까지 진행

 

create_boot_cache()
- 두 번째
- 모두 동일하고 함수 __kmem_cache_create() 호출한다

 

__kmem_cache_create()
- 두 번째
- 모두 동일하고 함수 kmem_cache_open() 호출

 

kmem_cache_open()
- 두 번째
- 모두 동일하고 함수 init_kmem_cache_nodes() 호출

 

init_kmem_cache_nodes()
- 두 번째
- L3962에서 slab_state == PARTIAL이므로 함수 kmem_cache_alloc_node()으로 kmem_cache_node slab object를 받아온다 (node partial list를 받아옴)
- L3974 : 함수 init_kmem_cache_node()로 node list head와 node partial list 등을 초기화한다
- L3975 : s->node[node]에 kmem cache node slab object n을 달아준다
- L3977 : 1을 리턴한다

 

kmem_cache_alloc_node()
- 함수 slab_alloc_node()로 node partial list에서 slab object를 할당 받아 온다

 

slab_alloc_node()
- 인자로 kmem_cache s와 flags, node, addr, object size를 받는다
- Slab allocator로부터 slab object를 할당 받는다
- 함수 slab_pre_alloc_hook()로 memcg 관련 부분을 처리한다
- 함수 kfence_alloc()로 kfence 관련 기능 처리 수행
- cpu_slab을 c로 가져오고 c->tid를 tid로 가져온다
- barrier()를 수행한다
- RT kernel이거나, c->freelist == NULL 또는 c->page == NULL 또는 page와 node의 node mismatch가 있으면 함수 __slab_alloc()로 이동해서 slowpath로 slab object를 할당 받는다
- 아니면 fast path로 slab object를 할당받는다
- c->freelist 다음의 object를 next_object로 받아온다
- cmpxchg_double() 함수로 c->freelist를 next_object로, c->tid를 next_tid로 변경한다
- 즉 freelist에서 첫번째 object를 넘겨주는 것이다
- next_tid는 cpu 번호부터 시작해서 TID_STEP 만큼 증가한다. TID_STEP은 NR_CPUS를 power of two 만큼 증가시킨 것이다
- 이후 함수 prefetch_freepointer()로 next_object의 free pointer를 cache로 prefetch한다
- 함수 maybe_wipe_obj_freeptr()로 만약 init on free 기능이 on되어 있는 경우 object의 free pointer 영역을 0으로 만든다
- 함수 slab_want_init_on_alloc()로 인자로 들어온 gfp flags에 따라서 init이 필요한지 아닌지 정한다. 즉 zeroing이 필요한지 아닌지 정한다
- 함수 slab_post_alloc_hook()로 object 영역에 init이 필요하면 0으로 만들고, memcg 관련 부분을 처리한다
- 할당받은 slab object를 리턴한다

 

__slab_alloc()
- 함수 slub_get_cpu_ptr()로 preempt를 disable한다
- 함수 ___slab_alloc() slab object를 할당받는다
- 함수 slub_put_cpu_ptr()로 preempt를 enable한다
- 할당받은 slab object를 리턴한다

 

___slab_alloc()
- slab allocator에서 인자로 요청된 slab cache type (kmem_cache)에 맞는 slab object를 받아서 리턴한다
- 4가지 경우로 나누어 생각한다
1) c->freelist는 고갈되었으나 c->page->freelist에 slab object가 남아있는 경우
2) c->freelist와 c->page->freelist 모두 고갈되고 c->partial에 slab page가 남아 있는 경우
3) c->freelist, c->page->freelist, c->partial 모두 고갈되고 n->partial에 slab page 존재하는 경우
4) c->freelist, c->page->freelist, c->partial 모두 고갈되고 n->partial에서도 slab page 못 찾는 경우

 

1) c->freelist는 고갈되었으나 c->page->freelist에 slab object가 남아있는 경우
- L2887 : c->page를 가져온다.
- L2900 : node match 및 pfmalloc match 여부를 체크한다
- L2925 : IRQ save & disable
- L2930 : c->freelist를 가져온다. 고갈되었으므로 NULL
- L2931 : freelist = NULL이므로 다음으로 진행
- L2934 : 함수 get_freelist()로 page에서 새로운 freelist를 받아온다
- L2936 : freelist가 NULL이 아니므로 if 문은 패스
- L2955 : 함수 get_freepointer()로 freelist의 다음 object를 c->freelist에 연결
- L2956 : 함수 next_tid()로 c->tid를 next tid로 변경
- L2957 : IRQ restore & enable
- L2958 : freelist의 첫 번째 slab object를 리턴하고 종료

 

2) c->freelist와 c->page->freelist 모두 고갈되고 c->partial에 slab page가 남아 있는 경우
- c->page가 NULL인 경우와 아닌 경우로 나뉜다. 결과는 동일하다
- L2887 : c->page를 가져온다. NULL이면 new_slab으로 점프한다
- L2900 : node match 및 pfmalloc match 여부를 체크한다
- L2925 : IRQ save & disable
- L2930 : c->freelist를 가져온다. 고갈되었으므로 NULL
- L2931 : freelist = NULL이므로 다음으로 진행
- L2934 : 함수 get_freelist()로 page에서 새로운 freelist를 받아온다
- L2936 : page->freelist가 NULL이므로 c->page = NULL로 만들고 IRQ restore & enable 이후 new_slab으로 점프
- L2973 : new_slab
- L2975 : 함수 slub_percpu_partial()로 c->partial list에 slab page가 있는지 체크
- L2976 : IRQ save & disable
- L2987 : page와 c->page에 c->partial의 첫 slab page를 가져옴
- L2988 : 매크로 slub_set_percpu_partial()로 c->partial에 c->page->next를 넣는다. 즉 첫 번째 slab page를 제거한다
- L2989 : IRQ restore & enable
- L2991 : redo로 이동한다
- L2900 : node match 및 pfmalloc match 여부를 체크한다
- L2925 : IRQ save & disable
- L2930 : c->freelist를 가져온다. 고갈되었으므로 NULL
- L2931 : freelist = NULL이므로 다음으로 진행
- L2934 : 함수 get_freelist()로 page에서 새로운 freelist를 받아온다
- L2936 : freelist가 NULL이 아니므로 if 문은 패스
- L2955 : 함수 get_freepointer()로 freelist의 다음 object를 c->freelist에 연결
- L2956 : 함수 next_tid()로 c->tid를 next tid로 변경
- L2957 : IRQ restore & enable
- L2958 : freelist의 첫 번째 slab object를 리턴하고 종료

 

3) c->freelist, c->page->freelist, c->partial 모두 고갈되고 n->partial에 slab page 존재하는 경우
- c->page가 NULL인 경우와 아닌 경우로 나뉜다. 결과는 동일하다
- L2887 : c->page를 가져온다. NULL이면 new_slab으로 점프한다
- L2900 : node match 및 pfmalloc match 여부를 체크한다
- L2925 : IRQ save & disable
- L2930 : c->freelist를 가져온다. 고갈되었으므로 NULL
- L2931 : freelist = NULL이므로 다음으로 진행
- L2934 : 함수 get_freelist()로 page에서 새로운 freelist를 받아온다
- L2936 : page->freelist가 NULL이므로 c->page = NULL로 만들고 IRQ restore & enable 이후 new_slab으로 점프
- L2973 : new_slab
- L2975 : 함수 slub_percpu_partial()로 c->partial list에 slab page가 있는지 체크. 없으므로 if 문 패스
- L2996 : 함수 get_partial()로 node partial list로부터 freelist와 slab page를 받아온다
- L2997 : freelist가 존재하므로 check_new_page로 이동
- L3020 : 디버그 설정 아니므로 if 문 패스
- L3033 : pfmemalloc match 체크
- L3042 : IRQ save & disable
- L3043 : c->page = NULL이므로 pass
- L3059 : c->page에 node partial list에서 가져온 첫 번째 slab page를 달아준다 (freelist에 해당하는 page)
- L3061 : load_freelist로 이동한다
- L2955 : c->freelist에 freelist의 두 번째 object를 연결한다
- L2956 : c->tid를 next tid로 업데이트한다
- L2957 : IRQ restore & enable
- L2859 : freelist의 첫 번째 object를 리턴한다

 

4) c->freelist, c->page->freelist, c->partial 모두 고갈되고 n->partial에서도 slab page 못 찾는 경우
- c->page가 NULL인 경우와 아닌 경우로 나뉜다. 결과는 동일하다
- L2887 : c->page를 가져온다. NULL이면 new_slab으로 점프한다
- L2900 : node match 및 pfmalloc match 여부를 체크한다
- L2925 : IRQ save & disable
- L2930 : c->freelist를 가져온다. 고갈되었으므로 NULL
- L2931 : freelist = NULL이므로 다음으로 진행
- L2934 : 함수 get_freelist()로 page에서 새로운 freelist를 받아온다
- L2936 : page->freelist가 NULL이므로 c->page = NULL로 만들고 IRQ restore & enable 이후 new_slab으로 점프
- L2973 : new_slab
- L2975 : 함수 slub_percpu_partial()로 c->partial list에 slab page가 있는지 체크. 없으므로 if 문 패스
- L2996 : 함수 get_partial()로 node partial list로부터 freelist를 받아오나 이 경우에는 NULL
- L3000 : migrate_disable()
- L3001 : 함수 new_slab()으로 buddy system으로부터 slab page 1개를 받아온다
- L3002 : migrate_enable()
- L3004 : buddy system에서도 slab page를 받아오지 못하면 NULL 리턴
- L3013 : freelist에 할당 받은 page->freelist를 넘겨주고 page->freelist는 NULL로 제거
- L3020 : 디버그 아니므로 pass
- L3033 : pfmemalloc match 체크
- L3042 : IRQ save & disable
- L3043 : c->page = NULL이므로 pass
- L3059 : c->page에 할당 받은 slab page인 page를 할당
- L3061 : load_freelist로 이동
- L2955 : c->freelist에 freelist의 두 번째 object 연결
- L2956 : c->tid를 next tid로 업데이트한다
- L2957 : IRQ restore & enable
- L2859 : freelist의 첫 번째 object를 리턴한다

 

get_freelist()
- 인자로 slab cache type인 kmem_cache와 slab page를 받는다
- cmpxchg_double을 이용해서 page->freelist를 NULL로 만들고, page->inuse를 page->objects, page->frozen을 page->freelist 존재여부에 따라 결정한다. page->freelist가 존재하면 frozen, NULL이면 unfrozen이다
- 즉 page->freelist를 모두 떼서 freelist로 반환하고 page->freelist는 NULL로 비어 있게 된다. 그리고 page->freelist에 object가 하나라도 있었으면 page는 frozen이 되고 없었으면 unfrozen이 된다
- page->freelist에서 떼어낸 freelist를 리턴한다

 

get_partial()
- node가 NUMA_NO_NODE가 아니므로 함수 get_partial_node()를 이용해서 slab page와 object를 node partial list로부터 받아온다
- object를 리턴한다. slab page는 page 포인터로 리턴

 

get_partial_node()
- L2119 : kmem_cache node n이 NULL이거나 partial list가 비어 있으면 NULL 리턴
- L2122 : node list lock을 잡고 IRQ save & disable
- L2123 : node->partial list의 각 slab page에 대해서 루프를 돌면서 아래를 수행한다
- L2126 : slab page의 pfmemalloc이 match가 안되면 다음 slab page로 continue
- L2129 : 함수 acquire_slab()로 slab page로부터 object list를 받아오고 slab page를 리턴받는다
- L2130 : 반환된 slab object가 NULL이면 루프를 종료한다
- L2133 : slab page에서 추가된 object 수를 누적한다
- L2134 : 첫 번째 slab page인 경우, slab page를 포인터로 반환하고, slab object list를 리턴하도록 한다
- L2139 : 두 번째 이후 slab page인 경우, 함수 put_cpu_partial()로 slab page를 cpu partial list에 추가한다
- L2142 : cpu partial 자료구조가 없는 config이거나, 이미 추가된 object의 수가 cpu partial 값의 절반 초과이면 루프를 종료한다

- L2147 : 루프가 종료되면 IRQ restore & enable
- L2148 : slab object를 리턴한다

 

acquire_slab()
- cmpxchg_double_slab()을 이용하여 주어진 page로부터 object를 떼어내 반환한다
- 첫 번째 slab page인 경우에는 page->freelist를 NULL로 만들고 page->inuse를 page->objects로 최대로 만든다.
- 두 번째 이후 slab page들의 경우에는 page->freelist는 그대로, page->inuse도 그대로이다
- 두 케이스 모두 page->frozen = 1로 하여 cpu로 slab page를 보낸다는 것을 명시한다
- 함수 remove_partial()로 node partial list로부터 page를 제거한 뒤, nr_partial을 1 감소시킨다
- page->freelist를 리턴한다

 

put_cpu_partial()
- 새로운 slab page를 cpu partial list에 추가한다
- L2551 : IRQ save & disable
- L2553 : cpu partial을 가져온다
- L2556 : drain = 0이므로 else로 이동
- L2564 : pobject에 cpu partial의 첫 slab page의 pobject를 넣고, pages에는 첫 slab page의 pages를 할당한다
- L2570 : pages를 1 증가시키고 pobject를 slab page의 새로 추가되는 object 만큼 증가시킨다
- L2573 : slab page의 pages, pobjects에 pages와 pobjects를 넣는다. 따라서 cpu partial list의 누적된 page 수와 object 수를 기록하는 것이 된다
- L2575 ~ L2577: 새 slab page를 cpu partial list의 맨 앞에 추가한다
- L2579 : IRQ restore & enable
- L2581 : page_to_unfreeze = 0이므로 리턴

 

get_any_partial()
- 현 노드가 아닌 타 노드를 검색해서 node partial list로부터 메모리를 받아온다
- L2161 : gfp zone flags를 parsing해서 메모리를 할당 받아올 highest zone index를 가져온다. 이보다 높은 존에서는 받아오지 않는다 (movable —> normal —> DMA32 —> DMA 순으로 높다)
- L2183 : s->remote_node_defrag_ratio에 따라서 확률적으로 타 노드에서 메모리를 할당할지 결정한다. defrag ratio = 1000인 경우, clock cycle % 1024가 1000보다 크면 타 노드에서 메모리 할당을 하지 않고 그냥 NULL 리턴한다. 반대로 1000보다 같거나 작으면 타 노드에서 메모리를 받아온다. 그러므로 defrag ratio가 작으면 타 노드에서 받아올 확률이 늘어나고 크면 타 노드에서 받아올 확률이 줄어든다
- L2187 ~ L2210 : 개별 cpu와 메모리 노드에 따라 cgroup 설정에 의해 접근이 허용되는 조합이 있고 아닌 조합이 있는 것으로 보인다. 이것이 cpuset인 것 같고 while 문에서 메모리 노드에 따라 현 cpu에서 접근 가능한 조합만 read count lock? 을 이용해서 접근하는 것 같음
- L2189 : 현 노드에서 접근 가능한 fallback zonelist를 받아온다
- L2190 : fallback zonelist에 대해서 루프를 돌면서 아래를 수행한다
- L2193 : 현재 zone에 해당하는 노드를 가져온다
- L2195 : 현재 zone이 cpuset에 따른 접근이 허용되고, 노드의 partial list slab page 수가 s->min_partial보다 크면 함수 get_partial_node()를 이용해서 노드로부터 slab object를 가져와서 리턴한다
- L2212 : while 문이 종료되었음에도 object 리턴이 안되었으면 NULL을 리턴한다

- 버디에서 메모리를 할당 받을 때 alloc flags에 의해서 메모리가 reclaimable/dma32/dma 인지 설정하는 부분이 있었다

 

deactivate_slab()
- c->freelist로부터 object를 제거해서 page->freelist로 연결하고, page를 node partial list로 반환한다. 만약 page에서 사용되고 있는 object가 없고 node partial list에 자리가 없으면 버디로 반환한다. page의 object가 모두 쓰이고 있으면 node full list에 page를 추가한다

- L2317 : page에 해당하는 노드를 가져온다
- L2326 : page->freelist가 있으면 page->freelist 뒤 쪽에 freelist object들을 연결해야 하고, page->freelist가 없으면 freelist object는 page->freelist head에 붙여야 하므로 그에 따라 tail 변수를 설정한다
- L2335 ~ L2352 : 인자로 들어온 freelist object 개수를 세고 freelist_tail이 마지막 object를 가리키게 한다
- L2372 ~ L2383 : freelist에 object가 하나라도 있었으면 page.inuse를 freelist에 있었던 object 수 만큼 감소시킨다. 그리고 page->freelist를 freelist 뒤에 붙이고 page->freelist가 freelist를 가리키도록 한다. 즉 page->freelist 앞에 freelist를 붙여준다. 만약 freelist에 object가 없었으면 page->freelist는 그대로 유지된다
- L2385 : page->frozen = 0으로 설정한다
- L2387 : page.inuse = 0이고 n->nr_parital >= s->min_partial이면 노드로 슬랩 페이지를 반환하는게 아니라 그냥 버디로 반환한다
- L2389 : 그게 아니고 page->freelist가 존재하면 page 일부 object는 사용되고 있거나 node partial list에 자리가 남는 상태이므로 page를 노드로 반환한다. 이를 위해 node list lock을 잡는다
- L2400 : page->freelist가 NULL이면 모든 object가 사용되고 있는 상태이다. 여기서는 STORE USER 디버그 옵션이 켜져 있는 경우에만 node list lock을 잡는데 이는 디버그 옵션에 있는 node full list 업데이를 위해서이다
- L2419 : 일부 업데이트가 필요한 경우 add_partial() 함수로 node partial list에 page를 추가한다
- L2421 : 모든 object가 사용되고 있는 경우에는 node full list에 page를 추가한다
- L2426 : cmpxchg_double_slab() 함수로 page를 업데이트한다
- L2432 : node list lock을 풀어준다
- L2430 : 버디로 page를 반환해야 하는 경우 함수 discard_slab()을 이용해서 반환한다

XE Login