Linux Test Project
 help / color / mirror / Atom feed
* [LTP] [PATCH v2 0/2] OOM protection for shell tests
@ 2026-07-30 13:05 Andrea Cervesato
  2026-07-30 13:05 ` [LTP] [PATCH v2 1/2] shell: add optional OOM protection Andrea Cervesato
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Andrea Cervesato @ 2026-07-30 13:05 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>
---
Changes in v2:
- update shell OOM protection functional test
- Link to v1: https://lore.kernel.org/20260713-shell_oom_protection-v1-0-b732e8647894@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        | 42 ++++++++++++++++
 testcases/lib/tst_test.sh                          | 57 +++++++++++++++++++++-
 4 files changed, 102 insertions(+), 1 deletion(-)
---
base-commit: 4918812af24a0e06fa3a9ba6da2679cfea1f2126
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] 6+ messages in thread

* [LTP] [PATCH v2 1/2] shell: add optional OOM protection
  2026-07-30 13:05 [LTP] [PATCH v2 0/2] OOM protection for shell tests Andrea Cervesato
@ 2026-07-30 13:05 ` Andrea Cervesato
  2026-07-30 14:10   ` [LTP] " linuxtestproject.agent
  2026-07-31 13:03   ` [LTP] [PATCH v2 1/2] " Petr Vorel
  2026-07-30 13:05 ` [LTP] [PATCH v2 2/2] memcg_stress: survive OOM by targeting the stressors Andrea Cervesato
  2026-07-30 13:06 ` [LTP] [PATCH v2 0/2] OOM protection for shell tests Andrea Cervesato via ltp
  2 siblings, 2 replies; 6+ 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] 6+ messages in thread

* [LTP] [PATCH v2 2/2] memcg_stress: survive OOM by targeting the stressors
  2026-07-30 13:05 [LTP] [PATCH v2 0/2] OOM protection for shell tests Andrea Cervesato
  2026-07-30 13:05 ` [LTP] [PATCH v2 1/2] shell: add optional OOM protection Andrea Cervesato
@ 2026-07-30 13:05 ` Andrea Cervesato
  2026-07-30 13:06 ` [LTP] [PATCH v2 0/2] OOM protection for shell tests Andrea Cervesato via ltp
  2 siblings, 0 replies; 6+ 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>

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] 6+ messages in thread

* Re: [LTP] [PATCH v2 0/2] OOM protection for shell tests
  2026-07-30 13:05 [LTP] [PATCH v2 0/2] OOM protection for shell tests Andrea Cervesato
  2026-07-30 13:05 ` [LTP] [PATCH v2 1/2] shell: add optional OOM protection Andrea Cervesato
  2026-07-30 13:05 ` [LTP] [PATCH v2 2/2] memcg_stress: survive OOM by targeting the stressors Andrea Cervesato
@ 2026-07-30 13:06 ` Andrea Cervesato via ltp
  2 siblings, 0 replies; 6+ messages in thread
From: Andrea Cervesato via ltp @ 2026-07-30 13:06 UTC (permalink / raw)
  To: Andrea Cervesato; +Cc: Linux Test Project, Li Wang

Hi Li,

can you please take a look at this patch?

Thanks,
--
Andrea Cervesato
SUSE QE Automation Engineer Linux
andrea.cervesato@suse.com

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 6+ 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
  2026-07-31 13:03   ` [LTP] [PATCH v2 1/2] " Petr Vorel
  1 sibling, 0 replies; 6+ 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] 6+ messages in thread

* Re: [LTP] [PATCH v2 1/2] 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   ` [LTP] " linuxtestproject.agent
@ 2026-07-31 13:03   ` Petr Vorel
  1 sibling, 0 replies; 6+ messages in thread
From: Petr Vorel @ 2026-07-31 13:03 UTC (permalink / raw)
  To: Andrea Cervesato; +Cc: Linux Test Project

Hi Andrea,

> 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).

> 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
> +}
> +

If the test always TPASS or TCONF, please add it to lib/newlib_tests/runtest.sh
(to be run in CI).

...

> +. 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
So, we want to hide "write error: Permission denied"?

> +}
> +
> +_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
> +
> +	(
Interesting idea. Hopefully it could work (I'm not sure if tst_test.sh API will
work well with yet another subshell, if killing will work correctly).

> +		_tst_disable_oom_protection
> +		_TST_OOM_PROTECTION=0
> +		export _TST_OOM_PROTECTION
NOTE: you can export variable with value in a single command:
export _TST_OOM_PROTECTION=0

Also, "$_TST_" does not have the protection about misuse which "$TST_" has (that
protection for _tst_i in $(grep ... you modified).
Please use a different name with "$TST_". But best would be just to assign a
different value:
export TST_OOM_PROTECTION=2

> +		tst_run "$@"
Running the test in the cleanup phase, that looks to me strange.

> +	) &
> +	_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
And here have just:
	if [ "$TST_OOM_PROTECTION" = 1 ]; then

Kind regards,
Petr

> +		_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);;

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-07-31 13:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-30 13:05 [LTP] [PATCH v2 0/2] OOM protection for shell tests Andrea Cervesato
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
2026-07-31 13:03   ` [LTP] [PATCH v2 1/2] " Petr Vorel
2026-07-30 13:05 ` [LTP] [PATCH v2 2/2] memcg_stress: survive OOM by targeting the stressors Andrea Cervesato
2026-07-30 13:06 ` [LTP] [PATCH v2 0/2] OOM protection for shell tests Andrea Cervesato via ltp

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox