Linux(x86-32bit)のページフォルトハンドラを読んでみる(その6)

## 今回やること
どうもです。@akachochinです。
[前回](http://qiita.com/akachochin/items/e7ffdca00c00d322022a)読んだdo_linear_fault()の続きです。
do_linear_fault()は前回の表を見るとわかりますが、バッキングストアあり/物理メモリ未割り当ての領域に対するページフォルトをハンドリングする初理です。

COWについては前回やりましたので、残り2つ(Read/Shared Write)について見たいと思います。


## まずは、do_read_fault()
バッキングストアありの領域/物理メモリ未割付/アクセスreadのときに以下の関数が呼ばれます。

```c:mm/memory.c
static int do_read_fault(struct mm_struct *mm, struct vm_area_struct *vma,
    unsigned long address, pmd_t *pmd,
    pgoff_t pgoff, unsigned int flags, pte_t orig_pte)
{
  /* 略 */
  ret = __do_fault(vma, address, pgoff, flags, &fault_page);
  if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY)))
    return ret;

  /* 略 */
  do_set_pte(vma, address, fault_page, pte, false, false);
  /* 略 */

  return ret;
}
```

__do_fault()で本筋の処理を行い、アドレス解決した結果をdo_set_pteでPTEに書き込むというところが大筋だとわかります。

## 次は、do_shared_fault()
バッキングストアありの領域/物理メモリ未割付/アクセスwriteのときに以下の関数が呼ばれます。
バッキングストアを共有しているので、書いた内容は他の共有者からも見えることになります。なので「shared」なのです。この点がCOWと違うところです。

```c:mm/memory.c
static int do_shared_fault(struct mm_struct *mm, struct vm_area_struct *vma,
    unsigned long address, pmd_t *pmd,
    pgoff_t pgoff, unsigned int flags, pte_t orig_pte)
{
  /* 略 */
  ret = __do_fault(vma, address, pgoff, flags, &fault_page);
  if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY)))
    return ret;

  /*
   * Check if the backing address space wants to know that the page is
   * about to become writable
   */
  /* バッキングストアがpage_mkwriteというI/Fを持つ場合、それを
   * do_page_mkwrite()で呼び出す。
   * page_mkwriteは「ページが書き込み可能になることを通知すること
   * でバッキングストア側で必要な処理をさせたり拒否させたりする」
   * ためのI/F。詳細はmm/memory.cのdo_page_mkwrite()参照。
   */
  if (vma->vm_ops->page_mkwrite) {
    unlock_page(fault_page);
    tmp = do_page_mkwrite(vma, fault_page, address);
    if (unlikely(!tmp ||
        (tmp & (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))) {
      page_cache_release(fault_page);
      return tmp;
    }
  }

  /* 略 */
  do_set_pte(vma, address, fault_page, pte, true, false);
  pte_unmap_unlock(pte, ptl);

  if (set_page_dirty(fault_page))
    dirtied = 1;
  mapping = fault_page->mapping;
  unlock_page(fault_page);
  /* 略 */
  /* file_update_time outside page_lock */
  /* バッキングストアにpage_mkwrite()がない場合、バッキングストア
   * に通知ができないので、せめて更新時刻だけでも変更する
   */
  if (vma->vm_file && !vma->vm_ops->page_mkwrite)
    file_update_time(vma->vm_file);

  return ret;
}
```

先のdo_read_fault()もdo_shared_fault()も肝心なところは__do_fault()で実施しているようだ。

## __do_fault()
よって、__do_fault()を見る。

```c:mm/fault.c
static int __do_fault(struct vm_area_struct *vma, unsigned long address,
    pgoff_t pgoff, unsigned int flags, struct page **page)
{
  struct vm_fault vmf;
  int ret;

  vmf.virtual_address = (void __user *)(address & PAGE_MASK);
  vmf.pgoff = pgoff;
  vmf.flags = flags;
  vmf.page = NULL;

  ret = vma->vm_ops->fault(vma, &vmf);
  if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY)))
    return ret; 

  if (unlikely(PageHWPoison(vmf.page))) {
    if (ret & VM_FAULT_LOCKED)
      unlock_page(vmf.page);
    page_cache_release(vmf.page);
    return VM_FAULT_HWPOISON;
  }

  if (unlikely(!(ret & VM_FAULT_LOCKED)))
    lock_page(vmf.page);
  else
    VM_BUG_ON_PAGE(!PageLocked(vmf.page), vmf.page);

  *page = vmf.page;
  return ret;
}
```

肝心のところは、ファイルのページャ(vma->vm_ops->fault)次第ということがわかります。
バッキングストアは各ファイル種別(Regular fileとかShared memoryなど)ごとに処理が違うためにこのようなことになっています。

## 次回
並行でAnonymous Memory(バッキングストアなし)の領域/物理メモリ未割付のケース、つまりdo_anonymous_page()を読みます。