Thread (22 messages) 22 messages, 3 authors, 2026-01-26

Re: [RFC PATCH bpf-next v3 09/12] selftests/bpf: Add tests for memcg_bpf_ops

From: <hidden>
Date: 2026-01-26 01:40:29
Also in: bpf, cgroups, linux-kselftest, linux-mm, lkml

2026年1月24日 04:47, "JP Kobryn" <inwardvessel@gmail.com mailto:inwardvessel@gmail.com?to=%22JP%20Kobryn%22%20%3Cinwardvessel%40gmail.com%3E > 写到:

Hi Hui,

On 1/23/26 1:00 AM, Hui Zhu wrote:
quoted
From: Hui Zhu <redacted>
 Add a comprehensive selftest suite for the `memcg_bpf_ops`
 functionality. These tests validate that BPF programs can correctly
 influence memory cgroup throttling behavior by implementing the new
 hooks.
 The test suite is added in `prog_tests/memcg_ops.c` and covers
 several key scenarios:
 1. `test_memcg_ops_over_high`:
 Verifies that a BPF program can trigger throttling on a low-priority
 cgroup by returning a delay from the `get_high_delay_ms` hook when a
 high-priority cgroup is under pressure.
 2. `test_memcg_ops_below_low_over_high`:
 Tests the combination of the `below_low` and `get_high_delay_ms`
 hooks, ensuring they work together as expected.
 3. `test_memcg_ops_below_min_over_high`:
 Validates the interaction between the `below_min` and
 `get_high_delay_ms` hooks.
 The test framework sets up a cgroup hierarchy with high and low
 priority groups, attaches BPF programs, runs memory-intensive
 workloads, and asserts that the observed throttling (measured by
 workload execution time) matches expectations.
 The BPF program (`progs/memcg_ops.c`) uses a tracepoint on
 `memcg:count_memcg_events` (specifically PGFAULT) to detect memory
 pressure and trigger the appropriate hooks in response. This test
 suite provides essential validation for the new memory control
 mechanisms.
 Signed-off-by: Geliang Tang [off-list ref]
 Signed-off-by: Hui Zhu [off-list ref]
 ---
[..]
quoted
diff --git a/tools/testing/selftests/bpf/prog_tests/memcg_ops.c b/tools/testing/selftests/bpf/prog_tests/memcg_ops.c
 new file mode 100644
 index 000000000000..9a8d16296f2d
 --- /dev/null
 +++ b/tools/testing/selftests/bpf/prog_tests/memcg_ops.c
 @@ -0,0 +1,537 @@
[..]
quoted
+
 +static void
 +real_test_memcg_ops_child_work(const char *cgroup_path,
 + char *data_filename,
 + char *time_filename,
 + int read_times)
 +{
 + struct timeval start, end;
 + double elapsed;
 + FILE *fp;
 +
 + if (!ASSERT_OK(join_parent_cgroup(cgroup_path), "join_parent_cgroup"))
 + goto out;
 +
 + if (env.verbosity >= VERBOSE_NORMAL)
 + printf("%s %d begin\n", __func__, getpid());
 +
 + gettimeofday(&start, NULL);
 +
 + if (!ASSERT_OK(write_file(data_filename), "write_file"))
 + goto out;
 +
 + if (env.verbosity >= VERBOSE_NORMAL)
 + printf("%s %d write_file done\n", __func__, getpid());
 +
 + if (!ASSERT_OK(read_file(data_filename, read_times), "read_file"))
 + goto out;
 +
 + gettimeofday(&end, NULL);
 +
 + elapsed = (end.tv_sec - start.tv_sec) +
 + (end.tv_usec - start.tv_usec) / 1000000.0;
 +
 + if (env.verbosity >= VERBOSE_NORMAL)
 + printf("%s %d end %.6f\n", __func__, getpid(), elapsed);
 +
 + fp = fopen(time_filename, "w");
 + if (!ASSERT_OK_PTR(fp, "fopen"))
 + goto out;
 + fprintf(fp, "%.6f", elapsed);
 + fclose(fp);
 +
 +out:
 + exit(0);
 +}
 +
[..]
quoted
+static void real_test_memcg_ops(int read_times)
 +{
 + int ret;
 + char data_file1[] = "/tmp/test_data_XXXXXX";
 + char data_file2[] = "/tmp/test_data_XXXXXX";
 + char time_file1[] = "/tmp/test_time_XXXXXX";
 + char time_file2[] = "/tmp/test_time_XXXXXX";
 + pid_t pid1, pid2;
 + double time1, time2;
 +
 + ret = mkstemp(data_file1);
 + if (!ASSERT_GT(ret, 0, "mkstemp"))
 + return;
 + close(ret);
 + ret = mkstemp(data_file2);
 + if (!ASSERT_GT(ret, 0, "mkstemp"))
 + goto cleanup_data_file1;
 + close(ret);
 + ret = mkstemp(time_file1);
 + if (!ASSERT_GT(ret, 0, "mkstemp"))
 + goto cleanup_data_file2;
 + close(ret);
 + ret = mkstemp(time_file2);
 + if (!ASSERT_GT(ret, 0, "mkstemp"))
 + goto cleanup_time_file1;
 + close(ret);
 +
 + pid1 = fork();
 + if (!ASSERT_GE(pid1, 0, "fork"))
 + goto cleanup;
 + if (pid1 == 0)
 + real_test_memcg_ops_child_work(CG_LOW_DIR,
 + data_file1,
 + time_file1,
 + read_times);
Would it be better to call exit() after real_test_memcg_ops_child_work()
instead of within it? This way the fork/exit/wait logic is contained in
the same scope making the lifetimes easier to track. I had to go back
and search for the call to exit() since at a glance this function
appears to proceed to call fork() and waitpid() from within both parent
and child procs (though it really does not).
I will fix it.

Best,
Hui
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help