Linux大页之复合页

大页的基础是复合页。在使用伙伴系统分配页时,如果指定了GFP标志__GFP_COMP,那么在分配到页后,会调用prep_compound_page将一组连续的页组合成一个复合页。

include/linux/gfp.h

#define ___GFP_COMP             0x4000u
#define __GFP_COMP      ((__force gfp_t)___GFP_COMP)    /* Add compound page metadata */


对于复合页中的每个page描述符,都会设置相应的标志来让内核是首页还是尾页:

include/linux/page-flags.h

enum pageflags {
 
#ifdef CONFIG_PAGEFLAGS_EXTENDED
        PG_head,                /* A head page */
        PG_tail,                /* A tail page */
#else
        PG_compound,            /* A compound page */
#endif
 
        PG_reclaim,             /* To be reclaimed asap */
 
}


注意了,对于复合页的标志有两种情况:

1. CONFIG_PAGEFLAGS_EXTENDED的情况:

include/linux/page-flags.h

#ifdef CONFIG_PAGEFLAGS_EXTENDED
/*
 * System with lots of page flags available. This allows separate
 * flags for PageHead() and PageTail() checks of compound pages so that bit
 * tests can be used in performance sensitive paths. PageCompound is
 * generally not used in hot code paths except arch/powerpc/mm/init_64.c
 * and arch/powerpc/kvm/book3s_64_vio_hv.c which use it to detect huge pages
 * and avoid handling those in real mode.
 */
__PAGEFLAG(Head, head) CLEARPAGEFLAG(Head, head)
__PAGEFLAG(Tail, tail)
 
static inline int PageCompound(struct page *page)
{
        return page->flags & ((1L << PG_head) | (1L << PG_tail));
 
}

即,正常使用PG_headPG_tail标志。

 

2. CONFIG_PAGEFLAGS_EXTENDED的情况:

include/linux/page-flags.h

/*
 * Reduce page flag use as much as possible by overlapping
 * compound page flags with the flags used for page cache pages. Possible
 * because PageCompound is always set for compound pages and not for
 * pages on the LRU and/or pagecache.
 */
TESTPAGEFLAG(Compound, compound)
__SETPAGEFLAG(Head, compound)  __CLEARPAGEFLAG(Head, compound)
 
/*
 * PG_reclaim is used in combination with PG_compound to mark the
 * head and tail of a compound page. This saves one page flag
 * but makes it impossible to use compound pages for the page cache.
 * The PG_reclaim bit would have to be used for reclaim or readahead
 * if compound pages enter the page cache.
 *
 * PG_compound & PG_reclaim     => Tail page
 * PG_compound & ~PG_reclaim    => Head page
 */
#define PG_head_mask ((1L << PG_compound))
#define PG_head_tail_mask ((1L << PG_compound) | (1L << PG_reclaim))
 
static inline int PageHead(struct page *page)
{
        return ((page->flags & PG_head_tail_mask) == PG_head_mask);
}
 
static inline int PageTail(struct page *page)
{
        return ((page->flags & PG_head_tail_mask) == PG_head_tail_mask);
}
 
static inline void __SetPageTail(struct page *page)
{
        page->flags |= PG_head_tail_mask;
}
 
static inline void __ClearPageTail(struct page *page)
{
        page->flags &= ~PG_head_tail_mask;
}

要标记一个复合页,需要PG_reclaim标志的配合了。


关于复合页的组成,内核中的注释也说的很清晰。

mm/page_alloc.c

/*
 * Higher-order pages are called "compound pages".  They are structured thusly:
 *
 * The first PAGE_SIZE page is called the "head page".
 *
 * The remaining PAGE_SIZE pages are called "tail pages".
 *
 * All pages have PG_compound set.  All tail pages have their ->first_page
 * pointing at the head page.
 *
 * The first tail page's ->lru.next holds the address of the compound page's
 * put_page() function.  Its ->lru.prev holds the order of allocation.
 * This usage means that zero-order pages may not be compound.
 */

你可能感兴趣的:(虚拟化,linux内核,大页技术)