On Thu, 01 Oct 2026 22:17:50 +0100 Yeoreum Yun <[email protected]> wrote:

> split_huge_page_test can fail for the following reasons:
> 
>   1. During the test, khugepaged may collapse previously split pages again,
>      causing intermittent failures.
> 
>   2. Since glibc commit 321e1fc73f (“malloc: Enable 2MB THP by default on 
> AArch64”),
>      glibc may call madvise(MADV_HUGEPAGE) for sufficiently large allocations
>      made by memalign(). The underlying VMA may start at a different address
>      from the aligned address returned by memalign(). Moreover, a subsequent
>      madvise(MADV_HUGEPAGE) call does not split the VMA because it already
>      has the same advice.
> 
>      This causes the test to fail because the check_huge_xxx() helpers
>      incorrectly require the address returned by memalign() to match the
>      VMA start address reported in /proc/self/smaps.
> 
> Address these issues by applying MADV_NOHUGEPAGE after faulting in the
> huge page, preventing khugepaged from collapsing it again, and instead of
> relying on /proc/self/smaps, use /proc/self/pagemap and
> /proc/kpageflags to detect huge-page mappings and large folios:

Thanks, I've updated mm.git's mm-unstable branch to this version.

> Changes in v9:
>   - fix some typos in comment and Fixes tag.
>   - separating huge and type checking.
>   - add r-b tags.

Here's how v9 altered mm.git:


 tools/testing/selftests/mm/split_huge_page_test.c |    7 
 tools/testing/selftests/mm/vm_util.c              |  108 ++++++------
 2 files changed, 64 insertions(+), 51 deletions(-)

--- a/tools/testing/selftests/mm/split_huge_page_test.c~b
+++ a/tools/testing/selftests/mm/split_huge_page_test.c
@@ -110,14 +110,11 @@ static char *allocate_zero_filled_hugepa
 
 static void disable_khugepaged(void *addr, size_t len)
 {
-       /*
-        * Disables khugepaged from collapsing THPs in range, existing THP
-        * pages remain.
-        */
+       /* Disables khugepaged from collapsing pages in range into THPs */
        if (!madvise(addr, len, MADV_NOHUGEPAGE))
                return;
 
-       ksft_exit_fail_msg("MADV_NOHUGEPAGE failed, err=%d\n", errno);
+       ksft_exit_fail_perror("MADV_NOHUGEPAGE failed");
 }
 
 static void verify_rss_anon_split_huge_page_all_zeroes(char *one_page, int 
nr_hpages, size_t len)
--- a/tools/testing/selftests/mm/vm_util.c~b
+++ a/tools/testing/selftests/mm/vm_util.c
@@ -351,13 +351,13 @@ err_out:
        return entry;
 }
 
