hugepage總結

0.概述

本文總結了hugepage使用過程當中的一些知識點,參考的內核版本是4.1.12
注:本文爲原創,轉載請註明出處html

1.hugepage是什麼,有什麼用?

大頁是相對傳統4K小頁而言的,通常來講常見的體系架構都會提供2種大頁大小,好比常見的2M大頁和1G大頁。其實這兩種大頁size也分別對應PMD和PUD的一個頁表項能夠cover的物理內存大小。固然某些體系架構(如arm64)經過contiguous-tlb特性支持2種以上的大頁。
大頁的主要優勢:使用大頁能夠減小頁表項數量,從而減小TLB Miss的機率,提高系統訪存性能。固然有利必有弊,使用大頁下降了內存管理的粒度和靈活性,若是程序並非對內存的使用量特別大,使用大頁還可能形成內存的浪費。node

2.如何使用hugepage

通常來講有三種方法:linux

  1. 使用mmap+MAP_HUGETLB直接匿名映射
  2. 使用shmget+SHM_HUGETLB屬性分配
  3. mount hugetlbfs後,在hugetlbfs下建立文件,而後經過mmap進行映射

注:上述方法使用的前提是,系統中加載了hugetlbfs,且存在空閒大頁。能夠經過cat /proc/meminfo查看是否有空閒內存。另外本質上匿名大頁也是有對應的大頁文件系統中的匿名文件,其實並非真正的匿名頁。數組

HugePages_Total: 10
HugePages_Free: 10
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 92308 kB
DirectMap2M: 1861632 kB架構

3.如何使能大頁功能

通常來講有兩種方法
1)在bootline中設置大頁參數,這種方式最穩妥,由於大頁本質上是連續的4K小頁組成的,因此在系統啓動時預留大頁成功率最高。有三個參數,看名字就很清楚了
default_hugepagesz=1G hugepagesz=1G hugepages=4
若是沒有定義default_hugepagesz,則大頁size默認是2M函數

#define HPAGE_SHIFT        PMD_SHIFT
#define HPAGE_SIZE        (_AC(1,UL) << HPAGE_SHIFT)
#define HPAGE_MASK        (~(HPAGE_SIZE - 1))
#define HUGETLB_PAGE_ORDER    (HPAGE_SHIFT - PAGE_SHIFT)

static int __init hugetlb_init(void)
{
...

    if (!hugepages_supported())//不支持hugepage直接返回
        return 0;

    if (!size_to_hstate(default_hstate_size)) {//沒有設置default_hugepagesz則默認2M
        default_hstate_size = HPAGE_SIZE;
        if (!size_to_hstate(default_hstate_size))
            hugetlb_add_hstate(HUGETLB_PAGE_ORDER);
    }

...
}

2)在系統啓動後,經過echo 10 > /proc/sys/vm/nr_hugepages這種方式預留空閒大頁性能

4.系統中是否能夠同時存在多種大頁類型

linux系統中是能夠同時存在2種大頁類型的,至於爲何是2種,我的理解是由於大頁每每對應PMD和PUD兩種級別的頁表項所cover的物理內存大小。再大意義也不大了。能夠經過下面的bootline設置兩種大頁類型
default_hugepagesz=1G hugepagesz=1G hugepages=4 hugepagesz=2M hugepages=4
內核啓動的時候會解析bootline,而後把相應的信息存放在下面的結構體中spa

#define HUGE_MAX_HSTATE 2
struct hstate hstates[HUGE_MAX_HSTATE];

void __init hugetlb_add_hstate(unsigned order)
{
    struct hstate *h;
    unsigned long i;

    if (size_to_hstate(PAGE_SIZE << order)) {
        pr_warning("hugepagesz= specified twice, ignoring\n");
        return;
    }
    BUG_ON(hugetlb_max_hstate >= HUGE_MAX_HSTATE);
    BUG_ON(order == 0);
    h = &hstates[hugetlb_max_hstate++];
    ...
    snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB",
                    huge_page_size(h)/1024);

    parsed_hstate = h; //解析hugepages參數時要使用這個變量
}


static __init int setup_hugepagesz(char *opt)
{
    unsigned long ps = memparse(opt, &opt);
    if (ps == PMD_SIZE) {//2M大頁
        hugetlb_add_hstate(PMD_SHIFT - PAGE_SHIFT);
    } else if (ps == PUD_SIZE) {//1G大頁
        hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT);
    } else {
        pr_err("hugepagesz: Unsupported page size %lu M\n", ps >> 20);
        return 0;
    }
    return 1;
}
__setup("hugepagesz=", setup_hugepagesz);

