From: "Kiryl Shutsemau (Meta)" <[email protected]>
khugepaged collapses to mTHP orders since 7.2, and 7.3 added three
selftest cases for it: the generic collapse cases run at one order named
by -c, with the result detected by counting folios of that order.
That leaves the collapse path largely untested. The suite does not run
where a PMD is 512M. A folio count cannot say where a collapse landed.
Fixed sleeps cannot tell "not collapsed" from "not scanned yet". And
nothing exercises collapse under contention.
Close those gaps in order:
- Make the suite run at a 512M PMD: scale the collapse wait with the PMD
size, skip what such a PMD cannot serve, make the swapout the swap
cases depend on deterministic, and keep khugepaged out of the
MADV_COLLAPSE cases.
- Detect results per window rather than by count, with folio-order
helpers in vm_util that are checked against the kernel before any
collapse test trusts them.
- Drive khugepaged deterministically: a completion barrier that wakes the
daemon and waits for a full pass, and a check that one pass yields one
attributed collapse.
- Cover collapse at every supported order by default: which window
collapses, occupancy at both limits, sources that are already large
folios, and a fork-shared source under concurrent writes.
- Race collapse against everything that can touch its sources, at both
occupancy limits and over whole-table zaps, checked by content and by
the kernel's own assertions.
Everything passes on an unmodified kernel.
Changes since v5:
- Rebased onto mm-new: hugepage_settings lives in tools/lib/mm now, so
the new helpers and the three new binaries follow it there.
- The MADV_COLLAPSE cases set the per-order controls to "never" rather
than "inherit", which "mm: shmem: ignore sysfs configs for shmem
forced collapse" (in mm-new) made safe for shmem, and the Fixes tag
names the khugepaged commit that started the interference (Baolin
Wang).
- The "-c at or below -s" check moves from patch 14 to patch 12, where
-s first gains that meaning (Baolin Wang).
- Patch 08's changelog no longer claims check_huge_anon() reads smaps
(Baolin Wang).
- The two Sashiko fixups posted against v5 are folded in: the fork race
closes its pipe and checks waitpid(), and the race harness keeps its
mremap scratch address reserved.
- Baolin Wang's Reviewed-by on 01-05, 07, 10 and 14, Tested-by on 14.
Merging is_range_backed_by_order() with the check_huge_*() family stays a
follow-up, once Yeoreum's rewrite of those on top of pagemap and
kpageflags has landed.
Tested on mm-new 498ee28e5ec4 with KASAN, lockdep, DEBUG_VM and
page_table_check, in 16G guests:
x86-64/4K khugepaged all:all 104 pass, 11 skip, 0 fail
arm64/64K khugepaged all:all 102 pass, 3 skip, 0 fail
folio_order_check, khugepaged_sync_check and khugepaged_race pass on both.
The skips are structural: tmpfs cannot host some file cases, and mixed
sources has no order below the smallest. arm64 never registers the file
and shmem contexts, having no PMD-order page cache folio.
Range-diff against v5:
1: 759b73a5f738 ! 1: 14a0a1d44862 selftests/mm: raise the khugepaged
test-case cap
@@ Commit message
Acked-by: Usama Arif <[email protected]>
Acked-by: Lorenzo Stoakes (ARM) <[email protected]>
Reviewed-by: Mike Rapoport (Microsoft) <[email protected]>
+ Reviewed-by: Baolin Wang <[email protected]>
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
## tools/testing/selftests/mm/khugepaged.c ##
2: 8160b5446e94 ! 2: 2265f32a677c selftests/mm: skip
collapse_compound_extreme() where the PMD is too large
@@ Commit message
Assisted-by: LLM
Acked-by: Lorenzo Stoakes (ARM) <[email protected]>
Reviewed-by: Mike Rapoport (Microsoft) <[email protected]>
+ Reviewed-by: Baolin Wang <[email protected]>
Tested-by: Muhammad Usama Anjum <[email protected]>
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
3: c6a21ef8d798 ! 3: f14ae037a749 selftests/mm: scale khugepaged's collapse
wait with the PMD size
@@ Commit message
Assisted-by: LLM
Acked-by: Lorenzo Stoakes (ARM) <[email protected]>
Reviewed-by: Mike Rapoport (Microsoft) <[email protected]>
+ Reviewed-by: Baolin Wang <[email protected]>
Tested-by: Muhammad Usama Anjum <[email protected]>
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
4: b039045f38b3 ! 4: cbe5dac4febc selftests/mm: skip khugepaged page cache
cases without a PMD folio
@@ Commit message
Assisted-by: LLM
Reviewed-by: Mike Rapoport (Microsoft) <[email protected]>
+ Reviewed-by: Baolin Wang <[email protected]>
Tested-by: Muhammad Usama Anjum <[email protected]>
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
- ## tools/testing/selftests/mm/hugepage_settings.h ##
-@@ tools/testing/selftests/mm/hugepage_settings.h: void
thp_set_read_ahead_path(char *path);
+ ## tools/lib/mm/hugepage_settings.h ##
+@@ tools/lib/mm/hugepage_settings.h: void thp_set_read_ahead_path(char
*path);
unsigned long thp_supported_orders(void);
unsigned long thp_shmem_supported_orders(void);
5: 0ee82084bd2b ! 5: 021a1fcf711a selftests/mm: make the swap cases'
swapout reliable
@@ Commit message
Assisted-by: LLM
Reviewed-by: Muhammad Usama Anjum <[email protected]>
+ Reviewed-by: Baolin Wang <[email protected]>
Tested-by: Muhammad Usama Anjum <[email protected]>
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
6: 4e48b431671e ! 6: e80ef0e8acc0 selftests/mm: stop khugepaged during the
MADV_COLLAPSE cases
@@ Commit message
collapses the very range the case is working on, and the case fails on
a
collapse that was interfered with rather than refused.
- Clear the per-order controls too, setting them to "inherit" rather than
- "never": khugepaged honours the global never and stays out, while
- MADV_COLLAPSE on shmem still finds an order to build.
+ Clear the per-order controls too. MADV_COLLAPSE does not consult them:
+ anon never did, and shmem stopped with "mm: shmem: ignore sysfs configs
+ for shmem forced collapse".
- Fixes: 9f0704eae8a4 ("selftests/mm/khugepaged: enlighten for
multi-size THP")
+ Fixes: b7f16963efe7 ("mm/khugepaged: run khugepaged for all orders")
Assisted-by: LLM
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
@@ tools/testing/selftests/mm/khugepaged.c: static bool is_anon(struct
mem_ops *ops
ksft_print_msg("%s...", msg);
@@ tools/testing/selftests/mm/khugepaged.c: static void
__madvise_collapse(const char *msg, char *p, int nr_hpages,
- /*
- * Prevent khugepaged interference and tests that MADV_COLLAPSE
- * ignores /sys/kernel/mm/transparent_hugepage/enabled
-+ *
-+ * "inherit" rather than "never" so that MADV_COLLAPSE on shmem still
-+ * finds an order to build.
*/
settings.thp_enabled = THP_NEVER;
settings.shmem_enabled = SHMEM_NEVER;
+ for (i = 0; i < NR_ORDERS; i++) {
-+ settings.hugepages[i].enabled = THP_INHERIT;
-+ settings.shmem_hugepages[i].enabled = SHMEM_INHERIT;
++ settings.hugepages[i].enabled = THP_NEVER;
++ settings.shmem_hugepages[i].enabled = SHMEM_NEVER;
+ }
thp_push_settings(&settings);
7: 154a014dc5ad ! 7: e61798d22782 selftests/mm: move is_backed_by_folio()
into vm_util
@@ Commit message
Assisted-by: LLM
Acked-by: Mike Rapoport (Microsoft) <[email protected]>
Acked-by: Lorenzo Stoakes (ARM) <[email protected]>
+ Reviewed-by: Baolin Wang <[email protected]>
Tested-by: Muhammad Usama Anjum <[email protected]>
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
@@ tools/testing/selftests/mm/split_huge_page_test.c: const char
*kpageflags_proc =
- return (pfn_flags & folio_tail_flags) != folio_tail_flags;
-fail:
- ksft_exit_fail_msg("Failed to get folio info\n");
-- return false;
-}
-
static int check_after_split_folio_orders(char *vaddr_start, size_t len,
@@ tools/testing/selftests/mm/vm_util.c: int pageflags_get(unsigned long
pfn, int k
+ return (pfn_flags & folio_tail_flags) != folio_tail_flags;
+fail:
+ ksft_exit_fail_msg("Failed to get folio info\n");
-+ return false;
+}
+
/* If `ioctls' non-NULL, the allowed ioctls will be returned into the var
*/
8: a23eab162641 ! 8: be5fb9342657 selftests/mm: add folio-order check for
address ranges
@@ Commit message
An mTHP collapse test needs to know that a range is backed by folios
of the
target order, and that they sit where a collapse would put them.
Nothing
- answers that today: is_backed_by_folio() classifies the folio behind a
- single page, and check_huge_anon() reads smaps AnonHugePages, which
only
- accounts PMD mappings.
+ answers both: is_backed_by_folio() classifies the folio behind a single
+ page, and check_huge_anon() counts the folios of an order in a range
+ without saying where they start.
Add is_range_backed_by_order(). It requires every folio-sized, folio-
aligned part of the range to map one folio of that order, head to tail,
@@ Commit message
## tools/testing/selftests/mm/vm_util.c ##
@@ tools/testing/selftests/mm/vm_util.c: bool is_backed_by_folio(char
*vaddr, int order, int pagemap_fd,
- return false;
+ ksft_exit_fail_msg("Failed to get folio info\n");
}
+/**
9: cd741198f81e ! 9: af0191f5548c selftests/mm: add folio-order detection
self-check
@@ tools/testing/selftests/mm/Makefile: TEST_GEN_FILES += guard-regions
TEST_GEN_FILES += rmap
TEST_GEN_FILES += folio_split_race_test
+TEST_GEN_FILES += folio_order_check
-
- ifneq ($(ARCH),arm64)
TEST_GEN_FILES += soft-dirty
+
+ ifeq ($(ARCH),x86_64)
## tools/testing/selftests/mm/folio_order_check.c (new) ##
@@
@@ tools/testing/selftests/mm/folio_order_check.c (new)
+
+#include "kselftest.h"
+#include "vm_util.h"
-+#include "hugepage_settings.h"
++#include <mm/hugepage_settings.h>
+
+static int pagemap_fd;
+static int kpageflags_fd;
@@ tools/testing/selftests/mm/run_vmtests.sh: CATEGORY="pfnmap" run_test
./pfnmap
## tools/testing/selftests/mm/vm_util.h ##
@@
- #include <linux/fs.h>
+ #include <mm/hugepage_settings.h>
#define BIT_ULL(nr) (1ULL << (nr))
+#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
10: 2099301ff2c1 ! 10: a105628f6e9b selftests/mm: add khugepaged completion
barrier helper
@@ Commit message
khugepaged knob only when its value changes.
Assisted-by: LLM
+ Reviewed-by: Baolin Wang <[email protected]>
Tested-by: Muhammad Usama Anjum <[email protected]>
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
- ## tools/testing/selftests/mm/hugepage_settings.c ##
-@@ tools/testing/selftests/mm/hugepage_settings.c: void
thp_read_settings(struct thp_settings *settings)
+ ## tools/lib/mm/hugepage_settings.c ##
+@@ tools/lib/mm/hugepage_settings.c: void thp_read_settings(struct
thp_settings *settings)
}
}
@@ tools/testing/selftests/mm/hugepage_settings.c: void
thp_read_settings(struct th
void thp_write_settings(struct thp_settings *settings)
{
struct khugepaged_settings *khugepaged = &settings->khugepaged;
-@@ tools/testing/selftests/mm/hugepage_settings.c: void
thp_write_settings(struct thp_settings *settings)
+@@ tools/lib/mm/hugepage_settings.c: void thp_write_settings(struct
thp_settings *settings)
shmem_enabled_strings[settings->shmem_enabled]);
thp_write_num("use_zero_page", settings->use_zero_page);
@@ tools/testing/selftests/mm/hugepage_settings.c: void
thp_write_settings(struct t
+ thp_update_num("khugepaged/max_ptes_shared",
khugepaged->max_ptes_shared);
+ thp_update_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan);
- if (dev_queue_read_ahead_path[0])
- write_num(dev_queue_read_ahead_path, settings->read_ahead_kb);
-@@ tools/testing/selftests/mm/hugepage_settings.c: void
thp_write_settings(struct thp_settings *settings)
+ if (dev_queue_read_ahead_path[0]) {
+ int ret = write_num(dev_queue_read_ahead_path,
+@@ tools/lib/mm/hugepage_settings.c: void thp_write_settings(struct
thp_settings *settings)
}
}
@@ tools/testing/selftests/mm/hugepage_settings.c: void
thp_write_settings(struct t
{
if (!settings_index) {
- ## tools/testing/selftests/mm/hugepage_settings.h ##
-@@ tools/testing/selftests/mm/hugepage_settings.h: static inline void
thp_save_settings(void)
+ ## tools/lib/mm/hugepage_settings.h ##
+@@ tools/lib/mm/hugepage_settings.h: static inline void
thp_save_settings(void)
hugepage_save_settings(/* thp = */ true, /* hugetlb = */ false);
}
11: 060448cb8a03 = 11: 27295f7decd1 selftests/mm: add order-parameterized
khugepaged collapse cases
12: 674fbf72a481 ! 12: 60e86026c227 selftests/mm: parameterize the
mixed-source collapse case by source order
@@ Commit message
supported mTHP order below the target. The other mTHP cases are
unaffected: mthp_push_target_order() enables only the target order.
+ A -c at or below -s is refused before any case runs: the sources would
+ already be the size being asked for. Without the check the generic
cases
+ fail on that one by one instead of saying why.
+
"-s 5 -c 7" on arm64/64K then collapses contpte-mapped sources into a
larger mTHP.
@@ tools/testing/selftests/mm/khugepaged.c: static void usage(void)
exit(1);
}
+@@ tools/testing/selftests/mm/khugepaged.c: int main(int argc, char **argv)
+ !(thp_supported_orders() & (1UL << collapse_order)))
+ ksft_exit_skip("Order %d is not a supported anon THP order\n",
+ collapse_order);
++ if (mthp_khugepaged_context && collapse_order <= anon_order)
++ ksft_exit_skip("-c %d needs a source order below it, -s says
%d\n",
++ collapse_order, anon_order);
+
+ if (mthp_khugepaged_context) {
+ pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
13: d2251908b73f ! 13: 39a895e66cf1 selftests/mm: cover a shared-source
collapse write race
@@ tools/testing/selftests/mm/khugepaged.c: static void
collapse_max_ptes_shared(st
+ close(sync[1]);
+ if (read(sync[0], &go, 1) != 1)
+ ksft_exit_fail_msg("child never reached the collapse\n");
++ close(sync[0]);
+
+ /*
+ * Unshare one page at a time: a burst would break CoW on the whole
@@ tools/testing/selftests/mm/khugepaged.c: static void
collapse_max_ptes_shared(st
+ */
+ i = 0;
+ for (;;) {
++ pid_t ret;
++
+ if (i < n)
+ ip[i * stride] = i + 0xbeef0000;
+ i++;
+ usleep(10 * 1000);
-+ if (waitpid(child, &wstatus, WNOHANG))
++ ret = waitpid(child, &wstatus, WNOHANG);
++ if (ret == child)
+ break;
++ if (ret < 0)
++ ksft_exit_fail_perror("waitpid()");
+ }
+
+ /* Finish whatever the paced sweep did not reach */
14: 30e316efbd9f ! 14: 8e214f9ef2ea selftests/mm: run every supported
collapse order by default
@@ Commit message
and "all". Also:
- -c still pins one order, and now says what is wrong instead of
- printing the usage text. An order at or below the -s source order
is
- skipped: the sources would already be the size being asked for.
+ printing the usage text.
- Both orders end up as array indices and shift counts, so -s and -c
are range-checked before they get there.
@@ Commit message
pinned order-4 line.
Assisted-by: LLM
+ Reviewed-by: Baolin Wang <[email protected]>
Tested-by: Muhammad Usama Anjum <[email protected]>
+ Tested-by: Baolin Wang <[email protected]>
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
## tools/testing/selftests/mm/khugepaged.c ##
@@ tools/testing/selftests/mm/khugepaged.c: int main(int argc, char **argv)
- !(thp_supported_orders() & (1UL << collapse_order)))
- ksft_exit_skip("Order %d is not a supported anon THP order\n",
- collapse_order);
+- if (mthp_khugepaged_context && collapse_order <= anon_order)
+- ksft_exit_skip("-c %d needs a source order below it, -s says
%d\n",
+- collapse_order, anon_order);
+ if (mthp_khugepaged_context) {
+ unsigned long orders = thp_supported_orders();
+
15: e891ab107e3a ! 15: 95ae01ec50dd selftests/mm: check that one khugepaged
pass collapses one window
@@ tools/testing/selftests/mm/Makefile: TEST_GEN_FILES += merge
TEST_GEN_FILES += folio_split_race_test
TEST_GEN_FILES += folio_order_check
+TEST_GEN_FILES += khugepaged_sync_check
-
- ifneq ($(ARCH),arm64)
TEST_GEN_FILES += soft-dirty
+
+ ifeq ($(ARCH),x86_64)
## tools/testing/selftests/mm/khugepaged_sync_check.c (new) ##
@@
@@ tools/testing/selftests/mm/khugepaged_sync_check.c (new)
+
+#include "kselftest.h"
+#include "vm_util.h"
-+#include "hugepage_settings.h"
++#include <mm/hugepage_settings.h>
+
+#define BASE_ADDR ((void *)(1UL << 30))
+/* Smallest order khugepaged considers */
16: d7f8e59c19f1 ! 16: 343b8684a8bd selftests/mm: add khugepaged race harness
@@ tools/testing/selftests/mm/Makefile: TEST_GEN_FILES += rmap
TEST_GEN_FILES += folio_order_check
TEST_GEN_FILES += khugepaged_sync_check
+TEST_GEN_FILES += khugepaged_race
-
- ifneq ($(ARCH),arm64)
TEST_GEN_FILES += soft-dirty
+
+ ifeq ($(ARCH),x86_64)
## tools/testing/selftests/mm/khugepaged_race.c (new) ##
@@
@@ tools/testing/selftests/mm/khugepaged_race.c (new)
+
+#include "kselftest.h"
+#include "vm_util.h"
-+#include "hugepage_settings.h"
++#include <mm/hugepage_settings.h>
+#include "../../../../mm/gup_test.h"
+
+#ifndef FOLL_WRITE
@@ tools/testing/selftests/mm/khugepaged_race.c (new)
+ MREMAP_MAYMOVE | MREMAP_FIXED, mremap_area);
+ if (p == MAP_FAILED)
+ ksft_exit_fail_perror("mremap() back");
++ /* The move back unmapped the scratch address: claim it again */
++ if (mmap(mremap_scratch, hpage_pmd_size, PROT_NONE,
++ MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE,
++ -1, 0) != (void *)mremap_scratch)
++ ksft_exit_fail_perror("mmap() mremap scratch");
+ usleep(rand_r(&seed) % 2000);
+ }
+ return NULL;
17: f85220ed8bb4 = 17: 09cfcb6afbc4 selftests/mm: race the collapse of
windows with holes
18: b3738ea322b7 = 18: b12f1f2d500c selftests/mm: add memory-pressure threads
to the khugepaged race harness
19: 66f5a400e015 = 19: ad0be4f7ec43 selftests/mm: zap whole PTE tables in the
khugepaged race harness
Kiryl Shutsemau (Meta) (19):
selftests/mm: raise the khugepaged test-case cap
selftests/mm: skip collapse_compound_extreme() where the PMD is too
large
selftests/mm: scale khugepaged's collapse wait with the PMD size
selftests/mm: skip khugepaged page cache cases without a PMD folio
selftests/mm: make the swap cases' swapout reliable
selftests/mm: stop khugepaged during the MADV_COLLAPSE cases
selftests/mm: move is_backed_by_folio() into vm_util
selftests/mm: add folio-order check for address ranges
selftests/mm: add folio-order detection self-check
selftests/mm: add khugepaged completion barrier helper
selftests/mm: add order-parameterized khugepaged collapse cases
selftests/mm: parameterize the mixed-source collapse case by source
order
selftests/mm: cover a shared-source collapse write race
selftests/mm: run every supported collapse order by default
selftests/mm: check that one khugepaged pass collapses one window
selftests/mm: add khugepaged race harness
selftests/mm: race the collapse of windows with holes
selftests/mm: add memory-pressure threads to the khugepaged race
harness
selftests/mm: zap whole PTE tables in the khugepaged race harness
tools/lib/mm/hugepage_settings.c | 60 +-
tools/lib/mm/hugepage_settings.h | 11 +
tools/testing/selftests/mm/Makefile | 3 +
.../testing/selftests/mm/folio_order_check.c | 122 ++++
tools/testing/selftests/mm/hmm-tests.c | 1 -
tools/testing/selftests/mm/khugepaged.c | 488 +++++++++++++++-
tools/testing/selftests/mm/khugepaged_race.c | 538 ++++++++++++++++++
.../selftests/mm/khugepaged_sync_check.c | 179 ++++++
tools/testing/selftests/mm/migration.c | 1 -
tools/testing/selftests/mm/run_vmtests.sh | 8 +-
.../selftests/mm/split_huge_page_test.c | 61 --
tools/testing/selftests/mm/vm_util.c | 146 +++++
tools/testing/selftests/mm/vm_util.h | 10 +
13 files changed, 1526 insertions(+), 102 deletions(-)
create mode 100644 tools/testing/selftests/mm/folio_order_check.c
create mode 100644 tools/testing/selftests/mm/khugepaged_race.c
create mode 100644 tools/testing/selftests/mm/khugepaged_sync_check.c
base-commit: 498ee28e5ec4727f829507c4a1bde3ab1b7704cd
--
2.54.0