1 /*
2 * Copyright © 2016 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "igt.h"
25 #include "igt_kmod.h"
26 #include "igt_vgem.h"
27 #include "igt_debugfs.h"
28 #include "igt_sysfs.h"
29
30 #include <sys/mman.h>
31 #include <sys/poll.h>
32 #include <sys/stat.h>
33 #include <dirent.h>
34
35 IGT_TEST_DESCRIPTION("Basic sanity check of Virtual GEM module (vGEM).");
36
__gem_setversion(int fd,drm_set_version_t * sv)37 static int __gem_setversion(int fd, drm_set_version_t *sv)
38 {
39 int err;
40
41 err = 0;
42 if (igt_ioctl(fd, DRM_IOCTL_SET_VERSION, sv))
43 err = -errno;
44 errno = 0;
45
46 return err;
47 }
48
test_setversion(int fd)49 static void test_setversion(int fd)
50 {
51 drm_set_version_t sv;
52
53 memset(&sv, 0, sizeof(sv));
54 sv.drm_di_major = 1; /* must be equal to DRM_IF_MAJOR */
55 sv.drm_di_minor = 4; /* must be less than DRM_IF_MINOR */
56 sv.drm_dd_major = -1; /* don't care */
57 sv.drm_dd_minor = -1; /* don't care */
58 igt_assert_eq(__gem_setversion(fd, &sv), 0);
59
60 igt_info("vgem DRM interface v%d.%d, device v%d.%d\n",
61 sv.drm_di_major, sv.drm_di_minor,
62 sv.drm_dd_major, sv.drm_dd_minor);
63 }
64
test_client(int fd)65 static void test_client(int fd)
66 {
67 close(drm_open_driver(DRIVER_VGEM));
68 close(drm_open_driver_render(DRIVER_VGEM));
69 }
70
test_create(int fd)71 static void test_create(int fd)
72 {
73 struct vgem_bo bo;
74
75 bo.width = 0;
76 bo.height = 0;
77 bo.bpp = 0;
78 igt_assert_eq(__vgem_create(fd, &bo), -EINVAL);
79
80 bo.width = 1;
81 bo.height = 1;
82 bo.bpp = 1;
83 vgem_create(fd, &bo);
84 igt_assert_eq(bo.size, 4096);
85 gem_close(fd, bo.handle);
86
87 bo.width = 1024;
88 bo.height = 1024;
89 bo.bpp = 8;
90 vgem_create(fd, &bo);
91 igt_assert_eq(bo.size, 1<<20);
92 gem_close(fd, bo.handle);
93
94 bo.width = 1<<15;
95 bo.height = 1<<15;
96 bo.bpp = 16;
97 vgem_create(fd, &bo);
98 igt_assert_eq(bo.size, 1<<31);
99 gem_close(fd, bo.handle);
100 }
101
test_mmap(int fd)102 static void test_mmap(int fd)
103 {
104 struct vgem_bo bo;
105 uint32_t *ptr;
106
107 bo.width = 1024;
108 bo.height = 1024;
109 bo.bpp = 32;
110 vgem_create(fd, &bo);
111
112 ptr = vgem_mmap(fd, &bo, PROT_WRITE);
113 gem_close(fd, bo.handle);
114
115 for (int page = 0; page < bo.size >> 12; page++)
116 ptr[page] = 0;
117
118 munmap(ptr, bo.size);
119 }
120
has_prime_import(int fd)121 static bool has_prime_import(int fd)
122 {
123 uint64_t value;
124
125 if (drmGetCap(fd, DRM_CAP_PRIME, &value))
126 return false;
127
128 return value & DRM_PRIME_CAP_IMPORT;
129 }
130
test_dmabuf_export(int fd)131 static void test_dmabuf_export(int fd)
132 {
133 struct vgem_bo bo;
134 uint32_t handle;
135 int other;
136 int dmabuf;
137
138 other = drm_open_driver(DRIVER_ANY);
139 igt_require(has_prime_import(other));
140
141 bo.width = 1024;
142 bo.height = 1;
143 bo.bpp = 32;
144
145 vgem_create(fd, &bo);
146 dmabuf = prime_handle_to_fd(fd, bo.handle);
147 gem_close(fd, bo.handle);
148
149 handle = prime_fd_to_handle(other, dmabuf);
150 close(dmabuf);
151 gem_close(other, handle);
152 close(other);
153 }
154
test_dmabuf_mmap(int fd)155 static void test_dmabuf_mmap(int fd)
156 {
157 struct vgem_bo bo;
158 uint32_t *ptr;
159 int export;
160
161 bo.width = 1024;
162 bo.height = 1024;
163 bo.bpp = 32;
164 vgem_create(fd, &bo);
165
166 export = prime_handle_to_fd_for_mmap(fd, bo.handle);
167 ptr = mmap(NULL, bo.size, PROT_WRITE, MAP_SHARED, export, 0);
168 close(export);
169 igt_assert(ptr != MAP_FAILED);
170
171 for (int page = 0; page < bo.size >> 12; page++)
172 ptr[page] = page;
173 munmap(ptr, bo.size);
174
175 ptr = vgem_mmap(fd, &bo, PROT_READ);
176 gem_close(fd, bo.handle);
177
178 for (int page = 0; page < bo.size >> 12; page++)
179 igt_assert_eq(ptr[page], page);
180 munmap(ptr, bo.size);
181 }
182
prime_busy(int fd,bool excl)183 static bool prime_busy(int fd, bool excl)
184 {
185 struct pollfd pfd = { .fd = fd, .events = excl ? POLLOUT : POLLIN };
186 return poll(&pfd, 1, 0) == 0;
187 }
188
test_dmabuf_fence(int fd)189 static void test_dmabuf_fence(int fd)
190 {
191 struct vgem_bo bo;
192 int dmabuf;
193 uint32_t fence;
194
195 bo.width = 1024;
196 bo.height = 1;
197 bo.bpp = 32;
198 vgem_create(fd, &bo);
199
200 /* export, then fence */
201
202 dmabuf = prime_handle_to_fd(fd, bo.handle);
203
204 fence = vgem_fence_attach(fd, &bo, 0);
205 igt_assert(!prime_busy(dmabuf, false));
206 igt_assert(prime_busy(dmabuf, true));
207
208 vgem_fence_signal(fd, fence);
209 igt_assert(!prime_busy(dmabuf, false));
210 igt_assert(!prime_busy(dmabuf, true));
211
212 fence = vgem_fence_attach(fd, &bo, VGEM_FENCE_WRITE);
213 igt_assert(prime_busy(dmabuf, false));
214 igt_assert(prime_busy(dmabuf, true));
215
216 vgem_fence_signal(fd, fence);
217 igt_assert(!prime_busy(dmabuf, false));
218 igt_assert(!prime_busy(dmabuf, true));
219
220 close(dmabuf);
221 gem_close(fd, bo.handle);
222 }
223
test_dmabuf_fence_before(int fd)224 static void test_dmabuf_fence_before(int fd)
225 {
226 struct vgem_bo bo;
227 int dmabuf;
228 uint32_t fence;
229
230 bo.width = 1024;
231 bo.height = 1;
232 bo.bpp = 32;
233 vgem_create(fd, &bo);
234
235 fence = vgem_fence_attach(fd, &bo, 0);
236 dmabuf = prime_handle_to_fd(fd, bo.handle);
237
238 igt_assert(!prime_busy(dmabuf, false));
239 igt_assert(prime_busy(dmabuf, true));
240
241 vgem_fence_signal(fd, fence);
242 igt_assert(!prime_busy(dmabuf, false));
243 igt_assert(!prime_busy(dmabuf, true));
244
245 close(dmabuf);
246 gem_close(fd, bo.handle);
247
248 vgem_create(fd, &bo);
249
250 fence = vgem_fence_attach(fd, &bo, VGEM_FENCE_WRITE);
251 dmabuf = prime_handle_to_fd(fd, bo.handle);
252 igt_assert(prime_busy(dmabuf, false));
253 igt_assert(prime_busy(dmabuf, true));
254
255 vgem_fence_signal(fd, fence);
256 igt_assert(!prime_busy(dmabuf, false));
257 igt_assert(!prime_busy(dmabuf, true));
258
259 close(dmabuf);
260 gem_close(fd, bo.handle);
261 }
262
test_sysfs_read(int fd)263 static void test_sysfs_read(int fd)
264 {
265 int dir = igt_sysfs_open(fd);
266 DIR *dirp = fdopendir(dir);
267 struct dirent *de;
268
269 while ((de = readdir(dirp))) {
270 struct stat st;
271
272 if (*de->d_name == '.')
273 continue;
274
275 if (fstatat(dir, de->d_name, &st, 0))
276 continue;
277
278 if (S_ISDIR(st.st_mode))
279 continue;
280
281 igt_debug("Reading %s\n", de->d_name);
282 igt_set_timeout(1, "vgem sysfs read stalled");
283 free(igt_sysfs_get(dir, de->d_name));
284 igt_reset_timeout();
285 }
286
287 closedir(dirp);
288 close(dir);
289 }
290
test_debugfs_read(int fd)291 static void test_debugfs_read(int fd)
292 {
293 int dir = igt_debugfs_dir(fd);
294 DIR *dirp = fdopendir(dir);
295 struct dirent *de;
296
297 igt_assert(dirp);
298 while ((de = readdir(dirp))) {
299 struct stat st;
300
301 if (*de->d_name == '.')
302 continue;
303
304 if (fstatat(dir, de->d_name, &st, 0))
305 continue;
306
307 if (S_ISDIR(st.st_mode))
308 continue;
309
310 igt_debug("Reading %s\n", de->d_name);
311 igt_set_timeout(1, "vgem debugfs read stalled");
312 free(igt_sysfs_get(dir, de->d_name));
313 igt_reset_timeout();
314 }
315
316 closedir(dirp);
317 close(dir);
318 }
319
module_unload(void)320 static int module_unload(void)
321 {
322 return igt_kmod_unload("vgem", 0);
323 }
324
test_unload(void)325 static void test_unload(void)
326 {
327 struct vgem_bo bo;
328 int vgem, dmabuf;
329 uint32_t *ptr;
330
331 /* Load and unload vgem just to make sure it exists */
332 vgem = __drm_open_driver(DRIVER_VGEM);
333 igt_require(vgem != -1);
334 close(vgem);
335 igt_require(module_unload() == 0);
336
337 vgem = __drm_open_driver(DRIVER_VGEM);
338 igt_assert(vgem != -1);
339
340 /* The driver should stop the module from unloading */
341 igt_assert_f(module_unload() != 0,
342 "open(//dev/vgem) should keep the module alive\n");
343
344 bo.width = 1024;
345 bo.height = 1;
346 bo.bpp = 32;
347 vgem_create(vgem, &bo);
348 close(vgem);
349
350 /* Closing the driver should clear all normal references */
351 igt_assert_f(module_unload() == 0,
352 "No open(/dev/vgem), should be able to unload\n");
353
354 vgem = __drm_open_driver(DRIVER_VGEM);
355 igt_assert(vgem != -1);
356 bo.width = 1024;
357 bo.height = 1;
358 bo.bpp = 32;
359 vgem_create(vgem, &bo);
360 dmabuf = prime_handle_to_fd(vgem, bo.handle);
361 close(vgem);
362
363 /* A dmabuf should prevent module unload. */
364 igt_assert_f(module_unload() != 0,
365 "A dmabuf should keep the module alive\n");
366
367 close(dmabuf);
368 igt_assert_f(module_unload() == 0,
369 "No open dmabuf, should be able to unload\n");
370
371 vgem = __drm_open_driver(DRIVER_VGEM);
372 igt_assert(vgem != -1);
373 bo.width = 1024;
374 bo.height = 1;
375 bo.bpp = 32;
376 vgem_create(vgem, &bo);
377 dmabuf = prime_handle_to_fd_for_mmap(vgem, bo.handle);
378 close(vgem);
379
380 ptr = mmap(NULL, bo.size, PROT_WRITE, MAP_SHARED, dmabuf, 0);
381 igt_assert(ptr != MAP_FAILED);
382 close(dmabuf);
383
384 /* Although closed, the mmap should keep the dmabuf/module alive */
385 igt_assert_f(module_unload() == 0,
386 "A mmap should not keep the module alive\n");
387
388 for (int page = 0; page < bo.size >> 12; page++)
389 ptr[1024*page + page%1024] = page;
390
391 /* And finally we should have no more uses on the module. */
392 munmap(ptr, bo.size);
393 }
394
has_prime_export(int fd)395 static bool has_prime_export(int fd)
396 {
397 uint64_t value;
398
399 if (drmGetCap(fd, DRM_CAP_PRIME, &value))
400 return false;
401
402 return value & DRM_PRIME_CAP_EXPORT;
403 }
404
405 igt_main
406 {
407 int fd = -1;
408
409 igt_subtest("unload")
410 test_unload();
411
412 igt_fixture {
413 fd = drm_open_driver(DRIVER_VGEM);
414 }
415
416 igt_subtest_f("setversion")
417 test_setversion(fd);
418
419 igt_subtest_f("second-client")
420 test_client(fd);
421
422 igt_subtest_f("create")
423 test_create(fd);
424
425 igt_subtest_f("mmap")
426 test_mmap(fd);
427
428 igt_subtest_group {
429 igt_fixture {
430 igt_require(has_prime_export(fd));
431 }
432
433 igt_subtest("dmabuf-export")
434 test_dmabuf_export(fd);
435
436 igt_subtest("dmabuf-mmap")
437 test_dmabuf_mmap(fd);
438
439 igt_subtest_group {
440 igt_fixture {
441 igt_require(vgem_has_fences(fd));
442 }
443
444 igt_subtest("dmabuf-fence")
445 test_dmabuf_fence(fd);
446 igt_subtest("dmabuf-fence-before")
447 test_dmabuf_fence_before(fd);
448 }
449 }
450
451 igt_subtest("sysfs")
452 test_sysfs_read(fd);
453 igt_subtest("debugfs")
454 test_debugfs_read(fd);
455
456 igt_fixture {
457 close(fd);
458 }
459 }
460