blob: ff4dda9a6372525fbe573e52d8fb73c260b281e8 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001From 31501b71a0237f3753d0210e3e122c548d6f3051 Mon Sep 17 00:00:00 2001
2From: John Stultz <john.stultz@linaro.org>
3Date: Tue, 3 Dec 2019 17:26:41 +0000
4Subject: [PATCH] kselftests: Add dma-heap test
5
6Commit a8779927fd86c91f5400bfcbccfa018a667d8350 upstream.
7
8Add very trivial allocation and import test for dma-heaps,
9utilizing the vgem driver as a test importer.
10
11A good chunk of this code taken from:
12 tools/testing/selftests/android/ion/ionmap_test.c
13 Originally by Laura Abbott <labbott@redhat.com>
14
15Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
16Cc: Sumit Semwal <sumit.semwal@linaro.org>
17Cc: Liam Mark <lmark@codeaurora.org>
18Cc: Pratik Patel <pratikp@codeaurora.org>
19Cc: Brian Starkey <Brian.Starkey@arm.com>
20Cc: Vincent Donnefort <Vincent.Donnefort@arm.com>
21Cc: Sudipto Paul <Sudipto.Paul@arm.com>
22Cc: Andrew F. Davis <afd@ti.com>
23Cc: Christoph Hellwig <hch@infradead.org>
24Cc: Chenbo Feng <fengc@google.com>
25Cc: Alistair Strachan <astrachan@google.com>
26Cc: Hridya Valsaraju <hridya@google.com>
27Cc: Sandeep Patil <sspatil@google.com>
28Cc: Hillf Danton <hdanton@sina.com>
29Cc: Dave Airlie <airlied@gmail.com>
30Cc: dri-devel@lists.freedesktop.org
31Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
32Reviewed-by: Brian Starkey <brian.starkey@arm.com>
33Acked-by: Sandeep Patil <sspatil@android.com>
34Acked-by: Laura Abbott <labbott@redhat.com>
35Tested-by: Ayan Kumar Halder <ayan.halder@arm.com>
36Signed-off-by: John Stultz <john.stultz@linaro.org>
37Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
38Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-6-john.stultz@linaro.org
39---
40 tools/testing/selftests/dmabuf-heaps/Makefile | 6 +
41 .../selftests/dmabuf-heaps/dmabuf-heap.c | 396 ++++++++++++++++++
42 2 files changed, 402 insertions(+)
43 create mode 100644 tools/testing/selftests/dmabuf-heaps/Makefile
44 create mode 100644 tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
45
46--- /dev/null
47+++ b/tools/testing/selftests/dmabuf-heaps/Makefile
48@@ -0,0 +1,6 @@
49+# SPDX-License-Identifier: GPL-2.0
50+CFLAGS += -static -O3 -Wl,-no-as-needed -Wall -I../../../../usr/include
51+
52+TEST_GEN_PROGS = dmabuf-heap
53+
54+include ../lib.mk
55--- /dev/null
56+++ b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
57@@ -0,0 +1,396 @@
58+// SPDX-License-Identifier: GPL-2.0
59+
60+#include <dirent.h>
61+#include <errno.h>
62+#include <fcntl.h>
63+#include <stdio.h>
64+#include <stdlib.h>
65+#include <stdint.h>
66+#include <string.h>
67+#include <unistd.h>
68+#include <sys/ioctl.h>
69+#include <sys/mman.h>
70+#include <sys/types.h>
71+
72+#include <linux/dma-buf.h>
73+#include <drm/drm.h>
74+
75+#include "../../../../include/uapi/linux/dma-heap.h"
76+
77+#define DEVPATH "/dev/dma_heap"
78+
79+static int check_vgem(int fd)
80+{
81+ drm_version_t version = { 0 };
82+ char name[5];
83+ int ret;
84+
85+ version.name_len = 4;
86+ version.name = name;
87+
88+ ret = ioctl(fd, DRM_IOCTL_VERSION, &version);
89+ if (ret)
90+ return 0;
91+
92+ return !strcmp(name, "vgem");
93+}
94+
95+static int open_vgem(void)
96+{
97+ int i, fd;
98+ const char *drmstr = "/dev/dri/card";
99+
100+ fd = -1;
101+ for (i = 0; i < 16; i++) {
102+ char name[80];
103+
104+ snprintf(name, 80, "%s%u", drmstr, i);
105+
106+ fd = open(name, O_RDWR);
107+ if (fd < 0)
108+ continue;
109+
110+ if (!check_vgem(fd)) {
111+ close(fd);
112+ fd = -1;
113+ continue;
114+ } else {
115+ break;
116+ }
117+ }
118+ return fd;
119+}
120+
121+static int import_vgem_fd(int vgem_fd, int dma_buf_fd, uint32_t *handle)
122+{
123+ struct drm_prime_handle import_handle = {
124+ .fd = dma_buf_fd,
125+ .flags = 0,
126+ .handle = 0,
127+ };
128+ int ret;
129+
130+ ret = ioctl(vgem_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import_handle);
131+ if (ret == 0)
132+ *handle = import_handle.handle;
133+ return ret;
134+}
135+
136+static void close_handle(int vgem_fd, uint32_t handle)
137+{
138+ struct drm_gem_close close = {
139+ .handle = handle,
140+ };
141+
142+ ioctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &close);
143+}
144+
145+static int dmabuf_heap_open(char *name)
146+{
147+ int ret, fd;
148+ char buf[256];
149+
150+ ret = snprintf(buf, 256, "%s/%s", DEVPATH, name);
151+ if (ret < 0) {
152+ printf("snprintf failed!\n");
153+ return ret;
154+ }
155+
156+ fd = open(buf, O_RDWR);
157+ if (fd < 0)
158+ printf("open %s failed!\n", buf);
159+ return fd;
160+}
161+
162+static int dmabuf_heap_alloc_fdflags(int fd, size_t len, unsigned int fd_flags,
163+ unsigned int heap_flags, int *dmabuf_fd)
164+{
165+ struct dma_heap_allocation_data data = {
166+ .len = len,
167+ .fd = 0,
168+ .fd_flags = fd_flags,
169+ .heap_flags = heap_flags,
170+ };
171+ int ret;
172+
173+ if (!dmabuf_fd)
174+ return -EINVAL;
175+
176+ ret = ioctl(fd, DMA_HEAP_IOC_ALLOC, &data);
177+ if (ret < 0)
178+ return ret;
179+ *dmabuf_fd = (int)data.fd;
180+ return ret;
181+}
182+
183+static int dmabuf_heap_alloc(int fd, size_t len, unsigned int flags,
184+ int *dmabuf_fd)
185+{
186+ return dmabuf_heap_alloc_fdflags(fd, len, O_RDWR | O_CLOEXEC, flags,
187+ dmabuf_fd);
188+}
189+
190+static void dmabuf_sync(int fd, int start_stop)
191+{
192+ struct dma_buf_sync sync = {
193+ .flags = start_stop | DMA_BUF_SYNC_RW,
194+ };
195+ int ret;
196+
197+ ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
198+ if (ret)
199+ printf("sync failed %d\n", errno);
200+}
201+
202+#define ONE_MEG (1024 * 1024)
203+
204+static int test_alloc_and_import(char *heap_name)
205+{
206+ int heap_fd = -1, dmabuf_fd = -1, importer_fd = -1;
207+ uint32_t handle = 0;
208+ void *p = NULL;
209+ int ret;
210+
211+ printf("Testing heap: %s\n", heap_name);
212+
213+ heap_fd = dmabuf_heap_open(heap_name);
214+ if (heap_fd < 0)
215+ return -1;
216+
217+ printf("Allocating 1 MEG\n");
218+ ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0, &dmabuf_fd);
219+ if (ret) {
220+ printf("Allocation Failed!\n");
221+ ret = -1;
222+ goto out;
223+ }
224+ /* mmap and write a simple pattern */
225+ p = mmap(NULL,
226+ ONE_MEG,
227+ PROT_READ | PROT_WRITE,
228+ MAP_SHARED,
229+ dmabuf_fd,
230+ 0);
231+ if (p == MAP_FAILED) {
232+ printf("mmap() failed: %m\n");
233+ ret = -1;
234+ goto out;
235+ }
236+ printf("mmap passed\n");
237+
238+ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
239+ memset(p, 1, ONE_MEG / 2);
240+ memset((char *)p + ONE_MEG / 2, 0, ONE_MEG / 2);
241+ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
242+
243+ importer_fd = open_vgem();
244+ if (importer_fd < 0) {
245+ ret = importer_fd;
246+ printf("Failed to open vgem\n");
247+ goto out;
248+ }
249+
250+ ret = import_vgem_fd(importer_fd, dmabuf_fd, &handle);
251+ if (ret < 0) {
252+ printf("Failed to import buffer\n");
253+ goto out;
254+ }
255+ printf("import passed\n");
256+
257+ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
258+ memset(p, 0xff, ONE_MEG);
259+ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
260+ printf("syncs passed\n");
261+
262+ close_handle(importer_fd, handle);
263+ ret = 0;
264+
265+out:
266+ if (p)
267+ munmap(p, ONE_MEG);
268+ if (importer_fd >= 0)
269+ close(importer_fd);
270+ if (dmabuf_fd >= 0)
271+ close(dmabuf_fd);
272+ if (heap_fd >= 0)
273+ close(heap_fd);
274+
275+ return ret;
276+}
277+
278+/* Test the ioctl version compatibility w/ a smaller structure then expected */
279+static int dmabuf_heap_alloc_older(int fd, size_t len, unsigned int flags,
280+ int *dmabuf_fd)
281+{
282+ int ret;
283+ unsigned int older_alloc_ioctl;
284+ struct dma_heap_allocation_data_smaller {
285+ __u64 len;
286+ __u32 fd;
287+ __u32 fd_flags;
288+ } data = {
289+ .len = len,
290+ .fd = 0,
291+ .fd_flags = O_RDWR | O_CLOEXEC,
292+ };
293+
294+ older_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
295+ struct dma_heap_allocation_data_smaller);
296+ if (!dmabuf_fd)
297+ return -EINVAL;
298+
299+ ret = ioctl(fd, older_alloc_ioctl, &data);
300+ if (ret < 0)
301+ return ret;
302+ *dmabuf_fd = (int)data.fd;
303+ return ret;
304+}
305+
306+/* Test the ioctl version compatibility w/ a larger structure then expected */
307+static int dmabuf_heap_alloc_newer(int fd, size_t len, unsigned int flags,
308+ int *dmabuf_fd)
309+{
310+ int ret;
311+ unsigned int newer_alloc_ioctl;
312+ struct dma_heap_allocation_data_bigger {
313+ __u64 len;
314+ __u32 fd;
315+ __u32 fd_flags;
316+ __u64 heap_flags;
317+ __u64 garbage1;
318+ __u64 garbage2;
319+ __u64 garbage3;
320+ } data = {
321+ .len = len,
322+ .fd = 0,
323+ .fd_flags = O_RDWR | O_CLOEXEC,
324+ .heap_flags = flags,
325+ .garbage1 = 0xffffffff,
326+ .garbage2 = 0x88888888,
327+ .garbage3 = 0x11111111,
328+ };
329+
330+ newer_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
331+ struct dma_heap_allocation_data_bigger);
332+ if (!dmabuf_fd)
333+ return -EINVAL;
334+
335+ ret = ioctl(fd, newer_alloc_ioctl, &data);
336+ if (ret < 0)
337+ return ret;
338+
339+ *dmabuf_fd = (int)data.fd;
340+ return ret;
341+}
342+
343+static int test_alloc_compat(char *heap_name)
344+{
345+ int heap_fd = -1, dmabuf_fd = -1;
346+ int ret;
347+
348+ heap_fd = dmabuf_heap_open(heap_name);
349+ if (heap_fd < 0)
350+ return -1;
351+
352+ printf("Testing (theoretical)older alloc compat\n");
353+ ret = dmabuf_heap_alloc_older(heap_fd, ONE_MEG, 0, &dmabuf_fd);
354+ if (ret) {
355+ printf("Older compat allocation failed!\n");
356+ ret = -1;
357+ goto out;
358+ }
359+ close(dmabuf_fd);
360+
361+ printf("Testing (theoretical)newer alloc compat\n");
362+ ret = dmabuf_heap_alloc_newer(heap_fd, ONE_MEG, 0, &dmabuf_fd);
363+ if (ret) {
364+ printf("Newer compat allocation failed!\n");
365+ ret = -1;
366+ goto out;
367+ }
368+ printf("Ioctl compatibility tests passed\n");
369+out:
370+ if (dmabuf_fd >= 0)
371+ close(dmabuf_fd);
372+ if (heap_fd >= 0)
373+ close(heap_fd);
374+
375+ return ret;
376+}
377+
378+static int test_alloc_errors(char *heap_name)
379+{
380+ int heap_fd = -1, dmabuf_fd = -1;
381+ int ret;
382+
383+ heap_fd = dmabuf_heap_open(heap_name);
384+ if (heap_fd < 0)
385+ return -1;
386+
387+ printf("Testing expected error cases\n");
388+ ret = dmabuf_heap_alloc(0, ONE_MEG, 0x111111, &dmabuf_fd);
389+ if (!ret) {
390+ printf("Did not see expected error (invalid fd)!\n");
391+ ret = -1;
392+ goto out;
393+ }
394+
395+ ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0x111111, &dmabuf_fd);
396+ if (!ret) {
397+ printf("Did not see expected error (invalid heap flags)!\n");
398+ ret = -1;
399+ goto out;
400+ }
401+
402+ ret = dmabuf_heap_alloc_fdflags(heap_fd, ONE_MEG,
403+ ~(O_RDWR | O_CLOEXEC), 0, &dmabuf_fd);
404+ if (!ret) {
405+ printf("Did not see expected error (invalid fd flags)!\n");
406+ ret = -1;
407+ goto out;
408+ }
409+
410+ printf("Expected error checking passed\n");
411+out:
412+ if (dmabuf_fd >= 0)
413+ close(dmabuf_fd);
414+ if (heap_fd >= 0)
415+ close(heap_fd);
416+
417+ return ret;
418+}
419+
420+int main(void)
421+{
422+ DIR *d;
423+ struct dirent *dir;
424+ int ret = -1;
425+
426+ d = opendir(DEVPATH);
427+ if (!d) {
428+ printf("No %s directory?\n", DEVPATH);
429+ return -1;
430+ }
431+
432+ while ((dir = readdir(d)) != NULL) {
433+ if (!strncmp(dir->d_name, ".", 2))
434+ continue;
435+ if (!strncmp(dir->d_name, "..", 3))
436+ continue;
437+
438+ ret = test_alloc_and_import(dir->d_name);
439+ if (ret)
440+ break;
441+
442+ ret = test_alloc_compat(dir->d_name);
443+ if (ret)
444+ break;
445+
446+ ret = test_alloc_errors(dir->d_name);
447+ if (ret)
448+ break;
449+ }
450+ closedir(d);
451+
452+ return ret;
453+}