vmalloc中物理地址重映射函數分析

vmalloc中物理地址重映射函數分析

                                     Figure 1:Vmalloc Areas

                           Figure 2:將非連續3個物理pages映射到連續虛擬地址

 

注:圖片來至《Professional Linux kernel architecture》

函數調用關係:

1) vmap_page_range(unsigned long start, unsigned long end,
      pgprot_t prot, struct page **pages)

 

2) static int vmap_page_range_noflush(unsigned long start, unsigned long end,
       pgprot_t prot, struct page **pages)

 

3) static int vmap_pud_range(pgd_t *pgd, unsigned long addr,
  unsigned long end, pgprot_t prot, struct page **pages, int *nr)

 

4) static int vmap_pmd_range(pud_t *pud, unsigned long addr,
  unsigned long end, pgprot_t prot, struct page **pages, int *nr)

//該兩個函數實際沒做任何事,因爲ARM920T只支持2級映射

 

5) static int vmap_pte_range(pmd_t *pmd, unsigned long addr,
  unsigned long end, pgprot_t prot, struct page **pages, int *nr)

//實際填充一級二級頁表的函數

 

6) flush_cache_vmap(start, end);

 

 

FIXME:以ARM920T爲例,small page(4KiB)映射,MMU只支持2級頁表映射,PGD &PTE.

kernel:2.6.35

 

#ifndef VMALLOC_START
#define VMALLOC_OFFSET  (8*1024*1024)
#define VMALLOC_START  (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#endif

 

 

static int vmap_page_range(unsigned long start, unsigned long end,
      pgprot_t prot, struct page **pages)
{
     int ret;

     ret = vmap_page_range_noflush(start, end, prot, pages);

     //start,end爲開始的虛擬地址,範圍在VMALLOC_START ~ VMALLOC_END之間

     //VMALLOC_START = high_mem+8MiB

     //prot爲MMU頁表標誌位

     //pages指向物理頁框表的指針(非連續的物理頁框)


     flush_cache_vmap(start, end);
     return ret;
}

 

 

static int vmap_page_range_noflush(unsigned long start, unsigned long end,
       pgprot_t prot, struct page **pages)
{
     pgd_t *pgd;
     unsigned long next;
     unsigned long addr = start;
     int err = 0;
     int nr = 0;

     BUG_ON(addr >= end);
     pgd = pgd_offset_k(addr);

     //得到的pgd的地址,pgd的基地址+(addr>>21),兩個連續的一級查找表(2×1MiB)
     do {
     next = pgd_addr_end(addr, end);
     err = vmap_pud_range(pgd, addr, next, prot, pages, &nr);
     if (err)
            return err;
     } while (pgd++, addr = next, addr != end);

     return nr;
}

 

 

static int vmap_pud_range(pgd_t *pgd, unsigned long addr,
  unsigned long end, pgprot_t prot, struct page **pages, int *nr)
{
 pud_t *pud;
 unsigned long next;

 pud = pud_alloc(&init_mm, pgd, addr);

 //定義了4level-fixup.h後,pud得到的還是pgd的地址

 //#define pud_t    pgd_t
 if (!pud)
  return -ENOMEM;
 do {
  next = pud_addr_end(addr, end);
  if (vmap_pmd_range(pud, addr, next, prot, pages, nr))
   return -ENOMEM;
 } while (pud++, addr = next, addr != end);
 return 0;
}

 

 

 

 

static int vmap_pmd_range(pud_t *pud, unsigned long addr,
  unsigned long end, pgprot_t prot, struct page **pages, int *nr)
{
 pmd_t *pmd;
 unsigned long next;

 pmd = pmd_alloc(&init_mm, pud, addr);

 //同pud_alloc,因爲只支持2級頁表映射,這個函數同樣返回pud的地址
 if (!pmd)
  return -ENOMEM;
 do {
  next = pmd_addr_end(addr, end);
  if (vmap_pte_range(pmd, addr, next, prot, pages, nr))
   return -ENOMEM;
 } while (pmd++, addr = next, addr != end);
 return 0;
}

 

 

//vmap映射的最關鍵函數

static int vmap_pte_range(pmd_t *pmd, unsigned long addr,
  unsigned long end, pgprot_t prot, struct page **pages, int *nr)
{
 pte_t *pte;

 /*
  * nr is a running index into the array which helps higher level
  * callers keep track of where we're up to.
  */

 pte = pte_alloc_kernel(pmd, addr);

 //分配二級表地址並返回,並完成一級表的賦值工作,*pmd中填寫二級頁表的基地址,

 //參照create_mapping
 if (!pte)
  return -ENOMEM;
 do {
  struct page *page = pages[*nr];

  if (WARN_ON(!pte_none(*pte)))
   return -EBUSY;
  if (WARN_ON(!page))
   return -ENOMEM;
  set_pte_at(&init_mm, addr, pte, mk_pte(page, prot));

  //設置2級頁表pte的內容,完成一次Vmap
  (*nr)++;
 } while (pte++, addr += PAGE_SIZE, addr != end);
 return 0;
}

 

 

#define pte_alloc_kernel(pmd, address)   /
 ((unlikely(!pmd_present(*(pmd))) && __pte_alloc_kernel(pmd, address))? /
  NULL: pte_offset_kernel(pmd, address))

//pmd_present(*(pmd))在未填充時爲空,!pmd_present(*(pmd))返回true

 

 

 

int __pte_alloc_kernel(pmd_t *pmd, unsigned long address)
{
 pte_t *new = pte_alloc_one_kernel(&init_mm, address);

 //利用Buddy allocator分配1page的空間
 if (!new)
  return -ENOMEM;

 smp_wmb(); /* See comment in __pte_alloc */

 spin_lock(&init_mm.page_table_lock);
 if (!pmd_present(*pmd)) { /* Has another populated it ? */
  pmd_populate_kernel(&init_mm, pmd, new);

  //給pgd的地址空間賦值,爲pte的基地址
  new = NULL;
 }
 spin_unlock(&init_mm.page_table_lock);
 if (new)
  pte_free_kernel(&init_mm, new);
 return 0;

 //返回爲false
}

 

Author :woodpecker <[email protected]>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章