Add test_cma_memcg with two simple tests to exercise CMA memcg accounting using the dma-buf CMA heap (/dev/dma_heap/default_cma_region). test_cma_charge allocates a 4MB buffer in the default CMA area, verifies memory.cma.reserved.current increased by that amount, then frees the buffer and verifies that the counter returns to its original value. test_cma_limit sets memory.cma.reserved.max to 2MB, attempts a 4MB allocation in the default CMA area and verifies it fails as expected. Skip the test if memory_cma_accounting is not enabled on the cgroupfs. Signed-off-by: Eric Chanudet --- tools/testing/selftests/cgroup/.gitignore | 1 + tools/testing/selftests/cgroup/Makefile | 2 + tools/testing/selftests/cgroup/test_cma_memcg.c | 193 ++++++++++++++++++++++++ 3 files changed, 196 insertions(+) diff --git a/tools/testing/selftests/cgroup/.gitignore b/tools/testing/selftests/cgroup/.gitignore index 952e4448bf07..b9a4298d4944 100644 --- a/tools/testing/selftests/cgroup/.gitignore +++ b/tools/testing/selftests/cgroup/.gitignore @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only test_core +test_cma_memcg test_cpu test_cpuset test_freezer diff --git a/tools/testing/selftests/cgroup/Makefile b/tools/testing/selftests/cgroup/Makefile index e01584c2189a..601d930160a4 100644 --- a/tools/testing/selftests/cgroup/Makefile +++ b/tools/testing/selftests/cgroup/Makefile @@ -8,6 +8,7 @@ TEST_PROGS := test_stress.sh test_cpuset_prs.sh test_cpuset_v1_hp.sh TEST_GEN_FILES := wait_inotify # Keep the lists lexicographically sorted TEST_GEN_PROGS = test_core +TEST_GEN_PROGS += test_cma_memcg TEST_GEN_PROGS += test_cpu TEST_GEN_PROGS += test_cpuset TEST_GEN_PROGS += test_freezer @@ -24,6 +25,7 @@ include ../lib.mk include lib/libcgroup.mk $(OUTPUT)/test_core: $(LIBCGROUP_O) +$(OUTPUT)/test_cma_memcg: $(LIBCGROUP_O) $(OUTPUT)/test_cpu: $(LIBCGROUP_O) $(OUTPUT)/test_cpuset: $(LIBCGROUP_O) $(OUTPUT)/test_freezer: $(LIBCGROUP_O) diff --git a/tools/testing/selftests/cgroup/test_cma_memcg.c b/tools/testing/selftests/cgroup/test_cma_memcg.c new file mode 100644 index 000000000000..b5b5ea140518 --- /dev/null +++ b/tools/testing/selftests/cgroup/test_cma_memcg.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include "kselftest.h" +#include "cgroup_util.h" + +#define CMA_HEAP_DEFAULT_PATH "/dev/dma_heap/default_cma_region" +#define CMA_RESERVED_CURRENT "memory.cma.reserved.current" +#define CMA_RESERVED_MAX "memory.cma.reserved.max" + +static int cma_heap_alloc(int heap_fd, size_t size) +{ + struct dma_heap_allocation_data data = { + .len = size, + .fd_flags = O_RDWR | O_CLOEXEC, + }; + + if (ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data) < 0) + return -1; + + return data.fd; +} + +static int test_cma_charge(const char *root) +{ + int heap_fd = -1, buf_fd = -1; + int rc = KSFT_FAIL, in_cg = 0; + size_t size = 4UL << 20; + long before, after; + char *cg; + + cg = cg_name(root, "memcg_cma_charge"); + if (!cg) + goto cleanup; + + if (cg_create(cg)) + goto cleanup; + + if (cg_enter_current(cg)) + goto cleanup; + in_cg = 1; + + heap_fd = open(CMA_HEAP_DEFAULT_PATH, O_RDONLY | O_CLOEXEC); + if (heap_fd < 0) { + ksft_print_msg("cannot open CMA heap (%m)\n"); + goto cleanup; + } + + before = cg_read_long(cg, CMA_RESERVED_CURRENT); + + buf_fd = cma_heap_alloc(heap_fd, size); + if (buf_fd < 0) { + ksft_print_msg("cannot allocate in CMA heap (%m)\n"); + goto cleanup; + } + + after = cg_read_long(cg, CMA_RESERVED_CURRENT); + + if (after - before < (long)size) { + ksft_print_msg( + "invalid CMA counter after charge: before=%ld after=%ld\n", + before, after); + goto cleanup; + } + + close(buf_fd); + buf_fd = -1; + + after = cg_read_long(cg, CMA_RESERVED_CURRENT); + if (!values_close(before, after, 3)) { + ksft_print_msg( + "invalid CMA counter after uncharge: before=%ld after=%ld\n", + before, after); + goto cleanup; + } + + rc = KSFT_PASS; + +cleanup: + if (buf_fd >= 0) + close(buf_fd); + if (heap_fd >= 0) + close(heap_fd); + + if (in_cg) + cg_enter_current(root); + cg_destroy(cg); + free(cg); + + return rc; +} + +static int test_cma_limit(const char *root) +{ + int heap_fd = -1, buf_fd = -1; + int rc = KSFT_FAIL, in_cg = 0; + size_t size = 4UL << 20; + char *cg; + + cg = cg_name(root, "memcg_cma_limit"); + if (!cg) + goto cleanup; + + if (cg_create(cg)) + goto cleanup; + + if (cg_enter_current(cg)) + goto cleanup; + in_cg = 1; + + if (cg_write(cg, CMA_RESERVED_MAX, "2M")) { + ksft_print_msg("cannot set CMA limit (%m)\n"); + goto cleanup; + } + + heap_fd = open(CMA_HEAP_DEFAULT_PATH, O_RDONLY | O_CLOEXEC); + if (heap_fd < 0) { + ksft_print_msg("cannot open CMA heap (%m)\n"); + goto cleanup; + } + + buf_fd = cma_heap_alloc(heap_fd, size); + if (buf_fd >= 0) { + ksft_print_msg("CMA limit not enforced.\n"); + close(buf_fd); + } else + rc = KSFT_PASS; + +cleanup: + if (heap_fd >= 0) + close(heap_fd); + + if (in_cg) + cg_enter_current(root); + cg_destroy(cg); + free(cg); + + return rc; +} + +#define T(x) { x, #x } +struct cma_memcg_tests { + int (*fn)(const char *root); + const char *name; +} tests[] = { + T(test_cma_charge), + T(test_cma_limit), +}; +#undef T + +int main(int argc, char *argv[]) +{ + char root[PATH_MAX]; + int has_memory_cma_acc, i; + + ksft_print_header(); + ksft_set_plan(ARRAY_SIZE(tests)); + + has_memory_cma_acc = proc_mount_contains("memory_cma_accounting"); + if (has_memory_cma_acc < 0) + ksft_exit_skip("cannot query cgroup mount option.\n"); + else if (!has_memory_cma_acc) + ksft_exit_skip("CMA accounting is disabled.\n"); + + if (cg_find_unified_root(root, sizeof(root), NULL)) + ksft_exit_skip("cgroupfs is not available.\n"); + + if (cg_read_strstr(root, "cgroup.controllers", "memory")) + ksft_exit_skip("memory controller is not available (CONFIG_CGROUP_MEMCG?)\n"); + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + switch (tests[i].fn(root)) { + case KSFT_PASS: + ksft_test_result_pass("%s\n", tests[i].name); + break; + case KSFT_SKIP: + ksft_test_result_skip("%s\n", tests[i].name); + break; + default: + ksft_test_result_fail("%s\n", tests[i].name); + break; + } + } + + ksft_finished(); +} -- 2.53.0