From: "Kiryl Shutsemau (Meta)" <[email protected]>
The mTHP collapse cases only run when the caller names both the context
and an order, so a plain ./khugepaged covers the PMD contexts on anon and
nothing else. run_vmtests.sh pinned order 4 and covered no other.
Run the mTHP cases once per supported anon THP order below the PMD when
-c is absent, and pull that context into both the no-argument invocation
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.
- Both orders end up as array indices and shift counts, so -s and -c
are range-checked before they get there.
- The mTHP context has only anon cases, so a run that names a different
mem_type -- "all:shmem", say -- drops it again rather than refusing
to start. Naming both explicitly still refuses.
- A case carries the order it was registered at, so a result names it:
# Run test: collapse_single_mthp (mthp_khugepaged:anon, order 6)
On x86-64 with 4K pages that is orders 2 through 8, and ./khugepaged goes
from 29 results in 17 seconds to 77 in 29, so run_vmtests.sh can drop its
pinned order-4 line.
Assisted-by: LLM
Tested-by: Muhammad Usama Anjum <[email protected]>
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
---
tools/testing/selftests/mm/khugepaged.c | 98 ++++++++++++++++++-----
tools/testing/selftests/mm/run_vmtests.sh | 2 -
2 files changed, 78 insertions(+), 22 deletions(-)
diff --git a/tools/testing/selftests/mm/khugepaged.c
b/tools/testing/selftests/mm/khugepaged.c
index f5d4847c50cd..335f946eca61 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -31,6 +31,9 @@ static unsigned long page_size;
static int hpage_pmd_nr;
static int anon_order;
static int collapse_order;
+static bool collapse_order_set;
+static int collapse_orders[NR_ORDERS];
+static int nr_collapse_orders;
static int pagemap_fd = -1;
static int kpageflags_fd = -1;
@@ -1517,12 +1520,14 @@ static void usage(void)
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");
+ fprintf(stderr, "\t\t Defaults to every supported order below the
PMD.\n");
fprintf(stderr, "\t\t -s, if set, is the source order for the
mixed-source case.\n");
exit(1);
}
static void parse_test_type(int argc, char **argv)
{
+ bool mthp_context_implied = false;
int opt;
char *buf;
const char *token;
@@ -1534,6 +1539,7 @@ static void parse_test_type(int argc, char **argv)
break;
case 'c':
collapse_order = atoi(optarg);
+ collapse_order_set = true;
break;
case 'h':
default:
@@ -1541,12 +1547,25 @@ static void parse_test_type(int argc, char **argv)
}
}
+ /*
+ * Both orders end up as array indices and shift counts, so neither
+ * can be negative, and a zero collapse order asks for base pages.
+ */
+ if (anon_order < 0 || anon_order > hpage_pmd_order)
+ ksft_exit_fail_msg("-s takes an order in 0..%d, not %d\n",
+ hpage_pmd_order, anon_order);
+ if (collapse_order_set &&
+ (collapse_order <= 0 || collapse_order >= hpage_pmd_order))
+ ksft_exit_fail_msg("-c takes an order in 1..%d, not %d\n",
+ hpage_pmd_order - 1, collapse_order);
+
argv += optind;
argc -= optind;
if (argc == 0) {
- /* Backwards compatibility */
+ /* No arguments: anon under every context */
khugepaged_context = &__khugepaged_context;
+ mthp_khugepaged_context = &__mthp_khugepaged_context;
madvise_context = &__madvise_context;
anon_ops = &__anon_ops;
return;
@@ -1557,13 +1576,14 @@ static void parse_test_type(int argc, char **argv)
if (!strcmp(token, "all")) {
khugepaged_context = &__khugepaged_context;
+ mthp_khugepaged_context = &__mthp_khugepaged_context;
madvise_context = &__madvise_context;
+ /* The mTHP context has only anon cases; let other mem_types
drop it */
+ mthp_context_implied = true;
} 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 {
@@ -1579,20 +1599,20 @@ static void parse_test_type(int argc, char **argv)
read_write_file_write_ops = &__read_write_file_write_ops;
anon_ops = &__anon_ops;
shmem_ops = &__shmem_ops;
- if (mthp_khugepaged_context)
- usage();
} else if (!strcmp(buf, "anon")) {
anon_ops = &__anon_ops;
} else if (!strcmp(buf, "file")) {
read_only_file_ops = &__read_only_file_ops;
read_write_file_read_ops = &__read_write_file_read_ops;
read_write_file_write_ops = &__read_write_file_write_ops;
- if (mthp_khugepaged_context)
+ if (mthp_khugepaged_context && !mthp_context_implied)
usage();
+ mthp_khugepaged_context = NULL;
} else if (!strcmp(buf, "shmem")) {
shmem_ops = &__shmem_ops;
- if (mthp_khugepaged_context)
+ if (mthp_khugepaged_context && !mthp_context_implied)
usage();
+ mthp_khugepaged_context = NULL;
} else {
usage();
}
@@ -1614,6 +1634,7 @@ struct test_case {
struct mem_ops *ops;
const char *desc;
test_fn fn;
+ int order; /* mTHP contexts: the collapse order */
};
#define MAX_TEST_CASES 256
@@ -1629,6 +1650,7 @@ static int nr_test_cases;
.ops = o, \
.desc = #t, \
.fn = t, \
+ .order = collapse_order, \
}; \
} \
} while (0)
@@ -1669,10 +1691,35 @@ int main(int argc, char **argv)
parse_test_type(argc, argv);
- if (mthp_khugepaged_context &&
- !(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) {
+ unsigned long orders = thp_supported_orders();
+
+ if (collapse_order_set) {
+ if (!(orders & (1UL << collapse_order)))
+ ksft_exit_skip("Order %d is not a supported
anon THP order\n",
+ collapse_order);
+ if (collapse_order <= anon_order)
+ ksft_exit_skip("-c %d needs a source order
below it, -s says %d\n",
+ collapse_order, anon_order);
+ collapse_orders[nr_collapse_orders++] = collapse_order;
+ } else {
+ /*
+ * Every supported order above the source: -s makes the
+ * fault path hand out folios of that order, so a target
+ * at or below it has nothing to collapse.
+ */
+ int first = anon_order + 1;
+
+ if (first < MIN_MTHP_ORDER)
+ first = MIN_MTHP_ORDER;
+ for (int i = first; i < hpage_pmd_order; i++) {
+ if (orders & (1UL << i))
+ collapse_orders[nr_collapse_orders++] =
i;
+ }
+ if (!nr_collapse_orders)
+ ksft_print_msg("mTHP cases skipped: no order
above the source\n");
+ }
+ }
if (mthp_khugepaged_context) {
pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
@@ -1721,7 +1768,17 @@ 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);
+ for (int i = 0; i < nr_collapse_orders; i++) {
+ collapse_order = collapse_orders[i];
+ TEST(collapse_full, mthp_khugepaged_context, anon_ops);
+ TEST(collapse_empty, mthp_khugepaged_context, anon_ops);
+ TEST(collapse_single_mthp, mthp_khugepaged_context, anon_ops);
+ TEST(collapse_order_single_window, mthp_khugepaged_context,
anon_ops);
+ TEST(collapse_order_partial_window, mthp_khugepaged_context,
anon_ops);
+ TEST(collapse_order_max_ptes_none, mthp_khugepaged_context,
anon_ops);
+ TEST(collapse_order_mixed_sources, 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);
@@ -1729,15 +1786,8 @@ 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_order_single_window, mthp_khugepaged_context, anon_ops);
- TEST(collapse_order_partial_window, mthp_khugepaged_context, anon_ops);
- TEST(collapse_order_max_ptes_none, mthp_khugepaged_context, anon_ops);
- TEST(collapse_order_mixed_sources, 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);
@@ -1810,7 +1860,15 @@ int main(int argc, char **argv)
for (int i = 0; i < nr_test_cases; i++) {
struct test_case *t = &test_cases[i];
- ksft_print_msg("\n# Run test: %s (%s:%s)\n", t->desc,
t->ctx->name, t->ops->name);
+ if (t->ctx == &__mthp_khugepaged_context) {
+ collapse_order = t->order;
+ ksft_print_msg("\n# Run test: %s (%s:%s, order %d)\n",
+ t->desc, t->ctx->name, t->ops->name,
+ t->order);
+ } else {
+ ksft_print_msg("\n# Run test: %s (%s:%s)\n", t->desc,
+ t->ctx->name, t->ops->name);
+ }
t->fn(t->ctx, t->ops);
}
diff --git a/tools/testing/selftests/mm/run_vmtests.sh
b/tools/testing/selftests/mm/run_vmtests.sh
index 2652a7920b80..8bf898b71350 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -412,8 +412,6 @@ 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
--
2.54.0