天天看點

[核心記憶體] [arm64] 記憶體回收5---add_to_swap函數詳解

add_to_swap函數是為匿名頁面配置設定交換空間.

當對一個匿名頁面page進行記憶體回收時,若該匿名頁面page具有如下狀态

1.該匿名頁未被使用者應用通路.(映射該匿名頁的所有pte頁表項的AF位為0)
2.PageAnon(page) && !PageSwapCache(page)為true.(該page未被配置設定swap空間)
           

若此時頁面回收則允許頁面進行檔案操作或磁盤I/0操作(sc->gfp_mask & __GFP_IO為true).則頁面回收則會調用add_to_swap函數對page配置設定swap交換空間

  1. 執行步驟
    1.通過get_swap_page在swap area中擷取一個slot(頁槽,一個頁槽用于存儲一個匿名頁swap out到swap area中的内   容資料),并将該slot的位置信
    	  息記錄在swp_entry_t結構體entry中
    2.通過add_to_swap_cache函數将1步驟配置設定的slot與匿名頁page關聯
    	a.将匿名頁page的flag成員的swap cache标志位置位
    	b.用匿名頁page的private成員記錄步驟1擷取的slot在swap area中的位置資訊(page->private = entry.val)
    	c.通過swp_offset(entry)函數擷取步驟1的slot在swap area中的偏移offset
    	d.通過radix_tree_insert函數将匿名頁page插入到對應swap area區域緩存管理器address_space的page_tree	
    	  容器中.
               
  2. 源碼
    //mm/swap_state.c
    /**
     * add_to_swap - allocate swap space for a page
     * @page: page we want to move to swap
     *
     * Allocate swap space for the page and add the page to the
     * swap cache.  Caller needs to hold the page lock. 
     */
    int add_to_swap(struct page *page, struct list_head *list)
    {
    	swp_entry_t entry;
    	int err;
    
    	VM_BUG_ON_PAGE(!PageLocked(page), page);
    	VM_BUG_ON_PAGE(!PageUptodate(page), page);
    	/*
    	 *從swap磁盤區域擷取一個slot(slot代表一個槽,每個槽對應記憶體區域的一個page),并将該槽的位置資訊記錄 	 *在swp_entry_t結構體entry中(通過entry可以在從一個swap磁盤區域定位到一個具體的slot,該slot用于存儲
    	 *匿名頁page對應的資料).
    	 */
    	entry = get_swap_page();
    	......
    	err = add_to_swap_cache(page, entry,
    			__GFP_HIGH|__GFP_NOMEMALLOC|__GFP_NOWARN);
    
    	......
        //成功傳回1,失敗傳回0
    }
               
    //mm/swap_state.c
    /*
     *int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask)
     *					|--->__add_to_swap_cache(page, entry)
     */
    /*
     * __add_to_swap_cache resembles add_to_page_cache_locked on swapper_space,
     * but sets SwapCache flag and private instead of mapping and index.
     */
    int __add_to_swap_cache(struct page *page, swp_entry_t entry)
    {
    	int error;
        //某個swap 記憶體區域的swap cache管理結構體struct address_space
    	struct address_space *address_space;
    	.....
        
    	get_page(page);
        //将匿名頁page的flag成員的swap cache标志位置位
    	SetPageSwapCache(page);
        //匿名頁page的private存儲page對應的swap slot的位置資訊
    	set_page_private(page, entry.val);
    	//擷取entry所在swap area區域的swap cache管理器,指派給address_space
    	address_space = swap_address_space(entry);
        //擷取address_space緩存管理器中page_tree的自旋鎖
    	spin_lock_irq(&address_space->tree_lock);
        
        //根據entry指向的slot在對應swap area的偏移,将page插入到緩存管理器的page_tree中
    	error = radix_tree_insert(&address_space->page_tree,
    				  swp_offset(entry), page);
    	if (likely(!error)) {
            //page插入成功,緩存管理器address_sapce維護的swap cache中匿名緩存頁數量++
    		address_space->nrpages++;
    		__inc_node_page_state(page, NR_FILE_PAGES);
    		INC_CACHE_INFO(add_total);
    	}
    	spin_unlock_irq(&address_space->tree_lock);
    
    	if (unlikely(error)) {
    		/*
    		 * Only the context which have set SWAP_HAS_CACHE flag
    		 * would call add_to_swap_cache().
    		 * So add_to_swap_cache() doesn't returns -EEXIST.
    		 */
    		VM_BUG_ON(error == -EEXIST);
    		set_page_private(page, 0UL);
    		ClearPageSwapCache(page);
    		put_page(page);
    	}
    
    	return error;
    }
               

繼續閱讀