* [LTP] [PATCH v1 0/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim
@ 2026-10-04 14:33 Wei Gao via ltp
2026-10-04 14:33 ` [LTP] [PATCH v1 1/2] memcontrol: Add helpers to set and restore parent memory knobs Wei Gao via ltp
2026-10-04 14:33 ` [LTP] [PATCH v1 2/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim Wei Gao via ltp
0 siblings, 2 replies; 7+ messages in thread
From: Wei Gao via ltp @ 2026-10-04 14:33 UTC (permalink / raw)
To: ltp; +Cc: Michal Koutný
Wei Gao (2):
memcontrol: Add helpers to set and restore parent memory knobs
memcontrol03, memcontrol04: Set protection on parent cgroups to
prevent kswapd over-reclaim
.../kernel/controllers/memcg/memcontrol03.c | 20 +++++---
.../kernel/controllers/memcg/memcontrol04.c | 19 +++++---
.../controllers/memcg/memcontrol_common.h | 46 +++++++++++++++++++
3 files changed, 71 insertions(+), 14 deletions(-)
--
2.55.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 7+ messages in thread
* [LTP] [PATCH v1 1/2] memcontrol: Add helpers to set and restore parent memory knobs
2026-10-04 14:33 [LTP] [PATCH v1 0/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim Wei Gao via ltp
@ 2026-10-04 14:33 ` Wei Gao via ltp
2026-10-04 15:47 ` [LTP] " linuxtestproject.agent
2026-10-04 14:33 ` [LTP] [PATCH v1 2/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim Wei Gao via ltp
1 sibling, 1 reply; 7+ messages in thread
From: Wei Gao via ltp @ 2026-10-04 14:33 UTC (permalink / raw)
To: ltp; +Cc: Michal Koutný
In cgroup v2, memory protection (memory.min and memory.low) is hierarchical
and evaluated top-down. The effective protection of descendant cgroups is
bounded by their parent and ancestor protection values. When LTP runs, the
test sandbox cgroups (tst_cg and its parent ltp) default to 0, which causes
background reclaim (such as kswapd under node-level memory pressure) to
reclaim memory from descendants regardless of local protection settings.
Add set_parent_memory_knob() and restore_parent_memory_knob() to
memcontrol_common.h to allow memcg tests to configure protection on both
Level 2 (tst_cg) and Level 1 (the shared ltp directory). The original values
are read and preserved before modification, and restored upon cleanup.
Signed-off-by: Wei Gao <wegao@suse.com>
---
.../controllers/memcg/memcontrol_common.h | 46 +++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/testcases/kernel/controllers/memcg/memcontrol_common.h b/testcases/kernel/controllers/memcg/memcontrol_common.h
index 495f924a0..e4b12aff3 100644
--- a/testcases/kernel/controllers/memcg/memcontrol_common.h
+++ b/testcases/kernel/controllers/memcg/memcontrol_common.h
@@ -10,10 +10,56 @@
#include <stdio.h>
#include "tst_test.h"
+#include "tst_safe_file_at.h"
#define TMPDIR "mntdir"
#define MB(x) (x << 20)
+static char orig_tst_val[64];
+static char orig_ltp_val[64];
+
+static inline void set_parent_memory_knob(const char *knob, const char *val)
+{
+ int tst_fd = tst_cg_group_unified_dir_fd(tst_cg);
+ int ltp_fd;
+
+ if (tst_fd < 0)
+ return;
+
+ ltp_fd = SAFE_OPENAT(tst_fd, "..", O_DIRECTORY | O_RDONLY);
+
+ /* 1. Read and save original values */
+ SAFE_CG_READ(tst_cg, knob, orig_tst_val, sizeof(orig_tst_val));
+ SAFE_FILE_READAT(ltp_fd, knob, orig_ltp_val, sizeof(orig_ltp_val));
+
+ /* 2. Apply new values to Level 2 (tst_cg) and Level 1 (ltp) */
+ SAFE_CG_PRINT(tst_cg, knob, val);
+ SAFE_FILE_PRINTFAT(ltp_fd, knob, "%s\n", val);
+
+ SAFE_CLOSE(ltp_fd);
+}
+
+static inline void restore_parent_memory_knob(const char *knob)
+{
+ int tst_fd, ltp_fd;
+
+ if (!orig_tst_val[0])
+ return;
+
+ tst_fd = tst_cg_group_unified_dir_fd(tst_cg);
+ if (tst_fd < 0)
+ return;
+
+ ltp_fd = SAFE_OPENAT(tst_fd, "..", O_DIRECTORY | O_RDONLY);
+
+ /* Restore original values */
+ SAFE_CG_PRINT(tst_cg, knob, orig_tst_val);
+ SAFE_FILE_PRINTFAT(ltp_fd, knob, "%s", orig_ltp_val);
+
+ SAFE_CLOSE(ltp_fd);
+ orig_tst_val[0] = '\0';
+}
+
/*
* Checks if two given values differ by less than err% of their
* sum. An extra percent is added for every doubling of the page size
--
2.55.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [LTP] [PATCH v1 2/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim
2026-10-04 14:33 [LTP] [PATCH v1 0/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim Wei Gao via ltp
2026-10-04 14:33 ` [LTP] [PATCH v1 1/2] memcontrol: Add helpers to set and restore parent memory knobs Wei Gao via ltp
@ 2026-10-04 14:33 ` Wei Gao via ltp
2026-10-05 15:37 ` Michal Koutný via ltp
1 sibling, 1 reply; 7+ messages in thread
From: Wei Gao via ltp @ 2026-10-04 14:33 UTC (permalink / raw)
To: ltp; +Cc: Michal Koutný
On NUMA systems (observed on PowerPC), allocating memory can trigger
node-level memory pressure, waking up kswapd when free pages fall below
the low watermark on a node.
In cgroup v2, both hard protection (memory.min) and best-effort protection
(memory.low) are hierarchical and calculated top-down: effective
protection of descendant cgroups is bounded by their ancestors' protection
values. When LTP runs inside an existing cgroup hierarchy, ancestor cgroups
above the test root have memory.min and memory.low set to 0.
As a result, when kswapd performs node-level background reclaim from the
root cgroup downwards, ancestor cgroups without protection provide no
protection boundary. kswapd reclaims pages from descendant leaf cgroups
regardless of their local memory.min / memory.low settings, causing
memcontrol03 and memcontrol04 to fail with over-reclaim:
NUMA node info:
NUMA Node 2: CPUs=[2], MemTotal=765 MB, MemFree=610 MB
NUMA Node 0: CPUs=[0], MemTotal=240 MB, MemFree=93 MB
NUMA Node 3: CPUs=[3], MemTotal=750 MB, MemFree=419 MB
NUMA Node 1: CPUs=[1], MemTotal=766 MB, MemFree=95 MB
Error info:
memcontrol03.c:519: TFAIL: Expect: (A/B/C memory.current=19070976) ~= 34603008
Set memory.min and memory.low on trunk_A and parent cgroups up through the
hierarchy before allocating child pagecache, ensuring protection propagates
down the tree to prevent kswapd over-reclaim under NUMA memory pressure,
and restore the original values upon cleanup.
Signed-off-by: Wei Gao <wegao@suse.com>
---
.../kernel/controllers/memcg/memcontrol03.c | 20 ++++++++++++-------
.../kernel/controllers/memcg/memcontrol04.c | 19 +++++++++++-------
2 files changed, 25 insertions(+), 14 deletions(-)
diff --git a/testcases/kernel/controllers/memcg/memcontrol03.c b/testcases/kernel/controllers/memcg/memcontrol03.c
index 25b84c24e..721ae0e46 100644
--- a/testcases/kernel/controllers/memcg/memcontrol03.c
+++ b/testcases/kernel/controllers/memcg/memcontrol03.c
@@ -184,21 +184,24 @@ static void test_memcg_min(void)
trunk_cg[G] = tst_cg_group_mk(trunk_cg[A], "trunk_G");
- for (i = 0; i < ARRAY_SIZE(leaf_cg); i++) {
+ for (i = 0; i < ARRAY_SIZE(leaf_cg); i++)
leaf_cg[i] = tst_cg_group_mk(trunk_cg[B],
"leaf_%c", 'C' + i);
- if (i == E)
- continue;
-
- alloc_pagecache_in_child(leaf_cg[i], MB(50));
- }
-
+ SAFE_CG_PRINT(trunk_cg[A], "memory.min", "50M");
SAFE_CG_PRINT(trunk_cg[B], "memory.min", "50M");
SAFE_CG_PRINT(leaf_cg[C], "memory.min", "75M");
SAFE_CG_PRINT(leaf_cg[D], "memory.min", "25M");
SAFE_CG_PRINT(leaf_cg[E], "memory.min", "500M");
SAFE_CG_PRINT(leaf_cg[F], "memory.min", "0");
+ set_parent_memory_knob("memory.min", "50M");
+
+ for (i = 0; i < ARRAY_SIZE(leaf_cg); i++) {
+ if (i == E)
+ continue;
+
+ alloc_pagecache_in_child(leaf_cg[i], MB(50));
+ }
for (attempts = 0; attempts < 5; attempts++) {
SAFE_CG_SCANF(trunk_cg[B], "memory.current", "%ld", c);
@@ -223,6 +226,7 @@ static void test_memcg_min(void)
"(A/B/D memory.current=%ld) ~= %d", c[1], MB(21));
TST_EXP_EXPR(values_close(c[2], 0, 1),
"(A/B/E memory.current=%ld) ~= 0", c[2]);
+ tst_res(TINFO, "A/B/F memory.current=%ld", c[F]);
alloc_anon_in_child(trunk_cg[G], MB(170), 1);
@@ -231,12 +235,14 @@ static void test_memcg_min(void)
"(A/B memory.current=%ld) ~= %d", c[0], MB(50));
cleanup_sub_groups();
+ restore_parent_memory_knob("memory.min");
SAFE_CLOSE(fd);
SAFE_UNLINK(TMPDIR"/tmpfile");
}
static void cleanup(void)
{
+ restore_parent_memory_knob("memory.min");
cleanup_sub_groups();
if (fd > -1)
SAFE_CLOSE(fd);
diff --git a/testcases/kernel/controllers/memcg/memcontrol04.c b/testcases/kernel/controllers/memcg/memcontrol04.c
index f1e136df4..10de95bec 100644
--- a/testcases/kernel/controllers/memcg/memcontrol04.c
+++ b/testcases/kernel/controllers/memcg/memcontrol04.c
@@ -157,21 +157,24 @@ static void test_memcg_low(void)
trunk_cg[G] = tst_cg_group_mk(trunk_cg[A], "trunk_G");
- for (i = 0; i < ARRAY_SIZE(leaf_cg); i++) {
+ for (i = 0; i < ARRAY_SIZE(leaf_cg); i++)
leaf_cg[i] = tst_cg_group_mk(trunk_cg[B],
"leaf_%c", 'C' + i);
- if (i == E)
- continue;
-
- alloc_pagecache_in_child(leaf_cg[i], MB(50));
- }
-
+ SAFE_CG_PRINT(trunk_cg[A], "memory.low", "50M");
SAFE_CG_PRINT(trunk_cg[B], "memory.low", "50M");
SAFE_CG_PRINT(leaf_cg[C], "memory.low", "75M");
SAFE_CG_PRINT(leaf_cg[D], "memory.low", "25M");
SAFE_CG_PRINT(leaf_cg[E], "memory.low", "500M");
SAFE_CG_PRINT(leaf_cg[F], "memory.low", "0");
+ set_parent_memory_knob("memory.low", "50M");
+
+ for (i = 0; i < ARRAY_SIZE(leaf_cg); i++) {
+ if (i == E)
+ continue;
+
+ alloc_pagecache_in_child(leaf_cg[i], MB(50));
+ }
alloc_anon_in_child(trunk_cg[G], MB(148));
@@ -230,12 +233,14 @@ static void test_memcg_low(void)
}
cleanup_sub_groups();
+ restore_parent_memory_knob("memory.low");
SAFE_CLOSE(fd);
SAFE_UNLINK(TMPDIR"/tmpfile");
}
static void cleanup(void)
{
+ restore_parent_memory_knob("memory.low");
cleanup_sub_groups();
if (fd > -1)
SAFE_CLOSE(fd);
--
2.55.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [LTP] memcontrol: Add helpers to set and restore parent memory knobs
2026-10-04 14:33 ` [LTP] [PATCH v1 1/2] memcontrol: Add helpers to set and restore parent memory knobs Wei Gao via ltp
@ 2026-10-04 15:47 ` linuxtestproject.agent
0 siblings, 0 replies; 7+ messages in thread
From: linuxtestproject.agent @ 2026-10-04 15:47 UTC (permalink / raw)
To: Wei Gao; +Cc: ltp
Hi Wei,
On 4 October 2026, Wei Gao wrote:
> memcontrol: Add helpers to set and restore parent memory knobs
--- [PATCH 2/2] ---
> + set_parent_memory_knob("memory.min", "50M");
> + set_parent_memory_knob("memory.low", "50M");
High severity: overlapping instances of the same test race on the shared
ltp knob: X saves 0, Y saves X's 50M, X restores 0 while Y is active, and
Y finally restores 50M instead of the original 0. This both removes an
active test's protection and leaves a pre-existing ltp group modified.
Serialize the complete shared-parent save/use/restore interval, not just
the individual reads and writes.
> static void cleanup(void)
> {
> + restore_parent_memory_knob("memory.min");
High severity: the same problem applies to memory.low cleanup: the
snapshots exist only in the forked test worker, which the framework kills
without running this callback on timeout or interruption. The supervisor
does not restore these knobs, so a pre-existing ltp group retains 50M.
Move shared-parent bookkeeping and restoration into supervising cgroup
cleanup, before cgroup teardown.
> Set memory.min and memory.low on trunk_A and parent cgroups up through the
> hierarchy before allocating child pagecache, ensuring protection propagates
> down the tree to prevent kswapd over-reclaim under NUMA memory pressure,
Medium severity: the helper stops at ltp, so it does not establish this
protection when the cgroup mount is rooted in a delegated subtree or
namespace whose root has memory.min/low set to 0. Global reclaim still
walks that real ancestor, and page_counter_calculate_protection() caps
the descendants' effective protection to 0. Handle or report insufficient
ancestor protection and document the restriction instead of claiming
protection throughout arbitrary hierarchies.
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] 7+ messages in thread
* Re: [LTP] [PATCH v1 2/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim
2026-10-04 14:33 ` [LTP] [PATCH v1 2/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim Wei Gao via ltp
@ 2026-10-05 15:37 ` Michal Koutný via ltp
2026-10-08 2:10 ` Wei Gao via ltp
0 siblings, 1 reply; 7+ messages in thread
From: Michal Koutný via ltp @ 2026-10-05 15:37 UTC (permalink / raw)
To: Wei Gao; +Cc: ltp
[-- Attachment #1.1: Type: text/plain, Size: 1696 bytes --]
Hello Wei.
On Sun, Oct 04, 2026 at 02:33:27PM +0000, Wei Gao <wegao@suse.com> wrote:
> On NUMA systems (observed on PowerPC), allocating memory can trigger
> node-level memory pressure, waking up kswapd when free pages fall below
> the low watermark on a node.
>
> In cgroup v2, both hard protection (memory.min) and best-effort protection
> (memory.low) are hierarchical and calculated top-down: effective
> protection of descendant cgroups is bounded by their ancestors' protection
> values. When LTP runs inside an existing cgroup hierarchy, ancestor cgroups
> above the test root have memory.min and memory.low set to 0.
Note that this is expected behavior of protection and the test is meant
to work "relatively" with its own pressure source and target [1].
> As a result, when kswapd performs node-level background reclaim from the
> root cgroup downwards, ancestor cgroups without protection provide no
> protection boundary. kswapd reclaims pages from descendant leaf cgroups
> regardless of their local memory.min / memory.low settings, causing
> memcontrol03 and memcontrol04 to fail with over-reclaim:
Generally, this is not specific to this test -- it is because it the
whole host is overcommitted (edit: as I see the NUMA info below it could
be perhaps that only individual nodes are too small).
So I wonder could this be ensured at higher level -- something that sets
up the environment of the individual runners (e.g. reduce parallelism,
partition the system with memory limits or as a variant configure
generic memory protection)?
WDYT?
Michal
[1] https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#reclaim-protection
[-- Attachment #2: Type: text/plain, Size: 60 bytes --]
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [LTP] [PATCH v1 2/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim
2026-10-05 15:37 ` Michal Koutný via ltp
@ 2026-10-08 2:10 ` Wei Gao via ltp
2026-10-12 14:13 ` Michal Koutný via ltp
0 siblings, 1 reply; 7+ messages in thread
From: Wei Gao via ltp @ 2026-10-08 2:10 UTC (permalink / raw)
To: Michal Koutný; +Cc: ltp
On Mon, Oct 05, 2026 at 05:37:46PM +0200, Michal Koutný wrote:
> Hello Wei.
>
> On Sun, Oct 04, 2026 at 02:33:27PM +0000, Wei Gao <wegao@suse.com> wrote:
> > On NUMA systems (observed on PowerPC), allocating memory can trigger
> > node-level memory pressure, waking up kswapd when free pages fall below
> > the low watermark on a node.
> >
> > In cgroup v2, both hard protection (memory.min) and best-effort protection
> > (memory.low) are hierarchical and calculated top-down: effective
> > protection of descendant cgroups is bounded by their ancestors' protection
> > values. When LTP runs inside an existing cgroup hierarchy, ancestor cgroups
> > above the test root have memory.min and memory.low set to 0.
>
> Note that this is expected behavior of protection and the test is meant
> to work "relatively" with its own pressure source and target [1].
>
>
> > As a result, when kswapd performs node-level background reclaim from the
> > root cgroup downwards, ancestor cgroups without protection provide no
> > protection boundary. kswapd reclaims pages from descendant leaf cgroups
> > regardless of their local memory.min / memory.low settings, causing
> > memcontrol03 and memcontrol04 to fail with over-reclaim:
>
> Generally, this is not specific to this test -- it is because it the
> whole host is overcommitted (edit: as I see the NUMA info below it could
> be perhaps that only individual nodes are too small).
>
> So I wonder could this be ensured at higher level -- something that sets
> up the environment of the individual runners (e.g. reduce parallelism,
> partition the system with memory limits or as a variant configure
> generic memory protection)?
>
> WDYT?
Protecting this solely from the external runner is tricky since LTP builds
a 3-level cgroup hierarchy:
- /sys/fs/cgroup/ltp (Level 1)
- tst_cg (/sys/fs/cgroup/ltp/ltp-testxxxx, Level 2 — trunk_A's parent)
- trunk_cg[A] (Level 3 — trunk_B's parent)
Every level in this chain needs memory.low = 50M. Even if an external runner
sets it at /sys/fs/cgroup/ltp, tst_cg or trunk_A will break the protection chain.
Another option is make sure enough memory to the test environment so kswapd
doesn't get triggered at all. I guess we can do this to avoid issue?
> Michal
>
>
> [1] https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#reclaim-protection
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [LTP] [PATCH v1 2/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim
2026-10-08 2:10 ` Wei Gao via ltp
@ 2026-10-12 14:13 ` Michal Koutný via ltp
0 siblings, 0 replies; 7+ messages in thread
From: Michal Koutný via ltp @ 2026-10-12 14:13 UTC (permalink / raw)
To: Wei Gao; +Cc: ltp
[-- Attachment #1.1: Type: text/plain, Size: 988 bytes --]
On Thu, Oct 08, 2026 at 02:10:17AM +0000, Wei Gao <wegao@suse.com> wrote:
> Protecting this solely from the external runner is tricky since LTP builds
> a 3-level cgroup hierarchy:
> - /sys/fs/cgroup/ltp (Level 1)
> - tst_cg (/sys/fs/cgroup/ltp/ltp-testxxxx, Level 2 — trunk_A's parent)
> - trunk_cg[A] (Level 3 — trunk_B's parent)
> Every level in this chain needs memory.low = 50M. Even if an external runner
> sets it at /sys/fs/cgroup/ltp, tst_cg or trunk_A will break the protection chain.
I see. (Note that this needn't be the same 50M as inside the test. The
value at this level would denote some protection sufficient for test's
stability.)
> Another option is make sure enough memory to the test environment so kswapd
> doesn't get triggered at all. I guess we can do this to avoid issue?
That sounds even better (or limit amount of tests running in parallel
wrt available amounts), memory.low on the high level is best-effort in
the end.
Michal
[-- Attachment #2: Type: text/plain, Size: 60 bytes --]
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-12 14:14 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-04 14:33 [LTP] [PATCH v1 0/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim Wei Gao via ltp
2026-10-04 14:33 ` [LTP] [PATCH v1 1/2] memcontrol: Add helpers to set and restore parent memory knobs Wei Gao via ltp
2026-10-04 15:47 ` [LTP] " linuxtestproject.agent
2026-10-04 14:33 ` [LTP] [PATCH v1 2/2] memcontrol03, memcontrol04: Set protection on parent cgroups to prevent kswapd over-reclaim Wei Gao via ltp
2026-10-05 15:37 ` Michal Koutný via ltp
2026-10-08 2:10 ` Wei Gao via ltp
2026-10-12 14:13 ` Michal Koutný via ltp
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox