* [LTP] [PATCH 0/2] OOM protection for shell tests
@ 2026-07-13 14:01 Andrea Cervesato
2026-07-13 14:01 ` [LTP] [PATCH 1/2] shell: add optional OOM protection Andrea Cervesato
2026-07-13 14:01 ` [LTP] [PATCH 2/2] memcg_stress: survive OOM by targeting the stressors Andrea Cervesato
0 siblings, 2 replies; 5+ messages in thread
From: Andrea Cervesato @ 2026-07-13 14:01 UTC (permalink / raw)
To: Linux Test Project
Under Li's idea, implement a OOM protection mechanism for the shell
tests so we can avoid OOM for memcg stress tests.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
Andrea Cervesato (2):
shell: add optional OOM protection
memcg_stress: survive OOM by targeting the stressors
doc/developers/writing_tests.rst | 3 ++
.../controllers/memcg/stress/memcg_stress_test.sh | 1 +
testcases/lib/tests/shell_oom_protection.sh | 34 +++++++++++++
testcases/lib/tst_test.sh | 57 +++++++++++++++++++++-
4 files changed, 94 insertions(+), 1 deletion(-)
---
base-commit: ed2758122173ffd8d1e8dff6c5efc1231073ee4a
change-id: 20260713-shell_oom_protection-6221ab825220
Best regards,
--
Andrea Cervesato <andrea.cervesato@suse.com>
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 5+ messages in thread* [LTP] [PATCH 1/2] shell: add optional OOM protection
2026-07-13 14:01 [LTP] [PATCH 0/2] OOM protection for shell tests Andrea Cervesato
@ 2026-07-13 14:01 ` Andrea Cervesato
2026-07-13 14:12 ` [LTP] " linuxtestproject.agent
2026-07-13 14:01 ` [LTP] [PATCH 2/2] memcg_stress: survive OOM by targeting the stressors Andrea Cervesato
1 sibling, 1 reply; 5+ messages in thread
From: Andrea Cervesato @ 2026-07-13 14:01 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Add TST_OOM_PROTECTION to activate OOM protection in shell tests. When
enabled, the shell harness shields itself from the OOM killer and runs
the test in a child process, so it survives memory pressure and can
still report results (e.g. during memcg stress tests).
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
doc/developers/writing_tests.rst | 3 ++
testcases/lib/tests/shell_oom_protection.sh | 34 +++++++++++++++++
testcases/lib/tst_test.sh | 57 ++++++++++++++++++++++++++++-
3 files changed, 93 insertions(+), 1 deletion(-)
diff --git a/doc/developers/writing_tests.rst b/doc/developers/writing_tests.rst
index 4db57898fcf08b83e68be996f666e91c418838fc..2d5bc294083fa2b89212714f0a6c5e5c3f22777a 100644
--- a/doc/developers/writing_tests.rst
+++ b/doc/developers/writing_tests.rst
@@ -549,6 +549,9 @@ LTP C And Shell Test API Comparison
* - not applicable
- TST_FS_TYPE
+ * - not applicable
+ - TST_OOM_PROTECTION
+
.. list-table::
:header-rows: 1
diff --git a/testcases/lib/tests/shell_oom_protection.sh b/testcases/lib/tests/shell_oom_protection.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a37ec51d2955e176c3b54297b8ca73ffbf2999f6
--- /dev/null
+++ b/testcases/lib/tests/shell_oom_protection.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2026 Linux Test Project
+
+TST_TESTFUNC=test
+TST_OOM_PROTECTION=1
+
+read_oom_score_adj()
+{
+ cat "/proc/$1/oom_score_adj" 2>/dev/null
+}
+
+test()
+{
+ local self_score harness_score
+
+ self_score=$(read_oom_score_adj self)
+ harness_score=$(read_oom_score_adj "$$")
+
+ if [ "$self_score" = 0 ]; then
+ tst_res TPASS "test process has oom_score_adj reset to 0"
+ else
+ tst_res TFAIL "test process oom_score_adj is $self_score, expected 0"
+ fi
+
+ if [ "$harness_score" = -1000 ]; then
+ tst_res TPASS "shell harness is protected from OOM"
+ else
+ tst_res TCONF "shell harness OOM protection unavailable"
+ fi
+}
+
+. tst_test.sh
+tst_run
diff --git a/testcases/lib/tst_test.sh b/testcases/lib/tst_test.sh
index 8701bb3903889b009f88ba98bac47534608cdd0a..48dacb432b953295ae4ad710e4f27ed87ac0a7e5 100644
--- a/testcases/lib/tst_test.sh
+++ b/testcases/lib/tst_test.sh
@@ -28,6 +28,57 @@ export TST_USR_GID="${LTP_USR_GID:-65534}"
trap "tst_brk TBROK 'test interrupted'" INT
trap "unset _tst_setup_timer_pid; tst_brk TBROK 'test terminated'" TERM
+_tst_set_oom_score_adj()
+{
+ local value="$1"
+ local path="/proc/self/oom_score_adj"
+
+ [ -e "$path" ] || return 0
+
+ echo "$value" > "$path" 2>/dev/null || return 0
+}
+
+_tst_enable_oom_protection()
+{
+ _tst_set_oom_score_adj -1000
+}
+
+_tst_disable_oom_protection()
+{
+ _tst_set_oom_score_adj 0
+}
+
+_tst_run_oom_protected()
+{
+ local _tst_pid
+ local _tst_ret
+
+ # Shield the harness from the OOM killer and run the test in a child.
+ # The child keeps the default oom_score_adj so that it, and any process
+ # it spawns, stay killable under memory pressure while the harness
+ # survives to report results.
+ _tst_enable_oom_protection
+
+ (
+ _tst_disable_oom_protection
+ _TST_OOM_PROTECTION=0
+ export _TST_OOM_PROTECTION
+ tst_run "$@"
+ ) &
+ _tst_pid=$!
+
+ wait "$_tst_pid"
+ _tst_ret=$?
+
+ if [ "$_tst_ret" -eq 137 ]; then
+ tst_res TINFO "Test was SIGKILLed: OOM killer or timeout?"
+ tst_res TINFO "On a slow machine try exporting LTP_TIMEOUT_MUL > 1"
+ tst_brk TBROK "Test killed!"
+ fi
+
+ exit "$_tst_ret"
+}
+
_tst_do_cleanup()
{
if [ -n "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" -a -z "$LTP_NO_CLEANUP" ]; then
@@ -680,12 +731,16 @@ tst_run()
local _tst_pattern='[='\''"} \t\/:`$\;|].*'
local ret
+ if [ "$TST_OOM_PROTECTION" = 1 -a "$_TST_OOM_PROTECTION" != 0 ]; then
+ _tst_run_oom_protected "$@"
+ fi
+
if [ -n "$TST_TEST_PATH" ]; then
for _tst_i in $(grep '^[^#]*\<TST_' "$TST_TEST_PATH" | sed "s/.*TST_//; s/$_tst_pattern//"); do
case "$_tst_i" in
ALL_FILESYSTEMS|DISABLE_APPARMOR|DISABLE_SELINUX);;
SETUP|CLEANUP|TESTFUNC|ID|CNT|MIN_KVER);;
- OPTS|USAGE|PARSE_ARGS|POS_ARGS);;
+ OPTS|USAGE|PARSE_ARGS|POS_ARGS|OOM_PROTECTION);;
NEEDS_ROOT|NEEDS_TMPDIR|TMPDIR|NEEDS_DEVICE|DEVICE);;
NEEDS_CMDS|NEEDS_MODULE|MODPATH|DATAROOT);;
NEEDS_DRIVERS|FS_TYPE|MNTPOINT|MNT_PARAMS);;
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [LTP] shell: add optional OOM protection
2026-07-13 14:01 ` [LTP] [PATCH 1/2] shell: add optional OOM protection Andrea Cervesato
@ 2026-07-13 14:12 ` linuxtestproject.agent
0 siblings, 0 replies; 5+ messages in thread
From: linuxtestproject.agent @ 2026-07-13 14:12 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: ltp
Hi Andrea,
On Mon, Jul 13 2026, Andrea Cervesato <andrea.cervesato@suse.com> wrote:
> shell: add optional OOM protection
--- [PATCH 1/2] ---
> +#!/bin/sh
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +# Copyright (c) 2026 Linux Test Project
> +
> +TST_TESTFUNC=test
> +TST_OOM_PROTECTION=1
The new test is missing both the mandatory doc block and the mandatory env
block. All other tests in testcases/lib/tests/ carry them (e.g.
shell_loader.sh, shell_loader_cleanup.sh). Add at minimum:
# ---
# doc
# Verify that TST_OOM_PROTECTION shields the shell harness from the OOM
# killer and resets the test process oom_score_adj to 0.
# ---
#
# ---
# env
# {
# }
# ---
> +TST_TESTFUNC=test
Is naming the test function "test" intentional? "test" is a POSIX shell
builtin and a standalone utility; shell-tests.md forbids naming functions
after common shell commands. Rename to e.g. "do_test" and update
TST_TESTFUNC accordingly.
> +. tst_test.sh
> +tst_run
Every other test in testcases/lib/tests/ uses the ". tst_loader.sh" /
". tst_run.sh" pattern. This test uses the pure-shell harness directly.
Is that because tst_loader.sh goes through the C binary (tst_run_shell)
and therefore never exercises the OOM protection that lives in tst_test.sh?
If so, can the test still be brought into the standard structure, or does
this require the OOM protection to be moved into the C backend first?
Verdict - Needs revision
Pre-existing issues:
testcases/kernel/controllers/memcg/stress/memcg_stress_test.sh uses
TST_TESTFUNC=test (function named after shell builtin); it also lacks
doc/env blocks. These pre-date this series.
---
Note:
The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.
Regards,
LTP AI Reviewer
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 5+ messages in thread
* [LTP] [PATCH 2/2] memcg_stress: survive OOM by targeting the stressors
2026-07-13 14:01 [LTP] [PATCH 0/2] OOM protection for shell tests Andrea Cervesato
2026-07-13 14:01 ` [LTP] [PATCH 1/2] shell: add optional OOM protection Andrea Cervesato
@ 2026-07-13 14:01 ` Andrea Cervesato
1 sibling, 0 replies; 5+ messages in thread
From: Andrea Cervesato @ 2026-07-13 14:01 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
The single-cgroup subtest intentionally lets one process fault in the
whole memory budget to push the system to its memory ceiling, so hitting
the OOM killer is an expected part of the stress. The problem is that the
OOM killer could reap the driver script, turning that expected pressure
into a spurious TBROK.
Activate OOM protection so the driver script won't be the OOM victim.
Fixes: 02961a7b2bb4 ("memcg_stress_test.sh: Fix reserved mem calculate")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
testcases/kernel/controllers/memcg/stress/memcg_stress_test.sh | 1 +
1 file changed, 1 insertion(+)
diff --git a/testcases/kernel/controllers/memcg/stress/memcg_stress_test.sh b/testcases/kernel/controllers/memcg/stress/memcg_stress_test.sh
index 47cac9af8bec970b7fb338c5469ab73d55267740..f32161f3f52a500d83cb2f6465adfb5130055dba 100755
--- a/testcases/kernel/controllers/memcg/stress/memcg_stress_test.sh
+++ b/testcases/kernel/controllers/memcg/stress/memcg_stress_test.sh
@@ -14,6 +14,7 @@ TST_CLEANUP=cleanup
TST_CNT=2
TST_NEEDS_ROOT=1
TST_NEEDS_CMDS="mount umount cat kill mkdir rmdir grep awk cut"
+TST_OOM_PROTECTION=1
# Each test case runs for 900 secs when everything fine
# therefore the default 5 mins timeout is not enough.
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [LTP] [PATCH v2 1/2] shell: add optional OOM protection
@ 2026-07-30 13:05 Andrea Cervesato
2026-07-30 14:10 ` [LTP] " linuxtestproject.agent
0 siblings, 1 reply; 5+ messages in thread
From: Andrea Cervesato @ 2026-07-30 13:05 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Add TST_OOM_PROTECTION to activate OOM protection in shell tests. When
enabled, the shell harness shields itself from the OOM killer and runs
the test in a child process, so it survives memory pressure and can
still report results (e.g. during memcg stress tests).
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
doc/developers/writing_tests.rst | 3 ++
testcases/lib/tests/shell_oom_protection.sh | 42 +++++++++++++++++++++
testcases/lib/tst_test.sh | 57 ++++++++++++++++++++++++++++-
3 files changed, 101 insertions(+), 1 deletion(-)
diff --git a/doc/developers/writing_tests.rst b/doc/developers/writing_tests.rst
index 4db57898fcf08b83e68be996f666e91c418838fc..2d5bc294083fa2b89212714f0a6c5e5c3f22777a 100644
--- a/doc/developers/writing_tests.rst
+++ b/doc/developers/writing_tests.rst
@@ -549,6 +549,9 @@ LTP C And Shell Test API Comparison
* - not applicable
- TST_FS_TYPE
+ * - not applicable
+ - TST_OOM_PROTECTION
+
.. list-table::
:header-rows: 1
diff --git a/testcases/lib/tests/shell_oom_protection.sh b/testcases/lib/tests/shell_oom_protection.sh
new file mode 100755
index 0000000000000000000000000000000000000000..aeab36816ee7d8eeae9366d2199a0aabde73e25a
--- /dev/null
+++ b/testcases/lib/tests/shell_oom_protection.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2026 Linux Test Project
+#
+# doc
+# Verify that TST_OOM_PROTECTION shields the shell harness from the OOM
+# killer and resets the test process oom_score_adj to 0.
+#
+# ---
+# env
+# {
+# }
+# ---
+
+TST_TESTFUNC=do_test
+TST_OOM_PROTECTION=1
+
+read_oom_score_adj() {
+ cat "/proc/$1/oom_score_adj" 2>/dev/null
+}
+
+do_test() {
+ local self_score harness_score
+
+ self_score=$(read_oom_score_adj self)
+ harness_score=$(read_oom_score_adj "$$")
+
+ if [ "$self_score" = 0 ]; then
+ tst_res TPASS "test process has oom_score_adj reset to 0"
+ else
+ tst_res TFAIL "test process oom_score_adj is $self_score, expected 0"
+ fi
+
+ if [ "$harness_score" = -1000 ]; then
+ tst_res TPASS "shell harness is protected from OOM"
+ else
+ tst_res TCONF "shell harness OOM protection unavailable"
+ fi
+}
+
+. tst_test.sh
+tst_run
diff --git a/testcases/lib/tst_test.sh b/testcases/lib/tst_test.sh
index 8701bb3903889b009f88ba98bac47534608cdd0a..48dacb432b953295ae4ad710e4f27ed87ac0a7e5 100644
--- a/testcases/lib/tst_test.sh
+++ b/testcases/lib/tst_test.sh
@@ -28,6 +28,57 @@ export TST_USR_GID="${LTP_USR_GID:-65534}"
trap "tst_brk TBROK 'test interrupted'" INT
trap "unset _tst_setup_timer_pid; tst_brk TBROK 'test terminated'" TERM
+_tst_set_oom_score_adj()
+{
+ local value="$1"
+ local path="/proc/self/oom_score_adj"
+
+ [ -e "$path" ] || return 0
+
+ echo "$value" > "$path" 2>/dev/null || return 0
+}
+
+_tst_enable_oom_protection()
+{
+ _tst_set_oom_score_adj -1000
+}
+
+_tst_disable_oom_protection()
+{
+ _tst_set_oom_score_adj 0
+}
+
+_tst_run_oom_protected()
+{
+ local _tst_pid
+ local _tst_ret
+
+ # Shield the harness from the OOM killer and run the test in a child.
+ # The child keeps the default oom_score_adj so that it, and any process
+ # it spawns, stay killable under memory pressure while the harness
+ # survives to report results.
+ _tst_enable_oom_protection
+
+ (
+ _tst_disable_oom_protection
+ _TST_OOM_PROTECTION=0
+ export _TST_OOM_PROTECTION
+ tst_run "$@"
+ ) &
+ _tst_pid=$!
+
+ wait "$_tst_pid"
+ _tst_ret=$?
+
+ if [ "$_tst_ret" -eq 137 ]; then
+ tst_res TINFO "Test was SIGKILLed: OOM killer or timeout?"
+ tst_res TINFO "On a slow machine try exporting LTP_TIMEOUT_MUL > 1"
+ tst_brk TBROK "Test killed!"
+ fi
+
+ exit "$_tst_ret"
+}
+
_tst_do_cleanup()
{
if [ -n "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" -a -z "$LTP_NO_CLEANUP" ]; then
@@ -680,12 +731,16 @@ tst_run()
local _tst_pattern='[='\''"} \t\/:`$\;|].*'
local ret
+ if [ "$TST_OOM_PROTECTION" = 1 -a "$_TST_OOM_PROTECTION" != 0 ]; then
+ _tst_run_oom_protected "$@"
+ fi
+
if [ -n "$TST_TEST_PATH" ]; then
for _tst_i in $(grep '^[^#]*\<TST_' "$TST_TEST_PATH" | sed "s/.*TST_//; s/$_tst_pattern//"); do
case "$_tst_i" in
ALL_FILESYSTEMS|DISABLE_APPARMOR|DISABLE_SELINUX);;
SETUP|CLEANUP|TESTFUNC|ID|CNT|MIN_KVER);;
- OPTS|USAGE|PARSE_ARGS|POS_ARGS);;
+ OPTS|USAGE|PARSE_ARGS|POS_ARGS|OOM_PROTECTION);;
NEEDS_ROOT|NEEDS_TMPDIR|TMPDIR|NEEDS_DEVICE|DEVICE);;
NEEDS_CMDS|NEEDS_MODULE|MODPATH|DATAROOT);;
NEEDS_DRIVERS|FS_TYPE|MNTPOINT|MNT_PARAMS);;
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [LTP] shell: add optional OOM protection
2026-07-30 13:05 [LTP] [PATCH v2 1/2] shell: add optional OOM protection Andrea Cervesato
@ 2026-07-30 14:10 ` linuxtestproject.agent
0 siblings, 0 replies; 5+ messages in thread
From: linuxtestproject.agent @ 2026-07-30 14:10 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: ltp
Hi Andrea,
On Thu, Jul 30, 2026, Andrea Cervesato wrote:
> shell: add optional OOM protection
--- [PATCH 1/2] ---
> --- /dev/null
> +++ b/testcases/lib/tests/shell_oom_protection.sh
The lib self-tests are launched from testcases/lib/run_tests.sh through the
explicit TESTS_PASS / TESTS_FAIL / TESTS_TBROK / TESTS_TCONF lists. This new
test is not added to any of them, so how is it expected to run in the
self-test suite?
As a root run it emits two TPASS (exit 0), which would place it in
TESTS_PASS.
> +# doc
> +# Verify that TST_OOM_PROTECTION shields the shell harness from the OOM
> +# killer and resets the test process oom_score_adj to 0.
> +#
> +# ---
> +# env
> +# {
> +# }
> +# ---
> +
> +TST_TESTFUNC=do_test
> +TST_OOM_PROTECTION=1
[...]
> +. tst_test.sh
> +tst_run
The other tests in this directory use the metadata loader style
(. tst_loader.sh ... . tst_run.sh), where the doc/env blocks are parsed.
Here the test sources tst_test.sh, so these comment blocks are not parsed
and become dead comments. The doc block is also missing its opening
"# ---" delimiter, so it would be malformed under the metadata loader too.
Would it be cleaner to either convert to the tst_loader.sh/tst_run.sh style
with a proper metadata block, or drop the metadata comments?
> + _tst_enable_oom_protection
> +
> + (
> + _tst_disable_oom_protection
> + _TST_OOM_PROTECTION=0
> + export _TST_OOM_PROTECTION
> + tst_run "$@"
> + ) &
> + _tst_pid=$!
> +
> + wait "$_tst_pid"
> + _tst_ret=$?
> +
> + if [ "$_tst_ret" -eq 137 ]; then
On the SIGKILL path the test runs in the child subshell, so its setup state
(TST_DO_CLEANUP, TST_TMPDIR, TST_MOUNT_FLAG, TST_DEVICE_FLAG) lives in the
killed child. The surviving harness reports TBROK but its
tst_brk -> _tst_do_exit then cleans nothing, so external resources created
by the child (tmpdir, mounts, devices, cgroups) are left behind.
This is largely pre-existing (an OOM-killed driver skipped cleanup before
too), but now the process survives to report while the cleanup is lost. Is
that acceptable, or should the surviving harness own the teardown? See the
concrete impact under patch 2/2.
--- [PATCH 2/2] ---
> +TST_OOM_PROTECTION=1
The mechanism protects the harness (oom_score_adj=-1000) but runs the driver
logic in a child with oom_score_adj=0, the same value as the
memcg_process_stress stressors. In practice the stressors have the larger
RSS and are the natural OOM victims, so the driver child survives.
If the driver child is nonetheless the OOM victim (the case the commit
message describes), the harness reports TBROK "Test killed!" and cleanup()
/ cgroup_cleanup() never run, leaving the memcg mount and cgroup directories
behind. Is that residual leak acceptable given the survival goal?
The Fixes: 02961a7b2bb4 tag is valid and matches local history.
Verdict - Needs revision
---
Note:
The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.
Regards,
LTP AI Reviewer
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-30 14:10 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 14:01 [LTP] [PATCH 0/2] OOM protection for shell tests Andrea Cervesato
2026-07-13 14:01 ` [LTP] [PATCH 1/2] shell: add optional OOM protection Andrea Cervesato
2026-07-13 14:12 ` [LTP] " linuxtestproject.agent
2026-07-13 14:01 ` [LTP] [PATCH 2/2] memcg_stress: survive OOM by targeting the stressors Andrea Cervesato
-- strict thread matches above, loose matches on Subject: below --
2026-07-30 13:05 [LTP] [PATCH v2 1/2] shell: add optional OOM protection Andrea Cervesato
2026-07-30 14:10 ` [LTP] " linuxtestproject.agent
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox