[PATCH 13/21] mm/page_alloc_test: unit test pindex helpers

Brendan Jackman posted 21 patches 1 week ago
[PATCH 13/21] mm/page_alloc_test: unit test pindex helpers
Posted by Brendan Jackman 1 week ago
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@google.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
Re: [PATCH 13/21] mm/page_alloc_test: unit test pindex helpers
Posted by kernel test robot 6 days, 11 hours ago
Hi Brendan,

kernel test robot noticed the following build errors:

[auto build test ERROR on bf2602a3cb2381fb1a04bf1c39a290518d2538d1]

url:    https://github.com/intel-lab-lkp/linux/commits/Brendan-Jackman/x86-mm-asi-Add-CONFIG_MITIGATION_ADDRESS_SPACE_ISOLATION/20250924-230633
base:   bf2602a3cb2381fb1a04bf1c39a290518d2538d1
patch link:    https://lore.kernel.org/r/20250924-b4-asi-page-alloc-v1-13-2d861768041f%40google.com
patch subject: [PATCH 13/21] mm/page_alloc_test: unit test pindex helpers
config: x86_64-buildonly-randconfig-006-20250925 (https://download.01.org/0day-ci/archive/20250925/202509252146.WmdVQlgy-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250925/202509252146.WmdVQlgy-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202509252146.WmdVQlgy-lkp@intel.com/

All errors (new ones prefixed by >>):

   mm/page_alloc_test.c: In function 'test_pindex_helpers':
>> mm/page_alloc_test.c:19:30: error: implicit declaration of function 'pcp_allowed_order' [-Wimplicit-function-declaration]
      19 |                         if (!pcp_allowed_order(order))
         |                              ^~~~~~~~~~~~~~~~~
>> mm/page_alloc_test.c:24:55: error: implicit declaration of function 'order_to_pindex' [-Wimplicit-function-declaration]
      24 |                                 unsigned int pindex = order_to_pindex(ft, order);
         |                                                       ^~~~~~~~~~~~~~~
>> mm/page_alloc_test.c:44:45: error: implicit declaration of function 'pindex_to_order'; did you mean 'next_order'? [-Wimplicit-function-declaration]
      44 |                                 got_order = pindex_to_order(pindex);
         |                                             ^~~~~~~~~~~~~~~
         |                                             next_order


vim +/pcp_allowed_order +19 mm/page_alloc_test.c

     8	
     9	/* This just checks for basic arithmetic errors. */
    10	static void test_pindex_helpers(struct kunit *test)
    11	{
    12		unsigned long bitmap[bitmap_size(NR_PCP_LISTS)];
    13	
    14		/* Bit means "pindex not yet used". */
    15		bitmap_fill(bitmap, NR_PCP_LISTS);
    16	
    17		for (unsigned int order = 0; order < NR_PAGE_ORDERS; order++) {
    18			for (unsigned int mt = 0; mt < MIGRATE_PCPTYPES; mt++)  {
  > 19				if (!pcp_allowed_order(order))
    20					continue;
    21	
    22				for (int sensitive = 0; sensitive < NR_SENSITIVITIES; sensitive++) {
    23					freetype_t ft = migrate_to_freetype(mt, sensitive);
  > 24					unsigned int pindex = order_to_pindex(ft, order);
    25					int got_order;
    26	
    27					KUNIT_ASSERT_LT_MSG(test, pindex, NR_PCP_LISTS,
    28						"invalid pindex %d (order %d mt %d sensitive %d)",
    29						pindex, order, mt, sensitive);
    30					KUNIT_EXPECT_TRUE_MSG(test, test_bit(pindex, bitmap),
    31						"pindex %d reused (order %d mt %d sensitive %d)",
    32						pindex, order, mt, sensitive);
    33	
    34					/*
    35					 * For THP, two migratetypes map to the
    36					 * same pindex, just manually exclude one
    37					 * of those cases.
    38					 */
    39					if (!(IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
    40					      order == HPAGE_PMD_ORDER &&
    41					      mt == min(MIGRATE_UNMOVABLE, MIGRATE_RECLAIMABLE)))
    42						clear_bit(pindex, bitmap);
    43	
  > 44					got_order = pindex_to_order(pindex);
    45					KUNIT_EXPECT_EQ_MSG(test, order, got_order,
    46						"roundtrip failed, got %d want %d (pindex %d mt %d sensitive %d)",
    47						got_order, order, pindex, mt, sensitive);
    48	
    49				}
    50			}
    51		}
    52	
    53		KUNIT_EXPECT_TRUE_MSG(test, bitmap_empty(bitmap, NR_PCP_LISTS),
    54			"unused pindices: %*pbl", NR_PCP_LISTS, bitmap);
    55	}
    56	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki