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 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 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