<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

