xref: /linux/tools/testing/selftests/bpf/prog_tests/cgroup_iter_memcg.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1*6bce6ddbSJP Kobryn // SPDX-License-Identifier: GPL-2.0
2*6bce6ddbSJP Kobryn /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3*6bce6ddbSJP Kobryn #include <test_progs.h>
4*6bce6ddbSJP Kobryn #include <bpf/libbpf.h>
5*6bce6ddbSJP Kobryn #include <bpf/btf.h>
6*6bce6ddbSJP Kobryn #include <fcntl.h>
7*6bce6ddbSJP Kobryn #include <sys/mman.h>
8*6bce6ddbSJP Kobryn #include <unistd.h>
9*6bce6ddbSJP Kobryn #include "cgroup_helpers.h"
10*6bce6ddbSJP Kobryn #include "cgroup_iter_memcg.h"
11*6bce6ddbSJP Kobryn #include "cgroup_iter_memcg.skel.h"
12*6bce6ddbSJP Kobryn 
13*6bce6ddbSJP Kobryn static int read_stats(struct bpf_link *link)
14*6bce6ddbSJP Kobryn {
15*6bce6ddbSJP Kobryn 	int fd, ret = 0;
16*6bce6ddbSJP Kobryn 	ssize_t bytes;
17*6bce6ddbSJP Kobryn 
18*6bce6ddbSJP Kobryn 	fd = bpf_iter_create(bpf_link__fd(link));
19*6bce6ddbSJP Kobryn 	if (!ASSERT_OK_FD(fd, "bpf_iter_create"))
20*6bce6ddbSJP Kobryn 		return 1;
21*6bce6ddbSJP Kobryn 
22*6bce6ddbSJP Kobryn 	/*
23*6bce6ddbSJP Kobryn 	 * Invoke iter program by reading from its fd. We're not expecting any
24*6bce6ddbSJP Kobryn 	 * data to be written by the bpf program so the result should be zero.
25*6bce6ddbSJP Kobryn 	 * Results will be read directly through the custom data section
26*6bce6ddbSJP Kobryn 	 * accessible through skel->data_query.memcg_query.
27*6bce6ddbSJP Kobryn 	 */
28*6bce6ddbSJP Kobryn 	bytes = read(fd, NULL, 0);
29*6bce6ddbSJP Kobryn 	if (!ASSERT_EQ(bytes, 0, "read fd"))
30*6bce6ddbSJP Kobryn 		ret = 1;
31*6bce6ddbSJP Kobryn 
32*6bce6ddbSJP Kobryn 	close(fd);
33*6bce6ddbSJP Kobryn 	return ret;
34*6bce6ddbSJP Kobryn }
35*6bce6ddbSJP Kobryn 
36*6bce6ddbSJP Kobryn static void test_anon(struct bpf_link *link, struct memcg_query *memcg_query)
37*6bce6ddbSJP Kobryn {
38*6bce6ddbSJP Kobryn 	void *map;
39*6bce6ddbSJP Kobryn 	size_t len;
40*6bce6ddbSJP Kobryn 
41*6bce6ddbSJP Kobryn 	len = sysconf(_SC_PAGESIZE) * 1024;
42*6bce6ddbSJP Kobryn 
43*6bce6ddbSJP Kobryn 	/*
44*6bce6ddbSJP Kobryn 	 * Increase memcg anon usage by mapping and writing
45*6bce6ddbSJP Kobryn 	 * to a new anon region.
46*6bce6ddbSJP Kobryn 	 */
47*6bce6ddbSJP Kobryn 	map = mmap(NULL, len, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
48*6bce6ddbSJP Kobryn 	if (!ASSERT_NEQ(map, MAP_FAILED, "mmap anon"))
49*6bce6ddbSJP Kobryn 		return;
50*6bce6ddbSJP Kobryn 
51*6bce6ddbSJP Kobryn 	memset(map, 1, len);
52*6bce6ddbSJP Kobryn 
53*6bce6ddbSJP Kobryn 	if (!ASSERT_OK(read_stats(link), "read stats"))
54*6bce6ddbSJP Kobryn 		goto cleanup;
55*6bce6ddbSJP Kobryn 
56*6bce6ddbSJP Kobryn 	ASSERT_GT(memcg_query->nr_anon_mapped, 0, "final anon mapped val");
57*6bce6ddbSJP Kobryn 
58*6bce6ddbSJP Kobryn cleanup:
59*6bce6ddbSJP Kobryn 	munmap(map, len);
60*6bce6ddbSJP Kobryn }
61*6bce6ddbSJP Kobryn 
62*6bce6ddbSJP Kobryn static void test_file(struct bpf_link *link, struct memcg_query *memcg_query)
63*6bce6ddbSJP Kobryn {
64*6bce6ddbSJP Kobryn 	void *map;
65*6bce6ddbSJP Kobryn 	size_t len;
66*6bce6ddbSJP Kobryn 	char *path;
67*6bce6ddbSJP Kobryn 	int fd;
68*6bce6ddbSJP Kobryn 
69*6bce6ddbSJP Kobryn 	len = sysconf(_SC_PAGESIZE) * 1024;
70*6bce6ddbSJP Kobryn 	path = "/tmp/test_cgroup_iter_memcg";
71*6bce6ddbSJP Kobryn 
72*6bce6ddbSJP Kobryn 	/*
73*6bce6ddbSJP Kobryn 	 * Increase memcg file usage by creating and writing
74*6bce6ddbSJP Kobryn 	 * to a mapped file.
75*6bce6ddbSJP Kobryn 	 */
76*6bce6ddbSJP Kobryn 	fd = open(path, O_CREAT | O_RDWR, 0644);
77*6bce6ddbSJP Kobryn 	if (!ASSERT_OK_FD(fd, "open fd"))
78*6bce6ddbSJP Kobryn 		return;
79*6bce6ddbSJP Kobryn 	if (!ASSERT_OK(ftruncate(fd, len), "ftruncate"))
80*6bce6ddbSJP Kobryn 		goto cleanup_fd;
81*6bce6ddbSJP Kobryn 
82*6bce6ddbSJP Kobryn 	map = mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, 0);
83*6bce6ddbSJP Kobryn 	if (!ASSERT_NEQ(map, MAP_FAILED, "mmap file"))
84*6bce6ddbSJP Kobryn 		goto cleanup_fd;
85*6bce6ddbSJP Kobryn 
86*6bce6ddbSJP Kobryn 	memset(map, 1, len);
87*6bce6ddbSJP Kobryn 
88*6bce6ddbSJP Kobryn 	if (!ASSERT_OK(read_stats(link), "read stats"))
89*6bce6ddbSJP Kobryn 		goto cleanup_map;
90*6bce6ddbSJP Kobryn 
91*6bce6ddbSJP Kobryn 	ASSERT_GT(memcg_query->nr_file_pages, 0, "final file value");
92*6bce6ddbSJP Kobryn 	ASSERT_GT(memcg_query->nr_file_mapped, 0, "final file mapped value");
93*6bce6ddbSJP Kobryn 
94*6bce6ddbSJP Kobryn cleanup_map:
95*6bce6ddbSJP Kobryn 	munmap(map, len);
96*6bce6ddbSJP Kobryn cleanup_fd:
97*6bce6ddbSJP Kobryn 	close(fd);
98*6bce6ddbSJP Kobryn 	unlink(path);
99*6bce6ddbSJP Kobryn }
100*6bce6ddbSJP Kobryn 
101*6bce6ddbSJP Kobryn static void test_shmem(struct bpf_link *link, struct memcg_query *memcg_query)
102*6bce6ddbSJP Kobryn {
103*6bce6ddbSJP Kobryn 	size_t len;
104*6bce6ddbSJP Kobryn 	int fd;
105*6bce6ddbSJP Kobryn 
106*6bce6ddbSJP Kobryn 	len = sysconf(_SC_PAGESIZE) * 1024;
107*6bce6ddbSJP Kobryn 
108*6bce6ddbSJP Kobryn 	/*
109*6bce6ddbSJP Kobryn 	 * Increase memcg shmem usage by creating and writing
110*6bce6ddbSJP Kobryn 	 * to a shmem object.
111*6bce6ddbSJP Kobryn 	 */
112*6bce6ddbSJP Kobryn 	fd = shm_open("/tmp_shmem", O_CREAT | O_RDWR, 0644);
113*6bce6ddbSJP Kobryn 	if (!ASSERT_OK_FD(fd, "shm_open"))
114*6bce6ddbSJP Kobryn 		return;
115*6bce6ddbSJP Kobryn 
116*6bce6ddbSJP Kobryn 	if (!ASSERT_OK(fallocate(fd, 0, 0, len), "fallocate"))
117*6bce6ddbSJP Kobryn 		goto cleanup;
118*6bce6ddbSJP Kobryn 
119*6bce6ddbSJP Kobryn 	if (!ASSERT_OK(read_stats(link), "read stats"))
120*6bce6ddbSJP Kobryn 		goto cleanup;
121*6bce6ddbSJP Kobryn 
122*6bce6ddbSJP Kobryn 	ASSERT_GT(memcg_query->nr_shmem, 0, "final shmem value");
123*6bce6ddbSJP Kobryn 
124*6bce6ddbSJP Kobryn cleanup:
125*6bce6ddbSJP Kobryn 	close(fd);
126*6bce6ddbSJP Kobryn 	shm_unlink("/tmp_shmem");
127*6bce6ddbSJP Kobryn }
128*6bce6ddbSJP Kobryn 
129*6bce6ddbSJP Kobryn #define NR_PIPES 64
130*6bce6ddbSJP Kobryn static void test_kmem(struct bpf_link *link, struct memcg_query *memcg_query)
131*6bce6ddbSJP Kobryn {
132*6bce6ddbSJP Kobryn 	int fds[NR_PIPES][2], i;
133*6bce6ddbSJP Kobryn 
134*6bce6ddbSJP Kobryn 	/*
135*6bce6ddbSJP Kobryn 	 * Increase kmem value by creating pipes which will allocate some
136*6bce6ddbSJP Kobryn 	 * kernel buffers.
137*6bce6ddbSJP Kobryn 	 */
138*6bce6ddbSJP Kobryn 	for (i = 0; i < NR_PIPES; i++) {
139*6bce6ddbSJP Kobryn 		if (!ASSERT_OK(pipe(fds[i]), "pipe"))
140*6bce6ddbSJP Kobryn 			goto cleanup;
141*6bce6ddbSJP Kobryn 	}
142*6bce6ddbSJP Kobryn 
143*6bce6ddbSJP Kobryn 	if (!ASSERT_OK(read_stats(link), "read stats"))
144*6bce6ddbSJP Kobryn 		goto cleanup;
145*6bce6ddbSJP Kobryn 
146*6bce6ddbSJP Kobryn 	ASSERT_GT(memcg_query->memcg_kmem, 0, "kmem value");
147*6bce6ddbSJP Kobryn 
148*6bce6ddbSJP Kobryn cleanup:
149*6bce6ddbSJP Kobryn 	for (i = i - 1; i >= 0; i--) {
150*6bce6ddbSJP Kobryn 		close(fds[i][0]);
151*6bce6ddbSJP Kobryn 		close(fds[i][1]);
152*6bce6ddbSJP Kobryn 	}
153*6bce6ddbSJP Kobryn }
154*6bce6ddbSJP Kobryn 
155*6bce6ddbSJP Kobryn static void test_pgfault(struct bpf_link *link, struct memcg_query *memcg_query)
156*6bce6ddbSJP Kobryn {
157*6bce6ddbSJP Kobryn 	void *map;
158*6bce6ddbSJP Kobryn 	size_t len;
159*6bce6ddbSJP Kobryn 
160*6bce6ddbSJP Kobryn 	len = sysconf(_SC_PAGESIZE) * 1024;
161*6bce6ddbSJP Kobryn 
162*6bce6ddbSJP Kobryn 	/* Create region to use for triggering a page fault. */
163*6bce6ddbSJP Kobryn 	map = mmap(NULL, len, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
164*6bce6ddbSJP Kobryn 	if (!ASSERT_NEQ(map, MAP_FAILED, "mmap anon"))
165*6bce6ddbSJP Kobryn 		return;
166*6bce6ddbSJP Kobryn 
167*6bce6ddbSJP Kobryn 	/* Trigger page fault. */
168*6bce6ddbSJP Kobryn 	memset(map, 1, len);
169*6bce6ddbSJP Kobryn 
170*6bce6ddbSJP Kobryn 	if (!ASSERT_OK(read_stats(link), "read stats"))
171*6bce6ddbSJP Kobryn 		goto cleanup;
172*6bce6ddbSJP Kobryn 
173*6bce6ddbSJP Kobryn 	ASSERT_GT(memcg_query->pgfault, 0, "final pgfault val");
174*6bce6ddbSJP Kobryn 
175*6bce6ddbSJP Kobryn cleanup:
176*6bce6ddbSJP Kobryn 	munmap(map, len);
177*6bce6ddbSJP Kobryn }
178*6bce6ddbSJP Kobryn 
179*6bce6ddbSJP Kobryn void test_cgroup_iter_memcg(void)
180*6bce6ddbSJP Kobryn {
181*6bce6ddbSJP Kobryn 	char *cgroup_rel_path = "/cgroup_iter_memcg_test";
182*6bce6ddbSJP Kobryn 	struct cgroup_iter_memcg *skel;
183*6bce6ddbSJP Kobryn 	struct bpf_link *link;
184*6bce6ddbSJP Kobryn 	int cgroup_fd;
185*6bce6ddbSJP Kobryn 
186*6bce6ddbSJP Kobryn 	cgroup_fd = cgroup_setup_and_join(cgroup_rel_path);
187*6bce6ddbSJP Kobryn 	if (!ASSERT_OK_FD(cgroup_fd, "cgroup_setup_and_join"))
188*6bce6ddbSJP Kobryn 		return;
189*6bce6ddbSJP Kobryn 
190*6bce6ddbSJP Kobryn 	skel = cgroup_iter_memcg__open_and_load();
191*6bce6ddbSJP Kobryn 	if (!ASSERT_OK_PTR(skel, "cgroup_iter_memcg__open_and_load"))
192*6bce6ddbSJP Kobryn 		goto cleanup_cgroup_fd;
193*6bce6ddbSJP Kobryn 
194*6bce6ddbSJP Kobryn 	DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
195*6bce6ddbSJP Kobryn 	union bpf_iter_link_info linfo = {
196*6bce6ddbSJP Kobryn 		.cgroup.cgroup_fd = cgroup_fd,
197*6bce6ddbSJP Kobryn 		.cgroup.order = BPF_CGROUP_ITER_SELF_ONLY,
198*6bce6ddbSJP Kobryn 	};
199*6bce6ddbSJP Kobryn 	opts.link_info = &linfo;
200*6bce6ddbSJP Kobryn 	opts.link_info_len = sizeof(linfo);
201*6bce6ddbSJP Kobryn 
202*6bce6ddbSJP Kobryn 	link = bpf_program__attach_iter(skel->progs.cgroup_memcg_query, &opts);
203*6bce6ddbSJP Kobryn 	if (!ASSERT_OK_PTR(link, "bpf_program__attach_iter"))
204*6bce6ddbSJP Kobryn 		goto cleanup_skel;
205*6bce6ddbSJP Kobryn 
206*6bce6ddbSJP Kobryn 	if (test__start_subtest("cgroup_iter_memcg__anon"))
207*6bce6ddbSJP Kobryn 		test_anon(link, &skel->data_query->memcg_query);
208*6bce6ddbSJP Kobryn 	if (test__start_subtest("cgroup_iter_memcg__shmem"))
209*6bce6ddbSJP Kobryn 		test_shmem(link, &skel->data_query->memcg_query);
210*6bce6ddbSJP Kobryn 	if (test__start_subtest("cgroup_iter_memcg__file"))
211*6bce6ddbSJP Kobryn 		test_file(link, &skel->data_query->memcg_query);
212*6bce6ddbSJP Kobryn 	if (test__start_subtest("cgroup_iter_memcg__kmem"))
213*6bce6ddbSJP Kobryn 		test_kmem(link, &skel->data_query->memcg_query);
214*6bce6ddbSJP Kobryn 	if (test__start_subtest("cgroup_iter_memcg__pgfault"))
215*6bce6ddbSJP Kobryn 		test_pgfault(link, &skel->data_query->memcg_query);
216*6bce6ddbSJP Kobryn 
217*6bce6ddbSJP Kobryn 	bpf_link__destroy(link);
218*6bce6ddbSJP Kobryn cleanup_skel:
219*6bce6ddbSJP Kobryn 	cgroup_iter_memcg__destroy(skel);
220*6bce6ddbSJP Kobryn cleanup_cgroup_fd:
221*6bce6ddbSJP Kobryn 	close(cgroup_fd);
222*6bce6ddbSJP Kobryn 	cleanup_cgroup_environment();
223*6bce6ddbSJP Kobryn }
224