* [PATCH 1/3] lib/test_mempress_timer: add module to generate kernel allocation pressure
2026-08-19 7:05 [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer Jason Miu
@ 2026-08-19 7:05 ` Jason Miu
2026-08-19 7:05 ` [PATCH 2/3] selftests: mm: add script to induce userspace memory contention Jason Miu
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Jason Miu @ 2026-08-19 7:05 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Shuah Khan, David Rientjes,
Shakeel Butt
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Greg Thelen, linux-kernel,
linux-mm, linux-kselftest, Jason Miu
Update lib/Kconfig.debug and lib/Makefile to introduce
test_mempress_timer, a kernel testing module designed to generate
synthetic memory allocation pressure within SoftIRQ contexts.
Bind independent timers to each online CPU to continuously execute
atomic page allocations (GFP_ATOMIC | __GFP_NOWARN). Provide this
workload generator to repeatedly emulate heavy kernel allocation demands.
Add a configurable termination threshold (test_duration_secs) to prevent
permanent test-node lockups, and provide batching control via the
allocs_per_iteration module parameter.
Signed-off-by: Jason Miu <jasonmiu@google.com>
---
lib/Kconfig.debug | 11 +++
lib/Makefile | 1 +
lib/test_mempress_timer.c | 140 ++++++++++++++++++++++++++++++++++++++
3 files changed, 152 insertions(+)
create mode 100644 lib/test_mempress_timer.c
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 1244dcac2294..00d32012e476 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2635,6 +2635,17 @@ config TEST_BITOPS
If unsure, say N.
+config TEST_MEMPRESS_TIMER
+ tristate "Test module for memory pressure and allocation stall timing"
+ default n
+ help
+ This builds the "test_mempress_timer" module that can be used to
+ provoke and profile page allocation stalls and direct reclaim
+ slowness. It periodically does a atomic page allocation to generate a
+ memory pressure.
+
+ If unsure, say N.
+
config TEST_VMALLOC
tristate "Test module for stress/performance analysis of vmalloc allocator"
default n
diff --git a/lib/Makefile b/lib/Makefile
index 7f75cc6edf94..0f35c2b78970 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -78,6 +78,7 @@ CFLAGS_test_ubsan.o += $(call cc-disable-warning, unused-but-set-variable)
UBSAN_SANITIZE_test_ubsan.o := y
obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
obj-$(CONFIG_TEST_LKM) += test_module.o
+obj-$(CONFIG_TEST_MEMPRESS_TIMER) += test_mempress_timer.o
obj-$(CONFIG_TEST_VMALLOC) += test_vmalloc.o
obj-$(CONFIG_TEST_WORKQUEUE) += test_workqueue.o
obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o
diff --git a/lib/test_mempress_timer.c b/lib/test_mempress_timer.c
new file mode 100644
index 000000000000..a102ac4f6725
--- /dev/null
+++ b/lib/test_mempress_timer.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * test_mempress_timer.c
+ *
+ * Simulates kernelspace memory allocation pressure via timers.
+ * This module binds individual timers to each online CPU to independently
+ * allocate and free physical pages concurrently. It serves as a workload
+ * generator to reproduce and measure page allocation stalls under system
+ * memory contention.
+ *
+ * Module Parameters:
+ * - allocs_per_iteration: Number of atomic page allocations executed
+ * during each timer callback.
+ * Default: 1000
+ * - test_duration_secs: Total duration in seconds to run the pressure test
+ * before automatically disengaging.
+ * Default: 900 (15 minutes)
+ *
+ * Usage:
+ * insmod test_mempress_timer.ko test_duration_secs=900 allocs_per_iteration=1000
+ */
+
+#include <linux/gfp.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <net/tcp.h>
+
+struct mempress_timer {
+ struct timer_list timer;
+ struct list_head list;
+ struct list_head page_list;
+ int cpu;
+};
+
+static LIST_HEAD(timers);
+static bool stop_timers;
+
+static unsigned long interval = 2;
+
+static unsigned long allocs_per_iteration = 1000;
+module_param(allocs_per_iteration, long, 0444);
+
+static unsigned long test_duration_secs = 900; /* 15 mins default */
+module_param(test_duration_secs, ulong, 0444);
+
+static unsigned long end_jiffies;
+
+static void alloc_pages_atomics(struct mempress_timer *test)
+{
+ int i;
+ struct page *page;
+
+ for (i = 0; i < allocs_per_iteration; i++) {
+ page = alloc_page(GFP_ATOMIC | __GFP_NOWARN);
+ if (!page) {
+ pr_warn("page allocation failure from cpu %d at iteration %d\n",
+ test->cpu, i);
+ break;
+ }
+ list_add(&page->lru, &test->page_list);
+ }
+}
+
+static void free_pages_atomics(struct mempress_timer *test)
+{
+ struct list_head *page;
+ struct list_head *iter;
+
+ list_for_each_safe(page, iter, &test->page_list)
+ __free_page(container_of(page, struct page, lru));
+ INIT_LIST_HEAD(&test->page_list);
+}
+
+static void atomic_timer_allocator(struct timer_list *timer)
+{
+ struct mempress_timer *test = timer_container_of(test, timer, timer);
+ bool is_expired = (test_duration_secs > 0 &&
+ time_after(jiffies, end_jiffies));
+
+ if (list_empty(&test->page_list) && !is_expired)
+ alloc_pages_atomics(test);
+ else
+ free_pages_atomics(test);
+
+ if (!READ_ONCE(stop_timers) && !is_expired) {
+ test->timer.expires = jiffies + interval;
+ add_timer_on(&test->timer, test->cpu);
+ } else if (is_expired) {
+ pr_info_once("Duration (%lu secs) reached, stopping.\n", test_duration_secs);
+ }
+}
+
+static int __init mempress_timers_init(void)
+{
+ int cpu;
+ struct mempress_timer *test;
+
+ if (test_duration_secs > 0)
+ end_jiffies = jiffies + (test_duration_secs * HZ);
+
+ for_each_online_cpu(cpu) {
+ test = kzalloc(sizeof(*test), GFP_KERNEL | __GFP_NOFAIL);
+
+ timer_setup(&test->timer, atomic_timer_allocator, 0);
+ list_add(&test->list, &timers);
+ INIT_LIST_HEAD(&test->page_list);
+ test->cpu = cpu;
+
+ /* For start, use 90 seconds. */
+ test->timer.expires = jiffies + (90 * HZ);
+ add_timer_on(&test->timer, test->cpu);
+ }
+
+ return 0;
+}
+module_init(mempress_timers_init);
+
+static void mempress_timers_exit(void)
+{
+ struct mempress_timer *test, *n;
+
+ pr_crit("exiting\n");
+ stop_timers = true;
+
+ list_for_each_entry_safe(test, n, &timers, list) {
+ timer_delete_sync(&test->timer);
+ list_del(&test->list);
+ cond_resched();
+ free_pages_atomics(test);
+ kfree(test);
+ }
+}
+module_exit(mempress_timers_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Test memory pressure from timers");
--
2.55.0.691.gc56d675ccc-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 2/3] selftests: mm: add script to induce userspace memory contention
2026-08-19 7:05 [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer Jason Miu
2026-08-19 7:05 ` [PATCH 1/3] lib/test_mempress_timer: add module to generate kernel allocation pressure Jason Miu
@ 2026-08-19 7:05 ` Jason Miu
2026-08-19 7:05 ` [PATCH 3/3] selftests: mm: add script for memory allocation stall test Jason Miu
2026-08-21 0:03 ` [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer Andrew Morton
3 siblings, 0 replies; 7+ messages in thread
From: Jason Miu @ 2026-08-19 7:05 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Shuah Khan, David Rientjes,
Shakeel Butt
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Greg Thelen, linux-kernel,
linux-mm, linux-kselftest, Jason Miu
Introduce page_alloc_stall_pressure.py, a userspace workload generator
designed to saturate available system memory and force high-frequency
page swapping.
Implement a dual-process architecture:
1. Map a specified percentage of system memory into a primary memory
hogger process to establish a low-watermark state.
2. Spawn concurrent worker processes to continuously overcommit the
remaining memory and touch anonymous pages, driving rapid page
replacement activity.
Provide this script to orchestrate artificial userspace memory demands.
Note: Execution requires the external memtoy binary
(https://github.com/kosaki/memtoy) to handle underlying anonymous memory
block mappings.
Signed-off-by: Jason Miu <jasonmiu@google.com>
---
.../selftests/mm/page_alloc_stall_pressure.py | 235 ++++++++++++++++++
1 file changed, 235 insertions(+)
create mode 100644 tools/testing/selftests/mm/page_alloc_stall_pressure.py
diff --git a/tools/testing/selftests/mm/page_alloc_stall_pressure.py b/tools/testing/selftests/mm/page_alloc_stall_pressure.py
new file mode 100644
index 000000000000..5e95a2355198
--- /dev/null
+++ b/tools/testing/selftests/mm/page_alloc_stall_pressure.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+"""
+page_alloc_stall_pressure.py
+
+Executes a multi-threaded userspace workload to consume and touch anonymous
+memory. It serves as a workload generator to create system memory contention.
+
+Mechanism:
+1. Instantiates a primary memory hogger process to occupy a predetermined
+ percentage of system memory, mimicking a low-watermark memory state.
+2. Spawns multiple concurrent worker processes to intentionally overcommit the
+ remaining memory, generating high-frequency page replacement and swapping
+ activity.
+
+Parameters:
+ - hog_percent: Percentage of available system memory to lock strictly
+ inside the primary memory hogger process. (Default: 50.0)
+ - num_workers: Number of concurrent worker processes mapping anonymous
+ memory. (Default: 64)
+ - overcommit_ratio: Ratio of remaining free memory intentionally overcommitted
+ and mapped across all workers. (Default: 2.5)
+ - memtoy_path: Local filesystem path to the compiled memtoy binary.
+ (Source available at: https://github.com/kosaki/memtoy)
+ The binary is operated via stdin and must support commands
+ to allocate, map, and continuously touch anonymous memory
+ regions. (Default: "memtoy/memtoy")
+
+Usage:
+ python3 page_alloc_stall_pressure.py <hog_percent> <num_workers> \
+ <overcommit_ratio> <memtoy_path>
+"""
+
+import os
+import sys
+import subprocess
+import time
+import threading
+
+def get_mem_free_bytes():
+ with open("/proc/meminfo") as f:
+ for line in f:
+ if line.startswith("MemFree:"):
+ return int(line.split()[1]) * 1024
+ return 0
+
+def get_cgroup_root():
+ # Check for both v2 and v1.
+ if os.path.exists("/sys/fs/cgroup/cgroup.controllers"):
+ return "/sys/fs/cgroup"
+ if os.path.exists("/sys/fs/cgroup/memory"):
+ return "/sys/fs/cgroup/memory"
+ if os.path.exists("/dev/cgroup/memory"):
+ return "/dev/cgroup/memory"
+
+ raise FileNotFoundError("Neither Cgroup v1 nor v2 controllers found.")
+
+def create_memcg(cg_name):
+ cg_root = get_cgroup_root()
+ path = os.path.join(cg_root, cg_name)
+ try:
+ os.makedirs(path, exist_ok=True)
+ except Exception as e:
+ print(f"Failed to create cgroup {path}: {e}")
+
+def remove_memcg(cg_name):
+ cg_root = get_cgroup_root()
+ path = os.path.join(cg_root, cg_name)
+ try:
+ os.rmdir(path)
+ except Exception as e:
+ print(f"Failed to remove cgroup {path}: {e}")
+
+def run_mem_hogger(cg_name, size_bytes, memtoy_path):
+ cg_root = get_cgroup_root()
+ try:
+ full_cg_path = os.path.join(cg_root, cg_name)
+ def assign_memcg():
+ pid = os.getpid()
+ with open(os.path.join(full_cg_path, "cgroup.procs"), "w") as f:
+ f.write(str(pid))
+ # Protect it from the OOM killing
+ with open(f"/proc/{pid}/oom_score_adj", "w") as f:
+ f.write("-1000")
+
+ p = subprocess.Popen([memtoy_path],
+ preexec_fn=assign_memcg,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
+
+ size_mb = int(size_bytes / 1024 / 1024)
+
+ p.stdin.write(f"anon region {size_mb}m\n")
+ p.stdin.write("map region\n")
+ p.stdin.write("lock region\n") # Do not swap out the hogged memory
+ p.stdin.write("touch region write 1\n")
+ p.stdin.flush()
+
+ for line in p.stdout:
+ if "touched" in line:
+ print(f"Mem Hogger (PID {p.pid}) allocated {size_mb} MB RAM.")
+ break
+
+ return p
+ except Exception as e:
+ print(f"Failed to run Mem Hogger: {e}")
+
+class WorkerThread(threading.Thread):
+ def __init__(self, name, cg_name, size_bytes, memtoy_path):
+ super().__init__(name=name)
+ self.cg_name = cg_name
+ self.size_bytes = size_bytes
+ self.process = None
+ self.daemon = True
+ self.should_stop = False
+ self.memtoy_path = memtoy_path
+
+ def run(self):
+ full_cg_path = os.path.join(get_cgroup_root(), self.cg_name)
+ def assign_memcg():
+ with open(os.path.join(full_cg_path, "cgroup.procs"), "w") as f:
+ f.write(str(os.getpid()))
+
+ # Allocating a region of memory with size_bytes.
+ while not self.should_stop:
+ try:
+ self.process = subprocess.Popen([self.memtoy_path],
+ preexec_fn=assign_memcg,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ universal_newlines=True)
+ size_mb = int(self.size_bytes / 1024 / 1024)
+
+ self.process.stdin.write(f"anon region {size_mb}m\n")
+ self.process.stdin.write("map region\n")
+ self.process.stdin.write("touch region write 1\n")
+ self.process.stdin.flush()
+
+ print(f"Worker {self.name} (PID {self.process.pid}) allocated {size_mb} MB RAM.")
+
+ while True:
+ try:
+ # Access the newly allocated memory continuously.
+ # If it get killed during stdin write, it is ok.
+ self.process.stdin.write("touch region read\n")
+ self.process.stdin.flush()
+ except (BrokenPipeError, ValueError):
+ break
+
+ for line in self.process.stdout:
+ if "touched" in line:
+ break
+
+ if self.process.poll() is not None:
+ break
+
+ except Exception as e:
+ print(f"Worker {self.name} process terminated: {e}. Respawning.")
+ if self.process:
+ try:
+ self.process.terminate()
+ except Exception:
+ pass
+ self.process = None
+ time.sleep(0.1) # Don't respawn too fast
+
+def main(hog_percent, num_workers, overcommit_ratio, memtoy_path):
+ free_mem_bytes = get_mem_free_bytes()
+ hogger_bytes = int(free_mem_bytes * (hog_percent / 100))
+ remain_bytes = free_mem_bytes - hogger_bytes
+
+ hogger_memcg_name = "mem_hogger"
+
+ print(f"Allocating {hogger_bytes} for the memory hogger.")
+
+ create_memcg(hogger_memcg_name)
+ hogger_process = run_mem_hogger(hogger_memcg_name, hogger_bytes, memtoy_path)
+
+ worker_size_bytes = int(remain_bytes / num_workers * overcommit_ratio)
+ print(f"Spawning {num_workers} with {worker_size_bytes} memory allocation each.")
+
+ threads = []
+ for i in range(num_workers):
+ worker_cg_name = f"worker_cg_{i}"
+ create_memcg(worker_cg_name)
+ t = WorkerThread(f"Worker_{i}", worker_cg_name, worker_size_bytes, memtoy_path)
+ threads.append(t)
+ t.start()
+ time.sleep(0.05)
+
+ try:
+ print("All memory loads are created. Will run for 10mins, or Ctrl-C to exit.")
+ start_time = time.time()
+ while time.time() - start_time < 600:
+ time.sleep(10)
+ except KeyboardInterrupt:
+ print("Got Ctrl-C. Exiting.")
+ finally:
+ print("Cleaning the processes and cgroups...")
+
+ if hogger_process:
+ try:
+ hogger_process.terminate()
+ if hogger_process.stdin: hogger_process.stdin.close()
+ if hogger_process.stdout: hogger_process.stdout.close()
+ hogger_process.wait()
+ except Exception:
+ pass
+
+ for t in threads:
+ t.should_stop = True
+ if t.process:
+ try:
+ t.process.terminate()
+ if t.process.stdin: t.process.stdin.close()
+ if t.process.stdout: t.process.stdout.close()
+ t.process.wait()
+ except Exception:
+ pass
+
+ # let the kernel settle down
+ time.sleep(1)
+
+ remove_memcg(hogger_memcg_name)
+ for i in range(num_workers):
+ remove_memcg(f"worker_cg_{i}")
+
+ print("Cleanup done.")
+
+ return 0
+
+if __name__ == "__main__":
+ hog_percent = float(sys.argv[1]) if len(sys.argv) > 1 else 50.0
+ num_workers = int(sys.argv[2]) if len(sys.argv) > 2 else 64
+ overcommit_ratio = float(sys.argv[3]) if len(sys.argv) > 3 else 2.5
+ memtoy_path = sys.argv[4] if len(sys.argv) > 4 else "memtoy/memtoy"
+ sys.exit(main(hog_percent, num_workers, overcommit_ratio, memtoy_path))
--
2.55.0.691.gc56d675ccc-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 3/3] selftests: mm: add script for memory allocation stall test
2026-08-19 7:05 [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer Jason Miu
2026-08-19 7:05 ` [PATCH 1/3] lib/test_mempress_timer: add module to generate kernel allocation pressure Jason Miu
2026-08-19 7:05 ` [PATCH 2/3] selftests: mm: add script to induce userspace memory contention Jason Miu
@ 2026-08-19 7:05 ` Jason Miu
2026-08-21 0:03 ` [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer Andrew Morton
3 siblings, 0 replies; 7+ messages in thread
From: Jason Miu @ 2026-08-19 7:05 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Shuah Khan, David Rientjes,
Shakeel Butt
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Greg Thelen, linux-kernel,
linux-mm, linux-kselftest, Jason Miu
Integrate page_alloc_stall.sh, a script to synchronize userspace and
kernelspace workloads to reproduce page allocation stalls.
Provision a synthetic loopback zswap block device, inject the
test_mempress_timer.ko module to generate atomic page allocations
inside kernel timers, and execute the page_alloc_stall_pressure.py
payload to induce system-wide swap activity.
Signed-off-by: Jason Miu <jasonmiu@google.com>
---
.../testing/selftests/mm/page_alloc_stall.sh | 80 +++++++++++++++++++
1 file changed, 80 insertions(+)
create mode 100644 tools/testing/selftests/mm/page_alloc_stall.sh
diff --git a/tools/testing/selftests/mm/page_alloc_stall.sh b/tools/testing/selftests/mm/page_alloc_stall.sh
new file mode 100644
index 000000000000..3ece026fd680
--- /dev/null
+++ b/tools/testing/selftests/mm/page_alloc_stall.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# page_alloc_stall.sh
+#
+# Orchestrator script to generate concurrent userspace and kernelspace
+# memory allocation pressure over a synthetic zswap backing block device
+# to artificially induce systemic memory contention.
+#
+# Dependencies:
+# - External memtoy binary (Source: https://github.com/kosaki/memtoy)
+#
+# Usage:
+# ./page_alloc_stall.sh [hog_percent=80] [workers=64] [overcommit=2.5] \
+# [swap_gb=10%_RAM] [memtoy_path="memtoy/memtoy"]
+
+set -e
+
+SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
+PYTHON_CMD=${PYTHON_CMD:-python3}
+
+HOG_PERCENT=${1:-80}
+NUM_WORKERS=${2:-64}
+OVERCOMMIT=${3:-2.5}
+TARGET_SWAP_GB=$4
+MEMTOY_PATH=${5:-"memtoy/memtoy"}
+
+function total_mem_gb()
+{
+ awk '/MemTotal/ {print int($2 / 1024 / 1024)}' /proc/meminfo
+}
+
+function enable_zswap_file()
+{
+ local swap_size_gb=$1
+
+ fallocate -l ${swap_size_gb}G "$SCRIPT_DIR/swap.img"
+
+ LOOP_DEV=$(losetup -f --show "$SCRIPT_DIR/swap.img")
+ mkswap $LOOP_DEV
+ swapon $LOOP_DEV
+
+ echo 1 > /sys/module/zswap/parameters/enabled
+}
+
+function cleanup()
+{
+ echo "Tearing down the test..."
+ if lsmod | grep -q test_mempress_timer; then
+ rmmod test_mempress_timer || echo "Failed to rmmod mempress_timer"
+ fi
+
+ # Check if loop dev was actually created before trying to destroy it
+ if [ -n "$LOOP_DEV" ]; then
+ swapoff $LOOP_DEV 2>/dev/null || true
+ losetup -d $LOOP_DEV 2>/dev/null || true
+ fi
+ rm -f "$SCRIPT_DIR/swap.img"
+}
+
+trap cleanup EXIT
+
+if [ -z "$TARGET_SWAP_GB" ]; then
+ # use 10% of the total mem for zswap file by default.
+ TARGET_SWAP_GB=$(($(total_mem_gb) / 10))
+fi
+
+enable_zswap_file $TARGET_SWAP_GB
+
+KROOT=$(cd "$SCRIPT_DIR/../../../.." && pwd)
+KO_PATH="$KROOT/lib/test_mempress_timer.ko"
+
+if [ "$KO_PATH" ]; then
+ insmod "$KO_PATH"
+else
+ echo "WARNING: $KO_PATH not found! Aborting the test."
+ exit 1
+fi
+
+"$PYTHON_CMD" "$SCRIPT_DIR/page_alloc_stall_pressure.py" $HOG_PERCENT $NUM_WORKERS $OVERCOMMIT $MEMTOY_PATH
--
2.55.0.691.gc56d675ccc-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer
2026-08-19 7:05 [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer Jason Miu
` (2 preceding siblings ...)
2026-08-19 7:05 ` [PATCH 3/3] selftests: mm: add script for memory allocation stall test Jason Miu
@ 2026-08-21 0:03 ` Andrew Morton
2026-08-21 9:53 ` David Hildenbrand (Arm)
3 siblings, 1 reply; 7+ messages in thread
From: Andrew Morton @ 2026-08-21 0:03 UTC (permalink / raw)
To: Jason Miu
Cc: David Hildenbrand, Shuah Khan, David Rientjes, Shakeel Butt,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Greg Thelen, linux-kernel,
linux-mm, linux-kselftest
On Wed, 19 Aug 2026 00:05:35 -0700 Jason Miu <jasonmiu@google.com> wrote:
> Background
> ==========
> Under severe system memory pressure, system unresponsiveness often
> occurs due to page allocation stalls. In commit 94e0bcde055e ("mm,
> page_alloc: reintroduce page allocation stall warning"), David Rientjes
> introduced a warning mechanism to emit a kernel log when a page
> allocation takes longer than 10 seconds. This log is used to correlate
> a frozen system with the system memory state at the time of failure.
>
> To further debug and analyze these allocation stalls, we need a
> reproducible test case. This patch series introduces a new selftest
> designed to artificially mimic the severe memory pressure scenarios
> seen in production, allowing us to observe the resulting allocation
> stalls.
Interesting.
> lib/Kconfig.debug | 11 +
> lib/Makefile | 1 +
> lib/test_mempress_timer.c | 140 +++++++++++
> .../testing/selftests/mm/page_alloc_stall.sh | 80 ++++++
> .../selftests/mm/page_alloc_stall_pressure.py | 235 ++++++++++++++++++
> 5 files changed, 467 insertions(+)
> create mode 100644 lib/test_mempress_timer.c
> create mode 100644 tools/testing/selftests/mm/page_alloc_stall.sh
> create mode 100644 tools/testing/selftests/mm/page_alloc_stall_pressure.py
Nothing fits very well, does it?
selftests is for quick tests which are run by run_kselftest.sh. You
had to place it in selftests because there isn't anywhere obvious for
it to live.
So I suggest a brand new tools/testing/stresstests/mm. If we create
this, people will jump on it and start adding things which presently
reside in their personal collections.
I can't say I like "mempress". Is "memory_pressure" too wordy?
All of lib/test*.c shouldn't be in lib/. lib/ is for library code!
Again, we put them there because people are shy about doing mkdir.
Sashiko said hello:
https://sashiko.dev/#/patchset/20260819070538.2404983-1-jasonmiu@google.com
In [patch 1/3], s/__GFP_NOFAIL// and s/cond_resched()//.
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer
2026-08-21 0:03 ` [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer Andrew Morton
@ 2026-08-21 9:53 ` David Hildenbrand (Arm)
2026-08-21 10:44 ` Lorenzo Stoakes (ARM)
0 siblings, 1 reply; 7+ messages in thread
From: David Hildenbrand (Arm) @ 2026-08-21 9:53 UTC (permalink / raw)
To: Andrew Morton, Jason Miu
Cc: Shuah Khan, David Rientjes, Shakeel Butt, Lorenzo Stoakes,
Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Greg Thelen, linux-kernel,
linux-mm, linux-kselftest
On 8/21/26 02:03, Andrew Morton wrote:
> On Wed, 19 Aug 2026 00:05:35 -0700 Jason Miu <jasonmiu@google.com> wrote:
>
>> Background
>> ==========
>> Under severe system memory pressure, system unresponsiveness often
>> occurs due to page allocation stalls. In commit 94e0bcde055e ("mm,
>> page_alloc: reintroduce page allocation stall warning"), David Rientjes
>> introduced a warning mechanism to emit a kernel log when a page
>> allocation takes longer than 10 seconds. This log is used to correlate
>> a frozen system with the system memory state at the time of failure.
>>
>> To further debug and analyze these allocation stalls, we need a
>> reproducible test case. This patch series introduces a new selftest
>> designed to artificially mimic the severe memory pressure scenarios
>> seen in production, allowing us to observe the resulting allocation
>> stalls.
>
> Interesting.
>
>> lib/Kconfig.debug | 11 +
>> lib/Makefile | 1 +
>> lib/test_mempress_timer.c | 140 +++++++++++
>> .../testing/selftests/mm/page_alloc_stall.sh | 80 ++++++
>> .../selftests/mm/page_alloc_stall_pressure.py | 235 ++++++++++++++++++
>> 5 files changed, 467 insertions(+)
>> create mode 100644 lib/test_mempress_timer.c
>> create mode 100644 tools/testing/selftests/mm/page_alloc_stall.sh
>> create mode 100644 tools/testing/selftests/mm/page_alloc_stall_pressure.py
>
> Nothing fits very well, does it?
>
Why are we mixing python and sh?
>
>
> selftests is for quick tests which are run by run_kselftest.sh. You
> had to place it in selftests because there isn't anywhere obvious for
> it to live.
>
> So I suggest a brand new tools/testing/stresstests/mm. If we create
> this, people will jump on it and start adding things which presently
> reside in their personal collections.
There was recently a discussion around performance tests, and one thought was to
not carry these in the kernel tree at all.
Stresstests, not sure.
So agreed, that this shouldn't be an ordinary selftests (nothing we would want
to run autoamtically), but I am also not 100% sure about having performance /
stress tests in the kernel tree. It's all stuff we have to maintain and drag along.
>
>
>
> I can't say I like "mempress". Is "memory_pressure" too wordy?
>
>
>
> All of lib/test*.c shouldn't be in lib/. lib/ is for library code!
> Again, we put them there because people are shy about doing mkdir.
There were recent discussions where I raised the same. I would prefer if testing
kernel modules are somewhere in tools/testing/ if possible.
--
Cheers,
David
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer
2026-08-21 9:53 ` David Hildenbrand (Arm)
@ 2026-08-21 10:44 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 7+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-08-21 10:44 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Jason Miu, Shuah Khan, David Rientjes,
Shakeel Butt, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Greg Thelen, linux-kernel,
linux-mm, linux-kselftest
On Fri, Aug 21, 2026 at 11:53:57AM +0200, David Hildenbrand (Arm) wrote:
> On 8/21/26 02:03, Andrew Morton wrote:
> > On Wed, 19 Aug 2026 00:05:35 -0700 Jason Miu <jasonmiu@google.com> wrote:
> >
> >> Background
> >> ==========
> >> Under severe system memory pressure, system unresponsiveness often
> >> occurs due to page allocation stalls. In commit 94e0bcde055e ("mm,
> >> page_alloc: reintroduce page allocation stall warning"), David Rientjes
> >> introduced a warning mechanism to emit a kernel log when a page
> >> allocation takes longer than 10 seconds. This log is used to correlate
> >> a frozen system with the system memory state at the time of failure.
> >>
> >> To further debug and analyze these allocation stalls, we need a
> >> reproducible test case. This patch series introduces a new selftest
> >> designed to artificially mimic the severe memory pressure scenarios
> >> seen in production, allowing us to observe the resulting allocation
> >> stalls.
> >
> > Interesting.
> >
> >> lib/Kconfig.debug | 11 +
> >> lib/Makefile | 1 +
> >> lib/test_mempress_timer.c | 140 +++++++++++
> >> .../testing/selftests/mm/page_alloc_stall.sh | 80 ++++++
> >> .../selftests/mm/page_alloc_stall_pressure.py | 235 ++++++++++++++++++
> >> 5 files changed, 467 insertions(+)
> >> create mode 100644 lib/test_mempress_timer.c
> >> create mode 100644 tools/testing/selftests/mm/page_alloc_stall.sh
> >> create mode 100644 tools/testing/selftests/mm/page_alloc_stall_pressure.py
> >
> > Nothing fits very well, does it?
> >
>
> Why are we mixing python and sh?
Oh HELL no.
Only C in the selftests please.
And python is replete with 'getting it to run locally' issues. I have venv
PTSD...
>
> >
> >
> > selftests is for quick tests which are run by run_kselftest.sh. You
> > had to place it in selftests because there isn't anywhere obvious for
> > it to live.
> >
> > So I suggest a brand new tools/testing/stresstests/mm. If we create
> > this, people will jump on it and start adding things which presently
> > reside in their personal collections.
>
> There was recently a discussion around performance tests, and one thought was to
> not carry these in the kernel tree at all.
>
> Stresstests, not sure.
>
> So agreed, that this shouldn't be an ordinary selftests (nothing we would want
> to run autoamtically), but I am also not 100% sure about having performance /
> stress tests in the kernel tree. It's all stuff we have to maintain and drag along.
Agreed. Separate please.
Stress tests are just asking for flakes :) they are useful + important but
something different.
>
> >
> >
> >
> > I can't say I like "mempress". Is "memory_pressure" too wordy?
> >
> >
> >
> > All of lib/test*.c shouldn't be in lib/. lib/ is for library code!
> > Again, we put them there because people are shy about doing mkdir.
>
> There were recent discussions where I raised the same. I would prefer if testing
> kernel modules are somewhere in tools/testing/ if possible.
Yes.
>
>
> --
> Cheers,
>
> David
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 7+ messages in thread