aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Xu <[email protected]>2023-01-04 17:52:05 -0500
committerAndrew Morton <[email protected]>2023-01-11 16:14:21 -0800
commitfed15f1345dc8a7fc8baa81e8b55c3ba010d7f4b (patch)
tree42131cf79fda9bf7b1681b84581fb54263abf221
parentb30c14cd61025eeea2f2e8569606cd167ba9ad2d (diff)
mm/hugetlb: pre-allocate pgtable pages for uffd wr-protects
Userfaultfd-wp uses pte markers to mark wr-protected pages for both shmem and hugetlb. Shmem has pre-allocation ready for markers, but hugetlb path was overlooked. Doing so by calling huge_pte_alloc() if the initial pgtable walk fails to find the huge ptep. It's possible that huge_pte_alloc() can fail with high memory pressure, in that case stop the loop immediately and fail silently. This is not the most ideal solution but it matches with what we do with shmem meanwhile it avoids the splat in dmesg. Link: https://lkml.kernel.org/r/[email protected] Fixes: 60dfaad65aa9 ("mm/hugetlb: allow uffd wr-protect none ptes") Signed-off-by: Peter Xu <[email protected]> Reported-by: James Houghton <[email protected]> Reviewed-by: Mike Kravetz <[email protected]> Acked-by: David Hildenbrand <[email protected]> Acked-by: James Houghton <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: Axel Rasmussen <[email protected]> Cc: Muchun Song <[email protected]> Cc: Nadav Amit <[email protected]> Cc: <[email protected]> [5.19+] Signed-off-by: Andrew Morton <[email protected]>
-rw-r--r--mm/hugetlb.c13
1 files changed, 11 insertions, 2 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 2ce912c915eb..f96794be71d5 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -6660,8 +6660,17 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
spinlock_t *ptl;
ptep = huge_pte_offset(mm, address, psize);
if (!ptep) {
- address |= last_addr_mask;
- continue;
+ if (!uffd_wp) {
+ address |= last_addr_mask;
+ continue;
+ }
+ /*
+ * Userfaultfd wr-protect requires pgtable
+ * pre-allocations to install pte markers.
+ */
+ ptep = huge_pte_alloc(mm, vma, address, psize);
+ if (!ptep)
+ break;
}
ptl = huge_pte_lock(h, mm, ptep);
if (huge_pmd_unshare(mm, vma, address, ptep)) {