|
@@ -3486,6 +3486,7 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
if (unlikely(is_vm_hugetlb_page(vma)))
|
|
|
return hugetlb_fault(mm, vma, address, flags);
|
|
|
|
|
|
+retry:
|
|
|
pgd = pgd_offset(mm, address);
|
|
|
pud = pud_alloc(mm, pgd, address);
|
|
|
if (!pud)
|
|
@@ -3499,13 +3500,24 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
pmd, flags);
|
|
|
} else {
|
|
|
pmd_t orig_pmd = *pmd;
|
|
|
+ int ret;
|
|
|
+
|
|
|
barrier();
|
|
|
if (pmd_trans_huge(orig_pmd)) {
|
|
|
if (flags & FAULT_FLAG_WRITE &&
|
|
|
!pmd_write(orig_pmd) &&
|
|
|
- !pmd_trans_splitting(orig_pmd))
|
|
|
- return do_huge_pmd_wp_page(mm, vma, address,
|
|
|
- pmd, orig_pmd);
|
|
|
+ !pmd_trans_splitting(orig_pmd)) {
|
|
|
+ ret = do_huge_pmd_wp_page(mm, vma, address, pmd,
|
|
|
+ orig_pmd);
|
|
|
+ /*
|
|
|
+ * If COW results in an oom, the huge pmd will
|
|
|
+ * have been split, so retry the fault on the
|
|
|
+ * pte for a smaller charge.
|
|
|
+ */
|
|
|
+ if (unlikely(ret & VM_FAULT_OOM))
|
|
|
+ goto retry;
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
return 0;
|
|
|
}
|
|
|
}
|