일시 : 2014.05.17 (54주차)
모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
장소 : 토즈 타워점
장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
참여인원 : 4명
스터디 진도 :
- 지난 스터디에 이어서 mem_init()을 계속 분석합니다.
- start_kernel()-> mm_init()->kmem_cache_init()->create_boot_cache() 분석중
- kmem_cache_init()이 분석 중이어서 이번 후기는 내용이 짧습니다.
- 자세한 후기도 그때 다시 정리하려고 합니다.
스터디 주요 내용
- buddy할당자에 이어서 kmem 할당자 (Slub)을 분석중입니다.
- mem_init()->kmem_cache_init()->create_boot_cache()->kmem_cache_create()
->kmem_cache_open()->init_kmem_cache_nodes->early_kmem_cache_node_alloc()
->new_slab()->allocate_slab()->alloc_slab_page()->alloc_pages_exact_node()
->alloc_pages()->__alloc_pages_nodemask();
vm_stat[?]
- vm_stat[0] : number of free pags 수
- vm_stat[1] : batch : chunk size for buddy add/remove
create_boot_cache()
- mem_init()->kmem_cache_init()->create_boot_cache()
err = __kmem_cache_create(s, flags);
- __kmem_cache_create()// s: &boot_kmem_cache_node, flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
err = kmem_cache_open(s, flags);
- kmem_cache_open()// s: &boot_kmem_cache_node
if (!init_kmem_cache_nodes(s)) - init_kmem_cache_nodes
// slab_state: DOWN: 1 if (slab_state == DOWN) { // node: 0 early_kmem_cache_node_alloc(node); continue; }
- early_kmem_cache_node_alloc()
// kmem_cache_node: &boot_kmem_cache_node, GFP_NOWAIT: 0, node: 0
page = new_slab(kmem_cache_node, GFP_NOWAIT, node); - new_slab()// 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);
- allocate_slab()// alloc_gfp: 0x1200, node: 0, oo: boot_kmem_cache_node.oo
page = alloc_slab_page(alloc_gfp, node, oo); - alloc_slab_page()
// node: 0, NUMA_NO_NODE: -1
if (node == NUMA_NO_NODE)return alloc_pages(flags, order);
else// node: 0, flags: 0x201200, order: 0 return alloc_pages_exact_node(node, flags, order);
- alloc_pages_exact_node()// gfp_mask: 0x201200, order: 0, nid: 0,
// node_zonelist(0, 0x201200): contig_page_data->node_zonelists
return __alloc_pages(gfp_mask, order, node_zonelist(nid, gfp_mask)); - __alloc_pages()// gfp_mask: 0x201200, order: 0
// zonelist: contig_page_data->node_zonelists, NULL
return __alloc_pages_nodemask(gfp_mask, order, zonelist, NULL); - __alloc_pages_nodemask();// gfp_mask: 0x201200, GFP_HARDWALL: 0x20000, nodemask: NULL, order: 0
// zonelist: contig_page_data->node_zonelists, high_zoneidx: ZONE_NORMAL: 0
// alloc_flags: 0x41, preferred_zone: (&contig_page_data)->node_zones[0]
// migratetype: MIGRATE_UNMOVABLE: 0
page = get_page_from_freelist(gfp_mask|GFP_HARDWALL, nodemask, order,zonelist, high_zoneidx, alloc_flags, preferred_zone, migratetype);
- get_page_from_freelist()
// zone: contig_page_data->node_zones[0], order: 0, mark: 0 // classzone_idx: 0 alloc_flags: 0x41 // zone_watermark_ok(contig_page_data->node_zones[0], 0, 0, 0, 0x41): 1 if (!zone_watermark_ok(zone, order, mark, classzone_idx, alloc_flags)) {
- zone_watermark_ok()// z: contig_page_data->node_zones[0], order: 0, mark: 0
// classzone_idx: 0 alloc_flags: 0x41
// zone_page_state(contig_page_data->node_zones[0], NR_FREE_PAGES): ????
return __zone_watermark_ok(z, order, mark, classzone_idx, alloc_flags,zone_page_state(z, NR_FREE_PAGES));
// return 1 - get_page_from_freelist()
// preferred_zone: (&contig_page_data)->node_zones[0] // zone: contig_page_data->node_zones[0], order: 0, gfp_mask: 0x221200 // migratetype: MIGRATE_UNMOVABLE: 0 page = buffered_rmqueue(preferred_zone, zone, order, gfp_mask, migratetype);
- buffered_rmqueue()
if (list_empty(list)) { pcp->count += rmqueue_bulk(zone, 0, pcp->batch, list, migratetype, cold); if (unlikely(list_empty(list))) goto failed; }
- rmqueue_bulk()for (i = 0; i < count; ++i) {
struct page *page = __rmqueue(zone, order, migratetype); if (unlikely(page == NULL)) break;
- __rmqueue()retry_reserve:
page = __rmqueue_smallest(zone, order, migratetype); - __rmqueue_smallest()// order : 0, MAX_ORDER : 11
for (current_order = order; current_order < MAX_ORDER; ++current_order) { // zone->free_area[0] : contig_page_data_node_zones[0].free_area[0]> area = &(zone->free_area[current_order]); // area: &(contig_page_data_node_zones[0].free_area[0]) // migratetype : 0, // area->free_list[0] : &&(contig_page_data_node_zones[0].free_area[0])->free_list[0] :1 if (list_empty(&area->free_list[migratetype])) continue;
- if (list_empty(&area->free_list[migratetype]))
- 여기서 free_list[0]이 의미하는 바를 알아보자.
- free_list가 현재 오더에서 빈것이 있는지 찾는다.
- 빈것이 없으면 continue로 오더가 증가한다.
- 빈것이 있으면 다음주로 실행된다.
- 그런데 migratetype : 0은 UNMOVEABLE이므로,
- if가 NULL이되어
- free_list[]는 __free_one_page에서 넣었다.
- if (unlikely(!page) && migratetype != MIGRATE_RESERVE) {page : NULL, migragetype : 0, MIGRATE_RESERVE : 3
// if문 실행
page = __rmqueue_fallback(zone, order, migratetype);
- __rmqueue_fallback()
// MAX_ORDER : 11, order : 0 for (current_order = MAX_ORDER-1; current_order >= order; --current_order) { for (i = 0;; i++) { // i:0, start_migratetype:0, fallback[0][0]: MIGRATE_RECLAIMABLE: 1 // i:1, start_migratetype:0, fallback[0][1]: MIGRATE_MOVABLE : 2 migratetype = fallbacks[start_migratetype][i]; // fallbacks[0][0] : MIGRATE_RECLAIMABLE : 1 // migratetype : MIGRATE_RECLAIMABLE : 1 // fallbacks[0][1] : MIGRATE_MOVABLE : 2 // migratetype : MIGRATE_MOVABLE : 2 /* MIGRATE_RESERVE handled later if necessary */ if (migratetype == MIGRATE_RESERVE) break; // current_order : 10 area = &(zone->free_area[current_order]); // area : &(zone->free_area[10]: // : contig_page_data->node_zones[0].free_area[10] // i:0, migratetype : MIGRATE_RECLIMABLE : 1 // i:0, area->free_list[1] : contig_page_data->node_zones[0].free_area[10]->free_list[1] // i:1, migratetype : MIGRATE_MOVABLE : 2 // i:1, area->free_list[2] : contig_page_data->node_zones[0].free_area[10]->free_list[2] if (list_empty(&area->free_list[migratetype])) continue; // current_order:5, migratetype : MIGRATE_MOVABLE: 2 아래 코드 수행할것으로 보고 분석 // i:1, curren_order:5 // i:1 ,are->free_list[2]: contig_page_data->node_zones[0].free_area[5]->free_list[2].next page = list_entry(area->free_list[migratetype].next, struct page, lru); // i:1 area->nr_free : (&contig_page_data->node_zones[0].free_area[5]->nr_free 1감소 area->nr_free--; // i:1 zone : contig_page_data->node_zone[0] // i new_type = try_to_steal_freepages(zone, page, start_migratetype, migratetype); /* Remove the page from the freelists */ list_del(&page->lru); rmv_page_order(page); expand(zone, page, order, current_order, area, new_type); trace_mm_page_alloc_extfrag(page, order, current_order, start_migratetype, migratetype, new_type); return page; } }
- new_type
new_type = try_to_steal_freepages(zone, page, start_migratetype, migratetype);
- new_type
- try_to_steal_freepages()
static int try_to_steal_freepages(struct zone *zone, struct page *page, int start_type, int fallback_type) { int current_order = page_order(page); /* * When borrowing from MIGRATE_CMA, we need to release the excess * buddy pages to CMA itself. */ if (is_migrate_cma(fallback_type)) return fallback_type; /* Take ownership for orders >= pageblock_order */ // current_order : 5 >= pageblock_order : 10 if (current_order >= pageblock_order) { change_pageblock_range(page, current_order, start_type); return start_type; } // current_order : 5 // page_group_by_mobility_disabled : 0 if (current_order >= pageblock_order / 2 || start_type == MIGRATE_RECLAIMABLE || page_group_by_mobility_disabled) { int pages; pages = move_freepages_block(zone, page, start_type); /* Claim the whole block if over half of it is free */ if (pages >= (1 << (pageblock_order-1)) || page_group_by_mobility_disabled) { set_pageblock_migratetype(page, start_type); return start_type; } } return fallback_type; }
- pages
pages = move_freepages_block(zone, page, start_type);
- move_freepages_block()zone : contig_page_data->node_zones[0],
- pages
- move_freepages_block()
int move_freepages_block(struct zone *zone, struct page *page, int migratetype) { unsigned long start_pfn, end_pfn; struct page *start_page, *end_page; // 가정 : order 5의 migratetype MIGRARE_MOVALE 인 lru page // 의 pfn값을 0x200000 으로 가정하고 분석 start_pfn = page_to_pfn(page); // start_pfn : 0x20000 // pageblock_nr_pages : 0x400 (1024) start_pfn = start_pfn & ~(pageblock_nr_pages-1); // start_pfn = 0x20000 start_page = pfn_to_page(start_pfn); // start_page : page 0x20000 (pfn) end_page = start_page + pageblock_nr_pages - 1; // end_page : page 0x20CFF (pfn) end_pfn = start_pfn + pageblock_nr_pages - 1; // end_pfn : 0x20000 + ( 0x400 -1) // end_pfn : 0x20CFF /* Do not cross zone boundaries */ // zone : contig_page_data->node_zones[0], start_pfn: 0x20000 // zone_spans_pfn : 1 if (!zone_spans_pfn(zone, start_pfn)) start_page = page; // zone : contig_page_data->node_zones[0], end_pfn: 0x20cff // zone_spans_pfn : 1 if (!zone_spans_pfn(zone, end_pfn)) return 0; return move_freepages(zone, start_page, end_page, migratetype); }
- return
return move_freepages(zone, start_page, end_page, migratetype);
- return
- move_freepages()
// Move the free pages in a range to the free lists of the requested type.
// Note that start_page and end_pages are not aligned on a pageblock
// boundary. If alignment is required, use move_freepages_block()
int move_freepages(struct zone *zone,
struct page *start_page, struct page *end_page,
int migratetype)
{
struct page *page;
unsigned long order;
int pages_moved = 0;
#ifndef CONFIG_HOLES_IN_ZONE
/*
* page_zone is not safe to call in this context when
* CONFIG_HOLES_IN_ZONE is set. This bug check is probably redundant
* anyway as we check zone boundaries in move_freepages_block().
* Remove at a later date when no bug reports exist related to
* grouping pages by mobility
*/
BUG_ON(page_zone(start_page) != page_zone(end_page));
// 시작과 끝이 같은 존에 있는지 확인하여 아니면 버그.
#endif
// start: page : 0x20000 (pfn), end_page : page 0x20CFF (pfn)
for (page = start_page; page <= end_page;) {
/* Make sure we are not inadvertently changing nodes */
// page_to_nid(page)
VM_BUG_ON(page_to_nid(page) != zone_to_nid(zone));
// 같은 존에 있는지 확인.
if (!pfn_valid_within(page_to_pfn(page))) {
page++;
continue;
}
if (!PageBuddy(page)) {
page++;
continue;
}
order = page_order(page);
// order : 5
// zone->free_area[5[.free_list[0]: cotig_page_data->node_zones[0].free_area[5].free_area[0]
list_move(&page->lru,
&zone->free_area[order].free_list[migratetype]);
// 에 page : 0x20000 (pfn)
// page->lru를 자기 자신으로 초기화하고, free_list에 불인다.
// migratetype 을 2에 있는 page를 0으로 바꾸고,
// zone->free_area[5[.free_list[0]: cotig_page_data->node_zones[0].free_area[5].free_area[2]
// contig_page_data->node_zones[0].free_list[2] 에서
// contig_page_data->node_zone[0.free_area[5].free_list[0]로 옮김
set_freepage_migratetype(page, migratetype);
// migrage type 이 2에서 0으로 바뀜.
page += 1 << order;
pages_moved += 1 << order;
}
return pages_moved;
// pages_moved : 1024 (옴겨진 page 수)
}
- VM_BUG_ON() : page의 nid와 zone의 nid가 일치하는지 ?
VM_BUG_ON(page_to_nid(page) != zone_to_nid(zone));
- page_to_nid()
static inline int page_to_nid(const struct page *page) { return (page->flags >> NODES_PGSHIFT) & NODES_MASK; // NODES_PGSHIFT : 0, // NODES_MASK : 0, // return (page->flags >> 0) & 3; 하위 2비트만 리턴. }
- zone_to_nid()return 0;
- pfn_valid_within(page_to_pfn(page))if (!pfn_valid_within(page_to_pfn(page))) {
page++;
continue; - pfn_valid_within()return 1;
- PageBuddy(page)
if (!PageBuddy(page)) { page++; continue; }
- PageBuddyreturn 1;
- try_to_steal_freepages()
if (pages >= (1 << (pageblock_order-1)) || page_group_by_mobility_disabled) { // pages: ??? // > set_pageblock_migratetype(page, start_type); return start_type;
- set_pageblock_migratetype()page 블록의 migratetype을 바꿔줌 (2->0)
- set_pageblock_migratetype()
- try_to_steal_freepages()return start_type : 0
- __rmqueue_fallback()new_type = try_to_steal_freepages() : 0o list_del(&page->lru);
rmv_page_order(page); - rmv_page_order()page->_mapcount : -1
page->private : 0
// page의 _mapcount 를 -1, private값을 0으로 초기화 - __rmqueue_fallback()expand(zone, page, order, current_order, area,
new_type);
// i: 1, order ; 0, current_order : 5, start_migratetype:
// new_type : 0
return page;
// page : migratetype이 MIGRATE_UNMOVEABLE인 page
expand ():
- 페이지가 0이 필요한데. MIGRATE_UNMOVABLE이 없어서.
- MIGRATE_MOVEABLE을 order 5부터 4, 3, 2, 1으로 해서
- order 1의 slub을 MIGRAT_UNMOVEABLE로 설정하고
- 나머지 5부터 반쪽씩 free로 설정한다.
- smallest에서 MIGRATE_UNMOVEABLE이 있었으면 프리페이지 확장을 하지만,
- 우리는 smallest가 없었기 때문에 unlikely를 들어와서 fallback에서
- 가장 큰 영역부터 (오더 5)에서 부터 순차적으로 내려오면서.
- 남은 영역을 free로 확장해서 설정하고,
- 오더 1에서 MIGRATE_MOVEABLE에서 할당 받아 MiGRATE_UNMOVEABLE로 설정하고
- 역시 남은 영역을 free로 확장해서 할당한다.
- (&contig_page_data->node_zones[0].free_area[4])->free_list[0] 에
- order 5의 migratetype MIGRATE_MOVEABLE인 lru page + 16 인 page추가
- free page 수 증가
- page의 _mapcount 값을 -128, private를 4로 변경.
static inline void expand(struct zone *zone, struct page *page,
int low, int high, struct free_area *area,
int migratetype)
{
// high : current_order : 5
unsigned long size = 1 << high;
// high : 5, low : 0
while (high > low) {
// area: &contig_page_data->node_zones[0].free_area[5]
area--;
// area: &contig_page_data->node_zones[0].free_area[4]
// high : 5
high--;
// high : 4
// size : 32
size >>= 1;
// size : 16
// zone : contig_page_data->node_zones[0]
// page : order 5의 migratetype MIGRATE_MOVEABLE인 lru page
VM_BUG_ON(bad_range(zone, &page[size]));
#ifdef CONFIG_DEBUG_PAGEALLOC // not set
if (high < debug_guardpage_minorder()) {
/*
* Mark as guard pages (or page), that will allow to
* merge back to allocator when buddy will be freed.
* Corresponding page table entries will not be touched,
* pages will stay not present in virtual address space
*/
INIT_LIST_HEAD(&page[size].lru);
set_page_guard_flag(&page[size]);
set_page_private(&page[size], high);
/* Guard pages are not available for any usage */
__mod_zone_freepage_state(zone, -(1 << high),
migratetype);
continue;
}
#endif
// size : 16
// page : order 5의 migratetype MIGRATE_MOVEABLE인 lru page + 16).lru
// migratetype : 0 ,
// area->free_list[0] : &contig_page_data->node_zones[0].free_area[4])->free_list[0]
list_add(&page[size].lru, &area->free_list[migratetype]);
// &contig_page_data->node_zones[0].free_area[4])->free_list[0]에
// order K의 migratetype MIGRATE_MOVEABLE lru page + 16 인 page 추가
// (&contig_page_data->node_zones[0].free_area[4]->free_list[0]
area->nr_free++;
// free page 수 증가
// size : 16,
// page[16] : order 5의 migratetype 이 MIGRATE_MOVEABLE인 lru page
set_page_order(&page[size], high);
// page의 _mapcount 값을 -128, private를 4로 변경.
}
}
- expand(zone, page, order, current_order, area, new_type)
- order는 필요한 크기
- current_order : 버디에서 가져온 크기
- 큰 버디 오더에서 사용하려는 오더를 제외한 나머지 버디영역을
- 미사용영역으로 정리함.
- (필요한 크기보다 가져온 크기가 클때 )실행함.
__rmqueue()
- 해당 zone에서 order(크기)에 맞고 migratetype이 같은 page를 구해옴
- migratetype이 일치 하지 않으면.
- fallbacks의 enum의 메모리정책에 따라서 할당 받는다.
- __rmque_samllest () UNMOVERABLE
- __rmqure_fallback () MOVEABLE
- RESERVE에서 찾는다.
if (!page) { migratetype = MIGRATE_RESERVE; goto retry_reserve;
trace_mm_page_alloc_zone_locked(page, order, migratetype);
// page : migratetype MIGRATE_
- fallback에서 메모리 정책이 결정된다.
requeue_bulk()
struct page *page = __rmqueue(zone, order, migratetype);
위의 분석이었다.// cold :0> if (likely(cold == 0)) list_add(&page->lru, list); // page->lru : (migtratetype이 MIGRATE_UNMOVEABLE인 page)->lru // list : (&boot_pageset + (__per_cpu_offset[0]))->pcp.lists[0] else list_add_tail(&page->lru, list); // page의 lru가 cold가 1일때 tail로 cold가 0일때 head로 등록됨 // IS_ENABLED(CONFIG_CMA) // n/A if (IS_ENABLED(CONFIG_CMA)) { mt = get_pageblock_migratetype(page); if (!is_migrate_cma(mt) && !is_migrate_isolate(mt)) mt = migratetype; } // page->index = migratetype set_freepage_migratetype(page, mt); // page의 index필드를 migratetype 0으로 변경 // list : (&boot_pageset + (__per_cpu_offset[0]))->pcp.lists[0] // page->lru : (migratetype 이 MIGRATE_UNMOVEABLE인 page)->lru list = &page->lru; // lru는 색인이고, 자료는 pcp.list[0]에 값이 업데이트 됨 // mt : 0 if (is_migrate_cma(mt)) // false __mod_zone_page_state(zone, NR_FREE_CMA_PAGES, -(1 << order)); } // zone: contig_page_data->node_zones[0], NR_FREE_PAGES : 0, i:1 __mod_zone_page_state(zone, NR_FREE_PAGES, -(i << order));
// vm_stat[enum]에서 NR_FREE_PAGES인 2번째 인자를 가지고 update하는 함수 // x = delta + __this_cpu_read(*p) // (&contig_pgate_data)->node_zones[ZONE_NORMAL].vm_stat[0]의 값을 update함 // t : 32???6 -1 5 return i; // order : 0, return i : 1
buffered_rmqueue()
if (list_empty(list)) { pcp->count += rmqueue_bulk(zone, 0, pcp->batch, list, migratetype, cold); // pcp->count : (&boot_pageset + (__per_cpu_offset[0]))->pcp.list[0] if (unlikely(list_empty(list))) // fault goto failed; }
// cold : 0if (cold) page = list_entry(list->prev, struct page, lru); else // list-next :((&boot_pageset + (__per_cpu_offset[0]))->pcp.list[0])->next page = list_entry(list->next, struct page, lru); // page : migratetype이 MIGRATE_UNMOVEABEL인 page list_del(&page->lru); // list-next :((&boot_pageset + (__per_cpu_offset[0]))->pcp.list[0]에서 현제 page->lru삭제 pcp->count--; // list-next :((&boot_pageset + (__per_cpu_offset[0]))->pcp.list[0]).count : 0 // gfp_flags : 0x221200 if (!gfp_thisnode_allocation(gfp_flags)) // true : !false // zone: contig_page_data->noce_zones[0], NR_ALLOC_BATCH :1, order: 0 __mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order)); // (&contig_page_data->node_zones[0]->vm_stat[1] update //
스터디 log
4824920..d9a9639 master -> origin/master
Merge made by the ‘recursive’ strategy.
arch/arm/include/asm/spinlock.h | 2 +
include/asm-generic/memory_model.h | 1 +
include/linux/list.h | 3 +
include/linux/lockdep.h | 2 +
include/linux/mm.h | 16 -
include/linux/mmzone.h | 6 +
include/linux/page-flags-layout.h | 1 +
include/linux/pageblock-flags.h | 4 +-
include/linux/preempt.h | 1 +
include/linux/spinlock.h | 5 +
include/linux/spinlock_api_smp.h | 3 +
include/linux/vmstat.h | 1 +
include/trace/events/kmem.h | 2 +
kernel/locking/spinlock.c | 2 +
kernel/locking/spinlock_debug.c | 5 +
mm/internal.h | 1 +
mm/page_alloc.c | 281 ++-
mm/vmstat.c | 10 +-
18 files changed, 335 insertions(+), 11 deletions(-)
Merge made by the ‘recursive’ strategy.
arch/arm/include/asm/spinlock.h | 2 +
include/asm-generic/memory_model.h | 1 +
include/linux/list.h | 3 +
include/linux/lockdep.h | 2 +
include/linux/mm.h | 16 -
include/linux/mmzone.h | 6 +
include/linux/page-flags-layout.h | 1 +
include/linux/pageblock-flags.h | 4 +-
include/linux/preempt.h | 1 +
include/linux/spinlock.h | 5 +
include/linux/spinlock_api_smp.h | 3 +
include/linux/vmstat.h | 1 +
include/trace/events/kmem.h | 2 +
kernel/locking/spinlock.c | 2 +
kernel/locking/spinlock_debug.c | 5 +
mm/internal.h | 1 +
mm/page_alloc.c | 281 ++-
mm/vmstat.c | 10 +-
18 files changed, 335 insertions(+), 11 deletions(-)
댓글 없음:
댓글 쓰기