디버그 메모리 -1- (Page Alloc)

<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의 절반까지 지정될 수 있다.
    • 이 값 이상의 페이지들은 가드 페이지들을 생성하지 않는다.
    • 이 값이 크면 클 수록 메모리 낭비가 커진다.

 

 

다음 그림은 가드 페이지를 사용하지 않는 보통 상태의 버디 시스템에서 페이지 할당을 보여준다.

 

다음 그림은 가드 페이지를 사용할 때 할당되는 페이지와 인접한 추가 가드 페이지들을 보여준다.

 

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
  • 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

 

참고

댓글 남기기