[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250924-b4-asi-page-alloc-v1-13-2d861768041f@google.com>
Date: Wed, 24 Sep 2025 14:59:48 +0000
From: Brendan Jackman <jackmanb@...gle.com>
To: jackmanb@...gle.com, Andy Lutomirski <luto@...nel.org>,
Lorenzo Stoakes <lorenzo.stoakes@...cle.com>, "Liam R. Howlett" <Liam.Howlett@...cle.com>,
Suren Baghdasaryan <surenb@...gle.com>, Michal Hocko <mhocko@...e.com>,
Johannes Weiner <hannes@...xchg.org>, Zi Yan <ziy@...dia.com>,
Axel Rasmussen <axelrasmussen@...gle.com>, Yuanchu Xie <yuanchu@...gle.com>,
Roman Gushchin <roman.gushchin@...ux.dev>
Cc: peterz@...radead.org, bp@...en8.de, dave.hansen@...ux.intel.com,
mingo@...hat.com, tglx@...utronix.de, akpm@...ux-foundation.org,
david@...hat.com, derkling@...gle.com, junaids@...gle.com,
linux-kernel@...r.kernel.org, linux-mm@...ck.org, reijiw@...gle.com,
rientjes@...gle.com, rppt@...nel.org, vbabka@...e.cz, x86@...nel.org,
yosry.ahmed@...ux.dev
Subject: [PATCH 13/21] mm/page_alloc_test: unit test pindex helpers
The author struggles with really basic arithmetic. This test checks for
errors in the helpers that are used to map to and from pcplist indices.
This can be run via a basic kunit.py invocation:
tools/testing/kunit/kunit.py run "page_alloc.*"
That will run it via UML which means no THP or ASI. If you want to test
with those enabled you can set the --arch flag to run it via QEMU:
tools/testing/kunit/kunit.py run --arch=x86_64 \
--kconfig_add CONFIG_TRANSPARENT_HUGEPAGE=y "page_alloc.*"
tools/testing/kunit/kunit.py run --arch=x86_64 \
--kconfig_add CONFIG_MITIGATION_ADDRESS_SPACE_ISOLATION=y "page_alloc.*"
tools/testing/kunit/kunit.py run --arch=x86_64 \
--kconfig_add CONFIG_MITIGATION_ADDRESS_SPACE_ISOLATION=y \
--kconfig_add CONFIG_TRANSPARENT_HUGEPAGE=y \
"page_alloc.*"
Signed-off-by: Brendan Jackman <jackmanb@...gle.com>
fix
---
mm/Kconfig | 5 ++++
mm/Makefile | 1 +
mm/internal.h | 6 +++++
mm/page_alloc.c | 10 +++++---
mm/page_alloc_test.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 89 insertions(+), 3 deletions(-)
diff --git a/mm/Kconfig b/mm/Kconfig
index 034a1662d8c1af320b2262ebcb0cb51d4622e6b0..e25451c1adbd6e079f2d00e3eb8a28affcedab7e 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1375,4 +1375,9 @@ config FIND_NORMAL_PAGE
source "mm/damon/Kconfig"
+config PAGE_ALLOC_KUNIT_TEST
+ tristate "KUnit Tests for page_alloc code" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+
endmenu
diff --git a/mm/Makefile b/mm/Makefile
index 21abb3353550153a7a477640e4fa6dc6df327541..c6ce46a2abf144f2e62df96ec7f606f90affc5f0 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -65,6 +65,7 @@ page-alloc-$(CONFIG_SHUFFLE_PAGE_ALLOCATOR) += shuffle.o
memory-hotplug-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o
obj-y += page-alloc.o
+obj-$(CONFIG_PAGE_ALLOC_KUNIT_TEST) += page_alloc_test.o
obj-y += page_frag_cache.o
obj-y += init-mm.o
obj-y += memblock.o
diff --git a/mm/internal.h b/mm/internal.h
index 0401412220a76a233e14a7ee7d64c1194fc3759d..6006cfb2b9c7e771a0c647c471901dc7fcdad242 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -1693,4 +1693,10 @@ static inline int io_remap_pfn_range_complete(struct vm_area_struct *vma,
return remap_pfn_range_complete(vma, addr, pfn, size, prot);
}
+#ifdef CONFIG_KUNIT
+unsigned int order_to_pindex(freetype_t freetype, int order);
+int pindex_to_order(unsigned int pindex);
+bool pcp_allowed_order(unsigned int order);
+#endif /* CONFIG_KUNIT */
+
#endif /* __MM_INTERNAL_H */
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 5943b821089b72fd148bd93ee035c0e70e45ec91..0b205aefd27e188c492c32754db08a4488317bd8 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -56,6 +56,7 @@
#include <linux/cacheinfo.h>
#include <linux/pgalloc_tag.h>
#include <asm/div64.h>
+#include <kunit/visibility.h>
#include "internal.h"
#include "shuffle.h"
#include "page_reporting.h"
@@ -691,7 +692,7 @@ static void bad_page(struct page *page, const char *reason)
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
}
-static inline unsigned int order_to_pindex(freetype_t freetype, int order)
+VISIBLE_IF_KUNIT inline unsigned int order_to_pindex(freetype_t freetype, int order)
{
int migratetype = free_to_migratetype(freetype);
/* pindex if the freetype is nonsensitive */
@@ -713,8 +714,9 @@ static inline unsigned int order_to_pindex(freetype_t freetype, int order)
return (NR_PCP_LISTS_PER_SENSITIVITY * freetype_sensitive(freetype))
+ pindex_ns;
}
+EXPORT_SYMBOL_IF_KUNIT(order_to_pindex);
-inline int pindex_to_order(unsigned int pindex)
+VISIBLE_IF_KUNIT inline int pindex_to_order(unsigned int pindex)
{
/* pindex if the freetype is nonsensitive */
int pindex_ns = (pindex % NR_PCP_LISTS_PER_SENSITIVITY);
@@ -731,8 +733,9 @@ inline int pindex_to_order(unsigned int pindex)
return order;
}
+EXPORT_SYMBOL_IF_KUNIT(pindex_to_order);
-static inline bool pcp_allowed_order(unsigned int order)
+VISIBLE_IF_KUNIT inline bool pcp_allowed_order(unsigned int order)
{
if (order <= PAGE_ALLOC_COSTLY_ORDER)
return true;
@@ -742,6 +745,7 @@ static inline bool pcp_allowed_order(unsigned int order)
#endif
return false;
}
+EXPORT_SYMBOL_IF_KUNIT(pcp_allowed_order);
/*
* Higher-order pages are called "compound pages". They are structured thusly:
diff --git a/mm/page_alloc_test.c b/mm/page_alloc_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..1cc615ce90d95c47ecae206a87f2af3fab3a5581
--- /dev/null
+++ b/mm/page_alloc_test.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitmap.h>
+
+#include <kunit/test.h>
+
+#include "internal.h"
+
+/* This just checks for basic arithmetic errors. */
+static void test_pindex_helpers(struct kunit *test)
+{
+ unsigned long bitmap[bitmap_size(NR_PCP_LISTS)];
+
+ /* Bit means "pindex not yet used". */
+ bitmap_fill(bitmap, NR_PCP_LISTS);
+
+ for (unsigned int order = 0; order < NR_PAGE_ORDERS; order++) {
+ for (unsigned int mt = 0; mt < MIGRATE_PCPTYPES; mt++) {
+ if (!pcp_allowed_order(order))
+ continue;
+
+ for (int sensitive = 0; sensitive < NR_SENSITIVITIES; sensitive++) {
+ freetype_t ft = migrate_to_freetype(mt, sensitive);
+ unsigned int pindex = order_to_pindex(ft, order);
+ int got_order;
+
+ KUNIT_ASSERT_LT_MSG(test, pindex, NR_PCP_LISTS,
+ "invalid pindex %d (order %d mt %d sensitive %d)",
+ pindex, order, mt, sensitive);
+ KUNIT_EXPECT_TRUE_MSG(test, test_bit(pindex, bitmap),
+ "pindex %d reused (order %d mt %d sensitive %d)",
+ pindex, order, mt, sensitive);
+
+ /*
+ * For THP, two migratetypes map to the
+ * same pindex, just manually exclude one
+ * of those cases.
+ */
+ if (!(IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+ order == HPAGE_PMD_ORDER &&
+ mt == min(MIGRATE_UNMOVABLE, MIGRATE_RECLAIMABLE)))
+ clear_bit(pindex, bitmap);
+
+ got_order = pindex_to_order(pindex);
+ KUNIT_EXPECT_EQ_MSG(test, order, got_order,
+ "roundtrip failed, got %d want %d (pindex %d mt %d sensitive %d)",
+ got_order, order, pindex, mt, sensitive);
+
+ }
+ }
+ }
+
+ KUNIT_EXPECT_TRUE_MSG(test, bitmap_empty(bitmap, NR_PCP_LISTS),
+ "unused pindices: %*pbl", NR_PCP_LISTS, bitmap);
+}
+
+static struct kunit_case page_alloc_test_cases[] = {
+ KUNIT_CASE(test_pindex_helpers),
+ {}
+};
+
+static struct kunit_suite page_alloc_test_suite = {
+ .name = "page_alloc",
+ .test_cases = page_alloc_test_cases,
+};
+
+kunit_test_suite(page_alloc_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
--
2.50.1
Powered by blists - more mailing lists