大页的基础是复合页。在使用伙伴系统分配页时,如果指定了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_head和PG_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.
*/
来源:oschina
链接:https://my.oschina.net/u/2302134/blog/363672