|
@@ -2180,6 +2180,42 @@ oom:
|
|
|
return VM_FAULT_OOM;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Handle write page faults for VM_MIXEDMAP or VM_PFNMAP for a VM_SHARED
|
|
|
+ * mapping
|
|
|
+ */
|
|
|
+static int wp_pfn_shared(struct mm_struct *mm,
|
|
|
+ struct vm_area_struct *vma, unsigned long address,
|
|
|
+ pte_t *page_table, spinlock_t *ptl, pte_t orig_pte,
|
|
|
+ pmd_t *pmd)
|
|
|
+{
|
|
|
+ if (vma->vm_ops && vma->vm_ops->pfn_mkwrite) {
|
|
|
+ struct vm_fault vmf = {
|
|
|
+ .page = NULL,
|
|
|
+ .pgoff = linear_page_index(vma, address),
|
|
|
+ .virtual_address = (void __user *)(address & PAGE_MASK),
|
|
|
+ .flags = FAULT_FLAG_WRITE | FAULT_FLAG_MKWRITE,
|
|
|
+ };
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ pte_unmap_unlock(page_table, ptl);
|
|
|
+ ret = vma->vm_ops->pfn_mkwrite(vma, &vmf);
|
|
|
+ if (ret & VM_FAULT_ERROR)
|
|
|
+ return ret;
|
|
|
+ page_table = pte_offset_map_lock(mm, pmd, address, &ptl);
|
|
|
+ /*
|
|
|
+ * We might have raced with another page fault while we
|
|
|
+ * released the pte_offset_map_lock.
|
|
|
+ */
|
|
|
+ if (!pte_same(*page_table, orig_pte)) {
|
|
|
+ pte_unmap_unlock(page_table, ptl);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return wp_page_reuse(mm, vma, address, page_table, ptl, orig_pte,
|
|
|
+ NULL, 0, 0);
|
|
|
+}
|
|
|
+
|
|
|
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,
|
|
@@ -2258,13 +2294,12 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
* VM_PFNMAP VMA.
|
|
|
*
|
|
|
* We should not cow pages in a shared writeable mapping.
|
|
|
- * Just mark the pages writable as we can't do any dirty
|
|
|
- * accounting on raw pfn maps.
|
|
|
+ * Just mark the pages writable and/or call ops->pfn_mkwrite.
|
|
|
*/
|
|
|
if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) ==
|
|
|
(VM_WRITE|VM_SHARED))
|
|
|
- return wp_page_reuse(mm, vma, address, page_table, ptl,
|
|
|
- orig_pte, old_page, 0, 0);
|
|
|
+ return wp_pfn_shared(mm, vma, address, page_table, ptl,
|
|
|
+ orig_pte, pmd);
|
|
|
|
|
|
pte_unmap_unlock(page_table, ptl);
|
|
|
return wp_page_copy(mm, vma, address, page_table, pmd,
|