[PATCH v4 3/5] selftests/mm: reimplement is_backed_by_thp() with more precise check

Zi Yan posted 5 patches 1 month, 2 weeks ago
There is a newer version of this series
[PATCH v4 3/5] selftests/mm: reimplement is_backed_by_thp() with more precise check
Posted by Zi Yan 1 month, 2 weeks ago
and rename it to is_backed_by_folio().

is_backed_by_folio() checks if the given vaddr is backed a folio with
a given order. It does so by:
1. getting the pfn of the vaddr;
2. checking kpageflags of the pfn;

if order is greater than 0:
3. checking kpageflags of the head pfn;
4. checking kpageflags of all tail pfns.

pmd_order is added to split_huge_page_test.c and replaces max_order.

Signed-off-by: Zi Yan <ziy@nvidia.com>
---
 .../selftests/mm/split_huge_page_test.c       | 90 ++++++++++++++-----
 tools/testing/selftests/mm/vm_util.c          | 13 +++
 tools/testing/selftests/mm/vm_util.h          |  4 +
 3 files changed, 84 insertions(+), 23 deletions(-)

diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index 89d3dc08fe4c..80f718ca21c7 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -25,6 +25,7 @@
 uint64_t pagesize;
 unsigned int pageshift;
 uint64_t pmd_pagesize;
+unsigned int pmd_order;
 
 #define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages"
 #define SMAP_PATH "/proc/self/smaps"
@@ -34,27 +35,71 @@ uint64_t pmd_pagesize;
 #define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d"
 #define PATH_FMT "%s,0x%lx,0x%lx,%d"
 
-#define PFN_MASK     ((1UL<<55)-1)
-#define KPF_THP      (1UL<<22)
 #define GET_ORDER(nr_pages)    (31 - __builtin_clz(nr_pages))
 
-static int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file)
+static int is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
+		int kpageflags_fd)
 {
-	uint64_t paddr;
-	uint64_t page_flags;
+	unsigned long pfn_head;
+	uint64_t pfn_flags;
+	unsigned long pfn;
+	unsigned long i;
 
-	if (pagemap_file) {
-		pread(pagemap_file, &paddr, sizeof(paddr),
-			((long)vaddr >> pageshift) * sizeof(paddr));
+	if (pagemap_fd == -1 || kpageflags_fd == -1)
+		goto fail;
 
-		if (kpageflags_file) {
-			pread(kpageflags_file, &page_flags, sizeof(page_flags),
-				(paddr & PFN_MASK) * sizeof(page_flags));
+	pfn = pagemap_get_pfn(pagemap_fd, vaddr);
 
-			return !!(page_flags & KPF_THP);
-		}
+	/* non present page */
+	if (pfn == -1UL)
+		return 0;
+
+	if (get_pfn_flags(pfn, kpageflags_fd, &pfn_flags))
+		goto fail;
+
+	/* check for order-0 pages */
+	if (!order) {
+		if (pfn_flags & (KPF_THP | KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))
+			return 0;
+		return 1;
 	}
-	return 0;
+
+	/* non THP folio */
+	if (!(pfn_flags & KPF_THP))
+		return 0;
+
+	pfn_head = pfn & ~((1 << order) - 1);
+
+	if (get_pfn_flags(pfn_head, kpageflags_fd, &pfn_flags))
+		goto fail;
+
+	/* head PFN has no compound_head flag set */
+	if (!(pfn_flags & (KPF_THP | KPF_COMPOUND_HEAD)))
+		return 0;
+
+	/* check all tail PFN flags */
+	for (i = 1; i < 1UL << order; i++) {
+		if (get_pfn_flags(pfn_head + i, kpageflags_fd, &pfn_flags))
+			goto fail;
+		if (!(pfn_flags & (KPF_THP | KPF_COMPOUND_TAIL)))
+			return 0;
+	}
+
+	/*
+	 * check the PFN after this folio, but if its flags cannot be obtained,
+	 * assume this folio has the expected order
+	 */
+	if (get_pfn_flags(pfn_head + (1UL << order), kpageflags_fd, &pfn_flags))
+		return 1;
+
+	/* this folio is bigger than the given order */
+	if (pfn_flags & (KPF_THP | KPF_COMPOUND_TAIL))
+		return 0;
+
+	return 1;
+fail:
+	ksft_exit_fail_msg("Failed to get folio info\n");
+	return -1;
 }
 
 static void write_file(const char *path, const char *buf, size_t buflen)
@@ -235,7 +280,7 @@ static void split_pte_mapped_thp(void)
 	thp_size = 0;
 	for (i = 0; i < pagesize * 4; i++)
 		if (i % pagesize == 0 &&
-		    is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd))
+		    is_backed_by_folio(&pte_mapped[i], pmd_order, pagemap_fd, kpageflags_fd) == 1)
 			thp_size++;
 
 	if (thp_size != 4)
