天天看点

[内核内存] [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;
    }
               

继续阅读