Skip to content

Commit ff9a337

Browse files
T.J. MercierKernel Patches Daemon
T.J. Mercier
authored and
Kernel Patches Daemon
committed
selftests/bpf: Add test for dmabuf_iter
This test creates a udmabuf, and a dmabuf from the system dmabuf heap, and uses a BPF program that prints dmabuf metadata with the new dmabuf_iter to verify they can be found. Signed-off-by: T.J. Mercier <[email protected]> Acked-by: Christian König <[email protected]> Acked-by: Song Liu <[email protected]>
1 parent 33e5c02 commit ff9a337

File tree

3 files changed

+300
-0
lines changed

3 files changed

+300
-0
lines changed

tools/testing/selftests/bpf/config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ CONFIG_CRYPTO_AES=y
2222
CONFIG_DEBUG_INFO=y
2323
CONFIG_DEBUG_INFO_BTF=y
2424
CONFIG_DEBUG_INFO_DWARF4=y
25+
CONFIG_DMABUF_HEAPS=y
26+
CONFIG_DMABUF_HEAPS_SYSTEM=y
2527
CONFIG_DUMMY=y
2628
CONFIG_DYNAMIC_FTRACE=y
2729
CONFIG_FPROBE=y
@@ -108,6 +110,7 @@ CONFIG_SECURITY=y
108110
CONFIG_SECURITYFS=y
109111
CONFIG_SYN_COOKIES=y
110112
CONFIG_TEST_BPF=m
113+
CONFIG_UDMABUF=y
111114
CONFIG_USERFAULTFD=y
112115
CONFIG_VSOCKETS=y
113116
CONFIG_VXLAN=y
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
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+
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+
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+
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+
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+
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+
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+
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+
222+
void test_dmabuf_iter(void)
223+
{
224+
struct dmabuf_iter *skel = NULL;
225+
226+
skel = dmabuf_iter__open_and_load();
227+
if (!ASSERT_OK_PTR(skel, "dmabuf_iter__open_and_load"))
228+
return;
229+
230+
if (!ASSERT_OK(create_test_buffers(), "create_test_buffers"))
231+
goto destroy;
232+
233+
if (!ASSERT_OK(dmabuf_iter__attach(skel), "skel_attach"))
234+
goto destroy;
235+
236+
if (test__start_subtest("no_infinite_reads"))
237+
subtest_dmabuf_iter_check_no_infinite_reads(skel);
238+
if (test__start_subtest("default_iter"))
239+
subtest_dmabuf_iter_check_default_iter(skel);
240+
241+
destroy:
242+
destroy_test_buffers();
243+
dmabuf_iter__destroy(skel);
244+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Google LLC */
3+
#include <vmlinux.h>
4+
#include <bpf/bpf_core_read.h>
5+
#include <bpf/bpf_helpers.h>
6+
7+
/* From uapi/linux/dma-buf.h */
8+
#define DMA_BUF_NAME_LEN 32
9+
10+
char _license[] SEC("license") = "GPL";
11+
12+
/*
13+
* Fields output by this iterator are delimited by newlines. Convert any
14+
* newlines in user-provided printed strings to spaces.
15+
*/
16+
static void sanitize_string(char *src, size_t size)
17+
{
18+
for (char *c = src; *c && (size_t)(c - src) < size; ++c)
19+
if (*c == '\n')
20+
*c = ' ';
21+
}
22+
23+
SEC("iter/dmabuf")
24+
int dmabuf_collector(struct bpf_iter__dmabuf *ctx)
25+
{
26+
const struct dma_buf *dmabuf = ctx->dmabuf;
27+
struct seq_file *seq = ctx->meta->seq;
28+
unsigned long inode = 0;
29+
size_t size;
30+
const char *pname, *exporter;
31+
char name[DMA_BUF_NAME_LEN] = {'\0'};
32+
33+
if (!dmabuf)
34+
return 0;
35+
36+
if (BPF_CORE_READ_INTO(&inode, dmabuf, file, f_inode, i_ino) ||
37+
bpf_core_read(&size, sizeof(size), &dmabuf->size) ||
38+
bpf_core_read(&pname, sizeof(pname), &dmabuf->name) ||
39+
bpf_core_read(&exporter, sizeof(exporter), &dmabuf->exp_name))
40+
return 1;
41+
42+
/* Buffers are not required to be named */
43+
if (pname) {
44+
if (bpf_probe_read_kernel(name, sizeof(name), pname))
45+
return 1;
46+
47+
/* Name strings can be provided by userspace */
48+
sanitize_string(name, sizeof(name));
49+
}
50+
51+
BPF_SEQ_PRINTF(seq, "%lu\n%llu\n%s\n%s\n", inode, size, name, exporter);
52+
return 0;
53+
}

0 commit comments

Comments
 (0)