読者です 読者をやめる 読者になる 読者になる

Linux: pud_alloc()周りのメモ

linux x86_64 kernel

前回はPGD周りをみたので今回はPUD周りを。PUDを作るのはpuc_alloc()でこれはいくつかの呼び出しパターンがあるのだけど(lxrでIdentifier Searchした結果からして)、一番基本であろうページフォルトの時で調べる。
ページフォルト発生時は__handle_mm_fault()内でpud_alloc()が呼ばれる。ここまでの流れは以下のようなフロー。ページフォルトが起きてarch依存部分からmmのほうに処理が流れていってます。

do_page_fault() @arch/x86/mm/fault.c
 -> __do_page_fault() @arch/x86/mm/fault.c
    -> handle_mm_fault() @mm/memory.c
      -> __handle_mm_fault() @mm/memory.c
        -> pud_alloc() @linux/mm.h

pud_alloc()はこのような関数で、pud_alloc()でpudの領域を作成してページフォルトが起きたアドレス(address)の該当するpudを返します。というわけで、pud_alloc()がここでのメイン処理ですね。

1379 static inline pud_t *pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
1380 {
1381         return (unlikely(pgd_none(*pgd)) && __pud_alloc(mm, pgd, address))?
1382                 NULL: pud_offset(pgd, address);
1383 }
1384 

__pud_alloc()はこのような処理です。大雑把に言うと、最初にpud_tの変数を作成するんだけど、もしpgtが既に設定されているならそれを使うので新規に作成したpudは破棄し、そうじゃない場合は、pgdに対して今作ったpudが設定される。

3990 int __pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
3991 {
3992         pud_t *new = pud_alloc_one(mm, address);
3993         if (!new)
3994                 return -ENOMEM;
3995 
3996         smp_wmb(); /* See comment in __pte_alloc */
3997 
3998         spin_lock(&mm->page_table_lock);
3999         if (pgd_present(*pgd))          /* Another has populated it */
4000                 pud_free(mm, new);
4001         else
4002                 pgd_populate(mm, pgd, new);
4003         spin_unlock(&mm->page_table_lock);
4004         return 0;
4005 }

pgd_populate()はこのような関数。

120 static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud)
121 {
122         paravirt_alloc_pud(mm, __pa(pud) >> PAGE_SHIFT);
123         set_pgd(pgd, __pgd(_PAGE_TABLE | __pa(pud)));
124 }

pmdもpudと同じような感じなんだけどそんなの面白いこともなさそうなのでこの辺で良いかな。。PGDをいつ作ってるのか?ってのが知りたかったというのが最初の目的だし。

ところで、__pud_alloc()で気になったのは何でpgd_present()で調べる前にpudをallocしてるのかってところなんですね。メモリバリアを張っている箇所のコメントに__pte_alloc()を見ろと書いてあるので確認をしてみます。

561 int __pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
562                 pmd_t *pmd, unsigned long address)
563 {
564         spinlock_t *ptl;
565         pgtable_t new = pte_alloc_one(mm, address);
566         int wait_split_huge_page;
567         if (!new)
568                 return -ENOMEM;
569 
570         /*
571          * Ensure all pte setup (eg. pte page lock and page clearing) are
572          * visible before the pte is made visible to other CPUs by being
573          * put into page tables.
574          *
575          * The other side of the story is the pointer chasing in the page
576          * table walking code (when walking the page table without locking;
577          * ie. most of the time). Fortunately, these data accesses consist
578          * of a chain of data-dependent loads, meaning most CPUs (alpha
579          * being the notable exception) will already guarantee loads are
580          * seen in-order. See the alpha page table accessors for the
581          * smp_read_barrier_depends() barriers in page table walking code.
582          */
583         smp_wmb(); /* Could be smp_wmb__xxx(before|after)_spin_lock */
584 

fmfm。どうなんだろ。 確実に言えるのはpudのallocはget_zeroed_page()を使っていて、この時のGFPフラグにGFP_KERNELを使っているので必要に応じてにsleepできるんだけど、そうするとpgd_present()を行った時にはスピンロックを取っているので寝るわけには行かない。というわけでロックを取る前にallocの処理をして不要ならfree()という感じかなあ。

126 static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr)
127 {
128         return (pud_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT);
129 }