Skip to content

Commit d84cf06

Browse files
minatorvalds
authored andcommitted
mm, hugetlb: fix simple resv_huge_pages underflow on UFFDIO_COPY
The userfaultfd hugetlb tests cause a resv_huge_pages underflow. This happens when hugetlb_mcopy_atomic_pte() is called with !is_continue on an index for which we already have a page in the cache. When this happens, we allocate a second page, double consuming the reservation, and then fail to insert the page into the cache and return -EEXIST. To fix this, we first check if there is a page in the cache which already consumed the reservation, and return -EEXIST immediately if so. There is still a rare condition where we fail to copy the page contents AND race with a call for hugetlb_no_page() for this index and again we will underflow resv_huge_pages. That is fixed in a more complicated patch not targeted for -stable. Test: Hacked the code locally such that resv_huge_pages underflows produce a warning, then: ./tools/testing/selftests/vm/userfaultfd hugetlb_shared 10 2 /tmp/kokonut_test/huge/userfaultfd_test && echo test success ./tools/testing/selftests/vm/userfaultfd hugetlb 10 2 /tmp/kokonut_test/huge/userfaultfd_test && echo test success Both tests succeed and produce no warnings. After the test runs number of free/resv hugepages is correct. [[email protected]: changelog fixes] Link: https://lkml.kernel.org/r/[email protected] Fixes: 8fb5deb ("userfaultfd: hugetlbfs: add hugetlb_mcopy_atomic_pte for userfaultfd support") Signed-off-by: Mina Almasry <[email protected]> Reviewed-by: Mike Kravetz <[email protected]> Cc: Axel Rasmussen <[email protected]> Cc: Peter Xu <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 7b6889f commit d84cf06

File tree

1 file changed

+12
-2
lines changed

1 file changed

+12
-2
lines changed

mm/hugetlb.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4889,10 +4889,20 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,
48894889
if (!page)
48904890
goto out;
48914891
} else if (!*pagep) {
4892-
ret = -ENOMEM;
4892+
/* If a page already exists, then it's UFFDIO_COPY for
4893+
* a non-missing case. Return -EEXIST.
4894+
*/
4895+
if (vm_shared &&
4896+
hugetlbfs_pagecache_present(h, dst_vma, dst_addr)) {
4897+
ret = -EEXIST;
4898+
goto out;
4899+
}
4900+
48934901
page = alloc_huge_page(dst_vma, dst_addr, 0);
4894-
if (IS_ERR(page))
4902+
if (IS_ERR(page)) {
4903+
ret = -ENOMEM;
48954904
goto out;
4905+
}
48964906

48974907
ret = copy_huge_page_from_user(page,
48984908
(const void __user *) src_addr,

0 commit comments

Comments
 (0)