-static bool check_large_folios(int pagemap_fd, int kpageflags_fd,
-               void *addr, size_t len, int nr_hpages,
+static bool check_large_folios(void *addr, size_t len, int nr_hpages,
                uint64_t hpage_size)
 {
        int order = 0, pagesize = getpagesize();
        unsigned int nr_pages = hpage_size / pagesize;
        int orders[MAX_NR_ORDERS], status;
+       int pagemap_fd, kpageflags_fd;
        bool ret = false;
 
        if (!nr_pages)
@@ -368,6 +368,15 @@ static bool check_large_folios(int pagem
                ksft_exit_fail_msg("invalid order\n");
 
        memset(orders, 0, sizeof(int) * MAX_NR_ORDERS);
+       pagemap_fd = open(PAGEMAP_PATH, O_RDONLY);
+       if (pagemap_fd == -1)
+               ksft_exit_fail_msg("read pagemap fail\n");
+
+       kpageflags_fd = open(KPAGEFLAGS_PATH, O_RDONLY);
+       if (kpageflags_fd == -1) {
+               close(pagemap_fd);
+               ksft_exit_fail_msg("read kpageflags fail\n");
+       }
 
        status = gather_folio_orders(addr, len, pagemap_fd,
                        kpageflags_fd, orders, MAX_NR_ORDERS);
@@ -378,38 +387,53 @@ static bool check_large_folios(int pagem
                ret = true;
 
 out:
+       close(pagemap_fd);
+       close(kpageflags_fd);
        return ret;
 }
 
-enum check_huge_type {
-       CHECK_HUGE_ANON,
-       CHECK_HUGE_FILE,
+enum check_type {
+       CHECK_TYPE_ANON,
+       CHECK_TYPE_FILE,
 };
 
-static bool check_huge_type(uint64_t categories, enum check_huge_type type)
+static bool __check_type(void *addr, size_t len, uint64_t page_size,
+               enum check_type type)
 {
-       const bool file = categories & PAGE_IS_FILE;
+       bool ret = false;
+       int pagemap_fd;
+       char *start = addr;
+       char *end = start + len;
+       uint64_t categories;
+
+       pagemap_fd = open(PAGEMAP_PATH, O_RDONLY);
+       if (pagemap_fd < 0)
+               ksft_exit_fail_perror("open pagemap");
 
-       switch (type) {
-       case CHECK_HUGE_ANON:
-               return !file;
-       case CHECK_HUGE_FILE:
-               return file;
+       for (; start < end; start += page_size) {
+               categories = pagemap_scan_get_categories(pagemap_fd, start);
+               if ((categories & PAGE_IS_PRESENT) != PAGE_IS_PRESENT)
+                       continue;
+
+               if ((type == CHECK_TYPE_FILE) != !!(categories & PAGE_IS_FILE))
+                       goto out;
        }
 
-       return false;
+       ret = true;
+
+out:
+       close(pagemap_fd);
+       return ret;
 }
 
 static bool __check_huge(void *addr, size_t len, int nr_hpages,
-               uint64_t hpage_size, enum check_huge_type type)
+               uint64_t hpage_size)
 {
        bool ret = false;
-       int pagemap_fd, kpageflags_fd;
+       int pagemap_fd;
        int nr_pmd_mappings = 0;
-       uint64_t pmd_pagesize, scan_mapping_size;
+       uint64_t pmd_pagesize;
        uint64_t categories;
-       unsigned long pfn;
-       bool check_pmd_mapping, allow_nonpresent;
        char *start = addr;
        char *end = start + len;
 
@@ -417,57 +441,49 @@ static bool __check_huge(void *addr, siz
        if (!pmd_pagesize)
                ksft_exit_fail_msg("reading PMD pagesize failed\n");
 
-       check_pmd_mapping = hpage_size == pmd_pagesize;
-       scan_mapping_size = (nr_hpages > 0) ? hpage_size : psize();
-       /* Some mTHP tests check a partially populated PMD-sized range. */
-       allow_nonpresent = (uint64_t)nr_hpages * hpage_size < len;
-
        pagemap_fd = open(PAGEMAP_PATH, O_RDONLY);
        if (pagemap_fd < 0)
-               ksft_exit_fail_msg("open pagemap fail\n");
-
-       kpageflags_fd = open(KPAGEFLAGS_PATH, O_RDONLY);
-       if (kpageflags_fd < 0)
-               ksft_exit_fail_msg("open kpageflags fail\n");
+               ksft_exit_fail_perror("open pagemap");
 
-       if (!check_pmd_mapping &&
-           !check_large_folios(pagemap_fd, kpageflags_fd,
-                               addr, len, nr_hpages, hpage_size))
+       if (hpage_size != pmd_pagesize) {
+               ret = check_large_folios(addr, len, nr_hpages, hpage_size);
                goto out;
+       }
 
-       for (; start < end; start += scan_mapping_size) {
+       for (; start < end; start += hpage_size) {
                categories = pagemap_scan_get_categories(pagemap_fd, start);
-               pfn = pagemap_get_pfn(pagemap_fd, start);
-               if (pfn == -1UL) {
-                       if (!allow_nonpresent)
-                               goto out;
-                       else
-                               continue;
-               }
-               if (check_pmd_mapping && (categories & PAGE_IS_HUGE))
+               if (categories & PAGE_IS_HUGE)
                        nr_pmd_mappings++;
-               if (!check_huge_type(categories, type))
-                       goto out;
        }
 
-       if (check_pmd_mapping && (nr_pmd_mappings != nr_hpages))
+       if (nr_pmd_mappings != nr_hpages)
                goto out;
+
        ret = true;
 
 out:
        close(pagemap_fd);
-       close(kpageflags_fd);
        return ret;
 }
 
 bool check_huge_anon(void *addr, size_t len, int nr_hpages, uint64_t 
hpage_size)
 {
-       return __check_huge(addr, len, nr_hpages, hpage_size, CHECK_HUGE_ANON);
+       const uint64_t scan_mapping_size = (nr_hpages > 0) ? hpage_size : 
psize();
+
+       if (!__check_huge(addr, len, nr_hpages, hpage_size))
+               return false;
+
+       return __check_type(addr, len, scan_mapping_size, CHECK_TYPE_ANON);
 }
 
 bool check_huge_file(void *addr, size_t len, int nr_hpages, uint64_t 
hpage_size)
 {
-       return __check_huge(addr, len, nr_hpages, hpage_size, CHECK_HUGE_FILE);
+       const uint64_t scan_mapping_size = (nr_hpages > 0) ? hpage_size : 
psize();
+
+       if (!__check_huge(addr, len, nr_hpages, hpage_size))
+               return false;
+
+       return __check_type(addr, len, scan_mapping_size, CHECK_TYPE_FILE);
 }
 
 int64_t allocate_transhuge(void *ptr, int pagemap_fd)
_


Reply via email to