[PATCH v5 4/5] selftests/mm: add check_after_split_folio_orders() helper.

Zi Yan posted 5 patches 1 month, 2 weeks ago
[PATCH v5 4/5] selftests/mm: add check_after_split_folio_orders() helper.
Posted by Zi Yan 1 month, 2 weeks ago
The helper gathers a folio order statistics of folios within a virtual
address range and checks it against a given order list. It aims to provide
a more precise folio order check instead of just checking the existence of
PMD folios.

The helper will be used the upcoming commit.

Signed-off-by: Zi Yan <ziy@nvidia.com>
---
 .../selftests/mm/split_huge_page_test.c       | 152 ++++++++++++++++++
 1 file changed, 152 insertions(+)

diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index 56d1eaf9a860..e24df02420ad 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -97,6 +97,158 @@ static bool is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
 	return false;
 }
 
+static int vaddr_pageflags_get(char *vaddr, int pagemap_fd, int kpageflags_fd,
+		uint64_t *flags)
+{
+	unsigned long pfn;
+
+	pfn = pagemap_get_pfn(pagemap_fd, vaddr);
+
+	/* non-present PFN */
+	if (pfn == -1UL)
+		return 1;
+
+	if (pageflags_get(pfn, kpageflags_fd, flags))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * gather_after_split_folio_orders - scan through [vaddr_start, len) and record
+ * folio orders
+ *
+ * @vaddr_start: start vaddr
+ * @len: range length
+ * @pagemap_fd: file descriptor to /proc/<pid>/pagemap
+ * @kpageflags_fd: file descriptor to /proc/kpageflags
+ * @orders: output folio order array
+ * @nr_orders: folio order array size
+ *
+ * gather_after_split_folio_orders() scan through [vaddr_start, len) and check
+ * all folios within the range and record their orders. All order-0 pages will
+ * be recorded. Non-present vaddr is skipped.
+ *
+ * NOTE: the function is used to check folio orders after a split is performed,
+ * so it assumes [vaddr_start, len) fully maps to after-split folios within that
+ * range.
+ *
+ * Return: 0 - no error, -1 - unhandled cases
+ */
+static int gather_after_split_folio_orders(char *vaddr_start, size_t len,
+		int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders)
+{
+	uint64_t page_flags = 0;
+	int cur_order = -1;
+	char *vaddr;
+
+	if (pagemap_fd == -1 || kpageflags_fd == -1)
+		return -1;
+	if (!orders)
+		return -1;
+	if (nr_orders <= 0)
+		return -1;
+
+	for (vaddr = vaddr_start; vaddr < vaddr_start + len;) {
+		char *next_folio_vaddr;
+		int status;
+
+		status = vaddr_pageflags_get(vaddr, pagemap_fd, kpageflags_fd,
+					&page_flags);
+		if (status < 0)
+			return -1;
+
+		/* skip non present vaddr */
+		if (status == 1) {
+			vaddr += psize();
+			continue;
+		}
+
+		/* all order-0 pages with possible false postive (non folio) */
+		if (!(page_flags & (KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))) {
+			orders[0]++;
+			vaddr += psize();
+			continue;
+		}
+
+		/* skip non thp compound pages */
+		if (!(page_flags & KPF_THP)) {
+			vaddr += psize();
+			continue;
+		}
+
+		/* vpn points to part of a THP at this point */
+		if (page_flags & KPF_COMPOUND_HEAD)
+			cur_order = 1;
+		else {
+			vaddr += psize();
+			continue;
+		}
+
+		next_folio_vaddr = vaddr + (1UL << (cur_order + pshift()));
+
+		if (next_folio_vaddr >= vaddr_start + len)
+			break;
+
+		while ((status = vaddr_pageflags_get(next_folio_vaddr,
+						     pagemap_fd, kpageflags_fd,
+						     &page_flags)) >= 0) {
+			/*
+			 * non present vaddr, next compound head page, or
+			 * order-0 page
+			 */
+			if (status == 1 ||
+			    (page_flags & KPF_COMPOUND_HEAD) ||
+			    !(page_flags & (KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))) {
+				if (cur_order < nr_orders) {
+					orders[cur_order]++;
+					cur_order = -1;
+					vaddr = next_folio_vaddr;
+				}
+				break;
+			}
+
+			cur_order++;
+			next_folio_vaddr = vaddr + (1UL << (cur_order + pshift()));
+		}
+
+		if (status < 0)
+			return status;
+	}
+	if (cur_order > 0 && cur_order < nr_orders)
+		orders[cur_order]++;
+	return 0;
+}
+
+static int check_after_split_folio_orders(char *vaddr_start, size_t len,
+		int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders)
+{
+	int *vaddr_orders;
+	int status;
+	int i;
+
+	vaddr_orders = (int *)malloc(sizeof(int) * nr_orders);
+
+	if (!vaddr_orders)
+		ksft_exit_fail_msg("Cannot allocate memory for vaddr_orders");
+
+	memset(vaddr_orders, 0, sizeof(int) * nr_orders);
+	status = gather_after_split_folio_orders(vaddr_start, len, pagemap_fd,
+				     kpageflags_fd, vaddr_orders, nr_orders);
+	if (status)
+		ksft_exit_fail_msg("gather folio info failed\n");
+
+	for (i = 0; i < nr_orders; i++)
+		if (vaddr_orders[i] != orders[i]) {
+			ksft_print_msg("order %d: expected: %d got %d\n", i,
+				       orders[i], vaddr_orders[i]);
+			status = -1;
+		}
+
+	free(vaddr_orders);
+	return status;
+}
+
 static void write_file(const char *path, const char *buf, size_t buflen)
 {
 	int fd;
-- 
2.50.1
Re: [PATCH v5 4/5] selftests/mm: add check_after_split_folio_orders() helper.
Posted by Baolin Wang 1 month, 2 weeks ago

On 2025/8/19 02:46, Zi Yan wrote:
> The helper gathers a folio order statistics of folios within a virtual
> address range and checks it against a given order list. It aims to provide
> a more precise folio order check instead of just checking the existence of
> PMD folios.
> 
> The helper will be used the upcoming commit.
> 
> Signed-off-by: Zi Yan <ziy@nvidia.com>
> ---

I tested this patch, and it works for me.
Tested-by: Baolin Wang <baolin.wang@linux.alibaba.com>

By the way, I moved gather_after_split_folio_orders() to the vm_util.c 
file as a helper for mTHP collapse checks in my patchset[1]. I'm not 
sure whether you need to move gather_after_split_folio_orders() to 
vm_util.c in this patch, or if I should move it in my patchset.

[1] 
https://lore.kernel.org/all/955e0b9682b1746c528a043f0ca530b54ee22536.1755677674.git.baolin.wang@linux.alibaba.com/
Re: [PATCH v5 4/5] selftests/mm: add check_after_split_folio_orders() helper.
Posted by Zi Yan 1 month, 2 weeks ago
On 20 Aug 2025, at 5:22, Baolin Wang wrote:

> On 2025/8/19 02:46, Zi Yan wrote:
>> The helper gathers a folio order statistics of folios within a virtual
>> address range and checks it against a given order list. It aims to provide
>> a more precise folio order check instead of just checking the existence of
>> PMD folios.
>>
>> The helper will be used the upcoming commit.
>>
>> Signed-off-by: Zi Yan <ziy@nvidia.com>
>> ---
>
> I tested this patch, and it works for me.
> Tested-by: Baolin Wang <baolin.wang@linux.alibaba.com>

Thanks.

>
> By the way, I moved gather_after_split_folio_orders() to the vm_util.c file as a helper for mTHP collapse checks in my patchset[1]. I'm not sure whether you need to move gather_after_split_folio_orders() to vm_util.c in this patch, or if I should move it in my patchset.

Feel free to move it in your patchset. My initial version has it in vm_util.c, but
I realized that its implementation is very limited to folio split check and moved
it to split_huge_page_test.c. If you find it suitable for your test cases, feel
free to move it. Just note that the code does not handle memremapped THP, since
it only checks page flags without checking the PFN. So when a vaddr range is mapped
to a THP/mTHP head page and some other THP/mTHP tail pages, the code just treats
the whole vaddr range as if it is mapped to a single THP/mTHP and gets a wrong
order. After-split folios do not have this concern, so
gather_after_split_folio_orders() is simplified to not handle such cases.

>
> [1] https://lore.kernel.org/all/955e0b9682b1746c528a043f0ca530b54ee22536.1755677674.git.baolin.wang@linux.alibaba.com/


--
Best Regards,
Yan, Zi
Re: [PATCH v5 4/5] selftests/mm: add check_after_split_folio_orders() helper.
Posted by Baolin Wang 1 month, 2 weeks ago

On 2025/8/20 21:49, Zi Yan wrote:
> On 20 Aug 2025, at 5:22, Baolin Wang wrote:
> 
>> On 2025/8/19 02:46, Zi Yan wrote:
>>> The helper gathers a folio order statistics of folios within a virtual
>>> address range and checks it against a given order list. It aims to provide
>>> a more precise folio order check instead of just checking the existence of
>>> PMD folios.
>>>
>>> The helper will be used the upcoming commit.
>>>
>>> Signed-off-by: Zi Yan <ziy@nvidia.com>
>>> ---
>>
>> I tested this patch, and it works for me.
>> Tested-by: Baolin Wang <baolin.wang@linux.alibaba.com>
> 
> Thanks.
> 
>>
>> By the way, I moved gather_after_split_folio_orders() to the vm_util.c file as a helper for mTHP collapse checks in my patchset[1]. I'm not sure whether you need to move gather_after_split_folio_orders() to vm_util.c in this patch, or if I should move it in my patchset.
> 
> Feel free to move it in your patchset. My initial version has it in vm_util.c, but
> I realized that its implementation is very limited to folio split check and moved
> it to split_huge_page_test.c. If you find it suitable for your test cases, feel
> free to move it. Just note that the code does not handle memremapped THP, since
> it only checks page flags without checking the PFN. So when a vaddr range is mapped
> to a THP/mTHP head page and some other THP/mTHP tail pages, the code just treats
> the whole vaddr range as if it is mapped to a single THP/mTHP and gets a wrong
> order. After-split folios do not have this concern, so
> gather_after_split_folio_orders() is simplified to not handle such cases.

Thanks for the information. khugepaged also does not have this case, so 
it works well for me.