[RFC PATCH 52/57] selftests/mm: cover a hole-y window in a sub-PMD VMA

Kiryl Shutsemau <[email protected]>
Newsgroups org.kernel.vger.linux-kselftest,org.kernel.vger.bpf,org.kernel.vger.linux-kernel,org.kernel.vger.linux-trace-kernel,org.kvack.linux-mm
Message-ID <[email protected]>
From: "Kiryl Shutsemau (Meta)" <[email protected]>

A collapse over a partially populated window has to zero the slots it
found empty.  The destination comes from the allocator holding whatever
was last written to it, and a slot the process never touched must read
back zero.

Nothing in the suite checks that.  validate_memory() only re-reads the
pattern fill_memory() wrote, and every caller passes it the faulted extent
alone, so a collapse that left stale bytes behind the holes would pass
every case here.

Check it where population and sub-PMD eligibility meet, which no existing
case covers either.  One page is faulted in a window-sized VMA; the case
expects the window collapsed, the faulted page unchanged, and every byte
behind the holes zero.

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

diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index 85f138cfb9b2..b61e32566d47 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -1599,6 +1599,55 @@ static void collapse_order_sub_pmd_range(struct collapse_context *c,
 	__collapse_order_sub_pmd_vma(c, ops, nr_windows, __func__);
 }
 
+/*
+ * A partially populated window in a sub-PMD VMA: population and
+ * sub-PMD eligibility at once. The unfaulted slots must come back
+ * zero-filled in the collapsed folio, and the faulted ones unchanged.
+ */
+static void collapse_order_sub_pmd_holes(struct collapse_context *c,
+					 struct mem_ops *ops)
+{
+	size_t size = mthp_window_size();
+	char *bytes;
+	void *p;
+	size_t i;
+
+	mthp_push_target_order();
+
+	p = mmap(BASE_ADDR, size, PROT_READ | PROT_WRITE,
+		 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	bytes = p;
+	if (p != BASE_ADDR)
+		ksft_exit_fail_msg("Failed to allocate VMA at %p\n", BASE_ADDR);
+
+	fill_memory(p, 0, page_size);
+	if (!window_not_collapsed(p, size))
+		ksft_exit_fail_msg("Unexpected large folio after fault\n");
+
+	madvise(p, size, MADV_HUGEPAGE);
+	ksft_print_msg("Collapse hole-y window inside a sub-PMD VMA...");
+	if (!khugepaged_wait_full_pass()) {
+		fail("Timeout");
+	} else if (window_collapsed(p, size)) {
+		/* The unfaulted tail must be zero-filled. */
+		for (i = page_size; i < size; i++) {
+			if (bytes[i])
+				break;
+		}
+		if (i == size)
+			success("OK");
+		else
+			fail("Fail");
+	} else {
+		fail("Fail");
+	}
+
+	validate_memory(p, 0, page_size);
+	munmap(p, size);
+	thp_pop_settings();
+	ksft_test_result_report(exit_status, "%s\n", __func__);
+}
+
 static void usage(void)
 {
 	fprintf(stderr, "\nUsage: ./khugepaged [OPTIONS] <test type> [dir]\n\n");
@@ -1888,6 +1937,7 @@ int main(int argc, char **argv)
 		TEST(collapse_order_mixed_sources, mthp_khugepaged_context, anon_ops);
 		TEST(collapse_order_sub_pmd_vma, mthp_khugepaged_context, anon_ops);
 		TEST(collapse_order_sub_pmd_range, mthp_khugepaged_context, anon_ops);
+		TEST(collapse_order_sub_pmd_holes, mthp_khugepaged_context, anon_ops);
 	}
 
 	TEST(collapse_full, madvise_context, anon_ops);
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.