xref: /linux/tools/testing/selftests/bpf/prog_tests/dmabuf_iter.c (revision f2161d5f1aae21a42b0a64d87e10cb31db423f42)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2025 Google */
3 
4 #include <test_progs.h>
5 #include <bpf/libbpf.h>
6 #include <bpf/btf.h>
7 #include "dmabuf_iter.skel.h"
8 
9 #include <fcntl.h>
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <sys/mman.h>
16 #include <unistd.h>
17 
18 #include <linux/dma-buf.h>
19 #include <linux/dma-heap.h>
20 #include <linux/udmabuf.h>
21 
22 static int udmabuf = -1;
23 static const char udmabuf_test_buffer_name[DMA_BUF_NAME_LEN] = "udmabuf_test_buffer_for_iter";
24 static size_t udmabuf_test_buffer_size;
25 static int sysheap_dmabuf = -1;
26 static const char sysheap_test_buffer_name[DMA_BUF_NAME_LEN] = "sysheap_test_buffer_for_iter";
27 static size_t sysheap_test_buffer_size;
28 
create_udmabuf(void)29 static int create_udmabuf(void)
30 {
31 	struct udmabuf_create create;
32 	int dev_udmabuf, memfd, local_udmabuf;
33 
34 	udmabuf_test_buffer_size = 10 * getpagesize();
35 
36 	if (!ASSERT_LE(sizeof(udmabuf_test_buffer_name), DMA_BUF_NAME_LEN, "NAMETOOLONG"))
37 		return -1;
38 
39 	memfd = memfd_create("memfd_test", MFD_ALLOW_SEALING);
40 	if (!ASSERT_OK_FD(memfd, "memfd_create"))
41 		return -1;
42 
43 	if (!ASSERT_OK(ftruncate(memfd, udmabuf_test_buffer_size), "ftruncate"))
44 		goto close_memfd;
45 
46 	if (!ASSERT_OK(fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK), "seal"))
47 		goto close_memfd;
48 
49 	dev_udmabuf = open("/dev/udmabuf", O_RDONLY);
50 	if (!ASSERT_OK_FD(dev_udmabuf, "open udmabuf"))
51 		goto close_memfd;
52 
53 	memset(&create, 0, sizeof(create));
54 	create.memfd = memfd;
55 	create.flags = UDMABUF_FLAGS_CLOEXEC;
56 	create.offset = 0;
57 	create.size = udmabuf_test_buffer_size;
58 
59 	local_udmabuf = ioctl(dev_udmabuf, UDMABUF_CREATE, &create);
60 	close(dev_udmabuf);
61 	if (!ASSERT_OK_FD(local_udmabuf, "udmabuf_create"))
62 		goto close_memfd;
63 
64 	if (!ASSERT_OK(ioctl(local_udmabuf, DMA_BUF_SET_NAME_B, udmabuf_test_buffer_name), "name"))
65 		goto close_udmabuf;
66 
67 	return local_udmabuf;
68 
69 close_udmabuf:
70 	close(local_udmabuf);
71 close_memfd:
72 	close(memfd);
73 	return -1;
74 }
75 
create_sys_heap_dmabuf(size_t bytes)76 static int create_sys_heap_dmabuf(size_t bytes)
77 {
78 	struct dma_heap_allocation_data data = {
79 		.len = bytes,
80 		.fd = 0,
81 		.fd_flags = O_RDWR | O_CLOEXEC,
82 		.heap_flags = 0,
83 	};
84 	int heap_fd, ret;
85 
86 	if (!ASSERT_LE(sizeof(sysheap_test_buffer_name), DMA_BUF_NAME_LEN, "NAMETOOLONG"))
87 		return -1;
88 
89 	heap_fd = open("/dev/dma_heap/system", O_RDONLY);
90 	if (!ASSERT_OK_FD(heap_fd, "open dma heap"))
91 		return -1;
92 
93 	ret = ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data);
94 	close(heap_fd);
95 	if (!ASSERT_OK(ret, "syheap alloc"))
96 		return -1;
97 
98 	if (!ASSERT_OK(ioctl(data.fd, DMA_BUF_SET_NAME_B, sysheap_test_buffer_name), "name"))
99 		goto close_sysheap_dmabuf;
100 
101 	return data.fd;
102 
103 close_sysheap_dmabuf:
104 	close(data.fd);
105 	return -1;
106 }
107 
create_test_buffers(void)108 static int create_test_buffers(void)
109 {
110 	udmabuf = create_udmabuf();
111 
112 	sysheap_test_buffer_size = 20 * getpagesize();
113 	sysheap_dmabuf = create_sys_heap_dmabuf(sysheap_test_buffer_size);
114 
115 	if (udmabuf < 0 || sysheap_dmabuf < 0)
116 		return -1;
117 
118 	return 0;
119 }
120 
destroy_test_buffers(void)121 static void destroy_test_buffers(void)
122 {
123 	close(udmabuf);
124 	udmabuf = -1;
125 
126 	close(sysheap_dmabuf);
127 	sysheap_dmabuf = -1;
128 }
129 
130 enum Fields { INODE, SIZE, NAME, EXPORTER, FIELD_COUNT };
131 struct DmabufInfo {
132 	unsigned long inode;
133 	unsigned long size;
134 	char name[DMA_BUF_NAME_LEN];
135 	char exporter[32];
136 };
137 
check_dmabuf_info(const struct DmabufInfo * bufinfo,unsigned long size,const char * name,const char * exporter)138 static bool check_dmabuf_info(const struct DmabufInfo *bufinfo,
139 			      unsigned long size,
140 			      const char *name, const char *exporter)
141 {
142 	return size == bufinfo->size &&
143 	       !strcmp(name, bufinfo->name) &&
144 	       !strcmp(exporter, bufinfo->exporter);
145 }
146 
subtest_dmabuf_iter_check_no_infinite_reads(struct dmabuf_iter * skel)147 static void subtest_dmabuf_iter_check_no_infinite_reads(struct dmabuf_iter *skel)
148 {
149 	int iter_fd;
150 	char buf[256];
151 
152 	iter_fd = bpf_iter_create(bpf_link__fd(skel->links.dmabuf_collector));
153 	if (!ASSERT_OK_FD(iter_fd, "iter_create"))
154 		return;
155 
156 	while (read(iter_fd, buf, sizeof(buf)) > 0)
157 		; /* Read out all contents */
158 
159 	/* Next reads should return 0 */
160 	ASSERT_EQ(read(iter_fd, buf, sizeof(buf)), 0, "read");
161 
162 	close(iter_fd);
163 }
164 
subtest_dmabuf_iter_check_default_iter(struct dmabuf_iter * skel)165 static void subtest_dmabuf_iter_check_default_iter(struct dmabuf_iter *skel)
166 {
167 	bool found_test_sysheap_dmabuf = false;
168 	bool found_test_udmabuf = false;
169 	struct DmabufInfo bufinfo;
170 	size_t linesize = 0;
171 	char *line = NULL;
172 	FILE *iter_file;
173 	int iter_fd, f = INODE;
174 
175 	iter_fd = bpf_iter_create(bpf_link__fd(skel->links.dmabuf_collector));
176 	if (!ASSERT_OK_FD(iter_fd, "iter_create"))
177 		return;
178 
179 	iter_file = fdopen(iter_fd, "r");
180 	if (!ASSERT_OK_PTR(iter_file, "fdopen"))
181 		goto close_iter_fd;
182 
183 	while (getline(&line, &linesize, iter_file) != -1) {
184 		if (f % FIELD_COUNT == INODE) {
185 			ASSERT_EQ(sscanf(line, "%ld", &bufinfo.inode), 1,
186 				  "read inode");
187 		} else if (f % FIELD_COUNT == SIZE) {
188 			ASSERT_EQ(sscanf(line, "%ld", &bufinfo.size), 1,
189 				  "read size");
190 		} else if (f % FIELD_COUNT == NAME) {
191 			ASSERT_EQ(sscanf(line, "%s", bufinfo.name), 1,
192 				  "read name");
193 		} else if (f % FIELD_COUNT == EXPORTER) {
194 			ASSERT_EQ(sscanf(line, "%31s", bufinfo.exporter), 1,
195 				  "read exporter");
196 
197 			if (check_dmabuf_info(&bufinfo,
198 					      sysheap_test_buffer_size,
199 					      sysheap_test_buffer_name,
200 					      "system"))
201 				found_test_sysheap_dmabuf = true;
202 			else if (check_dmabuf_info(&bufinfo,
203 						   udmabuf_test_buffer_size,
204 						   udmabuf_test_buffer_name,
205 						   "udmabuf"))
206 				found_test_udmabuf = true;
207 		}
208 		++f;
209 	}
210 
211 	ASSERT_EQ(f % FIELD_COUNT, INODE, "number of fields");
212 
213 	ASSERT_TRUE(found_test_sysheap_dmabuf, "found_test_sysheap_dmabuf");
214 	ASSERT_TRUE(found_test_udmabuf, "found_test_udmabuf");
215 
216 	free(line);
217 	fclose(iter_file);
218 close_iter_fd:
219 	close(iter_fd);
220 }
221 
subtest_dmabuf_iter_check_lots_of_buffers(struct dmabuf_iter * skel)222 static void subtest_dmabuf_iter_check_lots_of_buffers(struct dmabuf_iter *skel)
223 {
224 	int iter_fd;
225 	char buf[1024];
226 	size_t total_bytes_read = 0;
227 	ssize_t bytes_read;
228 
229 	iter_fd = bpf_iter_create(bpf_link__fd(skel->links.dmabuf_collector));
230 	if (!ASSERT_OK_FD(iter_fd, "iter_create"))
231 		return;
232 
233 	while ((bytes_read = read(iter_fd, buf, sizeof(buf))) > 0)
234 		total_bytes_read += bytes_read;
235 
236 	ASSERT_GT(total_bytes_read, getpagesize(), "total_bytes_read");
237 
238 	close(iter_fd);
239 }
240 
241 
subtest_dmabuf_iter_check_open_coded(struct dmabuf_iter * skel,int map_fd)242 static void subtest_dmabuf_iter_check_open_coded(struct dmabuf_iter *skel, int map_fd)
243 {
244 	LIBBPF_OPTS(bpf_test_run_opts, topts);
245 	char key[DMA_BUF_NAME_LEN];
246 	int err, fd;
247 	bool found;
248 
249 	/* No need to attach it, just run it directly */
250 	fd = bpf_program__fd(skel->progs.iter_dmabuf_for_each);
251 
252 	err = bpf_prog_test_run_opts(fd, &topts);
253 	if (!ASSERT_OK(err, "test_run_opts err"))
254 		return;
255 	if (!ASSERT_OK(topts.retval, "test_run_opts retval"))
256 		return;
257 
258 	if (!ASSERT_OK(bpf_map_get_next_key(map_fd, NULL, key), "get next key"))
259 		return;
260 
261 	do {
262 		ASSERT_OK(bpf_map_lookup_elem(map_fd, key, &found), "lookup");
263 		ASSERT_TRUE(found, "found test buffer");
264 	} while (bpf_map_get_next_key(map_fd, key, key));
265 }
266 
test_dmabuf_iter(void)267 void test_dmabuf_iter(void)
268 {
269 	struct dmabuf_iter *skel = NULL;
270 	int map_fd;
271 	const bool f = false;
272 
273 	skel = dmabuf_iter__open_and_load();
274 	if (!ASSERT_OK_PTR(skel, "dmabuf_iter__open_and_load"))
275 		return;
276 
277 	map_fd = bpf_map__fd(skel->maps.testbuf_hash);
278 	if (!ASSERT_OK_FD(map_fd, "map_fd"))
279 		goto destroy_skel;
280 
281 	if (!ASSERT_OK(bpf_map_update_elem(map_fd, udmabuf_test_buffer_name, &f, BPF_ANY),
282 		       "insert udmabuf"))
283 		goto destroy_skel;
284 	if (!ASSERT_OK(bpf_map_update_elem(map_fd, sysheap_test_buffer_name, &f, BPF_ANY),
285 		       "insert sysheap buffer"))
286 		goto destroy_skel;
287 
288 	if (!ASSERT_OK(create_test_buffers(), "create_test_buffers"))
289 		goto destroy;
290 
291 	if (!ASSERT_OK(dmabuf_iter__attach(skel), "skel_attach"))
292 		goto destroy;
293 
294 	if (test__start_subtest("no_infinite_reads"))
295 		subtest_dmabuf_iter_check_no_infinite_reads(skel);
296 	if (test__start_subtest("default_iter"))
297 		subtest_dmabuf_iter_check_default_iter(skel);
298 	if (test__start_subtest("lots_of_buffers")) {
299 		size_t NUM_BUFS = 100;
300 		int buffers[NUM_BUFS];
301 		int i;
302 
303 		for (i = 0; i < NUM_BUFS; ++i) {
304 			buffers[i] = create_sys_heap_dmabuf(getpagesize());
305 			if (!ASSERT_OK_FD(buffers[i], "dmabuf_fd"))
306 				goto cleanup_bufs;
307 		}
308 
309 		subtest_dmabuf_iter_check_lots_of_buffers(skel);
310 
311 cleanup_bufs:
312 		for (--i; i >= 0; --i)
313 			close(buffers[i]);
314 	}
315 	if (test__start_subtest("open_coded"))
316 		subtest_dmabuf_iter_check_open_coded(skel, map_fd);
317 
318 destroy:
319 	destroy_test_buffers();
320 destroy_skel:
321 	dmabuf_iter__destroy(skel);
322 }
323