今回やること
どうもです。@akachochinです。
前回読んだdo_linear_fault()の続きです。
do_linear_fault()は前回の表を見るとわかりますが、バッキングストアあり/物理メモリ未割り当ての領域に対するページフォルトをハンドリングする初理です。
COWについては前回やりましたので、残り2つ(Read/Shared Write)について見たいと思います。
まずは、do_read_fault()
バッキングストアありの領域/物理メモリ未割付/アクセスreadのときに以下の関数が呼ばれます。
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と違うところです。
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()を見る。
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()を読みます。