ret = vma->vm_ops->fault(vma, &vmf);//调用定义好的fault函数,确保将所需的文件数据读入到映射页
if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) return ret;
if (unlikely(PageHWPoison(vmf.page))) { if (ret & VM_FAULT_LOCKED) unlock_page(vmf.page); return VM_FAULT_HWPOISON; }
/* * For consistency in subsequent calls, make the faulted page always * locked. */ if (unlikely(!(ret & VM_FAULT_LOCKED))) lock_page(vmf.page); else VM_BUG_ON(!PageLocked(vmf.page));
/* * Should we do an early C-O-W break? */ page = vmf.page; if (flags & FAULT_FLAG_WRITE) {//写访问 if (!(vma->vm_flags & VM_SHARED)) {//私有映射,则要创建一个副本进行写时复制 anon = 1;// 标记为一个匿名映射 if (unlikely(anon_vma_prepare(vma))) {//创建一个anon_vma实例给vma ret = VM_FAULT_OOM; goto out; } page = alloc_page_vma(GFP_HIGHUSER_MOVABLE,//分配一个页 vma, address); if (!page) { ret = VM_FAULT_OOM; goto out; } if (mem_cgroup_newpage_charge(page, mm, GFP_KERNEL)) { ret = VM_FAULT_OOM; page_cache_release(page); goto out; } charged = 1; /* * Don"t let another task, with possibly unlocked vma, * keep the mlocked page. */ if (vma->vm_flags & VM_LOCKED) clear_page_mlock(vmf.page); /*创建数据的副本,将数据拷贝到新分配的页*/ copy_user_highpage(page, vmf.page, address, vma); __SetPageUptodate(page); } else { /* * If the page will be shareable, see if the backing * address space wants to know that the page is about * to become writable */ if (vma->vm_ops->page_mkwrite) { int tmp;
unlock_page(page); vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE; tmp = vma->vm_ops->page_mkwrite(vma, &vmf); if (unlikely(tmp & (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { ret = tmp; goto unwritable_page; } if (unlikely(!(tmp & VM_FAULT_LOCKED))) { lock_page(page); if (!page->mapping) { ret = 0; /* retry the fault */ unlock_page(page); goto unwritable_page; } } else VM_BUG_ON(!PageLocked(page)); page_mkwrite = 1; } }
/* * This silly early PAGE_DIRTY setting removes a race * due to the bad i386 page protection. But it"s valid * for other architectures too. * * Note that if FAULT_FLAG_WRITE is set, we either now have * an exclusive copy of the page, or this is a shared mapping, * so we can make it writable and dirty to avoid having to * handle that later. */ /* Only go through if we didn"t race with anybody else... */ if (likely(pte_same(*page_table, orig_pte))) {//确定没有竞争,也就是页表项中的内容和之前是一样的 flush_icache_page(vma, page); entry = mk_pte(page, vma->vm_page_prot);//页表项指向对应的物理页
/*如果是写操作,则将页的访问权限置为RW*/ if (flags & FAULT_FLAG_WRITE) entry = maybe_mkwrite(pte_mkdirty(entry), vma);
/* no need to invalidate: a not-present page won"t be cached */ update_mmu_cache(vma, address, entry); } else { if (charged) mem_cgroup_uncharge_page(page); if (anon) page_cache_release(page); else anon = 1; /* no anon but release faulted_page */ }
pte_unmap_unlock(page_table, ptl);
out: if (dirty_page) { struct address_space *mapping = page->mapping;
if (set_page_dirty(dirty_page)) page_mkwrite = 1; unlock_page(dirty_page); put_page(dirty_page); if (page_mkwrite && mapping) { /* * Some device drivers do not set page.mapping but still * dirty their pages */ balance_dirty_pages_ratelimited(mapping); }
/* file_update_time outside page_lock */ if (vma->vm_file) file_update_time(vma->vm_file); } else { unlock_page(vmf.page); if (anon) page_cache_release(vmf.page); }