* [LTP] [PATCH] memory: rewrite memcg_stress_test into C API
@ 2025-11-05 9:43 Andrea Cervesato
2025-11-05 16:45 ` Cyril Hrubis
0 siblings, 1 reply; 2+ messages in thread
From: Andrea Cervesato @ 2025-11-05 9:43 UTC (permalink / raw)
To: ltp
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>
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [LTP] [PATCH] memory: rewrite memcg_stress_test into C API
2025-11-05 9:43 [LTP] [PATCH] memory: rewrite memcg_stress_test into C API Andrea Cervesato
@ 2025-11-05 16:45 ` Cyril Hrubis
0 siblings, 0 replies; 2+ messages in thread
From: Cyril Hrubis @ 2025-11-05 16:45 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: ltp
Hi!
> + 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;
We just dropped caches, so mem_free should be close to mem_avail. I
guess that we can use only one of these two.
It looks more or less fine as the replacement, however I find some
things in the test a bit worrisome. For instance the sleep() between
forks of the children will be on some systems enough for the previous
child to finish while on bigger systems (with large RAM) would be too
short. So first of all we should decide if we want to run the children
in serial or in parallel mode and change the code accordingly. As for me
serial mode makes more sense to me, since with parallel mode we are
going to hit OOM under certain conditions.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-11-05 16:45 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-05 9:43 [LTP] [PATCH] memory: rewrite memcg_stress_test into C API Andrea Cervesato
2025-11-05 16:45 ` Cyril Hrubis
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox