Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <tjmercier@google.com>
Acked-by: Christian König <christian.koenig@amd.com>
Acked-by: Song Liu <song@kernel.org>
Link: https://lore.kernel.org/r/20250522230429.941193-5-tjmercier@google.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

T.J. Mercier and committed by
Alexei Starovoitov
ae5d2c59 6eab7ac7

+300
+3
tools/testing/selftests/bpf/config
··· 22 22 CONFIG_DEBUG_INFO=y 23 23 CONFIG_DEBUG_INFO_BTF=y 24 24 CONFIG_DEBUG_INFO_DWARF4=y 25 + CONFIG_DMABUF_HEAPS=y 26 + CONFIG_DMABUF_HEAPS_SYSTEM=y 25 27 CONFIG_DUMMY=y 26 28 CONFIG_DYNAMIC_FTRACE=y 27 29 CONFIG_FPROBE=y ··· 108 106 CONFIG_SECURITYFS=y 109 107 CONFIG_SYN_COOKIES=y 110 108 CONFIG_TEST_BPF=m 109 + CONFIG_UDMABUF=y 111 110 CONFIG_USERFAULTFD=y 112 111 CONFIG_VSOCKETS=y 113 112 CONFIG_VXLAN=y
+244
tools/testing/selftests/bpf/prog_tests/dmabuf_iter.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 + 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 + }
+53
tools/testing/selftests/bpf/progs/dmabuf_iter.c
··· 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; (size_t)(c - src) < size && *c; ++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 + }