[LTP] [PATCH] memory: rewrite memcg_stress_test into C API
Andrea Cervesato
andrea.cervesato@suse.de
Wed Nov 5 10:43:21 CET 2025
From: Andrea Cervesato <andrea.cervesato@suse.com>
This test will stress the cgroup implementation by allocating the whole
free system memory inside multiple containers, one page at time.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
The previous test was buggy and up to failures. This new implementation
deletes shell script code and the C utility used by it, merging the code
into C API libray.
---
runtest/controllers | 3 +-
.../kernel/controllers/memcg/stress/.gitignore | 1 +
testcases/kernel/controllers/memcg/stress/Makefile | 6 +-
.../memcg/stress/memcg_process_stress.c | 96 -----------------
.../controllers/memcg/stress/memcg_stress01.c | 118 +++++++++++++++++++++
.../controllers/memcg/stress/memcg_stress_test.sh | 108 -------------------
6 files changed, 122 insertions(+), 210 deletions(-)
diff --git a/runtest/controllers b/runtest/controllers
index 93c52c439..d7377e94c 100644
--- a/runtest/controllers
+++ b/runtest/controllers
@@ -16,7 +16,8 @@ memcg_memsw_limit_in_bytes memcg_memsw_limit_in_bytes_test.sh
memcg_stat memcg_stat_test.sh
memcg_use_hierarchy memcg_use_hierarchy_test.sh
memcg_usage_in_bytes memcg_usage_in_bytes_test.sh
-memcg_stress memcg_stress_test.sh
+memcg_stress01 memcg_stress01 -n 1
+memcg_stress01 memcg_stress01 -n 150
memcg_control memcg_control_test.sh
# kselftest ports
diff --git a/testcases/kernel/controllers/memcg/stress/.gitignore b/testcases/kernel/controllers/memcg/stress/.gitignore
new file mode 100644
index 000000000..17df954ed
--- /dev/null
+++ b/testcases/kernel/controllers/memcg/stress/.gitignore
@@ -0,0 +1 @@
+memcg_stress01
diff --git a/testcases/kernel/controllers/memcg/stress/Makefile b/testcases/kernel/controllers/memcg/stress/Makefile
index a9678bf3b..8413e98f9 100644
--- a/testcases/kernel/controllers/memcg/stress/Makefile
+++ b/testcases/kernel/controllers/memcg/stress/Makefile
@@ -6,10 +6,6 @@ top_srcdir ?= ../../../../..
include $(top_srcdir)/include/mk/testcases.mk
-CPPFLAGS += -I$(abs_srcdir)/../../libcontrollers
-
-INSTALL_TARGETS := *.sh
-
LDLIBS += -lm
-include $(top_srcdir)/include/mk/generic_leaf_target.mk
+include $(top_srcdir)/include/mk/generic_trunk_target.mk
diff --git a/testcases/kernel/controllers/memcg/stress/memcg_process_stress.c b/testcases/kernel/controllers/memcg/stress/memcg_process_stress.c
deleted file mode 100644
index 422deaeee..000000000
--- a/testcases/kernel/controllers/memcg/stress/memcg_process_stress.c
+++ /dev/null
@@ -1,96 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (c) 2009 FUJITSU LIMITED
- * Author: Li Zefan <lizf@cn.fujitsu.com>
- */
-
-#include <sys/mman.h>
-#include <err.h>
-#include <math.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-int flag_exit;
-int flag_ready;
-
-int interval;
-unsigned long memsize;
-
-char **pages;
-int nr_page;
-
-void touch_memory(void)
-{
- int i;
-
- for (i = 0; i < nr_page; i++)
- pages[i][0] = 0xef;
-}
-
-void sigusr_handler(int __attribute__ ((unused)) signo)
-{
- int i;
- int pagesize;
-
- pagesize = getpagesize();
-
- nr_page = ceil((double)memsize / pagesize);
-
- pages = calloc(nr_page, sizeof(char *));
- if (pages == NULL)
- errx(1, "calloc failed");
-
- for (i = 0; i < nr_page; i++) {
- pages[i] = mmap(NULL, pagesize, PROT_WRITE | PROT_READ,
- MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
- if (pages[i] == MAP_FAILED)
- err(1, "map failed\n");
- }
-
- flag_ready = 1;
-}
-
-void sigint_handler(int __attribute__ ((unused)) signo)
-{
- flag_exit = 1;
-}
-
-int main(int argc, char *argv[])
-{
- char *end;
- struct sigaction sigint_action;
- struct sigaction sigusr_action;
-
- if (argc != 3)
- errx(1, "wrong argument num");
-
- memsize = strtoul(argv[1], &end, 10);
- if (*end != '\0')
- errx(1, "wrong memsize");
- memsize = memsize * 1024 * 1024;
-
- interval = atoi(argv[2]);
- if (interval <= 0)
- interval = 1;
-
- memset(&sigint_action, 0, sizeof(sigint_action));
- sigint_action.sa_handler = &sigint_handler;
- if (sigaction(SIGINT, &sigint_action, NULL))
- err(1, "sigaction(%s) failed", "SIGINT");
-
- memset(&sigusr_action, 0, sizeof(sigusr_action));
- sigusr_action.sa_handler = &sigusr_handler;
- if (sigaction(SIGUSR1, &sigusr_action, NULL))
- err(1, "sigaction(%s) failed", "SIGUSR1");
-
- while (!flag_exit) {
- sleep(interval);
-
- if (flag_ready)
- touch_memory();
- }
-
- return 0;
-}
diff --git a/testcases/kernel/controllers/memcg/stress/memcg_stress01.c b/testcases/kernel/controllers/memcg/stress/memcg_stress01.c
new file mode 100644
index 000000000..d59f60447
--- /dev/null
+++ b/testcases/kernel/controllers/memcg/stress/memcg_stress01.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * This test will stress the cgroup implementation by allocating the whole
+ * free system memory inside multiple containers, one page at time.
+ */
+
+#include <math.h>
+#include "tst_test.h"
+#include "tst_cgroup.h"
+
+#define MAX_CGROUPS 200
+#define INTERVAL 5
+#define RUNTIME (15 * 60)
+
+static char *str_cgroups_num;
+static int cgroups_num = 1;
+static int page_size;
+static long pages_num;
+
+static void run_child(struct tst_cg_group *cg_child)
+{
+ char **pages;
+
+ SAFE_CG_PRINTF(cg_child, "cgroup.procs", "%d", getpid());
+
+ page_size = getpagesize();
+ pages = SAFE_CALLOC(pages_num, sizeof(char *));
+
+ for (int i = 0; i < pages_num; i++) {
+ pages[i] = (char *)SAFE_MMAP(NULL, page_size,
+ PROT_WRITE | PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+ }
+
+ for (int i = 0; i < pages_num; i++)
+ *(pages[i]) = 0xef;
+}
+
+static void run(void)
+{
+ struct tst_cg_group *cg_child[cgroups_num];
+
+ for (int i = 0; i < cgroups_num; i++) {
+ cg_child[i] = tst_cg_group_mk(tst_cg, "ltp_memcg_stress_%d", i);
+
+ if (!SAFE_FORK()) {
+ run_child(cg_child[i]);
+ exit(0);
+ }
+
+ sleep(INTERVAL);
+
+ if (!tst_remaining_runtime()) {
+ tst_res(TINFO, "Reached maximum runtime");
+ break;
+ }
+ }
+
+ tst_reap_children();
+
+ for (int i = 0; i < cgroups_num; i++)
+ cg_child[i] = tst_cg_group_rm(cg_child[i]);
+
+ tst_res(TPASS, "Stress test has passed");
+}
+
+static void setup(void)
+{
+ unsigned long reserved_mem, mem_free, mem_avail, swap_free, mem_min;
+ unsigned long mem_per_proc;
+ int page_size;
+
+ if (tst_parse_int(str_cgroups_num, &cgroups_num, 1, MAX_CGROUPS))
+ tst_brk(TCONF, "Invalid number of cgroups: %s", str_cgroups_num);
+
+ SAFE_FILE_PRINTF("/proc/sys/vm/drop_caches", "3");
+
+ mem_free = SAFE_READ_MEMINFO("MemFree:");
+ mem_avail = SAFE_READ_MEMINFO("MemAvailable:");
+ swap_free = SAFE_READ_MEMINFO("SwapFree:");
+
+ SAFE_FILE_SCANF("/proc/sys/vm/min_free_kbytes", "%zi", &mem_min);
+
+ mem_min = mem_min + mem_min / 10;
+ reserved_mem = swap_free > mem_min ? 0 : mem_min;
+
+ mem_per_proc = mem_free < mem_avail ? mem_free : mem_avail;
+ mem_per_proc -= reserved_mem;
+ mem_per_proc /= cgroups_num;
+ mem_per_proc *= TST_KB;
+
+ if (!mem_per_proc)
+ tst_brk(TCONF, "System memory has not enough available memory");
+
+ page_size = getpagesize();
+ pages_num = ceil((double)mem_per_proc / page_size);
+
+ tst_res(TINFO, "Testing %d cgroups, using %ld MB, interval %d secs",
+ cgroups_num, mem_per_proc / TST_MB, INTERVAL);
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .test_all = run,
+ .needs_root = 1,
+ .forks_child = 1,
+ .runtime = RUNTIME,
+ .needs_cgroup_ver = TST_CG_V2,
+ .needs_cgroup_ctrls = (const char *const []){ "memory", NULL },
+ .options = (struct tst_option []) {
+ {"n:", &str_cgroups_num, "Number of cgroups (default: 1)"},
+ {}
+ },
+};
diff --git a/testcases/kernel/controllers/memcg/stress/memcg_stress_test.sh b/testcases/kernel/controllers/memcg/stress/memcg_stress_test.sh
deleted file mode 100755
index 47cac9af8..000000000
--- a/testcases/kernel/controllers/memcg/stress/memcg_stress_test.sh
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Copyright (c) 2009 FUJITSU LIMITED
-# Copyright (c) 2018-2019 ARM Ltd. All Rights Reserved.
-# Copyright (c) 2019-2022 Petr Vorel <pvorel@suse.cz>
-#
-# Author: Li Zefan <lizf@cn.fujitsu.com>
-# Restructure for LTP: Shi Weihua <shiwh@cn.fujitsu.com>
-# Added memcg enable/disable functionality: Rishikesh K Rajak <risrajak@linux.vnet.ibm.com>
-
-TST_TESTFUNC=test
-TST_SETUP=setup
-TST_CLEANUP=cleanup
-TST_CNT=2
-TST_NEEDS_ROOT=1
-TST_NEEDS_CMDS="mount umount cat kill mkdir rmdir grep awk cut"
-
-# Each test case runs for 900 secs when everything fine
-# therefore the default 5 mins timeout is not enough.
-TST_TIMEOUT=2100
-
-setup()
-{
- cgroup_require "memory"
- cgroup_version=$(cgroup_get_version "memory")
- test_path=$(cgroup_get_test_path "memory")
- task_list=$(cgroup_get_task_list "memory")
- if [ "$cgroup_version" = "2" ]; then
- ROD echo "+memory" \> "$test_path/cgroup.subtree_control"
- fi
-
- tst_res TINFO "test starts with cgroup version $cgroup_version"
-
- echo 3 > /proc/sys/vm/drop_caches
- sleep 2
- local mem_free=$(awk '/MemFree/ {print $2}' /proc/meminfo)
- local mem_available=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
- local swap_free=$(awk '/SwapFree/ {print $2}' /proc/meminfo)
- local mem_min=$(cat /proc/sys/vm/min_free_kbytes)
-
- mem_min=$(( $mem_min + $mem_min/10 ))
- [ $swap_free -gt $mem_min ] && RESERVED_MEM=0 || RESERVED_MEM=$mem_min
- [ $mem_free -lt $mem_available ] && MEM=$mem_free || MEM=$mem_available
- MEM=$(( $MEM - $RESERVED_MEM ))
- MEM=$(( $MEM / 1024 ))
- RUN_TIME=$(( 15 * 60 ))
-
- tst_res TINFO "Calculated available memory $MEM MB"
-}
-
-cleanup()
-{
- cgroup_cleanup
-}
-
-# $1 Number of cgroups
-# $2 Allocated MB memory in one process
-# $3 The interval to touch memory in a process
-# $4 Test duration (sec)
-run_stress()
-{
- local cgroups="$1"
- local mem_size="$2"
- local interval="$3"
- local timeout="$4"
- local i pid pids
-
- tst_res TINFO "Testing $cgroups cgroups, using $mem_size MB, interval $interval"
-
- tst_res TINFO "Starting cgroups"
- for i in $(seq 0 $(($cgroups-1))); do
- ROD mkdir "$test_path/$i"
- memcg_process_stress $mem_size $interval &
- ROD echo $! \> "$test_path/$i/$task_list"
- pids="$pids $!"
- done
-
- for pid in $pids; do
- kill -USR1 $pid 2> /dev/null
- done
-
- tst_res TINFO "Testing cgroups for ${timeout}s"
- sleep $timeout
-
- tst_res TINFO "Killing groups"
- i=0
- for pid in $pids; do
- kill -KILL $pid 2> /dev/null
- wait $pid 2> /dev/null
- ROD rmdir "$test_path/$i"
- i=$((i+1))
- done
-
- tst_res TPASS "Test passed"
-}
-
-test1()
-{
- run_stress 150 $(( $MEM / 150 )) 5 $RUN_TIME
-}
-
-test2()
-{
- run_stress 1 $MEM 5 $RUN_TIME
-}
-
-. cgroup_lib.sh
-tst_run
---
base-commit: 19083415169a3c5f0e07a74bea07f3690e0d041c
change-id: 20251105-b4-memcg_stress_rewrite-8a133288f7d0
Best regards,
--
Andrea Cervesato <andrea.cervesato@suse.com>
More information about the ltp
mailing list