|
@@ -1457,25 +1457,60 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
{
|
|
{
|
|
struct page *old_page, *new_page;
|
|
struct page *old_page, *new_page;
|
|
pte_t entry;
|
|
pte_t entry;
|
|
- int ret = VM_FAULT_MINOR;
|
|
|
|
|
|
+ int reuse, ret = VM_FAULT_MINOR;
|
|
|
|
|
|
old_page = vm_normal_page(vma, address, orig_pte);
|
|
old_page = vm_normal_page(vma, address, orig_pte);
|
|
if (!old_page)
|
|
if (!old_page)
|
|
goto gotten;
|
|
goto gotten;
|
|
|
|
|
|
- if (PageAnon(old_page) && !TestSetPageLocked(old_page)) {
|
|
|
|
- int reuse = can_share_swap_page(old_page);
|
|
|
|
- unlock_page(old_page);
|
|
|
|
- if (reuse) {
|
|
|
|
- flush_cache_page(vma, address, pte_pfn(orig_pte));
|
|
|
|
- entry = pte_mkyoung(orig_pte);
|
|
|
|
- entry = maybe_mkwrite(pte_mkdirty(entry), vma);
|
|
|
|
- ptep_set_access_flags(vma, address, page_table, entry, 1);
|
|
|
|
- update_mmu_cache(vma, address, entry);
|
|
|
|
- lazy_mmu_prot_update(entry);
|
|
|
|
- ret |= VM_FAULT_WRITE;
|
|
|
|
- goto unlock;
|
|
|
|
|
|
+ if (unlikely((vma->vm_flags & (VM_SHARED|VM_WRITE)) ==
|
|
|
|
+ (VM_SHARED|VM_WRITE))) {
|
|
|
|
+ if (vma->vm_ops && vma->vm_ops->page_mkwrite) {
|
|
|
|
+ /*
|
|
|
|
+ * Notify the address space that the page is about to
|
|
|
|
+ * become writable so that it can prohibit this or wait
|
|
|
|
+ * for the page to get into an appropriate state.
|
|
|
|
+ *
|
|
|
|
+ * We do this without the lock held, so that it can
|
|
|
|
+ * sleep if it needs to.
|
|
|
|
+ */
|
|
|
|
+ page_cache_get(old_page);
|
|
|
|
+ pte_unmap_unlock(page_table, ptl);
|
|
|
|
+
|
|
|
|
+ if (vma->vm_ops->page_mkwrite(vma, old_page) < 0)
|
|
|
|
+ goto unwritable_page;
|
|
|
|
+
|
|
|
|
+ page_cache_release(old_page);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Since we dropped the lock we need to revalidate
|
|
|
|
+ * the PTE as someone else may have changed it. If
|
|
|
|
+ * they did, we just return, as we can count on the
|
|
|
|
+ * MMU to tell us if they didn't also make it writable.
|
|
|
|
+ */
|
|
|
|
+ page_table = pte_offset_map_lock(mm, pmd, address,
|
|
|
|
+ &ptl);
|
|
|
|
+ if (!pte_same(*page_table, orig_pte))
|
|
|
|
+ goto unlock;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ reuse = 1;
|
|
|
|
+ } else if (PageAnon(old_page) && !TestSetPageLocked(old_page)) {
|
|
|
|
+ reuse = can_share_swap_page(old_page);
|
|
|
|
+ unlock_page(old_page);
|
|
|
|
+ } else {
|
|
|
|
+ reuse = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (reuse) {
|
|
|
|
+ flush_cache_page(vma, address, pte_pfn(orig_pte));
|
|
|
|
+ entry = pte_mkyoung(orig_pte);
|
|
|
|
+ entry = maybe_mkwrite(pte_mkdirty(entry), vma);
|
|
|
|
+ ptep_set_access_flags(vma, address, page_table, entry, 1);
|
|
|
|
+ update_mmu_cache(vma, address, entry);
|
|
|
|
+ lazy_mmu_prot_update(entry);
|
|
|
|
+ ret |= VM_FAULT_WRITE;
|
|
|
|
+ goto unlock;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1535,6 +1570,10 @@ oom:
|
|
if (old_page)
|
|
if (old_page)
|
|
page_cache_release(old_page);
|
|
page_cache_release(old_page);
|
|
return VM_FAULT_OOM;
|
|
return VM_FAULT_OOM;
|
|
|
|
+
|
|
|
|
+unwritable_page:
|
|
|
|
+ page_cache_release(old_page);
|
|
|
|
+ return VM_FAULT_SIGBUS;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -2083,18 +2122,31 @@ retry:
|
|
/*
|
|
/*
|
|
* Should we do an early C-O-W break?
|
|
* Should we do an early C-O-W break?
|
|
*/
|
|
*/
|
|
- if (write_access && !(vma->vm_flags & VM_SHARED)) {
|
|
|
|
- struct page *page;
|
|
|
|
|
|
+ if (write_access) {
|
|
|
|
+ if (!(vma->vm_flags & VM_SHARED)) {
|
|
|
|
+ struct page *page;
|
|
|
|
|
|
- if (unlikely(anon_vma_prepare(vma)))
|
|
|
|
- goto oom;
|
|
|
|
- page = alloc_page_vma(GFP_HIGHUSER, vma, address);
|
|
|
|
- if (!page)
|
|
|
|
- goto oom;
|
|
|
|
- copy_user_highpage(page, new_page, address);
|
|
|
|
- page_cache_release(new_page);
|
|
|
|
- new_page = page;
|
|
|
|
- anon = 1;
|
|
|
|
|
|
+ if (unlikely(anon_vma_prepare(vma)))
|
|
|
|
+ goto oom;
|
|
|
|
+ page = alloc_page_vma(GFP_HIGHUSER, vma, address);
|
|
|
|
+ if (!page)
|
|
|
|
+ goto oom;
|
|
|
|
+ copy_user_highpage(page, new_page, address);
|
|
|
|
+ page_cache_release(new_page);
|
|
|
|
+ new_page = page;
|
|
|
|
+ anon = 1;
|
|
|
|
+
|
|
|
|
+ } 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 &&
|
|
|
|
+ vma->vm_ops->page_mkwrite(vma, new_page) < 0
|
|
|
|
+ ) {
|
|
|
|
+ page_cache_release(new_page);
|
|
|
|
+ return VM_FAULT_SIGBUS;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
page_table = pte_offset_map_lock(mm, pmd, address, &ptl);
|
|
page_table = pte_offset_map_lock(mm, pmd, address, &ptl);
|