From: Hui Zhu <[email protected]>

Add comprehensive selftest suite for memory controller eBPF
support. The tests validate the core functionality of the
memcg_ops struct ops implementation.

Test coverage includes:

1. test_memcg_ops_load: Validates that the eBPF object file
   can be successfully loaded by libbpf.

2. test_memcg_ops_attach: Tests attaching the memcg_ops struct
   ops to the kernel, verifying the basic attachment mechanism
   works correctly.

3. test_memcg_ops_double_attach: Validates that only one
   memcg_ops instance can be attached at a time. Attempts to
   attach a second program should fail, ensuring the
   single-handler design constraint is enforced.

The test suite includes:

- prog_tests/memcg_ops.c: Test entry point with individual
  test functions using standard BPF test framework helpers
  like ASSERT_OK_PTR and ASSERT_ERR_PTR
- progs/memcg_ops.bpf.c: Simple eBPF program implementing
  the struct ops interface

Uses standard test_progs framework macros for consistent error
reporting and handling. All tests properly clean up resources
in error paths.

Signed-off-by: Geliang Tang <[email protected]>
Signed-off-by: Hui Zhu <[email protected]>
---
 MAINTAINERS                                   |   1 +
 .../selftests/bpf/prog_tests/memcg_ops.c      | 117 ++++++++++++++++++
 tools/testing/selftests/bpf/progs/memcg_ops.c |  20 +++
 3 files changed, 138 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/memcg_ops.c
 create mode 100644 tools/testing/selftests/bpf/progs/memcg_ops.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 498d01c9a48e..dc3aa53d2346 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6356,6 +6356,7 @@ F:        mm/memcontrol_bpf.h
 F:     mm/page_counter.c
 F:     mm/swap_cgroup.c
 F:     samples/cgroup/*
+F:     tools/testing/selftests/bpf/*/memcg_ops.c
 F:     tools/testing/selftests/cgroup/memcg_protection.m
 F:     tools/testing/selftests/cgroup/test_hugetlb_memcg.c
 F:     tools/testing/selftests/cgroup/test_kmem.c
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..3f989bcfb8c4
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/memcg_ops.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Memory controller eBPF struct ops test
+ */
+
+#include <test_progs.h>
+#include <bpf/btf.h>
+
+void test_memcg_ops_load(void)
+{
+       struct bpf_object *obj;
+       int err;
+
+       obj = bpf_object__open_file("memcg_ops.bpf.o", NULL);
+       err = libbpf_get_error(obj);
+       if (CHECK_FAIL(err)) {
+               obj = NULL;
+               goto out;
+       }
+
+       err = bpf_object__load(obj);
+       if (CHECK_FAIL(err))
+               goto out;
+
+out:
+       if (obj)
+               bpf_object__close(obj);
+}
+
+void test_memcg_ops_attach(void)
+{
+       struct bpf_object *obj;
+       struct bpf_map *map;
+       struct bpf_link *link = NULL;
+       int err;
+
+       obj = bpf_object__open_file("memcg_ops.bpf.o", NULL);
+       err = libbpf_get_error(obj);
+       if (CHECK_FAIL(err)) {
+               obj = NULL;
+               goto out;
+       }
+
+       err = bpf_object__load(obj);
+       if (CHECK_FAIL(err))
+               goto out;
+
+       map = bpf_object__find_map_by_name(obj, "mcg_ops");
+       if (!ASSERT_OK_PTR(map, "bpf_object__find_map_by_name"))
+               goto out;
+
+       link = bpf_map__attach_struct_ops(map);
+       if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops"))
+               goto out;
+
+out:
+       if (link)
+               bpf_link__destroy(link);
+       if (obj)
+               bpf_object__close(obj);
+}
+
+void test_memcg_ops_double_attach(void)
+{
+       struct bpf_object *obj, *obj2;
+       struct bpf_map *map, *map2;
+       struct bpf_link *link = NULL, *link2 = NULL;
+       int err;
+
+       obj = bpf_object__open_file("memcg_ops.bpf.o", NULL);
+       err = libbpf_get_error(obj);
+       if (CHECK_FAIL(err)) {
+               obj = NULL;
+               goto out;
+       }
+
+       err = bpf_object__load(obj);
+       if (CHECK_FAIL(err))
+               goto out;
+
+       map = bpf_object__find_map_by_name(obj, "mcg_ops");
+       if (!ASSERT_OK_PTR(map, "bpf_object__find_map_by_name"))
+               goto out;
+
+       link = bpf_map__attach_struct_ops(map);
+       if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops"))
+               goto out;
+
+       obj2 = bpf_object__open_file("memcg_ops.bpf.o", NULL);
+       err = libbpf_get_error(obj2);
+       if (CHECK_FAIL(err)) {
+               obj2 = NULL;
+               goto out;
+       }
+
+       err = bpf_object__load(obj2);
+       if (CHECK_FAIL(err))
+               goto out;
+
+       map2 = bpf_object__find_map_by_name(obj2, "mcg_ops");
+       if (!ASSERT_OK_PTR(map, "bpf_object__find_map_by_name"))
+               goto out;
+
+       link2 = bpf_map__attach_struct_ops(map2);
+       if (!ASSERT_ERR_PTR(link2, "bpf_map__attach_struct_ops")) {
+               bpf_link__destroy(link2);
+               goto out;
+       }
+
+out:
+       if (link)
+               bpf_link__destroy(link);
+       if (obj)
+               bpf_object__close(obj);
+       if (obj2)
+               bpf_object__close(obj2);
+}
diff --git a/tools/testing/selftests/bpf/progs/memcg_ops.c 
b/tools/testing/selftests/bpf/progs/memcg_ops.c
new file mode 100644
index 000000000000..a21fbe859fd3
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/memcg_ops.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+SEC("struct_ops/try_charge_memcg")
+int BPF_PROG(test_try_charge_memcg,
+            struct try_charge_memcg *tcm)
+{
+       return 0;
+}
+
+SEC(".struct_ops")
+struct memcg_ops mcg_ops = {
+       .try_charge_memcg = (void *)test_try_charge_memcg,
+};
-- 
2.43.0


Reply via email to