diff options
author | Kirill A. Shutemov <kirill.shutemov@linux.intel.com> | 2014-04-03 17:48:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-03 19:21:03 -0400 |
commit | ec47c3b9543054f6f255d027100fa8214e637003 (patch) | |
tree | 9f8a3b0ad6e2c8fe110a2c68ae960426067e1798 /mm/memory.c | |
parent | e655fb29074a7aa471bfc9f51a0139c6f636a649 (diff) |
mm: introduce do_cow_fault()
Introduce do_cow_fault(). The function does what do_fault() does for
write page faults to private mappings.
Unlike do_fault(), do_read_fault() is relatively clean and
straight-forward.
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Rik van Riel <riel@redhat.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Matthew Wilcox <matthew.r.wilcox@intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/memory.c')
-rw-r--r-- | mm/memory.c | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/mm/memory.c b/mm/memory.c index 56784e9a7151..5be13e794a7c 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -3354,6 +3354,62 @@ static int do_read_fault(struct mm_struct *mm, struct vm_area_struct *vma, | |||
3354 | return ret; | 3354 | return ret; |
3355 | } | 3355 | } |
3356 | 3356 | ||
3357 | static int do_cow_fault(struct mm_struct *mm, struct vm_area_struct *vma, | ||
3358 | unsigned long address, pmd_t *pmd, | ||
3359 | pgoff_t pgoff, unsigned int flags, pte_t orig_pte) | ||
3360 | { | ||
3361 | struct page *fault_page, *new_page; | ||
3362 | spinlock_t *ptl; | ||
3363 | pte_t entry, *pte; | ||
3364 | int ret; | ||
3365 | |||
3366 | if (unlikely(anon_vma_prepare(vma))) | ||
3367 | return VM_FAULT_OOM; | ||
3368 | |||
3369 | new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address); | ||
3370 | if (!new_page) | ||
3371 | return VM_FAULT_OOM; | ||
3372 | |||
3373 | if (mem_cgroup_newpage_charge(new_page, mm, GFP_KERNEL)) { | ||
3374 | page_cache_release(new_page); | ||
3375 | return VM_FAULT_OOM; | ||
3376 | } | ||
3377 | |||
3378 | ret = __do_fault(vma, address, pgoff, flags, &fault_page); | ||
3379 | if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY))) | ||
3380 | goto uncharge_out; | ||
3381 | |||
3382 | copy_user_highpage(new_page, fault_page, address, vma); | ||
3383 | __SetPageUptodate(new_page); | ||
3384 | |||
3385 | pte = pte_offset_map_lock(mm, pmd, address, &ptl); | ||
3386 | if (unlikely(!pte_same(*pte, orig_pte))) { | ||
3387 | pte_unmap_unlock(pte, ptl); | ||
3388 | unlock_page(fault_page); | ||
3389 | page_cache_release(fault_page); | ||
3390 | goto uncharge_out; | ||
3391 | } | ||
3392 | |||
3393 | flush_icache_page(vma, new_page); | ||
3394 | entry = mk_pte(new_page, vma->vm_page_prot); | ||
3395 | entry = maybe_mkwrite(pte_mkdirty(entry), vma); | ||
3396 | inc_mm_counter_fast(mm, MM_ANONPAGES); | ||
3397 | page_add_new_anon_rmap(new_page, vma, address); | ||
3398 | set_pte_at(mm, address, pte, entry); | ||
3399 | |||
3400 | /* no need to invalidate: a not-present page won't be cached */ | ||
3401 | update_mmu_cache(vma, address, pte); | ||
3402 | |||
3403 | pte_unmap_unlock(pte, ptl); | ||
3404 | unlock_page(fault_page); | ||
3405 | page_cache_release(fault_page); | ||
3406 | return ret; | ||
3407 | uncharge_out: | ||
3408 | mem_cgroup_uncharge_page(new_page); | ||
3409 | page_cache_release(new_page); | ||
3410 | return ret; | ||
3411 | } | ||
3412 | |||
3357 | /* | 3413 | /* |
3358 | * do_fault() tries to create a new page mapping. It aggressively | 3414 | * do_fault() tries to create a new page mapping. It aggressively |
3359 | * tries to share with existing pages, but makes a separate copy if | 3415 | * tries to share with existing pages, but makes a separate copy if |
@@ -3550,6 +3606,9 @@ static int do_linear_fault(struct mm_struct *mm, struct vm_area_struct *vma, | |||
3550 | if (!(flags & FAULT_FLAG_WRITE)) | 3606 | if (!(flags & FAULT_FLAG_WRITE)) |
3551 | return do_read_fault(mm, vma, address, pmd, pgoff, flags, | 3607 | return do_read_fault(mm, vma, address, pmd, pgoff, flags, |
3552 | orig_pte); | 3608 | orig_pte); |
3609 | if (!(vma->vm_flags & VM_SHARED)) | ||
3610 | return do_cow_fault(mm, vma, address, pmd, pgoff, flags, | ||
3611 | orig_pte); | ||
3553 | return do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); | 3612 | return do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); |
3554 | } | 3613 | } |
3555 | 3614 | ||
@@ -3585,6 +3644,9 @@ static int do_nonlinear_fault(struct mm_struct *mm, struct vm_area_struct *vma, | |||
3585 | if (!(flags & FAULT_FLAG_WRITE)) | 3644 | if (!(flags & FAULT_FLAG_WRITE)) |
3586 | return do_read_fault(mm, vma, address, pmd, pgoff, flags, | 3645 | return do_read_fault(mm, vma, address, pmd, pgoff, flags, |
3587 | orig_pte); | 3646 | orig_pte); |
3647 | if (!(vma->vm_flags & VM_SHARED)) | ||
3648 | return do_cow_fault(mm, vma, address, pmd, pgoff, flags, | ||
3649 | orig_pte); | ||
3588 | return do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); | 3650 | return do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); |
3589 | } | 3651 | } |
3590 | 3652 | ||