From: Pavithra <pavrampu@linux.ibm.com>
To: ltp@lists.linux.it
Cc: pavrampu@linux.ibm.com
Subject: [LTP] [PATCH] memcg/memcontrol05: add cgroup v2 task migration charge accounting test
Date: Tue, 4 Aug 2026 15:29:25 +0530 [thread overview]
Message-ID: <20260804095925.495813-1-pavrampu@linux.ibm.com> (raw)
Verify that in cgroup v2, memory charges remain pinned to the cgroup
that allocated them when a task migrates, and that new allocations after
migration are charged to the destination cgroup.
Signed-off-by: Pavithra <pavrampu@linux.ibm.com>
---
runtest/controllers | 1 +
testcases/kernel/controllers/memcg/.gitignore | 1 +
.../kernel/controllers/memcg/memcontrol05.c | 160 ++++++++++++++++++
3 files changed, 162 insertions(+)
create mode 100644 testcases/kernel/controllers/memcg/memcontrol05.c
diff --git a/runtest/controllers b/runtest/controllers
index 93c52c439..a9050ef0e 100644
--- a/runtest/controllers
+++ b/runtest/controllers
@@ -24,6 +24,7 @@ memcontrol01 memcontrol01
memcontrol02 memcontrol02
memcontrol03 memcontrol03
memcontrol04 memcontrol04
+memcontrol05 memcontrol05
cgroup_fj_function_debug cgroup_fj_function.sh debug
cgroup_fj_function_cpuset cgroup_fj_function.sh cpuset
diff --git a/testcases/kernel/controllers/memcg/.gitignore b/testcases/kernel/controllers/memcg/.gitignore
index 3883cede6..8b9f6005c 100644
--- a/testcases/kernel/controllers/memcg/.gitignore
+++ b/testcases/kernel/controllers/memcg/.gitignore
@@ -9,3 +9,4 @@ memcontrol01
memcontrol02
memcontrol03
memcontrol04
+memcontrol05
diff --git a/testcases/kernel/controllers/memcg/memcontrol05.c b/testcases/kernel/controllers/memcg/memcontrol05.c
new file mode 100644
index 000000000..96621c478
--- /dev/null
+++ b/testcases/kernel/controllers/memcg/memcontrol05.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 IBM
+ * Author : Pavithra <pavrampu@linux.ibm.com>
+ */
+
+/*\
+ * Origin: testcases/kernel/controllers/memctl/memctl_testplan.txt
+ * Verify memory controller charge accounting during task migration.
+ *
+ * Two child cgroups (group_a, group_b) are created under the test cgroup.
+ * A worker process is placed in group_a and allocates a known amount of
+ * anonymous memory, touching every page to ensure physical allocation.
+ *
+ * Test 1 - No charge transfer on migration:
+ * The worker is migrated to group_b by writing its PID to
+ * group_b/cgroup.procs. In cgroup v2 memory charges are never
+ * transferred on task migration; they remain pinned to the cgroup
+ * that allocated them. group_b/memory.current must not increase
+ * beyond its baseline after the migration.
+ *
+ * Test 2 - New allocations charged to destination cgroup:
+ * After migration the worker allocates an additional chunk of memory.
+ * This new allocation must be charged to group_b, so
+ * group_b/memory.current must increase from the post-migration
+ * baseline.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "tst_test.h"
+#include "memcontrol_common.h"
+
+#define ALLOC_SIZE MB(60)
+#define ALLOC_SIZE2 MB(20)
+
+enum checkpoints {
+ WORKER_ALLOC_DONE,
+ WORKER_RESUME,
+ WORKER_ALLOC2_DONE,
+ WORKER_EXIT,
+};
+
+static struct tst_cg_group *group_a;
+static struct tst_cg_group *group_b;
+static bool worker_resumed;
+
+static void worker(void)
+{
+ char *buf1, *buf2;
+
+ SAFE_CG_PRINTF(group_a, "cgroup.procs", "%d", getpid());
+
+ buf1 = SAFE_MMAP(NULL, ALLOC_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
+
+ TST_CHECKPOINT_WAKE(WORKER_ALLOC_DONE);
+
+ TST_CHECKPOINT_WAIT(WORKER_RESUME);
+
+ buf2 = SAFE_MMAP(NULL, ALLOC_SIZE2, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
+
+ TST_CHECKPOINT_WAKE(WORKER_ALLOC2_DONE);
+
+ TST_CHECKPOINT_WAIT(WORKER_EXIT);
+
+ SAFE_MUNMAP(buf1, ALLOC_SIZE);
+ SAFE_MUNMAP(buf2, ALLOC_SIZE2);
+}
+
+static void test_memcg_task_migration(void)
+{
+ pid_t pid;
+ long baseline_b, after_migrate, after_alloc2, current_a;
+ worker_resumed = false;
+
+ group_a = tst_cg_group_mk(tst_cg, "group_a");
+ group_b = tst_cg_group_mk(tst_cg, "group_b");
+
+ SAFE_CG_SCANF(group_b, "memory.current", "%ld", &baseline_b);
+ tst_res(TINFO, "group_b baseline memory.current=%ld", baseline_b);
+
+ pid = SAFE_FORK();
+ if (!pid) {
+ worker();
+ exit(0);
+ }
+
+ TST_CHECKPOINT_WAIT(WORKER_ALLOC_DONE);
+
+ SAFE_CG_SCANF(group_a, "memory.current", "%ld", ¤t_a);
+ tst_res(TINFO, "group_a memory.current=%ld after alloc", current_a);
+ if (current_a < (long)ALLOC_SIZE) {
+ tst_res(TFAIL,
+ "group_a memory.current (%ld) < ALLOC_SIZE (%ld)",
+ current_a, (long)ALLOC_SIZE);
+ goto done;
+ }
+ tst_res(TPASS,
+ "group_a memory.current (%ld) >= ALLOC_SIZE (%ld)",
+ current_a, (long)ALLOC_SIZE);
+
+ SAFE_CG_PRINTF(group_b, "cgroup.procs", "%d", pid);
+ tst_res(TINFO, "Migrated worker PID %d to group_b", pid);
+
+ SAFE_CG_SCANF(group_b, "memory.current", "%ld", &after_migrate);
+ tst_res(TINFO,
+ "group_b memory.current=%ld after migration (baseline=%ld)",
+ after_migrate, baseline_b);
+
+ TST_EXP_EXPR(after_migrate <= baseline_b + (long)MB(4),
+ "group_b memory.current (%ld) not increased after migration (baseline=%ld)",
+ after_migrate, baseline_b);
+
+ TST_CHECKPOINT_WAKE(WORKER_RESUME);
+ worker_resumed = true;
+ TST_CHECKPOINT_WAIT(WORKER_ALLOC2_DONE);
+
+ SAFE_CG_SCANF(group_b, "memory.current", "%ld", &after_alloc2);
+ tst_res(TINFO, "group_b memory.current=%ld after second alloc (baseline=%ld)",
+ after_alloc2, after_migrate);
+
+ TST_EXP_EXPR(after_alloc2 >= after_migrate + (long)ALLOC_SIZE2,
+ "group_b memory.current (%ld) increased by >= ALLOC_SIZE2 (%ld)",
+ after_alloc2, (long)ALLOC_SIZE2);
+
+done:
+ if (!worker_resumed) {
+ TST_CHECKPOINT_WAKE(WORKER_RESUME);
+ TST_CHECKPOINT_WAIT(WORKER_ALLOC2_DONE);
+ }
+ TST_CHECKPOINT_WAKE(WORKER_EXIT);
+ tst_reap_children();
+
+ group_a = tst_cg_group_rm(group_a);
+ group_b = tst_cg_group_rm(group_b);
+}
+
+static void cleanup(void)
+{
+ if (group_a)
+ group_a = tst_cg_group_rm(group_a);
+ if (group_b)
+ group_b = tst_cg_group_rm(group_b);
+}
+
+static struct tst_test test = {
+ .test_all = test_memcg_task_migration,
+ .cleanup = cleanup,
+ .forks_child = 1,
+ .needs_root = 1,
+ .needs_checkpoints = 1,
+ .needs_cgroup_ver = TST_CG_V2,
+ .needs_cgroup_ctrls = (const char *const []){ "memory", NULL },
+};
--
2.55.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
next reply other threads:[~2026-08-04 9:59 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-04 9:59 Pavithra [this message]
2026-08-04 10:41 ` [LTP] memcg/memcontrol05: add cgroup v2 task migration charge accounting test linuxtestproject.agent
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260804095925.495813-1-pavrampu@linux.ibm.com \
--to=pavrampu@linux.ibm.com \
--cc=ltp@lists.linux.it \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox