[patch v2] lib: test_vmstat: add synthetic benchmark for vm stats

From: David Rientjes

Date: Sun Aug 02 2026 - 19:06:57 EST


From: Christoph Lameter <cl@xxxxxxxxxx>

Add a synthetic benchmark that can be used to measure performance of VM
statistics. This is used to analyze any improvements or regressions in
functions that are frequently used in hot code paths.

The test is run by inserting the module: modprobe test_vmstat
The module insertion will always fail so that it is automatically
unloaded; the results are in the kernel log.

Signed-off-by: Christoph Lameter <cl@xxxxxxxxxx>
Signed-off-by: David Rientjes <rientjes@xxxxxxxxxx>
---
v2:
- added bash wrapper to ksft per David
- integrated into ksft
- added MAINTAINERS entry per David
- fixed alloc_page() error code per Usama

Adding single test for now to solicit feedback before updating the other
two.

MAINTAINERS | 1 +
lib/Kconfig.debug | 10 +++
lib/Makefile | 1 +
lib/test_vmstat.c | 100 ++++++++++++++++++++++
tools/testing/selftests/mm/Makefile | 2 +
tools/testing/selftests/mm/config | 1 +
tools/testing/selftests/mm/ksft_vmstat.sh | 4 +
tools/testing/selftests/mm/run_vmtests.sh | 5 ++
tools/testing/selftests/mm/test_vmstat.sh | 71 +++++++++++++++
9 files changed, 195 insertions(+)
create mode 100644 lib/test_vmstat.c
create mode 100755 tools/testing/selftests/mm/ksft_vmstat.sh
create mode 100755 tools/testing/selftests/mm/test_vmstat.sh

diff --git a/MAINTAINERS b/MAINTAINERS
index 5114e6db7307..c76514d6c503 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17004,6 +17004,7 @@ F: mm/sparse.c
F: mm/util.c
F: mm/vmpressure.c
F: mm/vmstat.c
+F: lib/test_vmstat.c
N: include\/linux\/page[-_][a-zA-Z]*

MEMORY MANAGEMENT - EXECMEM
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 1244dcac2294..6d743c8e27fa 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3427,6 +3427,16 @@ config TEST_KEXEC_HANDOVER

If unsure, say N.

+config TEST_VMSTAT
+ tristate "VM Statistics Benchmark"
+ default n
+ help
+ A synthetic benchmark measuring the performance of VM statistics,
+ useful to analyze any improvements or regressions in functions that
+ are frequently used in hot code paths.
+
+ If unsure, say N.
+
config RATELIMIT_KUNIT_TEST
tristate "KUnit Test for correctness and stress of ratelimit" if !KUNIT_ALL_TESTS
depends on KUNIT
diff --git a/lib/Makefile b/lib/Makefile
index 7f75cc6edf94..5f3ebabd0224 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -105,6 +105,7 @@ obj-$(CONFIG_TEST_FREE_PAGES) += test_free_pages.o
obj-$(CONFIG_TEST_REF_TRACKER) += test_ref_tracker.o
obj-$(CONFIG_TEST_OBJPOOL) += test_objpool.o
obj-$(CONFIG_TEST_KEXEC_HANDOVER) += test_kho.o
+obj-$(CONFIG_TEST_VMSTAT) += test_vmstat.o

obj-$(CONFIG_TEST_FPU) += test_fpu.o
test_fpu-y := test_fpu_glue.o test_fpu_impl.o
diff --git a/lib/test_vmstat.c b/lib/test_vmstat.c
new file mode 100644
index 000000000000..ee4872a4c82d
--- /dev/null
+++ b/lib/test_vmstat.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test module for in-kernel synthetic vm statistics performance.
+ *
+ * execute
+ *
+ * modprobe test_vmstat
+ *
+ * to run this test
+ *
+ * (C) 2009 Linux Foundation, Christoph Lameter <cl@xxxxxxxxxx>
+ */
+
+#include <linux/jiffies.h>
+#include <linux/compiler.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <asm/timex.h>
+
+#define TEST_COUNT 10000
+
+static int vmstat_test_init(void)
+{
+ unsigned int i;
+ cycles_t time1, time2, time;
+ int rem;
+ struct page *page = alloc_page(GFP_KERNEL);
+
+ if (!page)
+ return -ENOMEM;
+
+ pr_alert("VMstat testing\n");
+ pr_alert("=====================\n");
+ pr_alert("1. inc_zone_page_state() then dec_zone_page_state()\n");
+ time1 = get_cycles();
+ for (i = 0; i < TEST_COUNT; i++)
+ inc_zone_page_state(page, NR_FREE_CMA_PAGES);
+
+ time2 = get_cycles();
+ time = time2 - time1;
+
+ pr_alert("%i times inc_zone_page_state() ", i);
+ time = div_u64_rem(time, TEST_COUNT, &rem);
+ pr_cont("-> %llu cycles ", (unsigned long long) time);
+
+ time1 = get_cycles();
+ for (i = 0; i < TEST_COUNT; i++)
+ dec_zone_page_state(page, NR_FREE_CMA_PAGES);
+
+ time2 = get_cycles();
+ time = time2 - time1;
+
+ pr_cont("__dec_z_p_s() ");
+ time = div_u64_rem(time, TEST_COUNT, &rem);
+ pr_cont("-> %llu cycles\n", (unsigned long long) time);
+
+ pr_alert("2. inc_zone_page_state()/dec_zone_page_state()\n");
+ time1 = get_cycles();
+ for (i = 0; i < TEST_COUNT; i++) {
+ inc_zone_page_state(page, NR_FREE_CMA_PAGES);
+ dec_zone_page_state(page, NR_FREE_CMA_PAGES);
+ }
+
+ time2 = get_cycles();
+ time = time2 - time1;
+
+ pr_alert("%i times inc/dec ", i);
+ time = div_u64_rem(time, TEST_COUNT, &rem);
+ pr_cont("-> %llu cycles\n", (unsigned long long) time);
+
+ pr_alert("3. count_vm_event()\n");
+ time1 = get_cycles();
+ for (i = 0; i < TEST_COUNT; i++)
+ count_vm_event(SLABS_SCANNED);
+
+ time2 = get_cycles();
+ time = time2 - time1;
+
+ count_vm_events(SLABS_SCANNED, -TEST_COUNT);
+ pr_alert("%i count_vm_events ", i);
+ time = div_u64_rem(time, TEST_COUNT, &rem);
+ pr_cont("-> %llu cycles\n", (unsigned long long) time);
+ __free_page(page);
+
+ /* Fail will directly unload the module */
+ return IS_BUILTIN(CONFIG_TEST_VMSTAT) ? 0 : -EAGAIN;
+}
+
+static void vmstat_test_exit(void)
+{
+ pr_alert("test exit\n");
+}
+
+module_init(vmstat_test_init)
+module_exit(vmstat_test_exit)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christoph Lameter");
+MODULE_DESCRIPTION("VM statistics test");
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index e6df968f0971..0d386c8b83b7 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -173,8 +173,10 @@ TEST_PROGS += ksft_thp.sh
TEST_PROGS += ksft_userfaultfd.sh
TEST_PROGS += ksft_vma_merge.sh
TEST_PROGS += ksft_vmalloc.sh
+TEST_PROGS += ksft_vmstat.sh

