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 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 #include #include +#include #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 + +#include + +#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