5.如何使用指定的大頁size

在使用mmap或者shmget獲取大頁時,若是直接使用MAP_HUGETLB或SHM_HUGETLB參數,則獲取的大頁size是2M.net

FILE:mmap.c
...
else if (flags & MAP_HUGETLB) {
        struct user_struct *user = NULL;
        struct hstate *hs;

        hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT) & SHM_HUGE_MASK);
        if (!hs)
            return -EINVAL;
...

FILE:hugetlb.h
static inline struct hstate *hstate_sizelog(int page_size_log)
{
    if (!page_size_log)
        return &default_hstate;

    return size_to_hstate(1UL << page_size_log);
}

#define default_hstate (hstates[default_hstate_idx])

若是須要指定大頁的size,則可使用MAP或SHM flag的bit[31:26],這6個bit的取值範圍是0~31,所以能夠表示的頁大小從0~2G,基本夠用了。下面是SHM flag的代碼,MAP flag與此相似rest

/* Bits [26:31] are reserved */

/*
 * When SHM_HUGETLB is set bits [26:31] encode the log2 of the huge page size.
 * This gives us 6 bits, which is enough until someone invents 128 bit address
 * spaces.
 *
 * Assume these are all power of twos.
 * When 0 use the default page size.
 */
#define SHM_HUGE_SHIFT  26
#define SHM_HUGE_MASK   0x3f
#define SHM_HUGE_2MB    (21 << SHM_HUGE_SHIFT)
#define SHM_HUGE_1GB    (30 << SHM_HUGE_SHIFT)

還有一種方法能夠指定大頁的size,那就是在mount時經過pagesize選項指定:
mount -t hugetlbfs -o pagesize=1G none /dev/hugepages1G

對應的代碼以下所示:

FILE:fs/hugetlbfs/inode.c

        case Opt_pagesize: {
            unsigned long ps;
            ps = memparse(args[0].from, &rest);
            //根據輸入的參數到hstates數組中查找匹配的數組項
            pconfig->hstate = size_to_hstate(ps);
            if (!pconfig->hstate) {
                //找不到匹配的大小就報錯
                pr_err("Unsupported page size %lu MB\n",
                    ps >> 20);
                return -EINVAL;
            }
            break;
        }

//最終hstate信息被保存在hugetlbfs的sb結構中
struct hugetlbfs_sb_info *sbinfo;
sb->s_fs_info = sbinfo;
sbinfo->hstate = config.hstate;

//後續hugetlbfs中的文件均可以經過file結構體關聯到hstate結構體
static inline struct hstate *hstate_inode(struct inode *i)
{
    struct hugetlbfs_sb_info *hsb;
    hsb = HUGETLBFS_SB(i->i_sb);
    return hsb->hstate;
}

static inline struct hstate *hstate_file(struct file *f)
{
    return hstate_inode(file_inode(f));
}

//從而mmap就知道使用什麼size的大頁了,具體能夠參見hugetlbfs_file_mmap函數

6.hugepage的缺頁處理

hugepage在缺頁時與普通小頁的缺頁流程是有區別的,具體流程以下
do_page_fault->__do_page_fault->handle_mm_fault->__handle_mm_fault->hugetlb_fault->hugetlb_no_page->alloc_huge_page
能夠看出在__handle_mm_fault對於大頁缺頁直接調用了hugetlb_fault。另外多說一點,alloc_huge_page函數的參數以下所示

static struct page *alloc_huge_page(struct vm_area_struct *vma,
                    unsigned long addr, int avoid_reserve)
//經過下面的代碼能夠經過vma或者hstate
struct hstate *h = hstate_vma(vma);
//本質上仍是調用了hstate_vma函數
static inline struct hstate *hstate_vma(struct vm_area_struct *vma)
{
    return hstate_file(vma->vm_file);
}

7.hugepage擴展

若是要同時支持2種以上的大頁類型,好比2M,32M,1G,能夠修改HUGE_MAX_HSTATE宏定義,同時也要修改
hugepage pte的底層操做接口,下面的例子是arm64 contiguous-tlb的patch,可供參考。
http://www.spinics.net/lists/...

相關文章
相關標籤/搜索