From nobody Mon Sep 28 18:33:56 2026 Received: from mail-pj1-f71.google.com (mail-pj1-f71.google.com [209.85.216.71]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 72F8D374192 for ; Wed, 19 Aug 2026 07:05:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.71 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787123151; cv=none; b=LgyiC8K63McRbpfJc4LsRsgwR8WJ71b+Wa+IukSR3uuoUB8/i20uED2zSSvJHywEzgHQq8JyepArL617BXpnszIJOtHwJDarEMWvSsBHvcg/TZttkJnYmwjisBCX8AWVZLTIBy6gzHUzziNPOMierCw1DfZwVfv1aAqK/3RAp0E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787123151; c=relaxed/simple; bh=PqYctq9OkbGbad376jXZa8QYf/yrQA5UAlv/R/RffpI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=uSs2IOTZQgn0yY7RURlFqNbNfKHYBTUgBbCxtUYWiHeyl49Xf3FbiKUXuSmkZAfNBJeJaaOx1ISvmVkbOxTP9Ak741mvOhfTLbl7TL6hcfG5E6+0zv83+LmGBtTPsDolLivVRYTvKoP6tVMSfo3ezlfevX69isaKKlRTGYFWOho= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--jasonmiu.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=NGG8KOjE; arc=none smtp.client-ip=209.85.216.71 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--jasonmiu.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="NGG8KOjE" Received: by mail-pj1-f71.google.com with SMTP id 98e67ed59e1d1-38f5ac7354dso1069270a91.1 for ; Wed, 19 Aug 2026 00:05:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1787123149; x=1787727949; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=r13rbsEeTBQUt7C0+umCAD05cQ1RPgR2iUrqqxSlQbQ=; b=NGG8KOjE1gfzjPuxH+IHtKVRCI95Rg5cXD0FlrBLDsilVxsyjyNlOvaF6nhC535uQI tvLv6Whk8WrioYHUAmgt7umEcGeToRuAGCeP96pNm6ZYkdUxpZ5x5tujdbPj7niubnch MGPKhyqlWBWP8/LxAB15QyzydbWuZvMYZI02J3gHx0l3g6xwUSBlabihK2h6+Pxz4YhL GvVv94FzPmp/9K6kYejdMObBac16uzve3fEGrigQYHP+pmxE0TASys77YPm3oOJWJNkZ 0TVAIm6VsM5xz6skR1EECoAiKXByVKyLqwWAoN2r3qzsEtxHi+TtqA3JDbGYbeO5GPCV w0gA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787123149; x=1787727949; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=r13rbsEeTBQUt7C0+umCAD05cQ1RPgR2iUrqqxSlQbQ=; b=LLKxpN41JeDk5Lqj7G0YMrUOw86hLWd0SahhqEw2Cof1YV6VB6mf5THm8mAetEDorT c22b3I7g0JxldgUt8KxCLHlKlQibUcm+yUubQuvEKN23RUI0DjTMwat6bc+ad6a2h2PB byO6trHA3akDJPpAI5kG60stcKtCc+mZs7laauHJGTqJnT5d90zVMf2AQfkfjpPvLWfl 5E7YJ/eGor+6YUJlpWoZQMQNOCBcHZyMN/n/r1sbEVbgd9kLtUEyc+Cbdz+1mgHJ7CPl zQ2H7/JdeUHCeee3zrAr93f9CbaPPtLGinjy2GBJtBTFolktnXFjB1d+/PgFHKY6Noqi xkAg== X-Forwarded-Encrypted: i=1; AHgh+RqdRD1KUBxpQx/Idgc2lxVI6Hsccn2AbwpfvXMOg4ZWxmLK3m8JY0UXDwRuHA+ReKx1Ig4MaR1AFQBkTXs=@vger.kernel.org X-Gm-Message-State: AOJu0Yz8yV+7GwZqwyq9TDN2TbhnBK1M3EXszSYQFO5aZL8SFCGpJlMQ T2wyX5Nthx7qNKkWqcccrdxxyq3ktTBH9070ftHhJCxgM2W3CFp9drL7Q9KD9RzJVAVq+NUIh21 uU2X8ccN7rnHwrg== X-Received: from dybhl1.prod.google.com ([2002:a05:7301:2701:b0:327:eee:4918]) (user=jasonmiu job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:4a46:b0:37f:e326:6557 with SMTP id 98e67ed59e1d1-3958083fc60mr4701528a91.4.1787123148550; Wed, 19 Aug 2026 00:05:48 -0700 (PDT) Date: Wed, 19 Aug 2026 00:05:36 -0700 In-Reply-To: <20260819070538.2404983-1-jasonmiu@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260819070538.2404983-1-jasonmiu@google.com> X-Mailer: git-send-email 2.55.0.737.g08866a6d13-goog Message-ID: <20260819070538.2404983-2-jasonmiu@google.com> Subject: [PATCH 1/3] lib/test_mempress_timer: add module to generate kernel allocation pressure From: Jason Miu 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@vger.kernel.org, linux-mm@kvack.org, linux-kselftest@vger.kernel.org, Jason Miu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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 =20 If unsure, say N. =20 +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 allocato= r" 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 +=3D $(call cc-disable-warning, unuse= d-but-set-variable) UBSAN_SANITIZE_test_ubsan.o :=3D y obj-$(CONFIG_TEST_KSTRTOX) +=3D test-kstrtox.o obj-$(CONFIG_TEST_LKM) +=3D test_module.o +obj-$(CONFIG_TEST_MEMPRESS_TIMER) +=3D test_mempress_timer.o obj-$(CONFIG_TEST_VMALLOC) +=3D test_vmalloc.o obj-$(CONFIG_TEST_WORKQUEUE) +=3D test_workqueue.o obj-$(CONFIG_TEST_RHASHTABLE) +=3D 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=3D900 allocs_per_itera= tion=3D1000 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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 =3D 2; + +static unsigned long allocs_per_iteration =3D 1000; +module_param(allocs_per_iteration, long, 0444); + +static unsigned long test_duration_secs =3D 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 =3D 0; i < allocs_per_iteration; i++) { + page =3D 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 =3D timer_container_of(test, timer, timer); + bool is_expired =3D (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 =3D jiffies + interval; + add_timer_on(&test->timer, test->cpu); + } else if (is_expired) { + pr_info_once("Duration (%lu secs) reached, stopping.\n", test_duration_s= ecs); + } +} + +static int __init mempress_timers_init(void) +{ + int cpu; + struct mempress_timer *test; + + if (test_duration_secs > 0) + end_jiffies =3D jiffies + (test_duration_secs * HZ); + + for_each_online_cpu(cpu) { + test =3D 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 =3D cpu; + + /* For start, use 90 seconds. */ + test->timer.expires =3D 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 =3D 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"); --=20 2.55.0.691.gc56d675ccc-goog From nobody Mon Sep 28 18:33:56 2026 Received: from mail-pj1-f69.google.com (mail-pj1-f69.google.com [209.85.216.69]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 006663D45D0 for ; Wed, 19 Aug 2026 07:05:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.69 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787123156; cv=none; b=bgZHyLmPxi9P1txcJnvQBGLS7PqpMj9tgdyxtf7r7fWZwdwxLXAWUWbkJLN14l0tNIlp1SR60w2F7U0YS3fLefcjOLIghrqnXrkf2/huj788QaOyzCcPNUyhJxNyspcodLOez1Guno5i74RdgCJbeLRqM9h1x6b/Ws0N736+8+E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787123156; c=relaxed/simple; bh=YYBG7r4hTYfwDkA04iF02M3b5ik0rkl+GUdJDpc7JyM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=fsn1FlDx3ZKY+4AXXbsAGQ4VAdA9wd/sc/M9JU4zlsW/8H18C9paWKD2a83Z+R/cp0J1m+XWLviMxcZpR/ncLdX7E2rGaHE88qsRoiwOCawwbT4lcRFtnGBnAXVvPHIysM5yNbWC4U2T4UVid7XxkRicMH9hGwanaTluXc3zOOU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--jasonmiu.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Jyhrmk+p; arc=none smtp.client-ip=209.85.216.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--jasonmiu.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Jyhrmk+p" Received: by mail-pj1-f69.google.com with SMTP id 98e67ed59e1d1-38ea32e57e2so1597373a91.1 for ; Wed, 19 Aug 2026 00:05:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1787123154; x=1787727954; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=E95q1x2DT6McfvZlrwH4oQfSk4gruvw+vn9eniPurXw=; b=Jyhrmk+pWhWdfb+L3NT4vnH3k9ipUUroDji0U181dzzWDHcvAHZR2/uyNnMRBqvwL2 OTa5EKh7ZyMXVgrH9oc++8euFQo2z06wF7usMm3O8SqtoFkSzF32y6wi6kEYR/lEY+pT 14sHoDf57kjcSNDEBo87SpNdSsnqAjZq+9/u1li0wC74bh3QtP1CkMP9o9ehZk9q8ZNS UxxGN6DcYmlgvwXWYNHipfeIBcVXJ/2ietpn1B5R4IdeuVUqR+8KZRiiDY5l5AEbulny QXxUAMY225JYPd3R6q5q0WMrhmcpqkG49O0dkGPzRHJy3TOffE5aghDO8H1DE1Z8aPO8 ng5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787123154; x=1787727954; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=E95q1x2DT6McfvZlrwH4oQfSk4gruvw+vn9eniPurXw=; b=geLGWl2UAmDw6m0UMgBorABwI79+249a6baz9jyDvLbqkxQCYKfo8qP6nsyM5bibYS vXY8mEd8/wX4pK7BNYL/iZBGMmDOL8Ygo/fGqXJtYJ2RVWD7g3OrVMgCNeXhbnXKNeRe cI3CL5fY3MXXI1ALHMtTMHKWEvzW6k1HHalEATBMD0CWiIzkPGwi326a6Z7pyHk/FbKh k9/2BFwQtiv9KfCDfDLXSl/uexqBeSNx4a5icJr5Ofm3EainwDuds4DFF2YaC9j5tNDW O9FXWcyUH6jbFKOGcPCB0pBovZqHPe4truMVVCr+tEDI5AaELWRhTmqsQ3V3AlDjokM1 V2Og== X-Forwarded-Encrypted: i=1; AHgh+Roovm2umsh/GnOJOjyzgDn/pQfZRWAWXPOVSmr0aRBrM/cGyuG0Vl5nO2FMO6liTzaMkj/7bp/sgS1mktc=@vger.kernel.org X-Gm-Message-State: AOJu0Yxxrl4CVkb2pF6hueqQ2A8DgP5XIVpSGZ12f/d0db0BmotSkIYv Ap/s37I+umKIcFNUJV17J8nGhLwD5EoSqjlT3FsWNPzXlG3rgB/OpEpBj9jYxXEMZi7dmtG4r3u Jk3QOfpucDnjDqA== X-Received: from dyctz24.prod.google.com ([2002:a05:7301:9f18:b0:327:b132:c5a9]) (user=jasonmiu job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:5250:b0:381:9028:5945 with SMTP id 98e67ed59e1d1-3958125375amr5159730a91.14.1787123154083; Wed, 19 Aug 2026 00:05:54 -0700 (PDT) Date: Wed, 19 Aug 2026 00:05:37 -0700 In-Reply-To: <20260819070538.2404983-1-jasonmiu@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260819070538.2404983-1-jasonmiu@google.com> X-Mailer: git-send-email 2.55.0.737.g08866a6d13-goog Message-ID: <20260819070538.2404983-3-jasonmiu@google.com> Subject: [PATCH 2/3] selftests: mm: add script to induce userspace memory contention From: Jason Miu 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@vger.kernel.org, linux-mm@kvack.org, linux-kselftest@vger.kernel.org, Jason Miu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- .../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/tool= s/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 contenti= on. + +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 swappi= ng + activity. + +Parameters: + - hog_percent: Percentage of available system memory to lock strictly + inside the primary memory hogger process. (Default: 5= 0.0) + - num_workers: Number of concurrent worker processes mapping anonymo= us + memory. (Default: 64) + - overcommit_ratio: Ratio of remaining free memory intentionally overcomm= itted + 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/memto= y) + The binary is operated via stdin and must support com= mands + to allocate, map, and continuously touch anonymous me= mory + regions. (Default: "memtoy/memtoy") + +Usage: + python3 page_alloc_stall_pressure.py \ + +""" + +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 =3D get_cgroup_root() + path =3D os.path.join(cg_root, cg_name) + try: + os.makedirs(path, exist_ok=3DTrue) + except Exception as e: + print(f"Failed to create cgroup {path}: {e}") + +def remove_memcg(cg_name): + cg_root =3D get_cgroup_root() + path =3D 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 =3D get_cgroup_root() + try: + full_cg_path =3D os.path.join(cg_root, cg_name) + def assign_memcg(): + pid =3D 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 =3D subprocess.Popen([memtoy_path], + preexec_fn=3Dassign_memcg, + stdin=3Dsubprocess.PIPE, stdout=3Dsubprocess.= PIPE, universal_newlines=3DTrue) + + size_mb =3D 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 RA= M.") + 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=3Dname) + self.cg_name =3D cg_name + self.size_bytes =3D size_bytes + self.process =3D None + self.daemon =3D True + self.should_stop =3D False + self.memtoy_path =3D memtoy_path + + def run(self): + full_cg_path =3D 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 =3D subprocess.Popen([self.memtoy_path], + preexec_fn=3Dassign_memcg, + stdin=3Dsubprocess.PIPE, s= tdout=3Dsubprocess.PIPE, + universal_newlines=3DTrue) + size_mb =3D 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}) alloca= ted {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}. Respaw= ning.") + if self.process: + try: + self.process.terminate() + except Exception: + pass + self.process =3D None + time.sleep(0.1) # Don't respawn too fast + +def main(hog_percent, num_workers, overcommit_ratio, memtoy_path): + free_mem_bytes =3D get_mem_free_bytes() + hogger_bytes =3D int(free_mem_bytes * (hog_percent / 100)) + remain_bytes =3D free_mem_bytes - hogger_bytes + + hogger_memcg_name =3D "mem_hogger" + + print(f"Allocating {hogger_bytes} for the memory hogger.") + + create_memcg(hogger_memcg_name) + hogger_process =3D run_mem_hogger(hogger_memcg_name, hogger_bytes, mem= toy_path) + + worker_size_bytes =3D int(remain_bytes / num_workers * overcommit_rati= o) + print(f"Spawning {num_workers} with {worker_size_bytes} memory allocat= ion each.") + + threads =3D [] + for i in range(num_workers): + worker_cg_name =3D f"worker_cg_{i}" + create_memcg(worker_cg_name) + t =3D WorkerThread(f"Worker_{i}", worker_cg_name, worker_size_byte= s, 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 =3D 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 =3D 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__ =3D=3D "__main__": + hog_percent =3D float(sys.argv[1]) if len(sys.argv) > 1 else 50.0 + num_workers =3D int(sys.argv[2]) if len(sys.argv) > 2 else 64 + overcommit_ratio =3D float(sys.argv[3]) if len(sys.argv) > 3 else 2.5 + memtoy_path =3D sys.argv[4] if len(sys.argv) > 4 else "memtoy/memtoy" + sys.exit(main(hog_percent, num_workers, overcommit_ratio, memtoy_path)) --=20 2.55.0.691.gc56d675ccc-goog From nobody Mon Sep 28 18:33:56 2026 Received: from mail-pj1-f69.google.com (mail-pj1-f69.google.com [209.85.216.69]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A60A53D4118 for ; Wed, 19 Aug 2026 07:06:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.69 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787123162; cv=none; b=d7jx6EgKSslSMDgzsBsXYWkm1rDcXXXmmOakWUHRT47/9F0fGdhtwxhK5QKnyfh4uN9dshJMz+vLBtec09wNR1d+/i1S4jBbhU78XbAaH0fD86JpYOBXsTe2dDlltyb2wHayU9w60HwgBo8U2Z/jv+a8Q0mJgbtuuL8Am2bGX7U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787123162; c=relaxed/simple; bh=3vd1riOKO040WnRWKoxbpwmzVP+Y/Z51qvCLfNFRGWQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=KOAMzvLmOA2moS8p3k8GfI+cCJ/oMYE+ca/KlAt5SzEgh8fYth5vRhv9j4U2S+jxBA07XasxeG+qqRsAwjdPOY8v4CYvy10m8XU6iFWkm69Cq/P36hpXvWWzgmujafORpWf9iae1ibpsjNuJqZS2oIJo6ZpKnaEtraq+Syjv08A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--jasonmiu.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=vH5UcqO6; arc=none smtp.client-ip=209.85.216.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--jasonmiu.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="vH5UcqO6" Received: by mail-pj1-f69.google.com with SMTP id 98e67ed59e1d1-38e7ff7b375so1241450a91.1 for ; Wed, 19 Aug 2026 00:06:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1787123160; x=1787727960; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=ts4aq9qFq6CIZlsiXvQuHpSHiQajqVM1hB12vxK4HZs=; b=vH5UcqO6XPjQBMaHe4Wm9s5qQ4bqH84sX08qee+X9fFhuUCBUioQkYIh+WXFfanrYL ugERQIf7Uxrg3n1kemecq8A/r6omQMsZpSsVI0CqaKTkmtyjOAPPj7t0uA02buu+sfZt 3buGw+MSjq9RmtUscNc+ff1M77G9e+3RYn7cl9A4yyp6VV3owf2L5dULfa0r5TNFrQ1N 4OpBibmzV1aFa3NK4DtXpbHHhQlgQvFrxEv6V0HNQeSm3r8S/HM4rjcqK9wPSyZygWKD 6kgeigGKM24g/vH74xvChTb3mO7y79kJuUuR1Dm9HdAGRyvEWl5ifoOI93Lvmr339vEH lhLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787123160; x=1787727960; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ts4aq9qFq6CIZlsiXvQuHpSHiQajqVM1hB12vxK4HZs=; b=FsEVsuwzW8k1rtAn4wvkUXmc3VKFJRru/7Ky3KDsCkjpCpnLH6er4AIDY+fxriQasT XCbT90CG4k6LZQ3OrDWqF/MVSCL9pbgstev9Tm5Kaj6NGNt1Gf96GNvodo7xfpKIdklJ 14Advt6oQd9EWSzTMN+5yWl84yXL9/PXEuXO/1haDoUDD3q3A78F7Q4EZMpHg1j4WqMY tRFPIyYJ4iCCAXwvzLBEDtQ+oysFN46AhWwEmCDDp6fOE67pWBf8JOUJzXMzLqfzceMc FskNGWPpgwJmrWhq0lXIq9YqvDya+N25SwXynuex47fwsMILcINn5LOEBUXeYp4at7h8 jUqA== X-Forwarded-Encrypted: i=1; AHgh+Rp0CbMAhztztdFbJKgoyiDB3uj1bBlQImwtLyscDtXJFAyMuKYsybV1hQ3ryyPvMmT9vm/mC920qviv45c=@vger.kernel.org X-Gm-Message-State: AOJu0Yzf+bbQYWKVhxKvAdKpGDKQKcLx9fHMBBjhuoZjyCk7mzZ1zOvT Vkp8zTYqX5XbiXpqmsoQg62yu+q6TZljK8jLpAxbnJLS8lb2oyakrVrPMuWsjgT+D/SEp7OyLgi L5/8lW+Qs+S14WA== X-Received: from dyjt7.prod.google.com ([2002:a05:7300:7b07:b0:311:7d79:c37f]) (user=jasonmiu job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:4c8d:b0:38e:ad9d:1151 with SMTP id 98e67ed59e1d1-39580a4c6aamr4886911a91.4.1787123159758; Wed, 19 Aug 2026 00:05:59 -0700 (PDT) Date: Wed, 19 Aug 2026 00:05:38 -0700 In-Reply-To: <20260819070538.2404983-1-jasonmiu@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260819070538.2404983-1-jasonmiu@google.com> X-Mailer: git-send-email 2.55.0.737.g08866a6d13-goog Message-ID: <20260819070538.2404983-4-jasonmiu@google.com> Subject: [PATCH 3/3] selftests: mm: add script for memory allocation stall test From: Jason Miu 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@vger.kernel.org, linux-mm@kvack.org, linux-kselftest@vger.kernel.org, Jason Miu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- .../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=3D80] [workers=3D64] [overcommit=3D2.= 5] \ +# [swap_gb=3D10%_RAM] [memtoy_path=3D"memtoy/memtoy"] + +set -e + +SCRIPT_DIR=3D$(cd "$(dirname "$0")" && pwd) +PYTHON_CMD=3D${PYTHON_CMD:-python3} + +HOG_PERCENT=3D${1:-80} +NUM_WORKERS=3D${2:-64} +OVERCOMMIT=3D${3:-2.5} +TARGET_SWAP_GB=3D$4 +MEMTOY_PATH=3D${5:-"memtoy/memtoy"} + +function total_mem_gb() +{ + awk '/MemTotal/ {print int($2 / 1024 / 1024)}' /proc/meminfo +} + +function enable_zswap_file() +{ + local swap_size_gb=3D$1 + + fallocate -l ${swap_size_gb}G "$SCRIPT_DIR/swap.img" + + LOOP_DEV=3D$(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=3D$(($(total_mem_gb) / 10)) +fi + +enable_zswap_file $TARGET_SWAP_GB + +KROOT=3D$(cd "$SCRIPT_DIR/../../../.." && pwd) +KO_PATH=3D"$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 --=20 2.55.0.691.gc56d675ccc-goog