TEST_FILES := test_vmalloc.sh
+TEST_FILES := test_vmstat.sh
TEST_FILES += test_hmm.sh
TEST_FILES += va_high_addr_switch.sh
TEST_FILES += charge_reserved_hugetlb.sh
diff --git a/tools/testing/selftests/mm/config b/tools/testing/selftests/mm/config
index 06f78bd232e2..16e02a2cb2a1 100644
--- a/tools/testing/selftests/mm/config
+++ b/tools/testing/selftests/mm/config
@@ -2,6 +2,7 @@ CONFIG_SYSVIPC=y
CONFIG_USERFAULTFD=y
CONFIG_PTE_MARKER_UFFD_WP=y
CONFIG_TEST_VMALLOC=m
+CONFIG_TEST_VMSTAT=m
CONFIG_DEVICE_PRIVATE=y
CONFIG_TEST_HMM=m
CONFIG_GUP_TEST=y
diff --git a/tools/testing/selftests/mm/ksft_vmstat.sh b/tools/testing/selftests/mm/ksft_vmstat.sh
new file mode 100755
index 000000000000..4e47112f9925
--- /dev/null
+++ b/tools/testing/selftests/mm/ksft_vmstat.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+# SPDX-License-Identifier: GPL-2.0
+
+./run_vmtests.sh -t vmstat
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 8c296dedf047..c5416eda4c2e 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -47,6 +47,8 @@ separated by spaces:
tests for very large virtual address space
- vmalloc
vmalloc smoke tests
+- vmstat
+ vmstat performance test
- hmm
hmm smoke tests
- madv_guard
@@ -338,6 +340,9 @@ fi # VADDR64
# vmalloc stability smoke test
CATEGORY="vmalloc" run_test bash ./test_vmalloc.sh smoke

+# vmstat performance test
+CATEGORY="vmstat" run_test bash ./test_vmstat.sh performance
+
CATEGORY="mremap" run_test ./mremap_dontunmap

CATEGORY="hmm" run_test bash ./test_hmm.sh smoke
diff --git a/tools/testing/selftests/mm/test_vmstat.sh b/tools/testing/selftests/mm/test_vmstat.sh
new file mode 100755
index 000000000000..a4fc7f7fec16
--- /dev/null
+++ b/tools/testing/selftests/mm/test_vmstat.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# This is a test script for the kernel test module to measure performance of VM
+# statistics. It simply loads the kernel module.
+
+TEST_NAME="vmstat"
+DRIVER="test_${TEST_NAME}"
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+check_test_requirements()
+{
+ uid=$(id -u)
+ if [ $uid -ne 0 ]; then
+ echo "$0: Must be run as root"
+ exit $ksft_skip
+ fi
+
+ if ! which modprobe > /dev/null 2>&1; then
+ echo "$0: You need modprobe installed"
+ exit $ksft_skip
+ fi
+
+ if ! modinfo $DRIVER > /dev/null 2>&1; then
+ echo "$0: You must have the following enabled in your kernel:"
+ echo "CONFIG_TEST_VMSTAT=m"
+ exit $ksft_skip
+ fi
+}
+
+run_performance_check()
+{
+ echo "Run performance tests to evaluate how fast vmstat is."
+
+ modprobe $DRIVER > /dev/null 2>&1
+ echo "Done."
+ echo "Check the kernel message buffer to see the summary."
+}
+
+usage()
+{
+ echo -n "Usage: $0 performance"
+ echo
+ echo "Example usage:"
+ echo
+ echo "# Shows help message"
+ echo "./${DRIVER}.sh"
+ echo
+ echo "# Performance analysis"
+ echo "./${DRIVER}.sh performance"
+ echo
+ exit 0
+}
+
+function run_test()
+{
+ if [ $# -eq 0 ]; then
+ usage
+ else
+ if [[ "$1" = "performance" ]]; then
+ run_performance_check
+ fi
+ fi
+}
+
+check_test_requirements
+run_test $@
+
+exit 0