2017-02-07

本文简化描述, 忽略compound page, 忽略HUGEPAGE, 未开启CONFIGMEMCG, 未开启CONFIGSWAP. 若无特别说明, 本文仅讨论ARM体系的情况.

1. 数据结构

per zone : active list, inactive list

struct zone {
    struct lruvec       lruvec;
};

struct lruvec {
    struct list_head lists[NR_LRU_LISTS];
    ...
};

per node :kswapd

kswapd_init -> for_each_node_state(nid, N_MEMORY) kswapd_run(nid)
kswapd_run -> pgdat->kswapd = kthread_run(kswapd, pgdat, "kswapd%d", nid);

per cpu :lru cache, 也就是struct pagevec

2. 调用逻辑

何时调用 shrinkactivelist, 何时调用 shrinkinactivelist ?

调用路径:

shrink_zones
|--shrink_zone -> shrink_lruvec
|    |--for_each_evictable_lru //顺序是INACTIVE_xx, ACTIVE_xx, ..
|    |  |--shrink_list
|    |  |   |--if active list, shrink_active_list 
|    |  |   |--else shrink_inactive_list

由于 shrinkactivelist 存在将page添加到inactive list的情况 , 若之后再 shrinkinactivelist 的话, 就是双重打击了, 故shrinklruvec()会 先调用 shrinkinactivelist, 后调用shrinkactive_list.

我们并不会总是shrinkactivelist, 只有在inactive list的page相对比较少时才会shrinkactivelist.

问题:如果shrinkinactivelist中把page移到active list, 那么, shrinkactivelist会不会又把该page挪到 inactive list?
答:我们是新添加到active list的, 队列是FIFO的方式, 所以, 只要nrtoscan不是很大, 就可以避免短时间内被扫描到.

由于page被回收, 相关页表项需要处理, 这部分需要rmap, 本文略去.

3. shrinkactivelist

shrink_active_list
|--lru_add_drain
|--isolate_lru_pages(, , &l_hold, )
|    |--__isolate_lru_page  isolate是让page还呆在lru上, 但清掉PG_lru
|    |--list_move(&page->lru, l_hold);  挪到链表(list_head) l_hold
|--for each page in struct list_head l_hold
|    |--page_referenced
|    |    |--page_referenced_file -> page_referenced_one -> ptep_clear_flush_young_notify -> pte_mkold
|    |    |--page_test_and_clear_young  ARM体系是空操作
|    |--ClearPageActive  and  list_add(&page->lru, &l_inactive);
|    |--or list_add(&page->lru, &l_active) //不讨论
|--move_active_pages_to_lru(lruvec, &l_active, &l_hold, lru);
|--move_active_pages_to_lru(lruvec, &l_inactive, &l_hold, lru - LRU_ACTIVE);
|    |--将l_inactive的page move到 lruvec->lists[lru - LRU_ACTIVE]中,
|    |--put page, 如果为zero, 则__ClearPageActive + 从lru中移除 + 添加到l_hold中, 待后续执行free 
|--free_hot_cold_page_list(&l_hold, 1);

上面的流程忽略Compound page. 所以, 常见的流程是: 从active list中移除, 添加到inactive list中. 如果page count为0, 就不必添加到list中, 而是free掉page. 对于可执行文件的page, 根据情况可重新放入active list.

关于moveactivepagestolru中put后, page count为0:
linux-3.10.86/mm/vmscan.c

move_active_pages_to_lru
{

        /*
        在buddy system中的page的_count为0.
        从buddy system取下, page为1.
        当前page还被isolate_lru_pages get.
        所以, 如果if (put_page_testzero(page))成立, 那就可能发生下面的序列:
        alloc page
        isolate_lru_pages
        free page
        put_page_testzero
        */
        if (put_page_testzero(page)) {

            list_add(&page->lru, pages_to_free);

        }

}

4. shrinkinactivelist

//忽略Compound Page

shrink_inactive_list(..., enum lru_list lru)
|--lru_add_drain
|--isolate_lru_pages(, &page_list, ) //挑出的page放入链表page_list
|--shrink_page_list(&page_list, ) //尝试reclaim, 根据情况可能给page设置PG_active.
|    |--VM_BUG_ON(PageActive(page));
|    |--page_check_references //判断是否进行reclaim, 是否设置active等
|    |  |--page_referenced
|    |--case PAGEREF_ACTIVATE:  ...
|    |--case PAGEREF_KEEP: ...
|    |--case PAGEREF_RECLAIM or PAGEREF_RECLAIM_CLEAN: ...
|--/*对page_list中的page, 根据page flag, 将其放入对应lru. 若->_count put后为0, 则放入链表, 后续free掉*/
|--putback_inactive_pages(, &page_list);
|-- /*现page_list中存放的是 putback_inactive_pages()检测put_page_testzero()成立的page*/
|--free_hot_cold_page_list(&page_list, )

4.1 shrinkpagelist

[Professional Linux Kernel Architecture] p1072
shrinkpagelist sends the page to the associated backing store (which means the page is synchronized, swapped out, or discarded)

[Professional Linux Kernel Architecture]p1074
shrinkpagelist returns the number of pages for which it succeeded to initiate writeout.

shrink_page_list
{

    if (PageWriteback(page)) {

        如果允许等待的话, wait_on_page_writeback(page);
    }

    若可回收, try_to_unmap

    if (PageDirty(page)) {
    //较少发生, 这里不讨论
    }

    if (page_has_private(page)) {
    //不是重点, 这里不讨论
    }

    free_hot_cold_page

}

5. 常见问题

direct reclaim可能导致堆栈overflow的问题
mm: vmscan: do not writeback filesystem pages in direct reclaim https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ee72886d8ed5d9de3fa0ed3b99a7ca7702576a96

本文地址: https://awakening-fong.github.io/posts/mm/reclaim_02_implement

转载请注明出处: https://awakening-fong.github.io


若无法评论, 请打开JavaScript, 并通过proxy.


blog comments powered by Disqus