@@ -252,7 +297,7 @@ static void split_pte_mapped_thp(void)
 			ksft_exit_fail_msg("%ld byte corrupted\n", i);
 
 		if (i % pagesize == 0 &&
-		    is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd))
+		    is_backed_by_folio(&pte_mapped[i], 0, pagemap_fd, kpageflags_fd) == 0)
 			thp_size++;
 	}
 
@@ -524,7 +569,6 @@ int main(int argc, char **argv)
 	const char *fs_loc;
 	bool created_tmp;
 	int offset;
-	unsigned int max_order;
 	unsigned int nr_pages;
 	unsigned int tests;
 
@@ -545,28 +589,28 @@ int main(int argc, char **argv)
 		ksft_exit_fail_msg("Reading PMD pagesize failed\n");
 
 	nr_pages = pmd_pagesize / pagesize;
-	max_order = GET_ORDER(nr_pages);
-	tests = 2 + (max_order - 1) + (2 * max_order) + (max_order - 1) * 4 + 2;
+	pmd_order = GET_ORDER(nr_pages);
+	tests = 2 + (pmd_order - 1) + (2 * pmd_order) + (pmd_order - 1) * 4 + 2;
 	ksft_set_plan(tests);
 
 	fd_size = 2 * pmd_pagesize;
 
 	split_pmd_zero_pages();
 
-	for (i = 0; i < max_order; i++)
+	for (i = 0; i < pmd_order; i++)
 		if (i != 1)
 			split_pmd_thp_to_order(i);
 
 	split_pte_mapped_thp();
-	for (i = 0; i < max_order; i++)
+	for (i = 0; i < pmd_order; i++)
 		split_file_backed_thp(i);
 
 	created_tmp = prepare_thp_fs(optional_xfs_path, fs_loc_template,
 			&fs_loc);
-	for (i = max_order - 1; i >= 0; i--)
+	for (i = pmd_order - 1; i >= 0; i--)
 		split_thp_in_pagecache_to_order_at(fd_size, fs_loc, i, -1);
 
-	for (i = 0; i < max_order; i++)
+	for (i = 0; i < pmd_order; i++)
 		for (offset = 0;
 		     offset < nr_pages;
 		     offset += MAX(nr_pages / 4, 1 << i))
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index 6a239aa413e2..18b7cb51fc56 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -338,6 +338,19 @@ int detect_hugetlb_page_sizes(size_t sizes[], int max)
 	return count;
 }
 
+int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags)
+{
+	size_t count;
+
+	count = pread(kpageflags_fd, flags, sizeof(*flags),
+		      pfn * sizeof(*flags));
+
+	if (count != sizeof(*flags))
+		return -1;
+
+	return 0;
+}
+
 /* If `ioctls' non-NULL, the allowed ioctls will be returned into the var */
 int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len,
 			      bool miss, bool wp, bool minor, uint64_t *ioctls)
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index 1843ad48d32b..03481ca0a1b4 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -18,6 +18,9 @@
 #define PM_SWAP                       BIT_ULL(62)
 #define PM_PRESENT                    BIT_ULL(63)
 
