File Index
Files and directories
Sign in
FILE

dmabuf_iter.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/prog_tests/dmabuf_iter.c C
  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
 22static int udmabuf = -1;
 23static const char udmabuf_test_buffer_name[DMA_BUF_NAME_LEN] = "udmabuf_test_buffer_for_iter";
 24static size_t udmabuf_test_buffer_size;
 25static int sysheap_dmabuf = -1;
 26static const char sysheap_test_buffer_name[DMA_BUF_NAME_LEN] = "sysheap_test_buffer_for_iter";
 27static size_t sysheap_test_buffer_size;
 28
 29static 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
 69close_udmabuf:
 70	close(local_udmabuf);
 71close_memfd:
 72	close(memfd);
 73	return -1;
 74}
 75
 76static 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
103close_sysheap_dmabuf:
104	close(data.fd);
105	return -1;
106}
107
108static 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
121static void destroy_test_buffers(void)
122{
123	close(udmabuf);
124	udmabuf = -1;
125
126	close(sysheap_dmabuf);
127	sysheap_dmabuf = -1;
128}
129
130enum Fields { INODE, SIZE, NAME, EXPORTER, FIELD_COUNT };
131struct DmabufInfo {
132	unsigned long inode;
133	unsigned long size;
134	char name[DMA_BUF_NAME_LEN];
135	char exporter[32];
136};
137
138static 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
147static 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
165static 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);
218close_iter_fd:
219	close(iter_fd);
220}
221
222static 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
242static 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
267void 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
311cleanup_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
318destroy:
319	destroy_test_buffers();
320destroy_skel:
321	dmabuf_iter__destroy(skel);
322}
~/ Read-only file preview.