|
|
@@ -2181,6 +2181,52 @@ oom:
|
|
|
return VM_FAULT_OOM;
|
|
|
}
|
|
|
|
|
|
+static int wp_page_shared(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
+ unsigned long address, pte_t *page_table,
|
|
|
+ pmd_t *pmd, spinlock_t *ptl, pte_t orig_pte,
|
|
|
+ struct page *old_page)
|
|
|
+ __releases(ptl)
|
|
|
+{
|
|
|
+ int page_mkwrite = 0;
|
|
|
+
|
|
|
+ page_cache_get(old_page);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Only catch write-faults on shared writable pages,
|
|
|
+ * read-only shared pages can get COWed by
|
|
|
+ * get_user_pages(.write=1, .force=1).
|
|
|
+ */
|
|
|
+ if (vma->vm_ops && vma->vm_ops->page_mkwrite) {
|
|
|
+ int tmp;
|
|
|
+
|
|
|
+ pte_unmap_unlock(page_table, ptl);
|
|
|
+ tmp = do_page_mkwrite(vma, old_page, address);
|
|
|
+ if (unlikely(!tmp || (tmp &
|
|
|
+ (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))) {
|
|
|
+ page_cache_release(old_page);
|
|
|
+ return tmp;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * 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)) {
|
|
|
+ unlock_page(old_page);
|
|
|
+ pte_unmap_unlock(page_table, ptl);
|
|
|
+ page_cache_release(old_page);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ page_mkwrite = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return wp_page_reuse(mm, vma, address, page_table, ptl,
|
|
|
+ orig_pte, old_page, page_mkwrite, 1);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* This routine handles present pages, when users try to write
|
|
|
* to a shared page. It is done by copying the page to a new address
|
|
|
@@ -2259,44 +2305,8 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
unlock_page(old_page);
|
|
|
} else if (unlikely((vma->vm_flags & (VM_WRITE|VM_SHARED)) ==
|
|
|
(VM_WRITE|VM_SHARED))) {
|
|
|
- int page_mkwrite = 0;
|
|
|
-
|
|
|
- page_cache_get(old_page);
|
|
|
-
|
|
|
- /*
|
|
|
- * Only catch write-faults on shared writable pages,
|
|
|
- * read-only shared pages can get COWed by
|
|
|
- * get_user_pages(.write=1, .force=1).
|
|
|
- */
|
|
|
- if (vma->vm_ops && vma->vm_ops->page_mkwrite) {
|
|
|
- int tmp;
|
|
|
-
|
|
|
- pte_unmap_unlock(page_table, ptl);
|
|
|
- tmp = do_page_mkwrite(vma, old_page, address);
|
|
|
- if (unlikely(!tmp || (tmp &
|
|
|
- (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))) {
|
|
|
- page_cache_release(old_page);
|
|
|
- return tmp;
|
|
|
- }
|
|
|
- /*
|
|
|
- * 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)) {
|
|
|
- unlock_page(old_page);
|
|
|
- pte_unmap_unlock(page_table, ptl);
|
|
|
- page_cache_release(old_page);
|
|
|
- return 0;
|
|
|
- }
|
|
|
- page_mkwrite = 1;
|
|
|
- }
|
|
|
-
|
|
|
- return wp_page_reuse(mm, vma, address, page_table, ptl,
|
|
|
- orig_pte, old_page, page_mkwrite, 1);
|
|
|
+ return wp_page_shared(mm, vma, address, page_table, pmd,
|
|
|
+ ptl, orig_pte, old_page);
|
|
|
}
|
|
|
|
|
|
/*
|