1 // SPDX-License-Identifier: GPL-2.0
11 #include <sys/ioctl.h>
13 #include <sys/types.h>
15 #include <linux/dma-buf.h>
16 #include <linux/dma-heap.h>
18 #include "../kselftest.h"
20 #define DEVPATH "/dev/dma_heap"
22 static int check_vgem(int fd
)
24 drm_version_t version
= { 0 };
31 ret
= ioctl(fd
, DRM_IOCTL_VERSION
, &version
);
32 if (ret
|| version
.name_len
!= 4)
37 return !strcmp(name
, "vgem");
40 static int open_vgem(void)
43 const char *drmstr
= "/dev/dri/card";
46 for (i
= 0; i
< 16; i
++) {
49 snprintf(name
, 80, "%s%u", drmstr
, i
);
51 fd
= open(name
, O_RDWR
);
55 if (!check_vgem(fd
)) {
66 static int import_vgem_fd(int vgem_fd
, int dma_buf_fd
, uint32_t *handle
)
68 struct drm_prime_handle import_handle
= {
75 ret
= ioctl(vgem_fd
, DRM_IOCTL_PRIME_FD_TO_HANDLE
, &import_handle
);
77 *handle
= import_handle
.handle
;
81 static void close_handle(int vgem_fd
, uint32_t handle
)
83 struct drm_gem_close close
= {
87 ioctl(vgem_fd
, DRM_IOCTL_GEM_CLOSE
, &close
);
90 static int dmabuf_heap_open(char *name
)
95 ret
= snprintf(buf
, 256, "%s/%s", DEVPATH
, name
);
97 ksft_exit_fail_msg("snprintf failed! %d\n", ret
);
99 fd
= open(buf
, O_RDWR
);
101 ksft_exit_fail_msg("open %s failed: %s\n", buf
, strerror(errno
));
106 static int dmabuf_heap_alloc_fdflags(int fd
, size_t len
, unsigned int fd_flags
,
107 unsigned int heap_flags
, int *dmabuf_fd
)
109 struct dma_heap_allocation_data data
= {
112 .fd_flags
= fd_flags
,
113 .heap_flags
= heap_flags
,
120 ret
= ioctl(fd
, DMA_HEAP_IOCTL_ALLOC
, &data
);
123 *dmabuf_fd
= (int)data
.fd
;
127 static int dmabuf_heap_alloc(int fd
, size_t len
, unsigned int flags
,
130 return dmabuf_heap_alloc_fdflags(fd
, len
, O_RDWR
| O_CLOEXEC
, flags
,
134 static int dmabuf_sync(int fd
, int start_stop
)
136 struct dma_buf_sync sync
= {
137 .flags
= start_stop
| DMA_BUF_SYNC_RW
,
140 return ioctl(fd
, DMA_BUF_IOCTL_SYNC
, &sync
);
143 #define ONE_MEG (1024 * 1024)
145 static void test_alloc_and_import(char *heap_name
)
147 int heap_fd
= -1, dmabuf_fd
= -1, importer_fd
= -1;
152 heap_fd
= dmabuf_heap_open(heap_name
);
154 ksft_print_msg("Testing allocation and importing:\n");
155 ret
= dmabuf_heap_alloc(heap_fd
, ONE_MEG
, 0, &dmabuf_fd
);
157 ksft_test_result_fail("FAIL (Allocation Failed!) %d\n", ret
);
161 /* mmap and write a simple pattern */
162 p
= mmap(NULL
, ONE_MEG
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, dmabuf_fd
, 0);
163 if (p
== MAP_FAILED
) {
164 ksft_test_result_fail("FAIL (mmap() failed): %s\n", strerror(errno
));
165 goto close_and_return
;
168 dmabuf_sync(dmabuf_fd
, DMA_BUF_SYNC_START
);
169 memset(p
, 1, ONE_MEG
/ 2);
170 memset((char *)p
+ ONE_MEG
/ 2, 0, ONE_MEG
/ 2);
171 dmabuf_sync(dmabuf_fd
, DMA_BUF_SYNC_END
);
173 importer_fd
= open_vgem();
174 if (importer_fd
< 0) {
175 ksft_test_result_skip("Could not open vgem %d\n", importer_fd
);
177 ret
= import_vgem_fd(importer_fd
, dmabuf_fd
, &handle
);
178 ksft_test_result(ret
>= 0, "Import buffer %d\n", ret
);
181 ret
= dmabuf_sync(dmabuf_fd
, DMA_BUF_SYNC_START
);
183 ksft_print_msg("FAIL (DMA_BUF_SYNC_START failed!) %d\n", ret
);
187 memset(p
, 0xff, ONE_MEG
);
188 ret
= dmabuf_sync(dmabuf_fd
, DMA_BUF_SYNC_END
);
190 ksft_print_msg("FAIL (DMA_BUF_SYNC_END failed!) %d\n", ret
);
194 close_handle(importer_fd
, handle
);
195 ksft_test_result_pass("%s dmabuf sync succeeded\n", __func__
);
199 ksft_test_result_fail("%s dmabuf sync failed\n", __func__
);
208 static void test_alloc_zeroed(char *heap_name
, size_t size
)
210 int heap_fd
= -1, dmabuf_fd
[32];
215 ksft_print_msg("Testing alloced %ldk buffers are zeroed:\n", size
/ 1024);
216 heap_fd
= dmabuf_heap_open(heap_name
);
218 /* Allocate and fill a bunch of buffers */
219 for (i
= 0; i
< 32; i
++) {
220 ret
= dmabuf_heap_alloc(heap_fd
, size
, 0, &dmabuf_fd
[i
]);
222 ksft_test_result_fail("FAIL (Allocation (%i) failed) %d\n", i
, ret
);
223 goto close_and_return
;
226 /* mmap and fill with simple pattern */
227 p
= mmap(NULL
, size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, dmabuf_fd
[i
], 0);
228 if (p
== MAP_FAILED
) {
229 ksft_test_result_fail("FAIL (mmap() failed!): %s\n", strerror(errno
));
230 goto close_and_return
;
233 dmabuf_sync(dmabuf_fd
[i
], DMA_BUF_SYNC_START
);
234 memset(p
, 0xff, size
);
235 dmabuf_sync(dmabuf_fd
[i
], DMA_BUF_SYNC_END
);
239 for (i
= 0; i
< 32; i
++)
241 ksft_test_result_pass("Allocate and fill a bunch of buffers\n");
243 /* Allocate and validate all buffers are zeroed */
244 for (i
= 0; i
< 32; i
++) {
245 ret
= dmabuf_heap_alloc(heap_fd
, size
, 0, &dmabuf_fd
[i
]);
247 ksft_test_result_fail("FAIL (Allocation (%i) failed) %d\n", i
, ret
);
248 goto close_and_return
;
251 /* mmap and validate everything is zero */
252 p
= mmap(NULL
, size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, dmabuf_fd
[i
], 0);
253 if (p
== MAP_FAILED
) {
254 ksft_test_result_fail("FAIL (mmap() failed!): %s\n", strerror(errno
));
255 goto close_and_return
;
258 dmabuf_sync(dmabuf_fd
[i
], DMA_BUF_SYNC_START
);
260 for (j
= 0; j
< size
; j
++) {
262 ksft_print_msg("FAIL (Allocated buffer not zeroed @ %i)\n", j
);
263 dmabuf_sync(dmabuf_fd
[i
], DMA_BUF_SYNC_END
);
268 dmabuf_sync(dmabuf_fd
[i
], DMA_BUF_SYNC_END
);
273 ksft_test_result(i
== 32, "Allocate and validate all buffers are zeroed\n");
277 for (k
= 0; k
< i
; k
++)
284 /* Test the ioctl version compatibility w/ a smaller structure then expected */
285 static int dmabuf_heap_alloc_older(int fd
, size_t len
, unsigned int flags
,
289 unsigned int older_alloc_ioctl
;
290 struct dma_heap_allocation_data_smaller
{
297 .fd_flags
= O_RDWR
| O_CLOEXEC
,
300 older_alloc_ioctl
= _IOWR(DMA_HEAP_IOC_MAGIC
, 0x0,
301 struct dma_heap_allocation_data_smaller
);
305 ret
= ioctl(fd
, older_alloc_ioctl
, &data
);
308 *dmabuf_fd
= (int)data
.fd
;
312 /* Test the ioctl version compatibility w/ a larger structure then expected */
313 static int dmabuf_heap_alloc_newer(int fd
, size_t len
, unsigned int flags
,
317 unsigned int newer_alloc_ioctl
;
318 struct dma_heap_allocation_data_bigger
{
329 .fd_flags
= O_RDWR
| O_CLOEXEC
,
331 .garbage1
= 0xffffffff,
332 .garbage2
= 0x88888888,
333 .garbage3
= 0x11111111,
336 newer_alloc_ioctl
= _IOWR(DMA_HEAP_IOC_MAGIC
, 0x0,
337 struct dma_heap_allocation_data_bigger
);
341 ret
= ioctl(fd
, newer_alloc_ioctl
, &data
);
345 *dmabuf_fd
= (int)data
.fd
;
349 static void test_alloc_compat(char *heap_name
)
351 int ret
, heap_fd
= -1, dmabuf_fd
= -1;
353 heap_fd
= dmabuf_heap_open(heap_name
);
355 ksft_print_msg("Testing (theoretical) older alloc compat:\n");
356 ret
= dmabuf_heap_alloc_older(heap_fd
, ONE_MEG
, 0, &dmabuf_fd
);
359 ksft_test_result(!ret
, "dmabuf_heap_alloc_older\n");
361 ksft_print_msg("Testing (theoretical) newer alloc compat:\n");
362 ret
= dmabuf_heap_alloc_newer(heap_fd
, ONE_MEG
, 0, &dmabuf_fd
);
365 ksft_test_result(!ret
, "dmabuf_heap_alloc_newer\n");
370 static void test_alloc_errors(char *heap_name
)
372 int heap_fd
= -1, dmabuf_fd
= -1;
375 heap_fd
= dmabuf_heap_open(heap_name
);
377 ksft_print_msg("Testing expected error cases:\n");
378 ret
= dmabuf_heap_alloc(0, ONE_MEG
, 0x111111, &dmabuf_fd
);
379 ksft_test_result(ret
, "Error expected on invalid fd %d\n", ret
);
381 ret
= dmabuf_heap_alloc(heap_fd
, ONE_MEG
, 0x111111, &dmabuf_fd
);
382 ksft_test_result(ret
, "Error expected on invalid heap flags %d\n", ret
);
384 ret
= dmabuf_heap_alloc_fdflags(heap_fd
, ONE_MEG
,
385 ~(O_RDWR
| O_CLOEXEC
), 0, &dmabuf_fd
);
386 ksft_test_result(ret
, "Error expected on invalid heap flags %d\n", ret
);
393 static int numer_of_heaps(void)
395 DIR *d
= opendir(DEVPATH
);
399 while ((dir
= readdir(d
))) {
400 if (!strncmp(dir
->d_name
, ".", 2))
402 if (!strncmp(dir
->d_name
, "..", 3))
417 d
= opendir(DEVPATH
);
419 ksft_print_msg("No %s directory?\n", DEVPATH
);
423 ksft_set_plan(11 * numer_of_heaps());
425 while ((dir
= readdir(d
))) {
426 if (!strncmp(dir
->d_name
, ".", 2))
428 if (!strncmp(dir
->d_name
, "..", 3))
431 ksft_print_msg("Testing heap: %s\n", dir
->d_name
);
432 ksft_print_msg("=======================================\n");
433 test_alloc_and_import(dir
->d_name
);
434 test_alloc_zeroed(dir
->d_name
, 4 * 1024);
435 test_alloc_zeroed(dir
->d_name
, ONE_MEG
);
436 test_alloc_compat(dir
->d_name
);
437 test_alloc_errors(dir
->d_name
);