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(void)76 static int create_sys_heap_dmabuf(void)
77 {
78 	sysheap_test_buffer_size = 20 * getpagesize();
79 
80 	struct dma_heap_allocation_data data = {
81 		.len = sysheap_test_buffer_size,
82 		.fd = 0,
83 		.fd_flags = O_RDWR | O_CLOEXEC,
84 		.heap_flags = 0,
85 	};
86 	int heap_fd, ret;
87 
88 	if (!ASSERT_LE(sizeof(sysheap_test_buffer_name), DMA_BUF_NAME_LEN, "NAMETOOLONG"))
89 		return -1;
90 
91 	heap_fd = open("/dev/dma_heap/system", O_RDONLY);
92 	if (!ASSERT_OK_FD(heap_fd, "open dma heap"))
93 		return -1;
94 
95 	ret = ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data);
96 	close(heap_fd);
97 	if (!ASSERT_OK(ret, "syheap alloc"))
98 		return -1;
99 
100 	if (!ASSERT_OK(ioctl(data.fd, DMA_BUF_SET_NAME_B, sysheap_test_buffer_name), "name"))
101 		goto close_sysheap_dmabuf;
102 
103 	return data.fd;
104 
105 close_sysheap_dmabuf:
106 	close(data.fd);
107 	return -1;
108 }
109 
create_test_buffers(void)110 static int create_test_buffers(void)
111 {
112 	udmabuf = create_udmabuf();
113 	sysheap_dmabuf = create_sys_heap_dmabuf();
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_open_coded(struct dmabuf_iter * skel,int map_fd)222 static void subtest_dmabuf_iter_check_open_coded(struct dmabuf_iter *skel, int map_fd)
223 {
224 	LIBBPF_OPTS(bpf_test_run_opts, topts);
225 	char key[DMA_BUF_NAME_LEN];
226 	int err, fd;
227 	bool found;
228 
229 	/* No need to attach it, just run it directly */
230 	fd = bpf_program__fd(skel->progs.iter_dmabuf_for_each);
231 
232 	err = bpf_prog_test_run_opts(fd, &topts);
233 	if (!ASSERT_OK(err, "test_run_opts err"))
234 		return;
235 	if (!ASSERT_OK(topts.retval, "test_run_opts retval"))
236 		return;
237 
238 	if (!ASSERT_OK(bpf_map_get_next_key(map_fd, NULL, key), "get next key"))
239 		return;
240 
241 	do {
242 		ASSERT_OK(bpf_map_lookup_elem(map_fd, key, &found), "lookup");
243 		ASSERT_TRUE(found, "found test buffer");
244 	} while (bpf_map_get_next_key(map_fd, key, key));
245 }
246 
test_dmabuf_iter(void)247 void test_dmabuf_iter(void)
248 {
249 	struct dmabuf_iter *skel = NULL;
250 	int map_fd;
251 	const bool f = false;
252 
253 	skel = dmabuf_iter__open_and_load();
254 	if (!ASSERT_OK_PTR(skel, "dmabuf_iter__open_and_load"))
255 		return;
256 
257 	map_fd = bpf_map__fd(skel->maps.testbuf_hash);
258 	if (!ASSERT_OK_FD(map_fd, "map_fd"))
259 		goto destroy_skel;
260 
261 	if (!ASSERT_OK(bpf_map_update_elem(map_fd, udmabuf_test_buffer_name, &f, BPF_ANY),
262 		       "insert udmabuf"))
263 		goto destroy_skel;
264 	if (!ASSERT_OK(bpf_map_update_elem(map_fd, sysheap_test_buffer_name, &f, BPF_ANY),
265 		       "insert sysheap buffer"))
266 		goto destroy_skel;
267 
268 	if (!ASSERT_OK(create_test_buffers(), "create_test_buffers"))
269 		goto destroy;
270 
271 	if (!ASSERT_OK(dmabuf_iter__attach(skel), "skel_attach"))
272 		goto destroy;
273 
274 	if (test__start_subtest("no_infinite_reads"))
275 		subtest_dmabuf_iter_check_no_infinite_reads(skel);
276 	if (test__start_subtest("default_iter"))
277 		subtest_dmabuf_iter_check_default_iter(skel);
278 	if (test__start_subtest("open_coded"))
279 		subtest_dmabuf_iter_check_open_coded(skel, map_fd);
280 
281 destroy:
282 	destroy_test_buffers();
283 destroy_skel:
284 	dmabuf_iter__destroy(skel);
285 }
286