<kernel v5.0>
디버그 메모리 -1- (Page Alloc)
minorder 미만의 페이지들 할당 시마다 별도의 가드 페이지를 준비하고, 이 페이지에 접근(R/W)하는 경우 예외를 발생시키기 위해 사용된다. 버디 시스템에서 minorder 미만의 페이지들에 대해 이에 대응하는 버디 페이지들을 별도의 가드 페이지들로 활용하기 위해 guard 플래그를 가진채로 free_list에서 제외된다. 이 guard 페이지들은 유저 가상 주소에 매핑하지 않으므로 접근할 수 없다.
사용 조건
- CONFIG_PAGE_EXTENSION & CONFIG_DEBUG_PAGEALLOC 커널 옵션
- 커널 v5.3-rc1 부터는 CONFIG_PAGE_EXTENSION 없이도 동작한다.
- CONFIG_HIBERNATION과 같이 사용할 수 없다.
- “debug_pagealloc=on” 커널 파라미터
- “debug_guardpage_minorder=<minorder>” 커널 파라미터
- minorder는 디폴트 값으로 가드 페이지가 동작하지 않는 0 이고, 최대 MAX_ORDER의 절반까지 지정될 수 있다.
- 이 값 이상의 페이지들은 가드 페이지들을 생성하지 않는다.
- 이 값이 크면 클 수록 메모리 낭비가 커진다.
- 이 기능은 커널 v5.3-rc1에서 page_ext 구조체를 사용하다 page 구조체로 이전시켰다.
다음 그림은 가드 페이지를 사용하지 않는 보통 상태의 버디 시스템에서 페이지 할당을 보여준다.
다음 그림은 가드 페이지를 사용할 때 할당되는 페이지와 인접한 추가 가드 페이지들을 보여준다.
need_debug_guardpage()
mm/page_alloc.c
static bool need_debug_guardpage(void) { /* If we don't use debug_pagealloc, we don't need guard page */ if (!debug_pagealloc_enabled()) return false; if (!debug_guardpage_minorder()) return false; return true; }
가드 페이지 디버깅이 필요한지 여부를 반환한다.
- 디버그용 guard 페이지를 사용하지 않는 경우에도 false를 반환한다.
debug_pagealloc_enabled()
include/linux/mm.h
static inline bool debug_pagealloc_enabled(void) { return _debug_pagealloc_enabled; }
페이지 할당 디버깅 기능 사용 여부를 반환한다.
debug_guardpage_minorder()
include/linux/mm.h
static inline unsigned int debug_guardpage_minorder(void) { return _debug_guardpage_minorder; }
디버그용 guard 페이지로 설정된 최소 order 값을 반환한다.
- “debug_guardpage_minorder=” 커널 파라메터 값은 1~MAX_ORDER의 절반까지 가능하다.
- MAX_ORDER가 11인 경우 1~5까지 설정 가능하다.
init_debug_guardpage()
mm/page_alloc.c
static void init_debug_guardpage(void) { if (!debug_pagealloc_enabled()) return; _debug_guardpage_enabled = true; }
가드 페이지 디버깅 기능을 enable 한다.
page_is_guard()
include/linux/mm.h
#ifdef CONFIG_DEBUG_PAGEALLOC static inline bool page_is_guard(struct page *page) { struct page_ext *page_ext; if (!debug_guardpage_enabled()) return false; page_ext = lookup_page_ext(page); return test_bit(PAGE_EXT_DEBUG_GUARD, &page_ext->flags); } #endif
페이지에 대항하는 page_ext의 guard 플래그 비트가 설정되었는지 여부를 반환한다.
clear_page_guard()
mm/page_alloc.c
static inline void clear_page_guard(struct zone *zone, struct page *page, unsigned int order, int migratetype) { struct page_ext *page_ext; if (!debug_guardpage_enabled()) return; page_ext = lookup_page_ext(page); __clear_bit(PAGE_EXT_DEBUG_GUARD, &page_ext->flags); set_page_private(page, 0); if (!is_migrate_isolate(migratetype)) __mod_zone_freepage_state(zone, (1 << order), migratetype); }
페이지에 대항하는 page_ext의 guard 플래그 비트를 clear 하고 _private에 0을 대입한다. zone에 대한 free page stat도 2^order 만큼 증가시킨다.
버디 페이지 expand 시 Guard 페이지로 사용
expand()
mm/page_alloc.c
/* * The order of subdivision here is critical for the IO subsystem. * Please do not alter this order without good reasons and regression * testing. Specifically, as large blocks of memory are subdivided, * the order in which smaller blocks are delivered depends on the order * they're subdivided in this function. This is the primary factor * influencing the order in which pages are delivered to the IO * subsystem according to empirical testing, and this is also justified * by considering the behavior of a buddy system containing a single * large block of memory acted on by a series of small allocations. * This behavior is a critical factor in sglist merging's success. * * -- nyc */
static inline void expand(struct zone *zone, struct page *page, int low, int high, struct free_area *area, int migratetype) { unsigned long size = 1 << high; while (high > low) { area--; high--; size >>= 1; VM_BUG_ON_PAGE(bad_range(zone, &page[size]), &page[size]); /* * 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 */ if (set_page_guard(zone, &page[size], high, migratetype)) continue; list_add(&page[size].lru, &area->free_list[migratetype]); area->nr_free++; set_page_order(&page[size], high); } }
버디 시스템에서 요청한 order 페이지가 부족한 경우 더 큰 order 페이지를 확장하여 사용할 수 있는데 이 때 호출되어 동작하는 expand() 함수이다. 가드 페이지와 관련된 코드만을 잠시 살펴본다.
- 코드 라인 10에서 size에는 할당할 페이지의 다음 페이지인 버디 페이지를 가드 페이지로 사용하기 위해 얻어온다.
- 코드 라인 19~20에서 guard 페이지를 사용하는 경우 guard 페이지 플래그를 설정한다. 그 후 가드 페이지는 free_list에 추가하지 않기 위해 skip 한다.
page_is_guard()
mm/page_alloc.c
static inline bool page_is_guard(struct page *page) { struct page_ext *page_ext; if (!debug_guardpage_enabled()) return false; page_ext = lookup_page_ext(page); if (unlikely(!page_ext)) return false; return test_bit(PAGE_EXT_DEBUG_GUARD, &page_ext->flags); }
@page가 가드 페이지인지 여부를 알아온다.
가드 페이지 지정
set_page_guard()
mm/page_alloc.c
static inline bool set_page_guard(struct zone *zone, struct page *page, unsigned int order, int migratetype) { struct page_ext *page_ext; if (!debug_guardpage_enabled()) return false; if (order >= debug_guardpage_minorder()) return false; page_ext = lookup_page_ext(page); if (unlikely(!page_ext)) return false; __set_bit(PAGE_EXT_DEBUG_GUARD, &page_ext->flags); INIT_LIST_HEAD(&page->lru); set_page_private(page, order); /* Guard pages are not available for any usage */ __mod_zone_freepage_state(zone, -(1 << order), migratetype); return true; }
@page를 guard 페이지로 지정한다. 정상적으로 설정한 경우 true를 반환한다.
- guard 페이지는 private 멤버에 order 값이 지정되고, 확장 페이지에 guard 플래그가 설정된다.
가드 페이지 지정 해제
clear_page_guard()
mm/page_alloc.c
static inline void clear_page_guard(struct zone *zone, struct page *page, unsigned int order, int migratetype) { struct page_ext *page_ext; if (!debug_guardpage_enabled()) return; page_ext = lookup_page_ext(page); if (unlikely(!page_ext)) return; __clear_bit(PAGE_EXT_DEBUG_GUARD, &page_ext->flags); set_page_private(page, 0); if (!is_migrate_isolate(migratetype)) __mod_zone_freepage_state(zone, (1 << order), migratetype); }
@page가 guard 페이지인 경우 취소한다.
- private 멤버에 0 값이 지정되고, 확장 페이지에 guard 플래그를 클리어한다.
Early 커널 파라메터
early_debug_pagealloc()
mm/page_alloc.c
static int __init early_debug_pagealloc(char *buf) { if (!buf) return -EINVAL; if (strcmp(buf, "on") == 0) _debug_pagealloc_enabled = true; return 0; } early_param("debug_pagealloc", early_debug_pagealloc);
“debug_pagealloc=on” early 커널 파라메터를 사용하는 경우 페이지 할당 디버깅 기능을 사용한다.
커널 Tools
tools/vm에서 make를 사용하여 빌드하면 3개의 유틸리티가 생성된다.
- page-types
- 참조하는 proc 인터페이스들
- /proc/<pid>/pagemap
- /proc/<pid>maps
- /proc/self/pagemap
- /proc/kpageflags
- /proc/kpagecount
- /proc/kpagecgroup
- /sys/kernel/mm/page_idle/bitmap
- 참조하는 proc 인터페이스들
- page_owner_sort
- slabinfo
page-types
이 유틸리티를 통해 페이지 타입 유형들을 알아볼 수 있다.
- 옵션을 사용하여 특정 가상 주소, 태스크, 파일에 관여된 페이지 타입들을 관찰할 수 있다.
$ tools/vm/page-types --help page-types [options] -r|--raw Raw mode, for kernel developers -d|--describe flags Describe flags -a|--addr addr-spec Walk a range of pages -b|--bits bits-spec Walk pages with specified bits -c|--cgroup path|@inode Walk pages within memory cgroup -p|--pid pid Walk process address space -f|--file filename Walk file address space -i|--mark-idle Mark pages idle -l|--list Show page details in ranges -L|--list-each Show page details one by one -C|--list-cgroup Show cgroup inode for pages -M|--list-mapcnt Show page map count -N|--no-summary Don't show summary info -X|--hwpoison hwpoison pages -x|--unpoison unpoison pages -F|--kpageflags filename kpageflags file to parse -h|--help Show this usage message flags: 0x10 bitfield format, e.g. anon bit-name, e.g. 0x10,anon comma-separated list, e.g. addr-spec: N one page at offset N (unit: pages) N+M pages range from N to N+M-1 N,M pages range from N to M-1 N, pages range from N to end ,M pages range from 0 to M-1 bits-spec: bit1,bit2 (flags & (bit1|bit2)) != 0 bit1,bit2=bit1 (flags & (bit1|bit2)) == bit1 bit1,~bit2 (flags & (bit1|bit2)) == bit1 =bit1,bit2 flags == (bit1|bit2) bit-names: locked error referenced uptodate dirty lru active slab writeback reclaim buddy mmap anonymous swapcache swapbacked compound_head compound_tail huge unevictable hwpoison nopage ksm thp balloon zero_page idle_page pgtable reserved(r) mlocked(r) mappedtodisk(r) private(r) private_2(r) owner_private(r) arch(r) uncached(r) softdirty(r) readahead(o) slob_free(o) slub_frozen(o) slub_debug(o) file(o) swap(o) mmap_exclusive(o) (r) raw mode bits (o) overloaded bits
$ tools/vm/page-types flags page-count MB symbolic-flags long-symbolic-flags 0x0000000000000000 92360 360 ___________________________________________ 0x0000000000100000 512 2 ____________________n______________________ nopage 0x0000000001000000 1 0 ________________________z__________________ zero_page 0x0000000000000014 10 0 __R_D______________________________________ referenced,dirty 0x0000000000000020 18 0 _____l_____________________________________ lru 0x0000000000000024 16 0 __R__l_____________________________________ referenced,lru 0x0000000000000028 102116 398 ___U_l_____________________________________ uptodate,lru 0x0000000000004028 1536 6 ___U_l________b____________________________ uptodate,lru,swapbacked 0x000000000000002c 90364 352 __RU_l_____________________________________ referenced,uptodate,lru 0x0000000000004030 11454 44 ____Dl________b____________________________ dirty,lru,swapbacked 0x000000000000403c 15846 61 __RUDl________b____________________________ referenced,uptodate,dirty,lru,swapbacked 0x0000000000000040 1 0 ______A____________________________________ active 0x0000000000000060 7293 28 _____lA____________________________________ lru,active 0x0000000000000064 19994 78 __R__lA____________________________________ referenced,lru,active 0x0000000000000068 340912 1331 ___U_lA____________________________________ uptodate,lru,active 0x000000000000006c 233190 910 __RU_lA____________________________________ referenced,uptodate,lru,active 0x0000000000000074 4 0 __R_DlA____________________________________ referenced,dirty,lru,active 0x0000000000004078 16146 63 ___UDlA_______b____________________________ uptodate,dirty,lru,active,swapbacked 0x0000000000000078 1 0 ___UDlA____________________________________ uptodate,dirty,lru,active 0x000000000000407c 91 0 __RUDlA_______b____________________________ referenced,uptodate,dirty,lru,active,swapbacked 0x000000000000007c 1 0 __RUDlA____________________________________ referenced,uptodate,dirty,lru,active 0x0000000000000080 37452 146 _______S___________________________________ slab 0x0000000000000228 2202 8 ___U_l___I_________________________________ uptodate,lru,reclaim 0x0000000000000268 26 0 ___U_lA__I_________________________________ uptodate,lru,active,reclaim 0x0000000000000400 965 3 __________B________________________________ buddy 0x0000000000000800 1 0 ___________M_______________________________ mmap 0x0000000000000804 1 0 __R________M_______________________________ referenced,mmap 0x0000000000000828 232 0 ___U_l_____M_______________________________ uptodate,lru,mmap 0x0000000000004828 8 0 ___U_l_____M__b____________________________ uptodate,lru,mmap,swapbacked 0x0000000000004838 4600 17 ___UDl_____M__b____________________________ uptodate,dirty,lru,mmap,swapbacked 0x000000000000483c 2 0 __RUDl_____M__b____________________________ referenced,uptodate,dirty,lru,mmap,swapbacked 0x0000000000000868 173 0 ___U_lA____M_______________________________ uptodate,lru,active,mmap 0x000000000000086c 4925 19 __RU_lA____M_______________________________ referenced,uptodate,lru,active,mmap 0x0000000000004878 1 0 ___UDlA____M__b____________________________ uptodate,dirty,lru,active,mmap,swapbacked 0x0000000000005048 3 0 ___U__A_____a_b____________________________ uptodate,active,anonymous,swapbacked 0x0000000000005848 2 0 ___U__A____Ma_b____________________________ uptodate,active,mmap,anonymous,swapbacked 0x0000000000005868 33295 130 ___U_lA____Ma_b____________________________ uptodate,lru,active,mmap,anonymous,swapbacked 0x000000000000586c 54 0 __RU_lA____Ma_b____________________________ referenced,uptodate,lru,active,mmap,anonymous,swapbacked total 1015808 3968
특정 유저 태스크(–pid 옵션)가 사용한 페이지 타입들을 보여준다.
$ tools/vm/page-types --pid 959 flags page-count MB symbolic-flags long-symbolic-flags 0x0000000000000800 1 0 ___________M_______________________________ mmap 0x0000000000000804 1 0 __R________M_______________________________ referenced,mmap 0x000000000000086c 1271 4 __RU_lA____M_______________________________ referenced,uptodate,lru,active,mmap 0x0000000000005868 311 1 ___U_lA____Ma_b____________________________ uptodate,lru,active,mmap,anonymous,swapbacked 0x000000000000586c 1 0 __RU_lA____Ma_b____________________________ referenced,uptodate,lru,active,mmap,anonymous,swapbacked total 1585 6
특정 파일(–file 옵션)에 사용한 페이지 타입들을 보여준다.
$ tools/vm/page-types --file abc.txt flags page-count MB symbolic-flags long-symbolic-flags 0x0000000000000068 1 0 ___U_lA____________________________________ uptodate,lru,active total 1 0