* [RFC PATCH 0/3] selftests: mm: introduce page allocation stall reproducer
@ 2026-08-19 7:05 Jason Miu
2026-08-19 7:05 ` [PATCH 1/3] lib/test_mempress_timer: add module to generate kernel allocation pressure Jason Miu
` (3 more replies)
0 siblings, 4 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
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.
Patch Series Architecture
=========================
This selftest creates memory contention by saturating both userspace
and kernelspace:
1. Kernelspace: A generic kernel module (test_mempress_timer.ko) binds
timers to every online CPU, continuously executing atomic page
allocations (GFP_ATOMIC | __GFP_NOWARN) from a timer. This behavior
simulates a massive influx of networking allocations. However, such
stalls can be triggered by any mechanism that allocates below the
per-zone min watermarks, which forces the page allocator to
continuously loop when servicing user allocations.
2. Userspace: A Python script creates a memory pressure scenario by
spawning a primary memory hogger process to lock a percentage of
system memory, driving the system to a low-watermark state. It
simultaneously spawns concurrent worker processes that intentionally
overcommit the remaining memory to churn anonymous pages.
3. Orchestration: A Bash script constructs a loopback zswap device and
coordinates the execution of both workloads. The memory pressure
situation is designed to last for 15 minutes by default.
Experimental Results
====================
We executed this test on a bare-metal node with the following
specifications:
- CPU: 224-core Intel(R) Xeon(R) Platinum 8481C (2 Sockets, 2 NUMA)
- Memory: 503 GiB RAM
Test parameters used in the orchestrator script:
./page_alloc_stall.sh 80 64 2.5 50 memtoy/memtoy
- 80: Primary memory hogger limits memory by locking 80% of RAM.
- 64: Spawns 64 concurrent memory worker processes.
- 2.5: Workers overcommit the remaining freely available memory by 2.5x.
- 50: Allocates 50 GiB for the synthetic loopback zswap device.
- memtoy/memtoy: Path to the external binary used to allocate memory.
During execution, we mimicked the memory pressure scenario observed on
production systems. Under these conditions, we observed page allocation
stalls occurring across various processes, highlighting the resulting
system unresponsiveness:
[ 7467.002149] watchdog: BUG: soft lockup - CPU#156 stuck for 21s!
...
[ 7561.805279] cron: page allocation stall for 566 secs...
[ 7668.275609] systemd: page allocation stall for 673 secs...
Test Execution
==============
This is submitted as an RFC. We are actively seeking feedback from the
community on this testing methodology and integration.
The primary objective of this selftest is to establish a measurable
baseline for page allocator responsiveness. This baseline will be used
to validate upcoming improvements to the allocator, and will serve as
a permanent framework to prevent future regressions.
To run the selftest:
1. Compile the kernel with CONFIG_TEST_MEMPRESS_TIMER=m,
CONFIG_MEMCG=y, and CONFIG_ZSWAP=y.
2. Execute with default parameter values:
./tools/testing/selftests/mm/page_alloc_stall.sh
WARNING: This selftest is explicitly designed to exhaust system
resources and heavily saturate the CPU. The machine will become highly
unresponsive. If executed remotely, active SSH/network connections are
expected to drop.
Credits and Dependencies
========================
This test depends on an external memory allocation tool, memtoy,
written by KOSAKI Motohiro (https://github.com/kosaki/memtoy), which we
utilize for handling the underlying anonymous mappings.
We would like to extend our thankfulness to Shakeel Butt
<shakeel.butt@linux.dev> for graciously allowing us to reuse some of
his original ideas and module implementations in this test.
---
Jason Miu (3):
lib/test_mempress_timer: add module to generate kernel allocation
pressure
selftests: mm: add script to induce userspace memory contention
selftests: mm: add script for memory allocation stall test
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
--
2.55.0.691.gc56d675ccc-goog
^ permalink raw reply [flat|nested] 7+ messages in thread
* [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
end of thread, other threads:[~2026-08-21 10:44 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [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
2026-08-21 9:53 ` David Hildenbrand (Arm)
2026-08-21 10:44 ` Lorenzo Stoakes (ARM)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox