On Thu, Aug 27, 2026 at 12:56:20PM +0200, David Hildenbrand (Arm) wrote: > On 8/27/26 10:28, Baolin Wang wrote: > > > > > > On 8/26/26 8:24 PM, Yeoreum Yun wrote: > >> 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(). Furthermore, a subsequent > >> madvise(MADV_HUGEPAGE) call does not split the VMA because the flag is > >> already set. > >> > >> This causes split_huge_page_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. > >> > >> Fix this by using /proc/self/pagemap and /proc/kpageflags instead of > >> /proc/self/smaps to detect huge pages. > >> > >> Reported-by: David Hildenbrand (Arm) <[email protected]> > >> Signed-off-by: Yeoreum Yun <[email protected]> > >> --- > >> tools/testing/selftests/mm/vm_util.c | 130 > >> ++++++++++++++++++++--------------- > >> tools/testing/selftests/mm/vm_util.h | 1 + > >> 2 files changed, 77 insertions(+), 54 deletions(-) > >> > >> diff --git a/tools/testing/selftests/mm/vm_util.c > >> b/tools/testing/selftests/ > >> mm/vm_util.c > >> index 4821a3563036..1d0959b3b9e8 100644 > >> --- a/tools/testing/selftests/mm/vm_util.c > >> +++ b/tools/testing/selftests/mm/vm_util.c > >> @@ -351,31 +351,13 @@ char *__get_smap_entry(void *addr, const char > >> *pattern, > >> char *buf, size_t len) > >> return entry; > >> } > >> -static bool __check_pmd_huge(void *addr, char *pattern, int nr_hpages, > >> - uint64_t hpage_size) > >> -{ > >> - char buffer[MAX_LINE_LENGTH]; > >> - uint64_t thp = -1; > >> - char *entry; > >> - > >> - entry = __get_smap_entry(addr, pattern, buffer, sizeof(buffer)); > >> - if (!entry) > >> - goto err_out; > >> - > >> - if (sscanf(entry, "%9" SCNu64 " kB", &thp) != 1) > >> - ksft_exit_fail_msg("Reading smap error\n"); > >> - > >> -err_out: > >> - return thp == (nr_hpages * (hpage_size >> 10)); > >> -} > >> - > >> -static bool check_large_folios(void *addr, size_t len, int nr_hpages, > >> - uint64_t hpage_size) > >> +static bool check_large_folios(int pagemap_fd, int kpageflags_fd, > >> + 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) > >> @@ -386,15 +368,6 @@ static bool check_large_folios(void *addr, size_t len, > >> int nr_hpages, > >> 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); > >> @@ -405,48 +378,97 @@ static bool check_large_folios(void *addr, size_t > >> len, > >> int nr_hpages, > >> 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) > >> +enum check_huge_type { > >> + CHECK_HUGE_ANON, > >> + CHECK_HUGE_FILE, > >> + CHECK_HUGE_SHMEM, > >> +}; > >> + > >> +static bool __check_pmd_huge(void *addr, size_t len, int nr_hpages, > >> + uint64_t hpage_size, enum check_huge_type type) > > > > The original __check_pmd_huge() is only for PMD-sized large folios, but now > > it > > not only checks PMD-sized large folios but also mTHP large folios, which I > > find > > confusing. Please keep its original semantics, and only check PMD-sized > > large > > folios. > > > >> { > >> - uint64_t pmd_pagesize = read_pmd_pagesize(); > >> + int pagemap_fd, kpageflags_fd; > >> + uint64_t pmd_pagesize, granule; > >> + uint64_t categories, kpf; > >> + unsigned long pfn; > >> + bool check_large, huge_mapped; > >> + char *start = addr; > >> + char *end = start + len; > >> + pmd_pagesize = read_pmd_pagesize(); > >> if (!pmd_pagesize) > >> ksft_exit_fail_msg("reading PMD pagesize failed\n"); > >> - if (hpage_size == pmd_pagesize) > >> - return __check_pmd_huge(addr, "AnonHugePages: ", nr_hpages, > >> hpage_size); > >> + if (nr_hpages > 0) { > >> + check_large = true; > >> + granule = hpage_size; > >> + } else { > >> + check_large = false; > >> + granule = psize(); > >> + } > > > > This is incorrect for the mTHP large folio check. I already hit a selftest > > failure. Please test your patches before sending them out. > > > > [root@]./khugepaged -c 4 mthp_khugepaged:anon > > TAP version 13 > > # Save THP and khugepaged settings... OK > > 1..4 > > # Allocate huge page on fault... OK > > # Split huge PMD on MADV_DONTNEED... OK > > ok 1 allocate on fault and split > > # > > # Run test: collapse_full (mthp_khugepaged:anon) > > # Collapse multiple fully populated PTE table.... OK > > ok 2 collapse_full > > # > > # Run test: collapse_empty (mthp_khugepaged:anon) > > # Do not collapse empty PTE table.... OK > > ok 3 collapse_empty > > # > > # Run test: collapse_single_mthp (mthp_khugepaged:anon) > > # Collapse PTE table with half PTE entries present.... Fail > > not ok 4 collapse_single_mthp > > # Totals: pass:3 fail:1 xfail:0 xpass:0 skip:0 error:0 > > FWIW, the CI flags this as well: > > https://github.com/linux-mm/linux-mm/actions/runs/33028595020/job/98375618491
Yeap. I've overlooked that case and here is the fix: - https://lore.kernel.org/all/[email protected]/ > > -- > Cheers, > > David -- Sincerely, Yeoreum Yun