+#define KPF_COMPOUND_HEAD             BIT_ULL(15)
+#define KPF_COMPOUND_TAIL             BIT_ULL(16)
+#define KPF_THP                       BIT_ULL(22)
 /*
  * Ignore the checkpatch warning, we must read from x but don't want to do
  * anything with it in order to trigger a read page fault. We therefore must use
@@ -85,6 +88,7 @@ bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size);
 int64_t allocate_transhuge(void *ptr, int pagemap_fd);
 unsigned long default_huge_page_size(void);
 int detect_hugetlb_page_sizes(size_t sizes[], int max);
+int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags);
 
 int uffd_register(int uffd, void *addr, uint64_t len,
 		  bool miss, bool wp, bool minor);
-- 
2.50.1
Re: [PATCH v4 3/5] selftests/mm: reimplement is_backed_by_thp() with more precise check
Posted by David Hildenbrand 1 month, 2 weeks ago
On 15.08.25 04:39, Zi Yan wrote:
> and rename it to is_backed_by_folio().
> 
> is_backed_by_folio() checks if the given vaddr is backed a folio with
> a given order. It does so by:
> 1. getting the pfn of the vaddr;
> 2. checking kpageflags of the pfn;
> 
> if order is greater than 0:
> 3. checking kpageflags of the head pfn;
> 4. checking kpageflags of all tail pfns.
> 
> pmd_order is added to split_huge_page_test.c and replaces max_order.
> 
> Signed-off-by: Zi Yan <ziy@nvidia.com>
> ---
>   .../selftests/mm/split_huge_page_test.c       | 90 ++++++++++++++-----
>   tools/testing/selftests/mm/vm_util.c          | 13 +++
>   tools/testing/selftests/mm/vm_util.h          |  4 +
>   3 files changed, 84 insertions(+), 23 deletions(-)
> 
> diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
> index 89d3dc08fe4c..80f718ca21c7 100644
> --- a/tools/testing/selftests/mm/split_huge_page_test.c
> +++ b/tools/testing/selftests/mm/split_huge_page_test.c
> @@ -25,6 +25,7 @@
>   uint64_t pagesize;
>   unsigned int pageshift;
>   uint64_t pmd_pagesize;
> +unsigned int pmd_order;
>   
>   #define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages"
>   #define SMAP_PATH "/proc/self/smaps"
> @@ -34,27 +35,71 @@ uint64_t pmd_pagesize;
>   #define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d"
>   #define PATH_FMT "%s,0x%lx,0x%lx,%d"
>   
> -#define PFN_MASK     ((1UL<<55)-1)
> -#define KPF_THP      (1UL<<22)
>   #define GET_ORDER(nr_pages)    (31 - __builtin_clz(nr_pages))
>   
> -static int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file)
> +static int is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
> +		int kpageflags_fd)

Could we convert this into a bool and simply return "false" on error 
instead of "-"? These tristate returns for a "is_*" function are a bit 
unfortunate.

>   {
> -	uint64_t paddr;
> -	uint64_t page_flags;
> +	unsigned long pfn_head;
> +	uint64_t pfn_flags;
> +	unsigned long pfn;
> +	unsigned long i;
>   
> -	if (pagemap_file) {
> -		pread(pagemap_file, &paddr, sizeof(paddr),
> -			((long)vaddr >> pageshift) * sizeof(paddr));
> +	if (pagemap_fd == -1 || kpageflags_fd == -1)
> +		goto fail;

Should we rather expect that callers make sure these are valid? In 
particular, because split_pte_mapped_thp() seems to ksft_exit_fail_msg() 
already.

>   
> -		if (kpageflags_file) {
> -			pread(kpageflags_file, &page_flags, sizeof(page_flags),
> -				(paddr & PFN_MASK) * sizeof(page_flags));
> +	pfn = pagemap_get_pfn(pagemap_fd, vaddr);

Hm, if it's swapped out we would get intermittent errors, but that just 
seems hard to avoid. The caller could mock to avoid swapout.

Memory migration is another possible problem ...

But this is nothing new regarding your patch, so no need to worry for now.

[...]

>   
> +int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags)
> +{
> +	size_t count;
> +
> +	count = pread(kpageflags_fd, flags, sizeof(*flags),
> +		      pfn * sizeof(*flags));
> +
> +	if (count != sizeof(*flags))
> +		return -1;
> +
> +	return 0;
> +}

I would have called this function "pageflags_get()" to resemble 
"pagemap_get"

-- 
Cheers

David / dhildenb
Re: [PATCH v4 3/5] selftests/mm: reimplement is_backed_by_thp() with more precise check
Posted by Zi Yan 1 month, 2 weeks ago
On 18 Aug 2025, at 4:33, David Hildenbrand wrote:

> On 15.08.25 04:39, Zi Yan wrote:
>> and rename it to is_backed_by_folio().
>>
>> is_backed_by_folio() checks if the given vaddr is backed a folio with
>> a given order. It does so by:
>> 1. getting the pfn of the vaddr;
>> 2. checking kpageflags of the pfn;
>>
>> if order is greater than 0:
>> 3. checking kpageflags of the head pfn;
>> 4. checking kpageflags of all tail pfns.
>>
>> pmd_order is added to split_huge_page_test.c and replaces max_order.
>>
>> Signed-off-by: Zi Yan <ziy@nvidia.com>
>> ---
>>   .../selftests/mm/split_huge_page_test.c       | 90 ++++++++++++++-----
>>   tools/testing/selftests/mm/vm_util.c          | 13 +++
>>   tools/testing/selftests/mm/vm_util.h          |  4 +
>>   3 files changed, 84 insertions(+), 23 deletions(-)
>>
>> diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
>> index 89d3dc08fe4c..80f718ca21c7 100644
>> --- a/tools/testing/selftests/mm/split_huge_page_test.c
>> +++ b/tools/testing/selftests/mm/split_huge_page_test.c
>> @@ -25,6 +25,7 @@
>>   uint64_t pagesize;
>>   unsigned int pageshift;
>>   uint64_t pmd_pagesize;
>> +unsigned int pmd_order;
>>    #define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages"
>>   #define SMAP_PATH "/proc/self/smaps"
>> @@ -34,27 +35,71 @@ uint64_t pmd_pagesize;
>>   #define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d"
>>   #define PATH_FMT "%s,0x%lx,0x%lx,%d"
>>  -#define PFN_MASK     ((1UL<<55)-1)
>> -#define KPF_THP      (1UL<<22)
>>   #define GET_ORDER(nr_pages)    (31 - __builtin_clz(nr_pages))
>>  -static int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file)
>> +static int is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
>> +		int kpageflags_fd)
>
> Could we convert this into a bool and simply return "false" on error instead of "-"? These tristate returns for a "is_*" function are a bit unfortunate.

OK.

>
>>   {
>> -	uint64_t paddr;
>> -	uint64_t page_flags;
>> +	unsigned long pfn_head;
>> +	uint64_t pfn_flags;
>> +	unsigned long pfn;
>> +	unsigned long i;
>>  -	if (pagemap_file) {
>> -		pread(pagemap_file, &paddr, sizeof(paddr),
>> -			((long)vaddr >> pageshift) * sizeof(paddr));
>> +	if (pagemap_fd == -1 || kpageflags_fd == -1)
>> +		goto fail;
>
> Should we rather expect that callers make sure these are valid? In particular, because split_pte_mapped_thp() seems to ksft_exit_fail_msg() already.

Sure.

>
>>  -		if (kpageflags_file) {
>> -			pread(kpageflags_file, &page_flags, sizeof(page_flags),
>> -				(paddr & PFN_MASK) * sizeof(page_flags));
>> +	pfn = pagemap_get_pfn(pagemap_fd, vaddr);
>
> Hm, if it's swapped out we would get intermittent errors, but that just seems hard to avoid. The caller could mock to avoid swapout.
>
> Memory migration is another possible problem ...
>
> But this is nothing new regarding your patch, so no need to worry for now.

Right. The function is only used by PTE-mapped THP split and I assume swapping
and migration would not happen. If this function is used more broadly, it
will need to take care of cases.

>
> [...]
>
>>  +int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags)
>> +{
>> +	size_t count;
>> +
>> +	count = pread(kpageflags_fd, flags, sizeof(*flags),
>> +		      pfn * sizeof(*flags));
>> +
>> +	if (count != sizeof(*flags))
>> +		return -1;
>> +
>> +	return 0;
>> +}
>
> I would have called this function "pageflags_get()" to resemble "pagemap_get"

OK. Will rename it.

Thanks.

--
Best Regards,
Yan, Zi
Re: [PATCH v4 3/5] selftests/mm: reimplement is_backed_by_thp() with more precise check
Posted by Andrew Morton 1 month, 2 weeks ago
I hit a large reject.  Can you please redo against next mm-new?

Thanks.
Re: [PATCH v4 3/5] selftests/mm: reimplement is_backed_by_thp() with more precise check
Posted by Zi Yan 1 month, 2 weeks ago
On 16 Aug 2025, at 3:30, Andrew Morton wrote:

> I hit a large reject.  Can you please redo against next mm-new?

Sure.

--
Best Regards,
Yan, Zi
Re: [PATCH v4 3/5] selftests/mm: reimplement is_backed_by_thp() with more precise check
Posted by wang lian 1 month, 2 weeks ago

> On Aug 15, 2025, at 10:39, Zi Yan <ziy@nvidia.com> wrote:
> 
> and rename it to is_backed_by_folio().
> 
> is_backed_by_folio() checks if the given vaddr is backed a folio with
> a given order. It does so by:
> 1. getting the pfn of the vaddr;
> 2. checking kpageflags of the pfn;
> 
> if order is greater than 0:
> 3. checking kpageflags of the head pfn;
> 4. checking kpageflags of all tail pfns.
> 
> pmd_order is added to split_huge_page_test.c and replaces max_order.
> 
> Signed-off-by: Zi Yan <ziy@nvidia.com>
> ---
> .../selftests/mm/split_huge_page_test.c       | 90 ++++++++++++++-----
> tools/testing/selftests/mm/vm_util.c          | 13 +++
> tools/testing/selftests/mm/vm_util.h          |  4 +
> 3 files changed, 84 insertions(+), 23 deletions(-)
> 
> diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
> index 89d3dc08fe4c..80f718ca21c7 100644
> --- a/tools/testing/selftests/mm/split_huge_page_test.c
> +++ b/tools/testing/selftests/mm/split_huge_page_test.c
> @@ -25,6 +25,7 @@
> uint64_t pagesize;
> unsigned int pageshift;
> uint64_t pmd_pagesize;
> +unsigned int pmd_order;
> 
> #define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages"
> #define SMAP_PATH "/proc/self/smaps"
> @@ -34,27 +35,71 @@ uint64_t pmd_pagesize;
> #define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d"
> #define PATH_FMT "%s,0x%lx,0x%lx,%d"
> 
> -#define PFN_MASK     ((1UL<<55)-1)
> -#define KPF_THP      (1UL<<22)
> #define GET_ORDER(nr_pages)    (31 - __builtin_clz(nr_pages))
> 
> -static int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file)
> +static int is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
> +		int kpageflags_fd)
> {
> -	uint64_t paddr;
> -	uint64_t page_flags;
> +	unsigned long pfn_head;
> +	uint64_t pfn_flags;
> +	unsigned long pfn;
> +	unsigned long i;
> 
> -	if (pagemap_file) {
> -		pread(pagemap_file, &paddr, sizeof(paddr),
> -			((long)vaddr >> pageshift) * sizeof(paddr));
> +	if (pagemap_fd == -1 || kpageflags_fd == -1)
> +		goto fail;
> 
> -		if (kpageflags_file) {
> -			pread(kpageflags_file, &page_flags, sizeof(page_flags),
> -				(paddr & PFN_MASK) * sizeof(page_flags));
> +	pfn = pagemap_get_pfn(pagemap_fd, vaddr);
> 
> -			return !!(page_flags & KPF_THP);
> -		}
> +	/* non present page */
> +	if (pfn == -1UL)
> +		return 0;
> +
> +	if (get_pfn_flags(pfn, kpageflags_fd, &pfn_flags))
> +		goto fail;
> +
> +	/* check for order-0 pages */
> +	if (!order) {
> +		if (pfn_flags & (KPF_THP | KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))
> +			return 0;
> +		return 1;
> 	}
> -	return 0;
> +
> +	/* non THP folio */
> +	if (!(pfn_flags & KPF_THP))
> +		return 0;
> +
> +	pfn_head = pfn & ~((1 << order) - 1);
> +
> +	if (get_pfn_flags(pfn_head, kpageflags_fd, &pfn_flags))
> +		goto fail;
> +
> +	/* head PFN has no compound_head flag set */
> +	if (!(pfn_flags & (KPF_THP | KPF_COMPOUND_HEAD)))
> +		return 0;
> +
> +	/* check all tail PFN flags */
> +	for (i = 1; i < 1UL << order; i++) {
> +		if (get_pfn_flags(pfn_head + i, kpageflags_fd, &pfn_flags))
> +			goto fail;
> +		if (!(pfn_flags & (KPF_THP | KPF_COMPOUND_TAIL)))
> +			return 0;
> +	}
> +
> +	/*
> +	 * check the PFN after this folio, but if its flags cannot be obtained,
> +	 * assume this folio has the expected order
> +	 */
> +	if (get_pfn_flags(pfn_head + (1UL << order), kpageflags_fd, &pfn_flags))
> +		return 1;
> +
> +	/* this folio is bigger than the given order */
> +	if (pfn_flags & (KPF_THP | KPF_COMPOUND_TAIL))
> +		return 0;
> +
> +	return 1;
> +fail:
> +	ksft_exit_fail_msg("Failed to get folio info\n");
> +	return -1;
> }
> 
> static void write_file(const char *path, const char *buf, size_t buflen)
> @@ -235,7 +280,7 @@ static void split_pte_mapped_thp(void)
> 	thp_size = 0;
> 	for (i = 0; i < pagesize * 4; i++)
> 		if (i % pagesize == 0 &&
> -		    is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd))
> +		    is_backed_by_folio(&pte_mapped[i], pmd_order, pagemap_fd, kpageflags_fd) == 1)
> 			thp_size++;
> 
> 	if (thp_size != 4)
> @@ -252,7 +297,7 @@ static void split_pte_mapped_thp(void)
> 			ksft_exit_fail_msg("%ld byte corrupted\n", i);
> 
> 		if (i % pagesize == 0 &&
> -		    is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd))
> +		    is_backed_by_folio(&pte_mapped[i], 0, pagemap_fd, kpageflags_fd) == 0)
> 			thp_size++;
> 	}
> 
> @@ -524,7 +569,6 @@ int main(int argc, char **argv)
> 	const char *fs_loc;
> 	bool created_tmp;
> 	int offset;
> -	unsigned int max_order;
> 	unsigned int nr_pages;
> 	unsigned int tests;
> 
> @@ -545,28 +589,28 @@ int main(int argc, char **argv)
> 		ksft_exit_fail_msg("Reading PMD pagesize failed\n");
> 
> 	nr_pages = pmd_pagesize / pagesize;
> -	max_order = GET_ORDER(nr_pages);
> -	tests = 2 + (max_order - 1) + (2 * max_order) + (max_order - 1) * 4 + 2;
> +	pmd_order = GET_ORDER(nr_pages);
> +	tests = 2 + (pmd_order - 1) + (2 * pmd_order) + (pmd_order - 1) * 4 + 2;
> 	ksft_set_plan(tests);
> 
> 	fd_size = 2 * pmd_pagesize;
> 
> 	split_pmd_zero_pages();
> 
> -	for (i = 0; i < max_order; i++)
> +	for (i = 0; i < pmd_order; i++)
> 		if (i != 1)
> 			split_pmd_thp_to_order(i);
> 
> 	split_pte_mapped_thp();
> -	for (i = 0; i < max_order; i++)
> +	for (i = 0; i < pmd_order; i++)
> 		split_file_backed_thp(i);
> 
> 	created_tmp = prepare_thp_fs(optional_xfs_path, fs_loc_template,
> 			&fs_loc);
> -	for (i = max_order - 1; i >= 0; i--)
> +	for (i = pmd_order - 1; i >= 0; i--)
> 		split_thp_in_pagecache_to_order_at(fd_size, fs_loc, i, -1);
> 
> -	for (i = 0; i < max_order; i++)
> +	for (i = 0; i < pmd_order; i++)
> 		for (offset = 0;
> 		     offset < nr_pages;
> 		     offset += MAX(nr_pages / 4, 1 << i))
> diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
> index 6a239aa413e2..18b7cb51fc56 100644
> --- a/tools/testing/selftests/mm/vm_util.c
> +++ b/tools/testing/selftests/mm/vm_util.c
> @@ -338,6 +338,19 @@ int detect_hugetlb_page_sizes(size_t sizes[], int max)
> 	return count;
> }
> 
> +int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags)
> +{
> +	size_t count;
> +
> +	count = pread(kpageflags_fd, flags, sizeof(*flags),
> +		      pfn * sizeof(*flags));
> +
> +	if (count != sizeof(*flags))
> +		return -1;
> +
> +	return 0;
> +}
> +
> /* If `ioctls' non-NULL, the allowed ioctls will be returned into the var */
> int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len,
> 			      bool miss, bool wp, bool minor, uint64_t *ioctls)
> diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
> index 1843ad48d32b..03481ca0a1b4 100644
> --- a/tools/testing/selftests/mm/vm_util.h
> +++ b/tools/testing/selftests/mm/vm_util.h
> @@ -18,6 +18,9 @@
> #define PM_SWAP                       BIT_ULL(62)
> #define PM_PRESENT                    BIT_ULL(63)
> 
> +#define KPF_COMPOUND_HEAD             BIT_ULL(15)
> +#define KPF_COMPOUND_TAIL             BIT_ULL(16)
> +#define KPF_THP                       BIT_ULL(22)
> /*
>  * Ignore the checkpatch warning, we must read from x but don't want to do
>  * anything with it in order to trigger a read page fault. We therefore must use
> @@ -85,6 +88,7 @@ bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size);
> int64_t allocate_transhuge(void *ptr, int pagemap_fd);
> unsigned long default_huge_page_size(void);
> int detect_hugetlb_page_sizes(size_t sizes[], int max);
> +int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags);
> 
> int uffd_register(int uffd, void *addr, uint64_t len,
> 		  bool miss, bool wp, bool minor);
> -- 
> 2.50.1

LGTM.
Reviewed-by: wang lian <lianux.mm@gmail.com>

Best regards,
wang lian
Re: [PATCH v4 3/5] selftests/mm: reimplement is_backed_by_thp() with more precise check
Posted by Wei Yang 1 month, 2 weeks ago
On Thu, Aug 14, 2025 at 10:39:12PM -0400, Zi Yan wrote:
>and rename it to is_backed_by_folio().
>
>is_backed_by_folio() checks if the given vaddr is backed a folio with
>a given order. It does so by:
>1. getting the pfn of the vaddr;
>2. checking kpageflags of the pfn;
>
>if order is greater than 0:
>3. checking kpageflags of the head pfn;
>4. checking kpageflags of all tail pfns.
>
>pmd_order is added to split_huge_page_test.c and replaces max_order.
>
>Signed-off-by: Zi Yan <ziy@nvidia.com>

Reviewed-by: Wei Yang <richard.weiyang@gmail.com>

-- 
Wei Yang
Help you, Help me