From: "Kiryl Shutsemau (Meta)" <[email protected]>

collapse_max_ptes_swap() and collapse_swapin_single_pte() set their
precondition with MADV_PAGEOUT and then require smaps to report exactly the
range they asked for.  MADV_PAGEOUT is best effort: shrink_folio_list()
leaves a folio alone when it cannot reclaim it right away, and a folio
still under writeback from an earlier pageout is the common case.  The
count comes up short by a page or two, and the case fails on its
precondition without testing anything.

It shows on arm64 with 64K pages, where max_ptes_swap is 1024 pages: 64M of
swap per step, and the second step pages out folios the first step has only
just written back.  About one run in four ends in

  # Swapout 1024 of 8192 pages... Fail
  not ok 95 collapse_max_ptes_swap

A probe around the same two steps hits it on roughly one attempt in twenty,
and asking again 50ms later closes the gap within four tries.

Ask again for up to two seconds before reporting failure.  The wait costs
nothing when the first attempt is enough, which is the usual case.

Assisted-by: Claude-Code:claude-opus-5
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
---
 tools/testing/selftests/mm/khugepaged.c | 32 ++++++++++++++++++-------
 1 file changed, 23 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/mm/khugepaged.c 
b/tools/testing/selftests/mm/khugepaged.c
index 9b1da0dd0f8e..69e0fc4b12fb 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -241,6 +241,26 @@ static bool check_swap(void *addr, unsigned long size)
        return swap;
 }
 
+/*
+ * MADV_PAGEOUT is best effort: shrink_folio_list() leaves a folio alone
+ * when it cannot reclaim it right away, and one still under writeback from
+ * an earlier pageout is the common case.  The swap count the caller asks
+ * for then arrives a moment later, so ask again before giving up.
+ */
+static bool swapout_range(void *p, unsigned long size)
+{
+       int i;
+
+       for (i = 0; i < 40; i++) {
+               if (madvise(p, size, MADV_PAGEOUT))
+                       ksft_exit_fail_perror("madvise(MADV_PAGEOUT)");
+               if (check_swap(p, size))
+                       return true;
+               usleep(50 * 1000);
+       }
+       return false;
+}
+
 static bool is_swap_available(unsigned long size)
 {
        unsigned long swap_total = 0;
@@ -881,9 +901,7 @@ static void collapse_swapin_single_pte(struct 
collapse_context *c, struct mem_op
        p = ops->setup_area(1);
        ops->fault(p, 0, hpage_pmd_size);
 
-       if (madvise(p, page_size, MADV_PAGEOUT))
-               ksft_exit_fail_perror("madvise(MADV_PAGEOUT)");
-       if (check_swap(p, page_size)) {
+       if (swapout_range(p, page_size)) {
                success("OK");
        } else {
                fail("Fail");
@@ -920,9 +938,7 @@ static void collapse_max_ptes_swap(struct collapse_context 
*c, struct mem_ops *o
        p = ops->setup_area(1);
        ops->fault(p, 0, hpage_pmd_size);
 
-       if (madvise(p, (max_ptes_swap + 1) * page_size, MADV_PAGEOUT))
-               ksft_exit_fail_perror("madvise(MADV_PAGEOUT)");
-       if (check_swap(p, (max_ptes_swap + 1) * page_size)) {
+       if (swapout_range(p, (max_ptes_swap + 1) * page_size)) {
                success("OK");
        } else {
                fail("Fail");
@@ -937,9 +953,7 @@ static void collapse_max_ptes_swap(struct collapse_context 
*c, struct mem_ops *o
                ops->fault(p, 0, hpage_pmd_size);
                ksft_print_msg("Swapout %d of %d pages...", max_ptes_swap,
                       hpage_pmd_nr);
-               if (madvise(p, max_ptes_swap * page_size, MADV_PAGEOUT))
-                       ksft_exit_fail_perror("madvise(MADV_PAGEOUT)");
-               if (check_swap(p, max_ptes_swap * page_size)) {
+               if (swapout_range(p, max_ptes_swap * page_size)) {
                        success("OK");
                } else {
                        fail("Fail");
-- 
2.54.0


Reply via email to