From: Nico Pache <npache@redhat.com>
To: Baolin Wang <baolin.wang@linux.alibaba.com>,
akpm@linux-foundation.org, david@kernel.org, ljs@kernel.org,
hughd@google.com
Cc: willy@infradead.org, ziy@nvidia.com, liam@infradead.org,
ryan.roberts@arm.com, dev.jain@arm.com, baohua@kernel.org,
lance.yang@linux.dev, linux-mm@kvack.org,
linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH v2 11/11] selftests: mm: add mTHP collapse test cases
Date: Fri, 12 Jun 2026 04:41:18 -0600 [thread overview]
Message-ID: <4406fdcf-098e-4523-a441-5343bb22e667@redhat.com> (raw)
In-Reply-To: <fcf0fcc52aa6c4e18682fcb302fe9371a8d800c3.1781083630.git.baolin.wang@linux.alibaba.com>
On 6/10/26 4:29 AM, Baolin Wang wrote:
> 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 both anonymous pages and shmem. All khugepaged test cases passed.
>
> Signed-off-by: Baolin Wang <baolin.wang@linux.alibaba.com>
Will rip out the Shmem stuff and test these selftests on top of my mTHP stuff
soon :)
Ill provide the diff so its easier for you and if I come up with more cases ill
add those too.
Thanks for doing this!!!
-- Nico
> ---
> tools/testing/selftests/mm/khugepaged.c | 135 +++++++++++++++++++---
> tools/testing/selftests/mm/run_vmtests.sh | 4 +
> 2 files changed, 120 insertions(+), 19 deletions(-)
>
> diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
> index f69be6be0ecd..8975be5b7b2f 100644
> --- a/tools/testing/selftests/mm/khugepaged.c
> +++ b/tools/testing/selftests/mm/khugepaged.c
> @@ -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,65 @@ 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_ALWAYS;
> + } else if (ops == &__shmem_ops) {
> + settings.shmem_enabled = SHMEM_NEVER;
> + settings.shmem_hugepages[collapse_order].enabled = SHMEM_ALWAYS;
> + }
> +
> + 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 +717,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 +739,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 +1165,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 +1182,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 +1193,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 +1203,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 +1236,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 +1305,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 +1330,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 +1337,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 +1358,8 @@ 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, mthp_khugepaged_context, shmem_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 +1367,12 @@ 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_mthp, mthp_khugepaged_context, shmem_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);
> diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
> index 8c296dedf047..c0f4f3e5f1f1 100755
> --- a/tools/testing/selftests/mm/run_vmtests.sh
> +++ b/tools/testing/selftests/mm/run_vmtests.sh
> @@ -411,6 +411,10 @@ 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
> +
> +CATEGORY="thp" run_test ./khugepaged -c 4 mthp_khugepaged:shmem
> +
> # Try to create XFS if not provided
> if [ -z "${SPLIT_HUGE_PAGE_TEST_XFS_PATH}" ]; then
> if test_selected "thp"; then
next prev parent reply other threads:[~2026-06-12 10:40 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-10 10:29 [RFC PATCH v2 00/11] add shmem mTHP collapse support Baolin Wang
2026-06-10 10:29 ` [RFC PATCH v2 01/11] mm: khugepaged: add max_ptes_none check in collapse_file() Baolin Wang
2026-06-12 10:10 ` Nico Pache
2026-06-10 10:29 ` [RFC PATCH v2 02/11] mm: khugepaged: generalize collapse_file() for shmem mTHP support Baolin Wang
2026-06-12 10:16 ` Nico Pache
2026-06-15 6:39 ` Baolin Wang
2026-06-10 10:29 ` [RFC PATCH v2 03/11] mm: khugepaged: add an order check for PMD-sized THP statistics Baolin Wang
2026-06-10 10:29 ` [RFC PATCH v2 04/11] mm: khugepaged: add shmem mTHP collapse support Baolin Wang
2026-06-10 12:13 ` Lance Yang
2026-06-11 0:31 ` Baolin Wang
2026-06-10 12:44 ` Lance Yang
2026-06-11 0:42 ` Baolin Wang
2026-06-11 2:47 ` Lance Yang
2026-06-11 11:15 ` Baolin Wang
2026-06-12 10:22 ` Nico Pache
2026-06-15 6:46 ` Baolin Wang
2026-06-11 6:06 ` Lance Yang
2026-06-11 12:11 ` Baolin Wang
2026-06-10 10:29 ` [RFC PATCH v2 05/11] mm: shmem: run khugepaged for all shmem mTHP orders Baolin Wang
2026-06-10 10:29 ` [RFC PATCH v2 06/11] mm: khugepaged: allow khugepaged to check all shmem mTHP-sized orders Baolin Wang
2026-06-10 11:33 ` Lance Yang
2026-06-11 0:47 ` Baolin Wang
2026-06-10 10:29 ` [RFC PATCH v2 07/11] mm: khugepaged: skip large folios that don't need to be collapsed Baolin Wang
2026-06-11 4:59 ` Lance Yang
2026-06-11 11:29 ` Baolin Wang
2026-06-10 10:29 ` [RFC PATCH v2 08/11] selftests: mm: extend the check_huge() to support mTHP check Baolin Wang
2026-06-12 10:32 ` Nico Pache
2026-06-15 6:51 ` Baolin Wang
2026-06-10 10:29 ` [RFC PATCH v2 09/11] selftests: mm: move gather_after_split_folio_orders() into vm_util.c file Baolin Wang
2026-06-10 10:29 ` [RFC PATCH v2 10/11] selftests: mm: implement the mTHP-sized hugepage check helpers Baolin Wang
2026-06-10 10:29 ` [RFC PATCH v2 11/11] selftests: mm: add mTHP collapse test cases Baolin Wang
2026-06-12 10:41 ` Nico Pache [this message]
2026-06-10 16:28 ` [RFC PATCH v2 00/11] add shmem mTHP collapse support Nico Pache
2026-06-11 0:52 ` Baolin Wang
2026-06-11 6:18 ` Lorenzo Stoakes
2026-06-11 11:46 ` Baolin Wang
2026-06-11 12:15 ` Lorenzo Stoakes
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4406fdcf-098e-4523-a441-5343bb22e667@redhat.com \
--to=npache@redhat.com \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=david@kernel.org \
--cc=dev.jain@arm.com \
--cc=hughd@google.com \
--cc=lance.yang@linux.dev \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=ryan.roberts@arm.com \
--cc=willy@infradead.org \
--cc=ziy@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.