Merge tag 'block-5.6-2020-02-16' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / tools / testing / selftests / dmabuf-heaps / dmabuf-heap.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <dirent.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <sys/ioctl.h>
12 #include <sys/mman.h>
13 #include <sys/types.h>
14
15 #include <linux/dma-buf.h>
16 #include <drm/drm.h>
17
18 #include "../../../../include/uapi/linux/dma-heap.h"
19
20 #define DEVPATH "/dev/dma_heap"
21
22 static int check_vgem(int fd)
23 {
24         drm_version_t version = { 0 };
25         char name[5];
26         int ret;
27
28         version.name_len = 4;
29         version.name = name;
30
31         ret = ioctl(fd, DRM_IOCTL_VERSION, &version);
32         if (ret)
33                 return 0;
34
35         return !strcmp(name, "vgem");
36 }
37
38 static int open_vgem(void)
39 {
40         int i, fd;
41         const char *drmstr = "/dev/dri/card";
42
43         fd = -1;
44         for (i = 0; i < 16; i++) {
45                 char name[80];
46
47                 snprintf(name, 80, "%s%u", drmstr, i);
48
49                 fd = open(name, O_RDWR);
50                 if (fd < 0)
51                         continue;
52
53                 if (!check_vgem(fd)) {
54                         close(fd);
55                         fd = -1;
56                         continue;
57                 } else {
58                         break;
59                 }
60         }
61         return fd;
62 }
63
64 static int import_vgem_fd(int vgem_fd, int dma_buf_fd, uint32_t *handle)
65 {
66         struct drm_prime_handle import_handle = {
67                 .fd = dma_buf_fd,
68                 .flags = 0,
69                 .handle = 0,
70          };
71         int ret;
72
73         ret = ioctl(vgem_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import_handle);
74         if (ret == 0)
75                 *handle = import_handle.handle;
76         return ret;
77 }
78
79 static void close_handle(int vgem_fd, uint32_t handle)
80 {
81         struct drm_gem_close close = {
82                 .handle = handle,
83         };
84
85         ioctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &close);
86 }
87
88 static int dmabuf_heap_open(char *name)
89 {
90         int ret, fd;
91         char buf[256];
92
93         ret = snprintf(buf, 256, "%s/%s", DEVPATH, name);
94         if (ret < 0) {
95                 printf("snprintf failed!\n");
96                 return ret;
97         }
98
99         fd = open(buf, O_RDWR);
100         if (fd < 0)
101                 printf("open %s failed!\n", buf);
102         return fd;
103 }
104
105 static int dmabuf_heap_alloc_fdflags(int fd, size_t len, unsigned int fd_flags,
106                                      unsigned int heap_flags, int *dmabuf_fd)
107 {
108         struct dma_heap_allocation_data data = {
109                 .len = len,
110                 .fd = 0,
111                 .fd_flags = fd_flags,
112                 .heap_flags = heap_flags,
113         };
114         int ret;
115
116         if (!dmabuf_fd)
117                 return -EINVAL;
118
119         ret = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data);
120         if (ret < 0)
121                 return ret;
122         *dmabuf_fd = (int)data.fd;
123         return ret;
124 }
125
126 static int dmabuf_heap_alloc(int fd, size_t len, unsigned int flags,
127                              int *dmabuf_fd)
128 {
129         return dmabuf_heap_alloc_fdflags(fd, len, O_RDWR | O_CLOEXEC, flags,
130                                          dmabuf_fd);
131 }
132
133 static void dmabuf_sync(int fd, int start_stop)
134 {
135         struct dma_buf_sync sync = {
136                 .flags = start_stop | DMA_BUF_SYNC_RW,
137         };
138         int ret;
139
140         ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
141         if (ret)
142                 printf("sync failed %d\n", errno);
143 }
144
145 #define ONE_MEG (1024 * 1024)
146
147 static int test_alloc_and_import(char *heap_name)
148 {
149         int heap_fd = -1, dmabuf_fd = -1, importer_fd = -1;
150         uint32_t handle = 0;
151         void *p = NULL;
152         int ret;
153
154         printf("Testing heap: %s\n", heap_name);
155
156         heap_fd = dmabuf_heap_open(heap_name);
157         if (heap_fd < 0)
158                 return -1;
159
160         printf("Allocating 1 MEG\n");
161         ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0, &dmabuf_fd);
162         if (ret) {
163                 printf("Allocation Failed!\n");
164                 ret = -1;
165                 goto out;
166         }
167         /* mmap and write a simple pattern */
168         p = mmap(NULL,
169                  ONE_MEG,
170                  PROT_READ | PROT_WRITE,
171                  MAP_SHARED,
172                  dmabuf_fd,
173                  0);
174         if (p == MAP_FAILED) {
175                 printf("mmap() failed: %m\n");
176                 ret = -1;
177                 goto out;
178         }
179         printf("mmap passed\n");
180
181         dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
182         memset(p, 1, ONE_MEG / 2);
183         memset((char *)p + ONE_MEG / 2, 0, ONE_MEG / 2);
184         dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
185
186         importer_fd = open_vgem();
187         if (importer_fd < 0) {
188                 ret = importer_fd;
189                 printf("Failed to open vgem\n");
190                 goto out;
191         }
192
193         ret = import_vgem_fd(importer_fd, dmabuf_fd, &handle);
194         if (ret < 0) {
195                 printf("Failed to import buffer\n");
196                 goto out;
197         }
198         printf("import passed\n");
199
200         dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
201         memset(p, 0xff, ONE_MEG);
202         dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
203         printf("syncs passed\n");
204
205         close_handle(importer_fd, handle);
206         ret = 0;
207
208 out:
209         if (p)
210                 munmap(p, ONE_MEG);
211         if (importer_fd >= 0)
212                 close(importer_fd);
213         if (dmabuf_fd >= 0)
214                 close(dmabuf_fd);
215         if (heap_fd >= 0)
216                 close(heap_fd);
217
218         return ret;
219 }
220
221 /* Test the ioctl version compatibility w/ a smaller structure then expected */
222 static int dmabuf_heap_alloc_older(int fd, size_t len, unsigned int flags,
223                                    int *dmabuf_fd)
224 {
225         int ret;
226         unsigned int older_alloc_ioctl;
227         struct dma_heap_allocation_data_smaller {
228                 __u64 len;
229                 __u32 fd;
230                 __u32 fd_flags;
231         } data = {
232                 .len = len,
233                 .fd = 0,
234                 .fd_flags = O_RDWR | O_CLOEXEC,
235         };
236
237         older_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
238                                   struct dma_heap_allocation_data_smaller);
239         if (!dmabuf_fd)
240                 return -EINVAL;
241
242         ret = ioctl(fd, older_alloc_ioctl, &data);
243         if (ret < 0)
244                 return ret;
245         *dmabuf_fd = (int)data.fd;
246         return ret;
247 }
248
249 /* Test the ioctl version compatibility w/ a larger structure then expected */
250 static int dmabuf_heap_alloc_newer(int fd, size_t len, unsigned int flags,
251                                    int *dmabuf_fd)
252 {
253         int ret;
254         unsigned int newer_alloc_ioctl;
255         struct dma_heap_allocation_data_bigger {
256                 __u64 len;
257                 __u32 fd;
258                 __u32 fd_flags;
259                 __u64 heap_flags;
260                 __u64 garbage1;
261                 __u64 garbage2;
262                 __u64 garbage3;
263         } data = {
264                 .len = len,
265                 .fd = 0,
266                 .fd_flags = O_RDWR | O_CLOEXEC,
267                 .heap_flags = flags,
268                 .garbage1 = 0xffffffff,
269                 .garbage2 = 0x88888888,
270                 .garbage3 = 0x11111111,
271         };
272
273         newer_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
274                                   struct dma_heap_allocation_data_bigger);
275         if (!dmabuf_fd)
276                 return -EINVAL;
277
278         ret = ioctl(fd, newer_alloc_ioctl, &data);
279         if (ret < 0)
280                 return ret;
281
282         *dmabuf_fd = (int)data.fd;
283         return ret;
284 }
285
286 static int test_alloc_compat(char *heap_name)
287 {
288         int heap_fd = -1, dmabuf_fd = -1;
289         int ret;
290
291         heap_fd = dmabuf_heap_open(heap_name);
292         if (heap_fd < 0)
293                 return -1;
294
295         printf("Testing (theoretical)older alloc compat\n");
296         ret = dmabuf_heap_alloc_older(heap_fd, ONE_MEG, 0, &dmabuf_fd);
297         if (ret) {
298                 printf("Older compat allocation failed!\n");
299                 ret = -1;
300                 goto out;
301         }
302         close(dmabuf_fd);
303
304         printf("Testing (theoretical)newer alloc compat\n");
305         ret = dmabuf_heap_alloc_newer(heap_fd, ONE_MEG, 0, &dmabuf_fd);
306         if (ret) {
307                 printf("Newer compat allocation failed!\n");
308                 ret = -1;
309                 goto out;
310         }
311         printf("Ioctl compatibility tests passed\n");
312 out:
313         if (dmabuf_fd >= 0)
314                 close(dmabuf_fd);
315         if (heap_fd >= 0)
316                 close(heap_fd);
317
318         return ret;
319 }
320
321 static int test_alloc_errors(char *heap_name)
322 {
323         int heap_fd = -1, dmabuf_fd = -1;
324         int ret;
325
326         heap_fd = dmabuf_heap_open(heap_name);
327         if (heap_fd < 0)
328                 return -1;
329
330         printf("Testing expected error cases\n");
331         ret = dmabuf_heap_alloc(0, ONE_MEG, 0x111111, &dmabuf_fd);
332         if (!ret) {
333                 printf("Did not see expected error (invalid fd)!\n");
334                 ret = -1;
335                 goto out;
336         }
337
338         ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0x111111, &dmabuf_fd);
339         if (!ret) {
340                 printf("Did not see expected error (invalid heap flags)!\n");
341                 ret = -1;
342                 goto out;
343         }
344
345         ret = dmabuf_heap_alloc_fdflags(heap_fd, ONE_MEG,
346                                         ~(O_RDWR | O_CLOEXEC), 0, &dmabuf_fd);
347         if (!ret) {
348                 printf("Did not see expected error (invalid fd flags)!\n");
349                 ret = -1;
350                 goto out;
351         }
352
353         printf("Expected error checking passed\n");
354 out:
355         if (dmabuf_fd >= 0)
356                 close(dmabuf_fd);
357         if (heap_fd >= 0)
358                 close(heap_fd);
359
360         return ret;
361 }
362
363 int main(void)
364 {
365         DIR *d;
366         struct dirent *dir;
367         int ret = -1;
368
369         d = opendir(DEVPATH);
370         if (!d) {
371                 printf("No %s directory?\n", DEVPATH);
372                 return -1;
373         }
374
375         while ((dir = readdir(d)) != NULL) {
376                 if (!strncmp(dir->d_name, ".", 2))
377                         continue;
378                 if (!strncmp(dir->d_name, "..", 3))
379                         continue;
380
381                 ret = test_alloc_and_import(dir->d_name);
382                 if (ret)
383                         break;
384
385                 ret = test_alloc_compat(dir->d_name);
386                 if (ret)
387                         break;
388
389                 ret = test_alloc_errors(dir->d_name);
390                 if (ret)
391                         break;
392         }
393         closedir(d);
394
395         return ret;
396 }