程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> hugetlb mips 分析

hugetlb mips 分析

編輯:C++入門知識

mips64 xlp832架構CPU 在閱讀此文前,可先閱讀ibm文庫中關於x86巨頁的使用,優點以及原理的文章:   巨頁的原理,概括起來,就是在內核頁面大小一定的情況下,分配物理地址連續的多個頁框,模擬出一個大頁面供用戶態程序訪問,從而減少用戶程序缺頁次數,提高性能。 為了讓內核將這連續的多個頁框視為一個整體,各個CPU架構分別做了手腳。 首先,hugetlbfs並不支持read/write。 實際上,巨頁的使用,是通過用戶態mmap一個hugetlbfs文件,然後第一次訪問時缺頁來完成。 例如,對於x86來說,有一個CR3頁表基址寄存器。   當發生普通頁面缺頁時(其實准確的講應該叫tlb miss),CPU根據CR3的內容,找到本進程的頁表基址,再依次遍歷頁表,最終定位到具體的頁表項(即pte entry)。 如果是hugetlb所在的虛擬空間缺頁,則do_page_fault會給倒數第二級的pmd entry加一個標志, 相當於告訴CPU說,pmd entry就是最後一級頁表。那麼當page fault返回,CPU再次訪問缺頁地址時,CPU遍歷頁表,當找到pmd entry就不會往下尋址了, 相當於提高了尋址范圍(此原理在上述ibm文檔中有詳細說明)   對於mips來說,由於頁表的遍歷和TLB的重填是軟件來完成的(x86通過硬件完成),因此這裡需要對頁表遍歷和TLB重填流程做一定修改。 所以,考察hugetlbfs的mmap驅動,以及hugetlbfs的page fault 。   mmap: hugetlbfs_file_mmap hugetlbfs_file_mmap主要是對映射到的vma區間設置VM_HUGETLB和VM_RESERVED屬性,前者用來在pagefault流程裡,識別巨頁 區間引發的缺頁,後者用來防止巨頁vma區間所包含的頁被換出。 page fault:  hugetlb_fault hugetlb_fault函數的作用,主要是分配出連續物理頁框,並把頁框地址按一定方式寫到頁表裡。這樣,再次發生tlb重填時,就可以根據頁表裡的 映射寫到tlb裡。 但是hugetlb的缺頁,和普通缺頁的區別又是在哪裡?根據上面的描述,我們提出兩個問題: 1)頁框地址按什麼方式寫到頁表裡 2)tlb重填時,如何根據頁表內容寫入tlb   我們繼續回到hugetlb_fault流程。 [cpp]  int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,               unsigned long address, unsigned int flags)   {       pte_t *ptep;           ptep = huge_pte_alloc(mm, address, huge_page_size(h));       entry = huge_ptep_get(ptep);       if (huge_pte_none(entry)) {           ret = hugetlb_no_page(mm, vma, address, ptep, flags);           goto out_mutex;       }   }     我們知道,對於普通的缺頁,需生成從pgd到pte的映射樹, 即,以發生缺頁的虛擬地址為key,依次搜索進程頁表,如果沒有對應的pte表, 就分配一個新的pte表,將pte表地址填入上一級的pmd entry,最後返回pte表裡,addr對應的pte entry地址(見pte_alloc_map函數)   然而對於巨頁來說,假設一個巨頁由n個連續頁框組成,則需要把這n個頁框可能的頁表路徑都先分配好(pgd->pud->pmd->pte), 也就是比普通缺頁,需要多“確認” (n-1)次頁表路徑的“暢通”。 所以,需要調用huge_pte_alloc函數,給addr --> (addr+n*PAGE_SIZE)的虛擬空間,按照PGAE_SIZE為步長,依次分配pgd到pte的路徑 當然,這裡最後一步,分配具體頁框還沒有執行。 [cpp]   pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr,                 unsigned long sz)   {       pte_t *pte = NULL;       unsigned long i = 0;       unsigned long htlb_entries = 1 << HUGETLB_PAGE_ORDER;          addr &= HPAGE_MASK;       for (i = 0; i < htlb_entries; i++) {           pte = huge_pte_alloc_single(mm, addr);           if (!pte)               return NULL;           addr += PAGE_SIZE;       }       return pte;   }     其中,htlb_entries是一個巨頁包含的頁框個數。 再接著往下看,如果是第一次訪問巨頁空間,那麼走的是hugetlb_no_page,這是個相對較大的函數。 [cpp]   static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma,               unsigned long address, pte_t *ptep, unsigned int flags)   {       idx = vma_hugecache_offset(h, vma, address);       page = find_lock_page(mapping, idx);       if (!page) {           size = i_size_read(mapping->host) >> huge_page_shift(h);           page = alloc_huge_page(vma, address, 0);           err = add_to_page_cache(page, mapping, idx, GFP_KERNEL);       }          new_pte = make_huge_pte(vma, page, ((vma->vm_flags & VM_WRITE)                   && (vma->vm_flags & VM_SHARED)));       set_huge_pte_at(mm, address, ptep, new_pte);   }     hugetlb_no_page負責分配一塊連續的頁框,根據一個巨頁所包含的虛擬空間,將所有涉及到的頁表項pte entry,都指向這塊連續頁框的起始地址。這樣,以後無論進程發生在這塊巨頁空間裡任何一處的tlb異常,都會將tlb的pfn指向這塊物理空間,保證完成最終的tlb映射。   vma_hugecache_offset根據傳入的缺頁地址address,計算出此address相對於整個地址空間的index(以巨頁大小為單位) 接著,根據idx在mapping空間裡找出對應的page, 即巨頁的第一個頁。如果找到不對應的頁,則說明還沒有分配巨頁,於是調用 alloc_huge_page,要麼從hstates高速緩沖,要麼從伙伴系統獲取物理連續的頁框,之後通過 add_to_page_cache加到page cache裡,可以看出,巨頁文件對應的mapping,都是由巨頁的index構成的radix tree。 緊接著,根據找到的page,生成一個pte entry,並給pte entry設置上_PAGE_HUGE標記,以便在缺頁返回後,再次訪址引起的tlb load異常時,可以 辨識出這是一個巨頁pte entry。 最後,set_huge_pte_at(mm,address,ptep,new_pte)是比較關鍵的地方。 [cpp]   void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep,   pte_t entry)   {       unsigned long i;       unsigned long htlb_entries = 1 << HUGETLB_PAGE_ORDER;       pte_t entry2;          entry2 =  __pte(pte_val(entry) + (HPAGE_SIZE >> 1));          addr &= HPAGE_MASK;       for (i = 0; i < htlb_entries; i += 2) {           ptep = huge_pte_offset(mm, addr);           set_pte_at(mm, addr, ptep, entry);           addr += PAGE_SIZE;              ptep = huge_pte_offset(mm, addr);           set_pte_at(mm, addr, ptep, entry2);           addr += PAGE_SIZE;       }   }     這個函數,首先計算出一個巨頁需要htlb_entries個連續頁框,接著,根據hugetlb奇數頁,加上一個巨頁一半大小,算出偶數頁所在的tlb entry2。 www.2cto.com 接著,對addr到addr+HPAGE_SIZE的空間內,凡是奇數頁地址,都設置為entry,而偶數頁則都設置為entry2,示意圖如下:       在 page fault返回後,再次訪址將產生tlb load/store異常。在build_r4000_tlbchange_handler_head函數裡, 經過一系列頁表尋址,找到pte entry條目,由於這個條目之前在hugetlb_no_page已經被設置了_PAGE_HUGE, 因此會走tlb_huge_update-->build_huge_handler_tail-->build_huge_update_entries將獲取到的奇偶頁表項, 寫入entrylo1 和 entrylo 2。 [cpp]   static __cpuinit void build_huge_update_entries(u32 **p,                           unsigned int pte,                           unsigned int tmp)   {       build_convert_pte_to_entrylo(p, pte);       UASM_i_MTC0(p, pte, C0_ENTRYLO0); /* load it */          uasm_i_ld(p, pte, sizeof(pte_t), tmp);       build_convert_pte_to_entrylo(p, pte);       UASM_i_MTC0(p, pte, C0_ENTRYLO1); /* load it */          uasm_i_ehb(p);   }     其中,uasm_i_ld(p,pte,sizeof(pte_t),tmp) 將傳入的奇數頁pte entry指針,加上一個pte_t的長度,取地址內容,得到偶數頁pte entry的值 接著通過build_huge_tlb_write_entry(p, l, r, pte, tlb_random);將奇偶數頁都寫入TLB條目。(因為此時系統中 沒有匹配虛擬地址的tlb條目,所以probe失敗,index小於0,會跳過build_huge_tlb_write_entry(p, l, r, pte, tlb_indexed); 直接走r4000_write_huge_probe_fail分支。 [cpp]   static __cpuinit void build_huge_tlb_write_entry(u32 **p,                            struct uasm_label **l,                            struct uasm_reloc **r,                            unsigned int tmp,                            enum tlb_write_entry wmode)   {       /* Set huge page tlb entry size */       uasm_i_lui(p, tmp, PM_HUGE_MASK >> 16);       uasm_i_ori(p, tmp, tmp, PM_HUGE_MASK & 0xffff);       uasm_i_mtc0(p, tmp, C0_PAGEMASK);          build_tlb_write_entry(p, l, r, wmode);          build_restore_pagemask(p, r, tmp, label_leave);      }     首先是根據巨頁大小設置pagemask,接著通過tlbw將pte entry寫入tlb 最後恢復pagemask跳轉至label_leave,tlb load退出。此時系統已經有了可用的巨頁tlb entry,以後訪問此巨頁空間不會再出現tlb miss了。   總結一下,tlb巨頁缺頁流程如下: tlb miss-->tlb refill填入invalid pte entry-->返回用戶態再次訪址發生tlb load異常-->tlb load異常發現頁表項不在內存中,引發page fault -->調用hugetlb_fault填充巨頁頁表項-->返回用戶態再次訪址發生tlb load異常(因為tlb裡仍然是invalid pte entry)--> 根據巨頁頁表項填充TLB條目-->返回用戶態再次訪址,不會出現tlb miss,程序正常運行。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved