Skip to contentSkip to conversation

add anon mTHP collapse test cases

PATCH Superseded Baolin Wang 4 participants 24 messages Aug 1 – Aug 6

Subsystems (5)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Baolin Wang

author reviewer
#0 lore raw

v2 cover add anon mTHP collapse test cases

This patchset is split from the previous shmem mTHP collapse patchset[1], which only adds anon mTHP collapse selftests. All khugepaged test cases can pass.

[1] https://lore.kernel.org/all/[email protected]/

Changes from v1:
- Add order and hpage_size check (per Sashiko).
- Change to use THP_MADVISE for mTHP collapse (per Sashiko).

Baolin Wang (4):
selftests: mm: extend the check_huge() to support mTHP check
selftests: mm: move gather_after_split_folio_orders() into vm_util.c
file
selftests: mm: implement the mTHP-sized hugepage check helpers
selftests: mm: add mTHP collapse test cases

 .../selftests/mm/folio_split_race_test.c      |   2 +-
 tools/testing/selftests/mm/khugepaged.c       | 190 +++++++++++++-----
 tools/testing/selftests/mm/pagemap_ioctl.c    |   2 +-
 .../testing/selftests/mm/prctl_thp_disable.c  |   2 +-
 tools/testing/selftests/mm/run_vmtests.sh     |   2 +
 tools/testing/selftests/mm/soft-dirty.c       |   2 +-
 .../selftests/mm/split_huge_page_test.c       | 139 +------------
 tools/testing/selftests/mm/uffd-common.c      |   4 +-
 tools/testing/selftests/mm/vm_util.c          | 185 ++++++++++++++++-
 tools/testing/selftests/mm/vm_util.h          |   8 +-
 10 files changed, 344 insertions(+), 192 deletions(-)
Signature · 1 line
2.47.3
author reviewer
#1 lore raw

PATCH v2 1/4 selftests: mm: extend the check_huge() to support mTHP check

To support checking for various sized mTHPs during mTHP collapse, extend the check_huge() function prototype to accept two new parameters specifying the address range and mTHP size, in preparation for the following patches.

No functional changes.

---
 .../selftests/mm/folio_split_race_test.c      |  2 +-
 tools/testing/selftests/mm/khugepaged.c       | 66 ++++++++++---------
 tools/testing/selftests/mm/pagemap_ioctl.c    |  2 +-
 .../testing/selftests/mm/prctl_thp_disable.c  |  2 +-
 tools/testing/selftests/mm/soft-dirty.c       |  2 +-
 .../selftests/mm/split_huge_page_test.c       | 14 ++--
 tools/testing/selftests/mm/uffd-common.c      |  4 +-
 tools/testing/selftests/mm/vm_util.c          |  6 +-
 tools/testing/selftests/mm/vm_util.h          |  6 +-
 9 files changed, 56 insertions(+), 48 deletions(-)
tools/testing/selftests/mm/folio_split_race_test.c +1 -1
index 6329e37fff4c..45b84f7b364e 100644
@@ -182,7 +182,7 @@ static uint64_t run_iteration(void)
 	for (i = 0; i < TOTAL_PAGES; i++)
 		fill_page(mmap_base, i);
 
-	if (!check_huge_shmem(mmap_base, NR_PMD_PAGE, pmd_pagesize))
+	if (!check_huge_shmem(mmap_base, FILE_SIZE, NR_PMD_PAGE, pmd_pagesize))
 		ksft_exit_fail_msg("No shmem THP is allocated\n");
 
 	if (pthread_barrier_init(&ctl.barrier, NULL, NUM_READER_THREADS + 1) != 0)
tools/testing/selftests/mm/khugepaged.c +36 -30

238 lines not on this page · raw message

tools/testing/selftests/mm/pagemap_ioctl.c +1 -1
index 1b2dffcc999b..cfd1987339c1 100644
@@ -1085,7 +1085,7 @@ static void unpopulated_written_test(const char *name, char *mem, long size,
 	memset(mem, 1, size);
 	if (use_thp &&
 	    (madvise(mem, size, MADV_COLLAPSE) ||
-	     !check_huge_anon(mem, size / hpage_size, hpage_size))) {
+	     !check_huge_anon(mem, size, size / hpage_size, hpage_size))) {
 		ksft_test_result_skip("%s could not form a THP\n", name);
 		goto out;
 	}
tools/testing/selftests/mm/prctl_thp_disable.c +1 -1
index d8d9d1de57b8..82c6e96ea6eb 100644
@@ -67,7 +67,7 @@ static int test_mmap_thp(enum thp_collapse_type madvise_buf, size_t pmdsize)
 	/* HACK: make sure we have a separate VMA that we can check reliably. */
 	mprotect(mem, pmdsize, PROT_READ);
 
-	ret = check_huge_anon(mem, 1, pmdsize);
+	ret = check_huge_anon(mem, pmdsize, 1, pmdsize);
 	munmap(mmap_mem, mmap_size);
 	return ret;
 }
tools/testing/selftests/mm/soft-dirty.c +1 -1
index fb1864a68e1c..e198facf78bb 100644
@@ -103,7 +103,7 @@ static void test_hugepage(int pagemap_fd, int pagesize)
 	for (i = 0; i < hpage_len; i++)
 		map[i] = (char)i;
 
-	if (check_huge_anon(map, 1, hpage_len)) {
+	if (check_huge_anon(map, hpage_len, 1, hpage_len)) {
 		ksft_test_result_pass("Test %s huge page allocation\n", __func__);
 
 		clear_softdirty();
tools/testing/selftests/mm/split_huge_page_test.c +7 -7
index 32b991472f74..4cc70873a674 100644
@@ -296,7 +296,7 @@ static void verify_rss_anon_split_huge_page_all_zeroes(char *one_page, int nr_hp
 	unsigned long rss_anon_before, rss_anon_after;
 	size_t i;
 
-	if (!check_huge_anon(one_page, nr_hpages, pmd_pagesize))
+	if (!check_huge_anon(one_page, nr_hpages * pmd_pagesize, nr_hpages, pmd_pagesize))
 		ksft_exit_fail_msg("No THP is allocated\n");
 
 	rss_anon_before = rss_anon();
@@ -311,7 +311,7 @@ static void verify_rss_anon_split_huge_page_all_zeroes(char *one_page, int nr_hp
 		if (one_page[i] != (char)0)
 			ksft_exit_fail_msg("%ld byte corrupted\n", i);
 
-	if (!check_huge_anon(one_page, 0, pmd_pagesize))
+	if (!check_huge_anon(one_page, nr_hpages * pmd_pagesize, 0, pmd_pagesize))
 		ksft_exit_fail_msg("Still AnonHugePages not split\n");
 
 	rss_anon_after = rss_anon();
@@ -347,7 +347,7 @@ static void split_pmd_thp_to_order(int order)
 	for (i = 0; i < len; i++)
 		one_page[i] = (char)i;
 
-	if (!check_huge_anon(one_page, 4, pmd_pagesize))
+	if (!check_huge_anon(one_page, 4 * pmd_pagesize, 4, pmd_pagesize))
 		ksft_exit_fail_msg("No THP is allocated\n");
 
 	/* split all THPs */
@@ -366,7 +366,7 @@ static void split_pmd_thp_to_order(int order)
 					   (pmd_order + 1)))
 		ksft_exit_fail_msg("Unexpected THP split\n");
 
-	if (!check_huge_anon(one_page, 0, pmd_pagesize))
+	if (!check_huge_anon(one_page, 4 * pmd_pagesize, 0, pmd_pagesize))
 		ksft_exit_fail_msg("Still AnonHugePages not split\n");
 
 	ksft_test_result_pass("Split huge pages to order %d successful\n", order);
@@ -393,7 +393,7 @@ static void split_pte_mapped_thp(void)
 	for (i = 0; i < thp_area_size; i++)
 		thp_area[i] = (char)i;
 
-	if (!check_huge_anon(thp_area, nr_thps, pmd_pagesize)) {
+	if (!check_huge_anon(thp_area, nr_thps * pmd_pagesize, nr_thps, pmd_pagesize)) {
 		ksft_test_result_skip("Not all THPs allocated\n");
 		goto out;
 	}
@@ -657,7 +657,7 @@ static int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size,
 
 	force_read_pages(*addr, fd_size / pmd_pagesize, pmd_pagesize);
 
-	if (!check_huge_file(*addr, fd_size / pmd_pagesize, pmd_pagesize)) {
+	if (!check_huge_file(*addr, fd_size, fd_size / pmd_pagesize, pmd_pagesize)) {
 		ksft_print_msg("No large pagecache folio generated, please provide a filesystem supporting large folio\n");
 		munmap(*addr, fd_size);
 		close(*fd);
@@ -735,7 +735,7 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size,
 		goto out;
 	}
 
-	if (!check_huge_file(addr, 0, pmd_pagesize)) {
+	if (!check_huge_file(addr, fd_size, 0, pmd_pagesize)) {
 		ksft_print_msg("Still FilePmdMapped not split\n");
 		err = EXIT_FAILURE;
 		goto out;
tools/testing/selftests/mm/uffd-common.c +3 -1
index f48f5d4594ab..fa46c8c3a434 100644
@@ -194,7 +194,9 @@ static void shmem_alias_mapping(uffd_global_test_opts_t *gopts, __u64 *start,
 
 static void shmem_check_pmd_mapping(uffd_global_test_opts_t *gopts, void *p, int expect_nr_hpages)
 {
-	if (!check_huge_shmem(gopts->area_dst_alias, expect_nr_hpages,
+	unsigned long size = expect_nr_hpages * read_pmd_pagesize();
+
+	if (!check_huge_shmem(gopts->area_dst_alias, size, expect_nr_hpages,
 			      read_pmd_pagesize()))
 		err("Did not find expected %d number of hugepages",
 		    expect_nr_hpages);
tools/testing/selftests/mm/vm_util.c +3 -3
index ef1ea11981a7..d92c819f3717 100644
@@ -247,17 +247,17 @@ bool __check_huge(void *addr, char *pattern, int nr_hpages,
 	return thp == (nr_hpages * (hpage_size >> 10));
 }
 
-bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size)
+bool check_huge_anon(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
 {
 	return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
 }
 
-bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size)
+bool check_huge_file(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
 {
 	return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
 }
 
-bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size)
+bool check_huge_shmem(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
 {
 	return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
 }
tools/testing/selftests/mm/vm_util.h +3 -3
index 7799154b67ee..c7ae28dfda63 100644
@@ -90,9 +90,9 @@ void clear_softdirty(void);
 bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len);
 uint64_t read_pmd_pagesize(void);
 unsigned long rss_anon(void);
-bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size);
-bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size);
-bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size);
+bool check_huge_anon(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size);
+bool check_huge_file(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size);
+bool check_huge_shmem(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size);
 int64_t allocate_transhuge(void *ptr, int pagemap_fd);
 int pageflags_get(unsigned long pfn, int kpageflags_fd, uint64_t *flags);
 
Signature · 1 line
2.47.3
reviewer Reviewed-by on v2 1/4
#2 lore raw

Re: [PATCH v2 1/4] selftests: mm: extend the check_huge() to support mTHP check

Quoted 7 lines

To support checking for various sized mTHPs during mTHP collapse, extend the check_huge() function prototype to accept two new parameters specifying the address range and mTHP size, in preparation for the following patches.

No functional changes.

LGTM!

Signature · 1 line
Nico Pache (Red Hat) <[email protected]>
reviewer Acked-by on v2 1/4
#3 lore raw

Re: [PATCH v2 1/4] selftests: mm: extend the check_huge() to support mTHP check

In reply to Baolin Wang
Quoted 19 lines · Baolin Wang

To support checking for various sized mTHPs during mTHP collapse, extend the check_huge() function prototype to accept two new parameters specifying the address range and mTHP size, in preparation for the following patches.

No functional changes.

---
 .../selftests/mm/folio_split_race_test.c      |  2 +-
 tools/testing/selftests/mm/khugepaged.c       | 66 ++++++++++---------
 tools/testing/selftests/mm/pagemap_ioctl.c    |  2 +-
 .../testing/selftests/mm/prctl_thp_disable.c  |  2 +-
 tools/testing/selftests/mm/soft-dirty.c       |  2 +-
 .../selftests/mm/split_huge_page_test.c       | 14 ++--
 tools/testing/selftests/mm/uffd-common.c      |  4 +-
 tools/testing/selftests/mm/vm_util.c          |  6 +-
 tools/testing/selftests/mm/vm_util.h          |  6 +-
 9 files changed, 56 insertions(+), 48 deletions(-)

<snip>

Quoted 7 lines
tools/testing/selftests/mm/vm_util.h +0 -0
index 7799154b67ee..c7ae28dfda63 100644
@@ -90,9 +90,9 @@ void clear_softdirty(void);
 bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len);
 uint64_t read_pmd_pagesize(void);

tools/testing/selftests/mm/vm_util.h void clear_softdirty(void); · line 98

Quoted 10 lines
tools/testing/selftests/mm/vm_util.h +3 -3
 unsigned long rss_anon(void);
-bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size);
-bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size);
-bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size);
+bool check_huge_anon(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size);
+bool check_huge_file(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size);
+bool check_huge_shmem(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size);
 int64_t allocate_transhuge(void *ptr, int pagemap_fd);
 int pageflags_get(unsigned long pfn, int kpageflags_fd, uint64_t *flags);
 

I get what "size" means only after I see Patch 3 where gather_after_split_folio_orders() is used. I think len is a better name if not addr_len and its type should be size_t to match gather_after_split_folio_orders()'s signature. The rest patches should have the same rename and type change. Thanks.

Otherwise, LGTM.

Signature · 2 lines
Best Regards,
Yan, Zi
author reviewer
#4 lore raw

Re: [PATCH v2 1/4] selftests: mm: extend the check_huge() to support mTHP check

Quoted 38 lines · Zi Yan
Quoted 19 lines · Baolin Wang

To support checking for various sized mTHPs during mTHP collapse, extend the check_huge() function prototype to accept two new parameters specifying the address range and mTHP size, in preparation for the following patches.

No functional changes.

---
  .../selftests/mm/folio_split_race_test.c      |  2 +-
  tools/testing/selftests/mm/khugepaged.c       | 66 ++++++++++---------
  tools/testing/selftests/mm/pagemap_ioctl.c    |  2 +-
  .../testing/selftests/mm/prctl_thp_disable.c  |  2 +-
  tools/testing/selftests/mm/soft-dirty.c       |  2 +-
  .../selftests/mm/split_huge_page_test.c       | 14 ++--
  tools/testing/selftests/mm/uffd-common.c      |  4 +-
  tools/testing/selftests/mm/vm_util.c          |  6 +-
  tools/testing/selftests/mm/vm_util.h          |  6 +-
  9 files changed, 56 insertions(+), 48 deletions(-)

<snip>

Quoted 15 lines
tools/testing/selftests/mm/vm_util.h +3 -3
index 7799154b67ee..c7ae28dfda63 100644
@@ -90,9 +90,9 @@ void clear_softdirty(void);
  bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len);
  uint64_t read_pmd_pagesize(void);
  unsigned long rss_anon(void);
-bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size);
-bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size);
-bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size);
+bool check_huge_anon(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size);
+bool check_huge_file(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size);
+bool check_huge_shmem(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size);
  int64_t allocate_transhuge(void *ptr, int pagemap_fd);
Quoted 8 lines

tools/testing/selftests/mm/vm_util.h void clear_softdirty(void); · line 98

Quoted 2 lines
tools/testing/selftests/mm/vm_util.h +0 -0
  int pageflags_get(unsigned long pfn, int kpageflags_fd, uint64_t *flags);
 

I get what "size" means only after I see Patch 3 where gather_after_split_folio_orders() is used. I think len is a better name if not addr_len and its type should be size_t to match gather_after_split_folio_orders()'s signature. The rest patches should have the same rename and type change. Thanks.

Will do.

Quoted 3 lines

Otherwise, LGTM.

Thanks.

author reviewer
#5 lore raw

PATCH v2 2/4 selftests: mm: move gather_after_split_folio_orders() into vm_util.c file

In reply to Baolin Wang

Move gather_after_split_folio_orders() to vm_util.c as a helper function in preparation for implementing checks for mTHP collapse. While we are at it, rename this function to indicate that it is not only used for large folio splits.

No functional changes.

---
 .../selftests/mm/split_huge_page_test.c       | 125 +-----------------
 tools/testing/selftests/mm/vm_util.c          | 119 +++++++++++++++++
 tools/testing/selftests/mm/vm_util.h          |   2 +
 3 files changed, 122 insertions(+), 124 deletions(-)
tools/testing/selftests/mm/split_huge_page_test.c +1 -124
index 4cc70873a674..86a603692826 100644
@@ -104,129 +104,6 @@ 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)
 {
@@ -240,7 +117,7 @@ static int check_after_split_folio_orders(char *vaddr_start, size_t len,
 		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,
+	status = gather_folio_orders(vaddr_start, len, pagemap_fd,
 				     kpageflags_fd, vaddr_orders, nr_orders);
 	if (status)
 		ksft_exit_fail_msg("gather folio info failed\n");
tools/testing/selftests/mm/vm_util.c +119 -0

125 lines not on this page · raw message

tools/testing/selftests/mm/vm_util.h +2 -0
index c7ae28dfda63..20af3e574cd8 100644
@@ -95,6 +95,8 @@ bool check_huge_file(void *addr, unsigned long size, int nr_hpages, uint64_t hpa
 bool check_huge_shmem(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size);
 int64_t allocate_transhuge(void *ptr, int pagemap_fd);
 int pageflags_get(unsigned long pfn, int kpageflags_fd, uint64_t *flags);
+int gather_folio_orders(char *vaddr_start, size_t len,
+		int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders);
 
 int uffd_register(int uffd, void *addr, uint64_t len,
 		  bool miss, bool wp, bool minor);
Signature · 1 line
2.47.3
reviewer Reviewed-by on v2 2/4
#6 lore raw

Re: [PATCH v2 2/4] selftests: mm: move gather_after_split_folio_orders() into vm_util.c file

Quoted 8 lines

Move gather_after_split_folio_orders() to vm_util.c as a helper function in preparation for implementing checks for mTHP collapse. While we are at it, rename this function to indicate that it is not only used for large folio splits.

No functional changes.

LGTM!

Signature · 1 line
Nico Pache (Red Hat) <[email protected]>
reviewer Reviewed-by on v2 2/4
#7 lore raw

Re: [PATCH v2 2/4] selftests: mm: move gather_after_split_folio_orders() into vm_util.c file

In reply to Baolin Wang
Quoted 5 lines · Baolin Wang

Move gather_after_split_folio_orders() to vm_util.c as a helper function in preparation for implementing checks for mTHP collapse. While we are at it, rename this function to indicate that it is not only used for large folio splits.

Quoted 9 lines

No functional changes.

---
 .../selftests/mm/split_huge_page_test.c       | 125 +-----------------
 tools/testing/selftests/mm/vm_util.c          | 119 +++++++++++++++++
 tools/testing/selftests/mm/vm_util.h          |   2 +
 3 files changed, 122 insertions(+), 124 deletions(-)
Signature · 2 lines
Best Regards,
Yan, Zi
author reviewer
#8 lore raw

PATCH v2 3/4 selftests: mm: implement the mTHP-sized hugepage check helpers

In reply to Baolin Wang

Implement mTHP-sized hugepage checking helpers using gather_folio_orders(). Also rename the existing PMD-sized huge page check function to __check_pmd_huge() for clarity.

---
 tools/testing/selftests/mm/vm_util.c | 60 ++++++++++++++++++++++++++--
 1 file changed, 56 insertions(+), 4 deletions(-)
tools/testing/selftests/mm/vm_util.c +56 -4
index 09e5d5cabe21..1240d783669c 100644
@@ -15,6 +15,10 @@
 #define SMAP_FILE_PATH "/proc/self/smaps"
 #define STATUS_FILE_PATH "/proc/self/status"
 #define MAX_LINE_LENGTH 500
+#define PAGEMAP_PATH "/proc/self/pagemap"
+#define KPAGEFLAGS_PATH "/proc/kpageflags"
+#define GET_ORDER(nr_pages)    (31 - __builtin_clz(nr_pages))
+#define NR_ORDERS 20
 
 unsigned int __page_size;
 unsigned int __page_shift;
@@ -348,7 +352,7 @@ char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len)
 	return entry;
 }
 
-bool __check_huge(void *addr, char *pattern, int nr_hpages,
+static bool __check_pmd_huge(void *addr, char *pattern, int nr_hpages,
 		  uint64_t hpage_size)
 {
 	char buffer[MAX_LINE_LENGTH];
@@ -366,19 +370,67 @@ bool __check_huge(void *addr, char *pattern, int nr_hpages,
 	return thp == (nr_hpages * (hpage_size >> 10));
 }
 
+static bool check_large_folios(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
+{
+	int order = 0, pagesize = getpagesize();
+	int nr_pages = hpage_size / pagesize;
+	int pagemap_fd, kpageflags_fd;
+	int orders[NR_ORDERS], status;
+	bool ret = false;
+
+	if (nr_pages > 0)
+		order = GET_ORDER(nr_pages);
+
+	if (!order || order >= NR_ORDERS)
+		ksft_exit_fail_msg("invalid order\n");
+
+	memset(orders, 0, sizeof(int) * 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, size, pagemap_fd,
+			kpageflags_fd, orders, NR_ORDERS);
+	if (status)
+		goto out;
+
+	if (orders[order] == nr_hpages)
+		ret = true;
+
+out:
+	close(pagemap_fd);
+	close(kpageflags_fd);
+	return ret;
+}
+
 bool check_huge_anon(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
 {
-	return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
+	if (hpage_size == read_pmd_pagesize())
+		return __check_pmd_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
+
+	return check_large_folios(addr, size, nr_hpages, hpage_size);
 }
 
 bool check_huge_file(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
 {
-	return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
+	if (hpage_size == read_pmd_pagesize())
+		return __check_pmd_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
+
+	return check_large_folios(addr, size, nr_hpages, hpage_size);
 }
 
 bool check_huge_shmem(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
 {
-	return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
+	if (hpage_size == read_pmd_pagesize())
+		return __check_pmd_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
+
+	return check_large_folios(addr, size, nr_hpages, hpage_size);
 }
 
 int64_t allocate_transhuge(void *ptr, int pagemap_fd)
Signature · 1 line
2.47.3
reviewer Reviewed-by on v2 3/4
#9 lore raw

Re: [PATCH v2 3/4] selftests: mm: implement the mTHP-sized hugepage check helpers

Quoted 5 lines

Implement mTHP-sized hugepage checking helpers using gather_folio_orders(). Also rename the existing PMD-sized huge page check function to __check_pmd_huge() for clarity.

LGTM!

Signature · 1 line
Nico Pache (Red Hat) <[email protected]>
reviewer
#10 lore raw

Re: [PATCH v2 3/4] selftests: mm: implement the mTHP-sized hugepage check helpers

In reply to Baolin Wang
Quoted 14 lines · Baolin Wang

Implement mTHP-sized hugepage checking helpers using gather_folio_orders(). Also rename the existing PMD-sized huge page check function to __check_pmd_huge() for clarity.

---
 tools/testing/selftests/mm/vm_util.c | 60 ++++++++++++++++++++++++++--
 1 file changed, 56 insertions(+), 4 deletions(-)
tools/testing/selftests/mm/vm_util.c +0 -0
index 09e5d5cabe21..1240d783669c 100644

tools/testing/selftests/mm/vm_util.c line 20

Quoted 6 lines
tools/testing/selftests/mm/vm_util.c +3 -0
@@ -15,6 +15,10 @@
 #define SMAP_FILE_PATH "/proc/self/smaps"
 #define STATUS_FILE_PATH "/proc/self/status"
 #define MAX_LINE_LENGTH 500
+#define PAGEMAP_PATH "/proc/self/pagemap"
+#define KPAGEFLAGS_PATH "/proc/kpageflags"
+#define GET_ORDER(nr_pages)    (31 - __builtin_clz(nr_pages))

This is a fast way of getting log2, but there is no check of the type of nr_pages. What prevents one using it on a long variable?

At least you can convert nr_pages to unsigned int and add a comment.

tools/testing/selftests/mm/vm_util.c line 21

Quoted 1 line
tools/testing/selftests/mm/vm_util.c +1 -0
+#define NR_ORDERS 20

Why 20? mTHP orders can only be in [1, pmd_order - 1] and different arch has different pmd_orders. check_large_folios() probably should just cap NR_ORDERS at pmd_order.

Quoted 13 lines
tools/testing/selftests/mm/vm_util.c +1 -1
 unsigned int __page_size;
 unsigned int __page_shift;
@@ -348,7 +352,7 @@ char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len)
 	return entry;
 }
 
-bool __check_huge(void *addr, char *pattern, int nr_hpages,
+static bool __check_pmd_huge(void *addr, char *pattern, int nr_hpages,
 		  uint64_t hpage_size)
 {
 	char buffer[MAX_LINE_LENGTH];

tools/testing/selftests/mm/vm_util.c bool __check_huge(void *addr, char *pattern, int nr_hpages, · line 378

Quoted 9 lines
tools/testing/selftests/mm/vm_util.c +6 -0
@@ -366,19 +370,67 @@ bool __check_huge(void *addr, char *pattern, int nr_hpages,
 	return thp == (nr_hpages * (hpage_size >> 10));
 }
 
+static bool check_large_folios(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
+{
+	int order = 0, pagesize = getpagesize();
+	int nr_pages = hpage_size / pagesize;
+	int pagemap_fd, kpageflags_fd;
+	int orders[NR_ORDERS], status;

NR_ORDERS is a constant, so you can put orders on stack. I think you can rename it to MAX_NR_ORDERS, use 20, and check pmd_order is not bigger than MAX_NR_ORDERS.

tools/testing/selftests/mm/vm_util.c bool __check_huge(void *addr, char *pattern, int nr_hpages, · line 382

Quoted 4 lines
tools/testing/selftests/mm/vm_util.c +4 -0
+	bool ret = false;
+
+	if (nr_pages > 0)
+		order = GET_ORDER(nr_pages);

This funciton can fail early if nr_pages is 0.

tools/testing/selftests/mm/vm_util.c bool __check_huge(void *addr, char *pattern, int nr_hpages, · line 385

Quoted 3 lines
tools/testing/selftests/mm/vm_util.c +3 -0
+
+	if (!order || order >= NR_ORDERS)
+		ksft_exit_fail_msg("invalid order\n");

It should check against pmd_order.

Quoted 20 lines
tools/testing/selftests/mm/vm_util.c +20 -0
+
+	memset(orders, 0, sizeof(int) * 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, size, pagemap_fd,
+			kpageflags_fd, orders, NR_ORDERS);
+	if (status)
+		goto out;
+
+	if (orders[order] == nr_hpages)
+		ret = true;
+

tools/testing/selftests/mm/vm_util.c bool __check_huge(void *addr, char *pattern, int nr_hpages, · line 414

Quoted 10 lines
tools/testing/selftests/mm/vm_util.c +7 -1
+out:
+	close(pagemap_fd);
+	close(kpageflags_fd);
+	return ret;
+}
+
 bool check_huge_anon(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
 {
-	return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
+	if (hpage_size == read_pmd_pagesize())

read_pmd_pagesize() can fail and return 0. Either you need a check here or add a ksft_exit_fail_msg() in read_pmd_pagesize() to remove the burden from all callers.

Quoted 14 lines
tools/testing/selftests/mm/vm_util.c +7 -1
+		return __check_pmd_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
+
+	return check_large_folios(addr, size, nr_hpages, hpage_size);
 }
 
 bool check_huge_file(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
 {
-	return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
+	if (hpage_size == read_pmd_pagesize())
+		return __check_pmd_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
+
+	return check_large_folios(addr, size, nr_hpages, hpage_size);
 }
 

tools/testing/selftests/mm/vm_util.c bool __check_huge(void *addr, char *pattern, int nr_hpages, · line 436

Quoted 10 lines
tools/testing/selftests/mm/vm_util.c +4 -1
 bool check_huge_shmem(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
 {
-	return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
+	if (hpage_size == read_pmd_pagesize())
+		return __check_pmd_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
+
+	return check_large_folios(addr, size, nr_hpages, hpage_size);
 }
 
 int64_t allocate_transhuge(void *ptr, int pagemap_fd)

All read_pmd_pagesize()s above need to be handled.

Signature · 2 lines
Best Regards,
Yan, Zi
author reviewer
#11 lore raw

Re: [PATCH v2 3/4] selftests: mm: implement the mTHP-sized hugepage check helpers

Quoted 25 lines · Zi Yan
Quoted 14 lines · Baolin Wang

Implement mTHP-sized hugepage checking helpers using gather_folio_orders(). Also rename the existing PMD-sized huge page check function to __check_pmd_huge() for clarity.

---
  tools/testing/selftests/mm/vm_util.c | 60 ++++++++++++++++++++++++++--
  1 file changed, 56 insertions(+), 4 deletions(-)
tools/testing/selftests/mm/vm_util.c +0 -0
index 09e5d5cabe21..1240d783669c 100644

tools/testing/selftests/mm/vm_util.c line 20

Quoted 6 lines
tools/testing/selftests/mm/vm_util.c +3 -0
@@ -15,6 +15,10 @@
  #define SMAP_FILE_PATH "/proc/self/smaps"
  #define STATUS_FILE_PATH "/proc/self/status"
  #define MAX_LINE_LENGTH 500
+#define PAGEMAP_PATH "/proc/self/pagemap"
+#define KPAGEFLAGS_PATH "/proc/kpageflags"
+#define GET_ORDER(nr_pages)    (31 - __builtin_clz(nr_pages))

This is a fast way of getting log2, but there is no check of the type of nr_pages. What prevents one using it on a long variable?

Quoted 1 line

At least you can convert nr_pages to unsigned int and add a comment.

Good point. Ack.

Quoted 5 lines

tools/testing/selftests/mm/vm_util.c line 21

Quoted 1 line
tools/testing/selftests/mm/vm_util.c +1 -0
+#define NR_ORDERS 20

Why 20? mTHP orders can only be in [1, pmd_order - 1] and different arch has different pmd_orders. check_large_folios() probably should just cap NR_ORDERS at pmd_order.

I followed the NR_ORDERS definition in selftests/mm/hugepage_settings.h, and I think this function is not only for mTHP. Other test cases may also call it to get the count of large folios beyond PMD order in the future.

Quoted 15 lines
Quoted 15 lines
tools/testing/selftests/mm/vm_util.c +1 -1
  unsigned int __page_size;
  unsigned int __page_shift;
@@ -348,7 +352,7 @@ char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len)
  	return entry;
  }
 
-bool __check_huge(void *addr, char *pattern, int nr_hpages,
+static bool __check_pmd_huge(void *addr, char *pattern, int nr_hpages,
  		  uint64_t hpage_size)
  {
  	char buffer[MAX_LINE_LENGTH];
@@ -366,19 +370,67 @@ bool __check_huge(void *addr, char *pattern, int nr_hpages,
  	return thp == (nr_hpages * (hpage_size >> 10));
  }
 
Quoted 9 lines

tools/testing/selftests/mm/vm_util.c bool __check_huge(void *addr, char *pattern, int nr_hpages, · line 378

Quoted 6 lines
tools/testing/selftests/mm/vm_util.c +6 -0
+static bool check_large_folios(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
+{
+	int order = 0, pagesize = getpagesize();
+	int nr_pages = hpage_size / pagesize;
+	int pagemap_fd, kpageflags_fd;
+	int orders[NR_ORDERS], status;

NR_ORDERS is a constant, so you can put orders on stack. I think you can rename it to MAX_NR_ORDERS, use 20,

Sure.

and check pmd_order is not bigger

Quoted 1 line

than MAX_NR_ORDERS.

I don't think we need this. As I mentioned above, I hope this function can also be used in the future to get the number of large folios beyond PMD order.

Quoted 6 lines

tools/testing/selftests/mm/vm_util.c bool __check_huge(void *addr, char *pattern, int nr_hpages, · line 382

Quoted 4 lines
tools/testing/selftests/mm/vm_util.c +4 -0
+	bool ret = false;
+
+	if (nr_pages > 0)
+		order = GET_ORDER(nr_pages);

This funciton can fail early if nr_pages is 0.

Ack.

Quoted 5 lines

tools/testing/selftests/mm/vm_util.c bool __check_huge(void *addr, char *pattern, int nr_hpages, · line 385

Quoted 3 lines
tools/testing/selftests/mm/vm_util.c +3 -0
+
+	if (!order || order >= NR_ORDERS)
+		ksft_exit_fail_msg("invalid order\n");

It should check against pmd_order.

Ditto.

Quoted 27 lines
Quoted 27 lines
tools/testing/selftests/mm/vm_util.c +26 -0
+
+	memset(orders, 0, sizeof(int) * 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, size, pagemap_fd,
+			kpageflags_fd, orders, NR_ORDERS);
+	if (status)
+		goto out;
+
+	if (orders[order] == nr_hpages)
+		ret = true;
+
+out:
+	close(pagemap_fd);
+	close(kpageflags_fd);
+	return ret;
+}
+
Quoted 8 lines

tools/testing/selftests/mm/vm_util.c bool __check_huge(void *addr, char *pattern, int nr_hpages, · line 414

Quoted 4 lines
tools/testing/selftests/mm/vm_util.c +1 -1
  bool check_huge_anon(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
  {
-	return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
+	if (hpage_size == read_pmd_pagesize())

read_pmd_pagesize() can fail and return 0. Either you need a check here or add a ksft_exit_fail_msg() in read_pmd_pagesize() to remove the burden from all callers.

Ack.

Quoted 24 lines
Quoted 24 lines
tools/testing/selftests/mm/vm_util.c +11 -2
+		return __check_pmd_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
+
+	return check_large_folios(addr, size, nr_hpages, hpage_size);
  }
 
  bool check_huge_file(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
  {
-	return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
+	if (hpage_size == read_pmd_pagesize())
+		return __check_pmd_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
+
+	return check_large_folios(addr, size, nr_hpages, hpage_size);
  }
 
  bool check_huge_shmem(void *addr, unsigned long size, int nr_hpages, uint64_t hpage_size)
  {
-	return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
+	if (hpage_size == read_pmd_pagesize())
+		return __check_pmd_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
+
+	return check_large_folios(addr, size, nr_hpages, hpage_size);
  }
 
Quoted 3 lines

tools/testing/selftests/mm/vm_util.c bool __check_huge(void *addr, char *pattern, int nr_hpages, · line 436

Quoted 1 line
tools/testing/selftests/mm/vm_util.c +0 -0
  int64_t allocate_transhuge(void *ptr, int pagemap_fd)

All read_pmd_pagesize()s above need to be handled.

Sure. Thanks for taking a look.

author reviewer
#12 lore raw

PATCH v2 4/4 selftests: mm: add mTHP collapse test cases

In reply to Baolin Wang

Added a new command 'mthp_khugepaged' for mTHP collapse, along with the '-c' parameter to specify the collapse order. Additionally, added mTHP collapse test cases for 'collapse_full', 'collapse_empty', and 'collapse_single_mthp' for anonymous folios. All khugepaged test cases passed.

---
 tools/testing/selftests/mm/khugepaged.c   | 130 ++++++++++++++++++----
 tools/testing/selftests/mm/run_vmtests.sh |   2 +
 2 files changed, 113 insertions(+), 19 deletions(-)
tools/testing/selftests/mm/khugepaged.c +111 -19
index f69be6be0ecd..64d6da6aa90d 100644
@@ -26,9 +26,11 @@
 
 #define BASE_ADDR ((void *)(1UL << 30))
 static unsigned long hpage_pmd_size;
+static int hpage_pmd_order;
 static unsigned long page_size;
 static int hpage_pmd_nr;
 static int anon_order;
+static int collapse_order;
 
 #define PID_SMAPS "/proc/self/smaps"
 #define TEST_FILE "collapse_test_file"
@@ -69,6 +71,7 @@ struct collapse_context {
 };
 
 static struct collapse_context *khugepaged_context;
+static struct collapse_context *mthp_khugepaged_context;
 static struct collapse_context *madvise_context;
 
 struct file_info {
@@ -554,25 +557,25 @@ static void madvise_collapse(const char *msg, char *p, int nr_hpages,
 }
 
 #define TICK 500000
-static bool wait_for_scan(const char *msg, char *p, int nr_hpages,
-			  struct mem_ops *ops)
+static bool wait_for_scan(const char *msg, char *p, unsigned long size,
+		int nr_hpages, int collap_order, struct mem_ops *ops)
 {
-	unsigned long size = nr_hpages * hpage_pmd_size;
+	unsigned long hpage_size = page_size << collap_order;
 	int full_scans;
 	int timeout = 6; /* 3 seconds */
 
 	/* Sanity check */
-	if (!ops->check_huge(p, size, 0, hpage_pmd_size))
+	if (!ops->check_huge(p, size, 0, hpage_size))
 		ksft_exit_fail_msg("Unexpected huge page\n");
 
-	madvise(p, nr_hpages * hpage_pmd_size, MADV_HUGEPAGE);
+	madvise(p, size, MADV_HUGEPAGE);
 
 	/* Wait until the second full_scan completed */
 	full_scans = thp_read_num("khugepaged/full_scans") + 2;
 
 	ksft_print_msg("%s...", msg);
 	while (timeout--) {
-		if (ops->check_huge(p, size, nr_hpages, hpage_pmd_size))
+		if (ops->check_huge(p, size, nr_hpages, hpage_size))
 			break;
 		if (thp_read_num("khugepaged/full_scans") >= full_scans)
 			break;
@@ -595,7 +598,7 @@ static void khugepaged_collapse(const char *msg, char *p, int nr_hpages,
 	if (!is_tmpfs(ops) && ops == &__read_write_file_write_ops)
 		expect = false;
 
-	if (wait_for_scan(msg, p, nr_hpages, ops)) {
+	if (wait_for_scan(msg, p, size, nr_hpages, hpage_pmd_order, ops)) {
 		if (expect)
 			fail("Timeout");
 		else
@@ -617,12 +620,62 @@ static void khugepaged_collapse(const char *msg, char *p, int nr_hpages,
 		fail("Fail");
 }
 
+static void mthp_khugepaged_collapse(const char *msg, char *p, int nr_hpages,
+				struct mem_ops *ops, bool expect)
+{
+	unsigned long hpage_size = page_size << collapse_order;
+	struct thp_settings settings = *thp_current_settings();
+	/* mTHP collpase only allocates PMD sized memory */
+	unsigned long size = hpage_pmd_size;
+
+	/* Set mTHP setting for mTHP collapse */
+	if (ops == &__anon_ops) {
+		settings.thp_enabled = THP_NEVER;
+		settings.hugepages[collapse_order].enabled = THP_MADVISE;
+	}
+
+	thp_push_settings(&settings);
+
+	if (wait_for_scan(msg, p, size, nr_hpages, collapse_order, ops)) {
+		if (expect)
+			fail("Timeout");
+		else
+			success("OK");
+
+		/* Restore THP settings for mTHP collapse. */
+		thp_pop_settings();
+		return;
+	}
+
+	/*
+	 * For file and shmem memory, khugepaged only retracts pte entries after
+	 * putting the new hugepage in the page cache. The hugepage must be
+	 * subsequently refaulted to install the pmd mapping for the mm.
+	 */
+	if (ops != &__anon_ops)
+		ops->fault(p, 0, nr_hpages * hpage_size);
+
+	if (ops->check_huge(p, size, expect ? nr_hpages : 0, hpage_size))
+		success("OK");
+	else
+		fail("Fail");
+
+	/* Restore THP settings for mTHP collapse. */
+	thp_pop_settings();
+}
+
 static struct collapse_context __khugepaged_context = {
 	.collapse = &khugepaged_collapse,
 	.enforce_pte_scan_limits = true,
 	.name = "khugepaged",
 };
 
+static struct collapse_context __mthp_khugepaged_context = {
+	.collapse = &mthp_khugepaged_collapse,
+	.enforce_pte_scan_limits = true,
+	.name = "mthp_khugepaged",
+};
+
 static struct collapse_context __madvise_context = {
 	.collapse = &madvise_collapse,
 	.enforce_pte_scan_limits = false,
@@ -661,10 +714,17 @@ static void alloc_at_fault(void)
 static void collapse_full(struct collapse_context *c, struct mem_ops *ops)
 {
 	void *p;
-	int nr_hpages = 4;
+	int nr_pmds = 4, nr_hpages = 4;
 	unsigned long size = nr_hpages * hpage_pmd_size;
 
-	p = ops->setup_area(nr_hpages);
+	/* Only try 1 PMD sized range for mTHP collapse. */
+	if (c == &__mthp_khugepaged_context) {
+		nr_pmds = 1;
+		nr_hpages = 1 << (hpage_pmd_order - collapse_order);
+		size = hpage_pmd_size;
+	}
+
+	p = ops->setup_area(nr_pmds);
 	ops->fault(p, 0, size);
 	c->collapse("Collapse multiple fully populated PTE table", p, nr_hpages,
 		    ops, true);
@@ -676,10 +736,31 @@ static void collapse_full(struct collapse_context *c, struct mem_ops *ops)
 
 static void collapse_empty(struct collapse_context *c, struct mem_ops *ops)
 {
+	int nr_hpages = 1;
+	void *p;
+
+	if (c == &__mthp_khugepaged_context)
+		nr_hpages = 1 << (hpage_pmd_order - collapse_order);
+
+	p = ops->setup_area(1);
+	c->collapse("Do not collapse empty PTE table", p, nr_hpages, ops, false);
+	ops->cleanup_area(p, hpage_pmd_size);
+	ksft_test_result_report(exit_status, "%s\n", __func__);
+}
+
+static void collapse_single_mthp(struct collapse_context *c, struct mem_ops *ops)
+{
+	unsigned long hpage_size = page_size << collapse_order;
 	void *p;
 
 	p = ops->setup_area(1);
-	c->collapse("Do not collapse empty PTE table", p, 1, ops, false);
+	/*
+	 * Only fault collapse_order sized ranges, and only check 1
+	 * collapse_order sized huge page.
+	 */
+	ops->fault(p, 0, hpage_size);
+	c->collapse("Collapse PTE table with half PTE entries present",
+		p, 1, ops, true);
 	ops->cleanup_area(p, hpage_pmd_size);
 	ksft_test_result_report(exit_status, "%s\n", __func__);
 }
@@ -1081,8 +1162,8 @@ static void madvise_retracted_page_tables(struct collapse_context *c,
 	ops->fault(p, 0, size);
 
 	/* Let khugepaged collapse and leave pmd cleared */
-	if (wait_for_scan("Collapse and leave PMD cleared", p, nr_hpages,
-			  ops)) {
+	if (wait_for_scan("Collapse and leave PMD cleared", p, size, nr_hpages,
+			  hpage_pmd_order, ops)) {
 		fail("Timeout");
 		return;
 	}
@@ -1098,7 +1179,7 @@ static void usage(void)
 {
 	fprintf(stderr, "\nUsage: ./khugepaged [OPTIONS] <test type> [dir]\n\n");
 	fprintf(stderr, "\t<test type>\t: <context>:<mem_type>\n");
-	fprintf(stderr, "\t<context>\t: [all|khugepaged|madvise]\n");
+	fprintf(stderr, "\t<context>\t: [all|khugepaged|mthp_khugepaged|madvise]\n");
 	fprintf(stderr, "\t<mem_type>\t: [all|anon|file|shmem]\n");
 	fprintf(stderr, "\n\t\"file,all\" mem_type requires [dir] argument\n");
 	fprintf(stderr, "\n\t\"file,all\" mem_type requires a file system\n");
@@ -1109,6 +1190,7 @@ static void usage(void)
 	fprintf(stderr,	"\t\t-h: This help message.\n");
 	fprintf(stderr,	"\t\t-s: mTHP size, expressed as page order.\n");
 	fprintf(stderr,	"\t\t    Defaults to 0. Use this size for anon or shmem allocations.\n");
+	fprintf(stderr,	"\t\t-c: collapse order for mTHP collapse, expressed as page order.\n");
 	exit(1);
 }
 
@@ -1118,11 +1200,14 @@ static void parse_test_type(int argc, char **argv)
 	char *buf;
 	const char *token;
 
-	while ((opt = getopt(argc, argv, "s:h")) != -1) {
+	while ((opt = getopt(argc, argv, "s:c:h")) != -1) {
 		switch (opt) {
 		case 's':
 			anon_order = atoi(optarg);
 			break;
+		case 'c':
+			collapse_order = atoi(optarg);
+			break;
 		case 'h':
 		default:
 			usage();
@@ -1148,6 +1233,10 @@ static void parse_test_type(int argc, char **argv)
 		madvise_context =  &__madvise_context;
 	} else if (!strcmp(token, "khugepaged")) {
 		khugepaged_context =  &__khugepaged_context;
+	} else if (!strcmp(token, "mthp_khugepaged")) {
+		mthp_khugepaged_context =  &__mthp_khugepaged_context;
+		if (collapse_order <= 0 || collapse_order >= hpage_pmd_order)
+			usage();
 	} else if (!strcmp(token, "madvise")) {
 		madvise_context =  &__madvise_context;
 	} else {
@@ -1213,7 +1302,6 @@ static int nr_test_cases;
 
 int main(int argc, char **argv)
 {
-	int hpage_pmd_order;
 	struct thp_settings default_settings = {
 		.thp_enabled = THP_MADVISE,
 		.thp_defrag = THP_DEFRAG_ALWAYS,
@@ -1239,10 +1327,6 @@ int main(int argc, char **argv)
 	if (!thp_is_enabled())
 		ksft_exit_skip("Transparent Hugepages not available\n");
 
-	parse_test_type(argc, argv);
-
-	setbuf(stdout, NULL);
-
 	page_size = getpagesize();
 	hpage_pmd_size = read_pmd_pagesize();
 	if (!hpage_pmd_size)
@@ -1250,6 +1334,10 @@ int main(int argc, char **argv)
 	hpage_pmd_nr = hpage_pmd_size / page_size;
 	hpage_pmd_order = __builtin_ctz(hpage_pmd_nr);
 
+	parse_test_type(argc, argv);
+
+	setbuf(stdout, NULL);
+
 	default_settings.khugepaged.max_ptes_none = hpage_pmd_nr - 1;
 	default_settings.khugepaged.max_ptes_swap = hpage_pmd_nr / 8;
 	default_settings.khugepaged.max_ptes_shared = hpage_pmd_nr / 2;
@@ -1267,6 +1355,7 @@ int main(int argc, char **argv)
 	TEST(collapse_full, khugepaged_context, read_write_file_read_ops);
 	TEST(collapse_full, khugepaged_context, read_write_file_write_ops);
 	TEST(collapse_full, khugepaged_context, shmem_ops);
+	TEST(collapse_full, mthp_khugepaged_context, anon_ops);
 	TEST(collapse_full, madvise_context, anon_ops);
 	TEST(collapse_full, madvise_context, read_only_file_ops);
 	TEST(collapse_full, madvise_context, read_write_file_read_ops);
@@ -1274,8 +1363,11 @@ int main(int argc, char **argv)
 	TEST(collapse_full, madvise_context, shmem_ops);
 
 	TEST(collapse_empty, khugepaged_context, anon_ops);
+	TEST(collapse_empty, mthp_khugepaged_context, anon_ops);
 	TEST(collapse_empty, madvise_context, anon_ops);
 
+	TEST(collapse_single_mthp, mthp_khugepaged_context, anon_ops);
+
 	TEST(collapse_single_pte_entry, khugepaged_context, anon_ops);
 	TEST(collapse_single_pte_entry, khugepaged_context, read_only_file_ops);
 	TEST(collapse_single_pte_entry, khugepaged_context, read_write_file_read_ops);
tools/testing/selftests/mm/run_vmtests.sh +2 -0
index 687d115e3bd8..d09f9f6a384e 100755
@@ -410,6 +410,8 @@ CATEGORY="thp" run_test ./khugepaged all:shmem
 
 CATEGORY="thp" run_test ./khugepaged -s 4 all:shmem
 
+CATEGORY="thp" run_test ./khugepaged -c 4 mthp_khugepaged:anon
+
 # Try to create XFS if not provided
 if [ -z "${SPLIT_HUGE_PAGE_TEST_XFS_PATH}" ]; then
     if test_selected "thp"; then
Signature · 1 line
2.47.3
reviewer Reviewed-by on v2 4/4
#13 lore raw

Re: [PATCH v2 4/4] selftests: mm: add mTHP collapse test cases

Quoted 6 lines

Added a new command 'mthp_khugepaged' for mTHP collapse, along with the '-c' parameter to specify the collapse order. Additionally, added mTHP collapse test cases for 'collapse_full', 'collapse_empty', and 'collapse_single_mthp' for anonymous folios. All khugepaged test cases passed.

LGTM! I will try to come up with more test cases, and send a followup in the future.

Thank you for working on this :)

Signature · 1 line
Nico Pache (Red Hat) <[email protected]>
reviewer
#14 lore raw

Re: [PATCH v2 4/4] selftests: mm: add mTHP collapse test cases

In reply to Baolin Wang
Quoted 24 lines · Baolin Wang

Added a new command 'mthp_khugepaged' for mTHP collapse, along with the '-c' parameter to specify the collapse order. Additionally, added mTHP collapse test cases for 'collapse_full', 'collapse_empty', and 'collapse_single_mthp' for anonymous folios. All khugepaged test cases passed.

---
 tools/testing/selftests/mm/khugepaged.c   | 130 ++++++++++++++++++----
 tools/testing/selftests/mm/run_vmtests.sh |   2 +
 2 files changed, 113 insertions(+), 19 deletions(-)
tools/testing/selftests/mm/khugepaged.c +2 -0
index f69be6be0ecd..64d6da6aa90d 100644
@@ -26,9 +26,11 @@
 
 #define BASE_ADDR ((void *)(1UL << 30))
 static unsigned long hpage_pmd_size;
+static int hpage_pmd_order;
 static unsigned long page_size;
 static int hpage_pmd_nr;
 static int anon_order;
+static int collapse_order;

<snip>

tools/testing/selftests/mm/run_vmtests.sh CATEGORY="thp" run_test ./khugepaged all:shmem · line 413

Quoted 10 lines
tools/testing/selftests/mm/run_vmtests.sh +1 -0
index 687d115e3bd8..d09f9f6a384e 100755
@@ -410,6 +410,8 @@ CATEGORY="thp" run_test ./khugepaged all:shmem
 
 CATEGORY="thp" run_test ./khugepaged -s 4 all:shmem
 
+CATEGORY="thp" run_test ./khugepaged -c 4 mthp_khugepaged:anon

Why only testing collapse_order == 4? It is more useful to test all possible mTHP orders. -c can accept common separated order and a range.

Why only testing mthp_khugepaged:anon? IIRC, file and shmem are not supported, so khugepaged should reject them, right?

Quoted 4 lines
tools/testing/selftests/mm/run_vmtests.sh +1 -0
+
 # Try to create XFS if not provided
 if [ -z "${SPLIT_HUGE_PAGE_TEST_XFS_PATH}" ]; then
     if test_selected "thp"; then
Signature · 2 lines
Best Regards,
Yan, Zi
author reviewer
#15 lore raw

Re: [PATCH v2 4/4] selftests: mm: add mTHP collapse test cases

Quoted 35 lines · Zi Yan
Quoted 24 lines · Baolin Wang

Added a new command 'mthp_khugepaged' for mTHP collapse, along with the '-c' parameter to specify the collapse order. Additionally, added mTHP collapse test cases for 'collapse_full', 'collapse_empty', and 'collapse_single_mthp' for anonymous folios. All khugepaged test cases passed.

---
  tools/testing/selftests/mm/khugepaged.c   | 130 ++++++++++++++++++----
  tools/testing/selftests/mm/run_vmtests.sh |   2 +
  2 files changed, 113 insertions(+), 19 deletions(-)
tools/testing/selftests/mm/khugepaged.c +2 -0
index f69be6be0ecd..64d6da6aa90d 100644
@@ -26,9 +26,11 @@
 
  #define BASE_ADDR ((void *)(1UL << 30))
  static unsigned long hpage_pmd_size;
+static int hpage_pmd_order;
  static unsigned long page_size;
  static int hpage_pmd_nr;
  static int anon_order;
+static int collapse_order;

<snip>

Quoted 7 lines
tools/testing/selftests/mm/run_vmtests.sh +0 -0
index 687d115e3bd8..d09f9f6a384e 100755
@@ -410,6 +410,8 @@ CATEGORY="thp" run_test ./khugepaged all:shmem
 
Quoted 6 lines

tools/testing/selftests/mm/run_vmtests.sh CATEGORY="thp" run_test ./khugepaged all:shmem · line 413

Quoted 3 lines
tools/testing/selftests/mm/run_vmtests.sh +1 -0
  CATEGORY="thp" run_test ./khugepaged -s 4 all:shmem
 
+CATEGORY="thp" run_test ./khugepaged -c 4 mthp_khugepaged:anon

Why only testing collapse_order == 4? It is more useful to test all possible mTHP orders. -c can accept common separated order and a range.

This also follows the previous '-s' parameter configuration, which is used to specify the mTHP order allocation.

I think specifying a single order size already tests the overall mTHP collapse flow, so I'm not sure whether testing all orders is worthwhile. Maybe I can add a few more orders? ./khugepaged -c 2 mthp_khugepaged:anon
./khugepaged -c 4 mthp_khugepaged:anon
./khugepaged -c 8 mthp_khugepaged:anon

Quoted 2 lines

Why only testing mthp_khugepaged:anon? IIRC, file and shmem are not supported, so khugepaged should reject them, right?

Currently, I have not added test cases for shmem mTHP collapse, so adding file or shmem would be no-ops and might mislead others. I will add the shmem/file mTHP collapse feature and test cases in a following patchset.

reviewer
#16 lore raw

Re: [PATCH v2 4/4] selftests: mm: add mTHP collapse test cases

Quoted 39 lines · Baolin Wang
Quoted 27 lines · Zi Yan
Quoted 23 lines · Baolin Wang

Added a new command 'mthp_khugepaged' for mTHP collapse, along with the '-c' parameter to specify the collapse order. Additionally, added mTHP collapse test cases for 'collapse_full', 'collapse_empty', and 'collapse_single_mthp' for anonymous folios. All khugepaged test cases passed.

---
  tools/testing/selftests/mm/khugepaged.c   | 130 ++++++++++++++++++----
  tools/testing/selftests/mm/run_vmtests.sh |   2 +
  2 files changed, 113 insertions(+), 19 deletions(-)
tools/testing/selftests/mm/khugepaged.c +2 -0
index f69be6be0ecd..64d6da6aa90d 100644
@@ -26,9 +26,11 @@
   #define BASE_ADDR ((void *)(1UL << 30))
  static unsigned long hpage_pmd_size;
+static int hpage_pmd_order;
  static unsigned long page_size;
  static int hpage_pmd_nr;
  static int anon_order;
+static int collapse_order;

<snip>

Quoted 10 lines

tools/testing/selftests/mm/run_vmtests.sh CATEGORY="thp" run_test ./khugepaged all:shmem · line 411

Quoted 7 lines

diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 687d115e3bd8..d09f9f6a384e 100755

tools/testing/selftests/mm/run_vmtests.sh +0 -0
@@ -410,6 +410,8 @@ CATEGORY="thp" run_test ./khugepaged all:shmem
   CATEGORY="thp" run_test ./khugepaged -s 4 all:shmem
 +CATEGORY="thp" run_test ./khugepaged -c 4 mthp_khugepaged:anon

Why only testing collapse_order == 4? It is more useful to test all possible mTHP orders. -c can accept common separated order and a range.

Quoted 3 lines

This also follows the previous '-s' parameter configuration, which is used to specify the mTHP order allocation.

I think specifying a single order size already tests the overall mTHP collapse

Got it. If a single order covers the flow, I am fine with it.

Quoted 9 lines

flow, so I'm not sure whether testing all orders is worthwhile. Maybe I can add a few more orders? ./khugepaged -c 2 mthp_khugepaged:anon
./khugepaged -c 4 mthp_khugepaged:anon
./khugepaged -c 8 mthp_khugepaged:anon

Quoted 2 lines

Why only testing mthp_khugepaged:anon? IIRC, file and shmem are not supported, so khugepaged should reject them, right?

Currently, I have not added test cases for shmem mTHP collapse, so adding file or shmem would be no-ops and might mislead others. I will add the shmem/file mTHP collapse feature and test cases in a following patchset.

Right. I mean when one specifies ./khugepaged mthp_khugepaged:file/shmem, the program should show some error like “file/shmem not supported”.

Best Regards,
Yan, Zi

author reviewer
#17 lore raw

Re: [PATCH v2 4/4] selftests: mm: add mTHP collapse test cases

Quoted 52 lines · Zi Yan
Quoted 39 lines · Baolin Wang
Quoted 27 lines · Zi Yan
Quoted 23 lines · Baolin Wang

Added a new command 'mthp_khugepaged' for mTHP collapse, along with the '-c' parameter to specify the collapse order. Additionally, added mTHP collapse test cases for 'collapse_full', 'collapse_empty', and 'collapse_single_mthp' for anonymous folios. All khugepaged test cases passed.

---
   tools/testing/selftests/mm/khugepaged.c   | 130 ++++++++++++++++++----
   tools/testing/selftests/mm/run_vmtests.sh |   2 +
   2 files changed, 113 insertions(+), 19 deletions(-)
tools/testing/selftests/mm/khugepaged.c +2 -0
index f69be6be0ecd..64d6da6aa90d 100644
@@ -26,9 +26,11 @@
    #define BASE_ADDR ((void *)(1UL << 30))
   static unsigned long hpage_pmd_size;
+static int hpage_pmd_order;
   static unsigned long page_size;
   static int hpage_pmd_nr;
   static int anon_order;
+static int collapse_order;

<snip>

Quoted 10 lines

tools/testing/selftests/mm/run_vmtests.sh CATEGORY="thp" run_test ./khugepaged all:shmem · line 411

Quoted 7 lines

diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 687d115e3bd8..d09f9f6a384e 100755

tools/testing/selftests/mm/run_vmtests.sh +0 -0
@@ -410,6 +410,8 @@ CATEGORY="thp" run_test ./khugepaged all:shmem
    CATEGORY="thp" run_test ./khugepaged -s 4 all:shmem
  +CATEGORY="thp" run_test ./khugepaged -c 4 mthp_khugepaged:anon

Why only testing collapse_order == 4? It is more useful to test all possible mTHP orders. -c can accept common separated order and a range.

Quoted 3 lines

This also follows the previous '-s' parameter configuration, which is used to specify the mTHP order allocation.

I think specifying a single order size already tests the overall mTHP collapse

Got it. If a single order covers the flow, I am fine with it.

Quoted 5 lines

flow, so I'm not sure whether testing all orders is worthwhile. Maybe I can add a few more orders? ./khugepaged -c 2 mthp_khugepaged:anon
./khugepaged -c 4 mthp_khugepaged:anon
./khugepaged -c 8 mthp_khugepaged:anon

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

Why only testing mthp_khugepaged:anon? IIRC, file and shmem are not supported, so khugepaged should reject them, right?

Currently, I have not added test cases for shmem mTHP collapse, so adding file or shmem would be no-ops and might mislead others. I will add the shmem/file mTHP collapse feature and test cases in a following patchset.

Right. I mean when one specifies ./khugepaged mthp_khugepaged:file/shmem, the program should show some error like “file/shmem not supported”.

Yes, that works too. I still prefer to add the corresponding test cases after I add shmem mTHP collapse support:)

But if you have a strong opinion on this, I can also add a "not supported" message first.

reviewer
#18 lore raw

Re: [PATCH v2 4/4] selftests: mm: add mTHP collapse test cases

Quoted 56 lines · Baolin Wang
Quoted 55 lines · Zi Yan
Quoted 39 lines · Baolin Wang
Quoted 27 lines · Zi Yan
Quoted 23 lines · Baolin Wang

Added a new command 'mthp_khugepaged' for mTHP collapse, along with the '-c' parameter to specify the collapse order. Additionally, added mTHP collapse test cases for 'collapse_full', 'collapse_empty', and 'collapse_single_mthp' for anonymous folios. All khugepaged test cases passed.

---
   tools/testing/selftests/mm/khugepaged.c   | 130 ++++++++++++++++++----
   tools/testing/selftests/mm/run_vmtests.sh |   2 +
   2 files changed, 113 insertions(+), 19 deletions(-)
tools/testing/selftests/mm/khugepaged.c +2 -0
index f69be6be0ecd..64d6da6aa90d 100644
@@ -26,9 +26,11 @@
    #define BASE_ADDR ((void *)(1UL << 30))
   static unsigned long hpage_pmd_size;
+static int hpage_pmd_order;
   static unsigned long page_size;
   static int hpage_pmd_nr;
   static int anon_order;
+static int collapse_order;

<snip>

Quoted 10 lines

tools/testing/selftests/mm/run_vmtests.sh CATEGORY="thp" run_test ./khugepaged all:shmem · line 411

Quoted 7 lines

diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 687d115e3bd8..d09f9f6a384e 100755

tools/testing/selftests/mm/run_vmtests.sh +0 -0
@@ -410,6 +410,8 @@ CATEGORY="thp" run_test ./khugepaged all:shmem
    CATEGORY="thp" run_test ./khugepaged -s 4 all:shmem
  +CATEGORY="thp" run_test ./khugepaged -c 4 mthp_khugepaged:anon

Why only testing collapse_order == 4? It is more useful to test all possible mTHP orders. -c can accept common separated order and a range.

Quoted 3 lines

This also follows the previous '-s' parameter configuration, which is used to specify the mTHP order allocation.

I think specifying a single order size already tests the overall mTHP collapse

Got it. If a single order covers the flow, I am fine with it.

Quoted 8 lines

flow, so I'm not sure whether testing all orders is worthwhile. Maybe I can add a few more orders? ./khugepaged -c 2 mthp_khugepaged:anon
./khugepaged -c 4 mthp_khugepaged:anon
./khugepaged -c 8 mthp_khugepaged:anon

Quoted 2 lines

Why only testing mthp_khugepaged:anon? IIRC, file and shmem are not supported, so khugepaged should reject them, right?

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

Currently, I have not added test cases for shmem mTHP collapse, so adding file or shmem would be no-ops and might mislead others. I will add the shmem/file mTHP collapse feature and test cases in a following patchset.

Right. I mean when one specifies ./khugepaged mthp_khugepaged:file/shmem, the program should show some error like “file/shmem not supported”.

Yes, that works too. I still prefer to add the corresponding test cases after I add shmem mTHP collapse support:)

Sure. Or update usage() to print “mthp_khugepaged only supports anon”. You can remove that after you add other support types.

Quoted 2 lines

But if you have a strong opinion on this, I can also add a "not supported" message first.

Best Regards,
Yan, Zi

author reviewer
#19 lore raw

Re: [PATCH v2 4/4] selftests: mm: add mTHP collapse test cases

Quoted 60 lines · Zi Yan
Quoted 58 lines · Baolin Wang
Quoted 57 lines · Zi Yan
Quoted 39 lines · Baolin Wang
Quoted 27 lines · Zi Yan
Quoted 23 lines · Baolin Wang

Added a new command 'mthp_khugepaged' for mTHP collapse, along with the '-c' parameter to specify the collapse order. Additionally, added mTHP collapse test cases for 'collapse_full', 'collapse_empty', and 'collapse_single_mthp' for anonymous folios. All khugepaged test cases passed.

---
    tools/testing/selftests/mm/khugepaged.c   | 130 ++++++++++++++++++----
    tools/testing/selftests/mm/run_vmtests.sh |   2 +
    2 files changed, 113 insertions(+), 19 deletions(-)
tools/testing/selftests/mm/khugepaged.c +2 -0
index f69be6be0ecd..64d6da6aa90d 100644
@@ -26,9 +26,11 @@
     #define BASE_ADDR ((void *)(1UL << 30))
    static unsigned long hpage_pmd_size;
+static int hpage_pmd_order;
    static unsigned long page_size;
    static int hpage_pmd_nr;
    static int anon_order;
+static int collapse_order;

<snip>

Quoted 10 lines

tools/testing/selftests/mm/run_vmtests.sh CATEGORY="thp" run_test ./khugepaged all:shmem · line 411

Quoted 7 lines
   diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh

index 687d115e3bd8..d09f9f6a384e 100755

tools/testing/selftests/mm/run_vmtests.sh +0 -0
@@ -410,6 +410,8 @@ CATEGORY="thp" run_test ./khugepaged all:shmem
     CATEGORY="thp" run_test ./khugepaged -s 4 all:shmem
   +CATEGORY="thp" run_test ./khugepaged -c 4 mthp_khugepaged:anon

Why only testing collapse_order == 4? It is more useful to test all possible mTHP orders. -c can accept common separated order and a range.

Quoted 3 lines

This also follows the previous '-s' parameter configuration, which is used to specify the mTHP order allocation.

I think specifying a single order size already tests the overall mTHP collapse

Got it. If a single order covers the flow, I am fine with it.

Quoted 9 lines

flow, so I'm not sure whether testing all orders is worthwhile. Maybe I can add a few more orders? ./khugepaged -c 2 mthp_khugepaged:anon
./khugepaged -c 4 mthp_khugepaged:anon
./khugepaged -c 8 mthp_khugepaged:anon

Quoted 2 lines

Why only testing mthp_khugepaged:anon? IIRC, file and shmem are not supported, so khugepaged should reject them, right?

Currently, I have not added test cases for shmem mTHP collapse, so adding file or shmem would be no-ops and might mislead others. I will add the shmem/file mTHP collapse feature and test cases in a following patchset.

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

Right. I mean when one specifies ./khugepaged mthp_khugepaged:file/shmem, the program should show some error like “file/shmem not supported”.

Yes, that works too. I still prefer to add the corresponding test cases after I add shmem mTHP collapse support:)

Sure. Or update usage() to print “mthp_khugepaged only supports anon”. You can remove that after you add other support types.

Ack. Thanks for all your valuable input:)

reviewer Tested-by on the v2 series
#20 lore raw
In reply to Baolin Wang
Quoted 3 lines

This patchset is split from the previous shmem mTHP collapse patchset[1], which only adds anon mTHP collapse selftests. All khugepaged test cases can pass.

Thank you for sending this out :) I played around with it and it seems to be working. This lays good groundwork to expand khugepaged mTHP testing in the future too.

Signature · 1 line
Nico Pache (Red Hat) <[email protected]>
author reviewer
#21 lore raw
Quoted 9 lines · Nico Pache (Red Hat)
Quoted 3 lines

This patchset is split from the previous shmem mTHP collapse patchset[1], which only adds anon mTHP collapse selftests. All khugepaged test cases can pass.

Thank you for sending this out :) I played around with it and it seems to be working. This lays good groundwork to expand khugepaged mTHP testing in the future too.

Thanks Nico :)

Acked-by Kiryl Shutsemau (Meta) on the v2 series
#22 lore raw
In reply to Baolin Wang
Quoted 3 lines · Baolin Wang

This patchset is split from the previous shmem mTHP collapse patchset[1], which only adds anon mTHP collapse selftests. All khugepaged test cases can pass.

The patchset looks good to me:

Thanks for doing this.

Signature · 1 line
  Kiryl Shutsemau / Kirill A. Shutemov
author reviewer
#23 lore raw
Quoted 8 lines · Kiryl Shutsemau
Quoted 3 lines · Baolin Wang

This patchset is split from the previous shmem mTHP collapse patchset[1], which only adds anon mTHP collapse selftests. All khugepaged test cases can pass.

The patchset looks good to me:

Thanks Kiryl.