Thread (1 message) 1 message, 1 author, 2021-05-12

Re: [PATCH] mm, hugetlb: fix resv_huge_pages underflow on UFFDIO_COPY

From: Mina Almasry <hidden>
Date: 2021-05-12 07:45:12
Also in: lkml

On Tue, May 11, 2021 at 11:58 PM Mina Almasry [off-list ref] wrote:
quoted hunk
When hugetlb_mcopy_atomic_pte() is called with:
- mode==MCOPY_ATOMIC_NORMAL and,
- we already have a page in the page cache corresponding to the
associated address,

We will allocate a huge page from the reserves, and then fail to insert it
into the cache and return -EEXIST.

In this case, we need to return -EEXIST without allocating a new page as
the page already exists in the cache. Allocating the extra page causes
the resv_huge_pages to underflow temporarily until the extra page is
freed.

Also, add the warning so that future similar instances of resv_huge_pages
underflowing will be caught.

Also, minor drive-by cleanups to this code path:
- pagep is an out param never set by calling code, so delete code
assuming there could be a valid page in there.
- use hugetlbfs_pagecache_page() instead of repeating its
implementation.

Tested using:
./tools/testing/selftests/vm/userfaultfd hugetlb_shared 1024 200 \
/mnt/huge

Test passes, and dmesg shows no underflow warnings.

Signed-off-by: Mina Almasry <redacted>
Cc: Mike Kravetz <redacted>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Peter Xu <peterx@redhat.com>

---
 mm/hugetlb.c | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 629aa4c2259c..40f4ad1bca29 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1165,6 +1165,7 @@ static struct page *dequeue_huge_page_vma(struct hstate *h,
        page = dequeue_huge_page_nodemask(h, gfp_mask, nid, nodemask);
        if (page && !avoid_reserve && vma_has_reserves(vma, chg)) {
                SetHPageRestoreReserve(page);
+               WARN_ON_ONCE(!h->resv_huge_pages);
                h->resv_huge_pages--;
        }
@@ -4868,30 +4869,39 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,
                            struct page **pagep)
 {
        bool is_continue = (mode == MCOPY_ATOMIC_CONTINUE);
-       struct address_space *mapping;
-       pgoff_t idx;
+       struct hstate *h = hstate_vma(dst_vma);
+       struct address_space *mapping = dst_vma->vm_file->f_mapping;
+       pgoff_t idx = vma_hugecache_offset(h, dst_vma, dst_addr);
        unsigned long size;
        int vm_shared = dst_vma->vm_flags & VM_SHARED;
-       struct hstate *h = hstate_vma(dst_vma);
        pte_t _dst_pte;
        spinlock_t *ptl;
-       int ret;
+       int ret = -ENOMEM;
        struct page *page;
        int writable;

-       mapping = dst_vma->vm_file->f_mapping;
-       idx = vma_hugecache_offset(h, dst_vma, dst_addr);
+       /* Out parameter. */
+       WARN_ON(*pagep);

        if (is_continue) {
                ret = -EFAULT;
-               page = find_lock_page(mapping, idx);
+               page = hugetlbfs_pagecache_page(h, dst_vma, dst_addr);
                if (!page)
                        goto out;
-       } else if (!*pagep) {
-               ret = -ENOMEM;
+       } else {
+               /* If a page already exists, then it's UFFDIO_COPY for
+                * a non-missing case. Return -EEXIST.
+                */
+               if (hugetlbfs_pagecache_present(h, dst_vma, dst_addr)) {
+                       ret = -EEXIST;
+                       goto out;
+               }
+
                page = alloc_huge_page(dst_vma, dst_addr, 0);
-               if (IS_ERR(page))
+               if (IS_ERR(page)) {
+                       ret = -ENOMEM;
                        goto out;
+               }

                ret = copy_huge_page_from_user(page,
                                                (const void __user *) src_addr,
@@ -4904,9 +4914,6 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,
                        /* don't free the page */
                        goto out;
                }
-       } else {
-               page = *pagep;
-               *pagep = NULL;
        }

        /*
--
2.31.1.607.g51e8a6a459-goog
I just realized I missed CCing Andrew and the mailing lists to this
patch's review. I'll collect review comments from folks and send a v2
to the correct folks and mailing lists.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help