1 /*
2 * Copyright © 2018 Google, Inc.
3 * Copyright © 2015 Intel Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdint.h>
28 #include <sys/ioctl.h>
29 #include <sys/mman.h>
30 #include <xf86drm.h>
31
32 #include "vk_util.h"
33
34 #include "drm-uapi/msm_drm.h"
35
36 #include "tu_private.h"
37
38 struct tu_syncobj {
39 struct vk_object_base base;
40 uint32_t permanent, temporary;
41 };
42
43 static int
tu_drm_get_param(const struct tu_physical_device * dev,uint32_t param,uint64_t * value)44 tu_drm_get_param(const struct tu_physical_device *dev,
45 uint32_t param,
46 uint64_t *value)
47 {
48 /* Technically this requires a pipe, but the kernel only supports one pipe
49 * anyway at the time of writing and most of these are clearly pipe
50 * independent. */
51 struct drm_msm_param req = {
52 .pipe = MSM_PIPE_3D0,
53 .param = param,
54 };
55
56 int ret = drmCommandWriteRead(dev->local_fd, DRM_MSM_GET_PARAM, &req,
57 sizeof(req));
58 if (ret)
59 return ret;
60
61 *value = req.value;
62
63 return 0;
64 }
65
66 static int
tu_drm_get_gpu_id(const struct tu_physical_device * dev,uint32_t * id)67 tu_drm_get_gpu_id(const struct tu_physical_device *dev, uint32_t *id)
68 {
69 uint64_t value;
70 int ret = tu_drm_get_param(dev, MSM_PARAM_GPU_ID, &value);
71 if (ret)
72 return ret;
73
74 *id = value;
75 return 0;
76 }
77
78 static int
tu_drm_get_gmem_size(const struct tu_physical_device * dev,uint32_t * size)79 tu_drm_get_gmem_size(const struct tu_physical_device *dev, uint32_t *size)
80 {
81 uint64_t value;
82 int ret = tu_drm_get_param(dev, MSM_PARAM_GMEM_SIZE, &value);
83 if (ret)
84 return ret;
85
86 *size = value;
87 return 0;
88 }
89
90 static int
tu_drm_get_gmem_base(const struct tu_physical_device * dev,uint64_t * base)91 tu_drm_get_gmem_base(const struct tu_physical_device *dev, uint64_t *base)
92 {
93 return tu_drm_get_param(dev, MSM_PARAM_GMEM_BASE, base);
94 }
95
96 int
tu_drm_submitqueue_new(const struct tu_device * dev,int priority,uint32_t * queue_id)97 tu_drm_submitqueue_new(const struct tu_device *dev,
98 int priority,
99 uint32_t *queue_id)
100 {
101 struct drm_msm_submitqueue req = {
102 .flags = 0,
103 .prio = priority,
104 };
105
106 int ret = drmCommandWriteRead(dev->fd,
107 DRM_MSM_SUBMITQUEUE_NEW, &req, sizeof(req));
108 if (ret)
109 return ret;
110
111 *queue_id = req.id;
112 return 0;
113 }
114
115 void
tu_drm_submitqueue_close(const struct tu_device * dev,uint32_t queue_id)116 tu_drm_submitqueue_close(const struct tu_device *dev, uint32_t queue_id)
117 {
118 drmCommandWrite(dev->fd, DRM_MSM_SUBMITQUEUE_CLOSE,
119 &queue_id, sizeof(uint32_t));
120 }
121
122 static void
tu_gem_close(const struct tu_device * dev,uint32_t gem_handle)123 tu_gem_close(const struct tu_device *dev, uint32_t gem_handle)
124 {
125 struct drm_gem_close req = {
126 .handle = gem_handle,
127 };
128
129 drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
130 }
131
132 /** Helper for DRM_MSM_GEM_INFO, returns 0 on error. */
133 static uint64_t
tu_gem_info(const struct tu_device * dev,uint32_t gem_handle,uint32_t info)134 tu_gem_info(const struct tu_device *dev, uint32_t gem_handle, uint32_t info)
135 {
136 struct drm_msm_gem_info req = {
137 .handle = gem_handle,
138 .info = info,
139 };
140
141 int ret = drmCommandWriteRead(dev->fd,
142 DRM_MSM_GEM_INFO, &req, sizeof(req));
143 if (ret < 0)
144 return 0;
145
146 return req.value;
147 }
148
149 static VkResult
tu_bo_init(struct tu_device * dev,struct tu_bo * bo,uint32_t gem_handle,uint64_t size,bool dump)150 tu_bo_init(struct tu_device *dev,
151 struct tu_bo *bo,
152 uint32_t gem_handle,
153 uint64_t size,
154 bool dump)
155 {
156 uint64_t iova = tu_gem_info(dev, gem_handle, MSM_INFO_GET_IOVA);
157 if (!iova) {
158 tu_gem_close(dev, gem_handle);
159 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
160 }
161
162 *bo = (struct tu_bo) {
163 .gem_handle = gem_handle,
164 .size = size,
165 .iova = iova,
166 };
167
168 mtx_lock(&dev->bo_mutex);
169 uint32_t idx = dev->bo_count++;
170
171 /* grow the bo list if needed */
172 if (idx >= dev->bo_list_size) {
173 uint32_t new_len = idx + 64;
174 struct drm_msm_gem_submit_bo *new_ptr =
175 vk_realloc(&dev->vk.alloc, dev->bo_list, new_len * sizeof(*dev->bo_list),
176 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
177 if (!new_ptr) {
178 tu_gem_close(dev, gem_handle);
179 return VK_ERROR_OUT_OF_HOST_MEMORY;
180 }
181
182 dev->bo_list = new_ptr;
183 dev->bo_list_size = new_len;
184 }
185
186 /* grow the "bo idx" list (maps gem handles to index in the bo list) */
187 if (bo->gem_handle >= dev->bo_idx_size) {
188 uint32_t new_len = bo->gem_handle + 256;
189 uint32_t *new_ptr =
190 vk_realloc(&dev->vk.alloc, dev->bo_idx, new_len * sizeof(*dev->bo_idx),
191 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
192 if (!new_ptr) {
193 tu_gem_close(dev, gem_handle);
194 return VK_ERROR_OUT_OF_HOST_MEMORY;
195 }
196
197 dev->bo_idx = new_ptr;
198 dev->bo_idx_size = new_len;
199 }
200
201 dev->bo_idx[bo->gem_handle] = idx;
202 dev->bo_list[idx] = (struct drm_msm_gem_submit_bo) {
203 .flags = MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE |
204 COND(dump, MSM_SUBMIT_BO_DUMP),
205 .handle = gem_handle,
206 .presumed = iova,
207 };
208 mtx_unlock(&dev->bo_mutex);
209
210 return VK_SUCCESS;
211 }
212
213 VkResult
tu_bo_init_new(struct tu_device * dev,struct tu_bo * bo,uint64_t size,bool dump)214 tu_bo_init_new(struct tu_device *dev, struct tu_bo *bo, uint64_t size, bool dump)
215 {
216 /* TODO: Choose better flags. As of 2018-11-12, freedreno/drm/msm_bo.c
217 * always sets `flags = MSM_BO_WC`, and we copy that behavior here.
218 */
219 struct drm_msm_gem_new req = {
220 .size = size,
221 .flags = MSM_BO_WC
222 };
223
224 int ret = drmCommandWriteRead(dev->fd,
225 DRM_MSM_GEM_NEW, &req, sizeof(req));
226 if (ret)
227 return vk_error(dev->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);
228
229 return tu_bo_init(dev, bo, req.handle, size, dump);
230 }
231
232 VkResult
tu_bo_init_dmabuf(struct tu_device * dev,struct tu_bo * bo,uint64_t size,int prime_fd)233 tu_bo_init_dmabuf(struct tu_device *dev,
234 struct tu_bo *bo,
235 uint64_t size,
236 int prime_fd)
237 {
238 /* lseek() to get the real size */
239 off_t real_size = lseek(prime_fd, 0, SEEK_END);
240 lseek(prime_fd, 0, SEEK_SET);
241 if (real_size < 0 || (uint64_t) real_size < size)
242 return vk_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
243
244 uint32_t gem_handle;
245 int ret = drmPrimeFDToHandle(dev->fd, prime_fd,
246 &gem_handle);
247 if (ret)
248 return vk_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
249
250 return tu_bo_init(dev, bo, gem_handle, size, false);
251 }
252
253 int
tu_bo_export_dmabuf(struct tu_device * dev,struct tu_bo * bo)254 tu_bo_export_dmabuf(struct tu_device *dev, struct tu_bo *bo)
255 {
256 int prime_fd;
257 int ret = drmPrimeHandleToFD(dev->fd, bo->gem_handle,
258 DRM_CLOEXEC, &prime_fd);
259
260 return ret == 0 ? prime_fd : -1;
261 }
262
263 VkResult
tu_bo_map(struct tu_device * dev,struct tu_bo * bo)264 tu_bo_map(struct tu_device *dev, struct tu_bo *bo)
265 {
266 if (bo->map)
267 return VK_SUCCESS;
268
269 uint64_t offset = tu_gem_info(dev, bo->gem_handle, MSM_INFO_GET_OFFSET);
270 if (!offset)
271 return vk_error(dev->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);
272
273 /* TODO: Should we use the wrapper os_mmap() like Freedreno does? */
274 void *map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
275 dev->fd, offset);
276 if (map == MAP_FAILED)
277 return vk_error(dev->instance, VK_ERROR_MEMORY_MAP_FAILED);
278
279 bo->map = map;
280 return VK_SUCCESS;
281 }
282
283 void
tu_bo_finish(struct tu_device * dev,struct tu_bo * bo)284 tu_bo_finish(struct tu_device *dev, struct tu_bo *bo)
285 {
286 assert(bo->gem_handle);
287
288 if (bo->map)
289 munmap(bo->map, bo->size);
290
291 mtx_lock(&dev->bo_mutex);
292 uint32_t idx = dev->bo_idx[bo->gem_handle];
293 dev->bo_count--;
294 dev->bo_list[idx] = dev->bo_list[dev->bo_count];
295 dev->bo_idx[dev->bo_list[idx].handle] = idx;
296 mtx_unlock(&dev->bo_mutex);
297
298 tu_gem_close(dev, bo->gem_handle);
299 }
300
301 static VkResult
tu_drm_device_init(struct tu_physical_device * device,struct tu_instance * instance,drmDevicePtr drm_device)302 tu_drm_device_init(struct tu_physical_device *device,
303 struct tu_instance *instance,
304 drmDevicePtr drm_device)
305 {
306 const char *path = drm_device->nodes[DRM_NODE_RENDER];
307 VkResult result = VK_SUCCESS;
308 drmVersionPtr version;
309 int fd;
310 int master_fd = -1;
311
312 fd = open(path, O_RDWR | O_CLOEXEC);
313 if (fd < 0) {
314 return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
315 "failed to open device %s", path);
316 }
317
318 /* Version 1.6 added SYNCOBJ support. */
319 const int min_version_major = 1;
320 const int min_version_minor = 6;
321
322 version = drmGetVersion(fd);
323 if (!version) {
324 close(fd);
325 return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
326 "failed to query kernel driver version for device %s",
327 path);
328 }
329
330 if (strcmp(version->name, "msm")) {
331 drmFreeVersion(version);
332 close(fd);
333 return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
334 "device %s does not use the msm kernel driver",
335 path);
336 }
337
338 if (version->version_major != min_version_major ||
339 version->version_minor < min_version_minor) {
340 result = vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
341 "kernel driver for device %s has version %d.%d, "
342 "but Vulkan requires version >= %d.%d",
343 path,
344 version->version_major, version->version_minor,
345 min_version_major, min_version_minor);
346 drmFreeVersion(version);
347 close(fd);
348 return result;
349 }
350
351 device->msm_major_version = version->version_major;
352 device->msm_minor_version = version->version_minor;
353
354 drmFreeVersion(version);
355
356 if (instance->debug_flags & TU_DEBUG_STARTUP)
357 mesa_logi("Found compatible device '%s'.", path);
358
359 vk_object_base_init(NULL, &device->base, VK_OBJECT_TYPE_PHYSICAL_DEVICE);
360 device->instance = instance;
361
362 if (instance->enabled_extensions.KHR_display) {
363 master_fd =
364 open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);
365 if (master_fd >= 0) {
366 /* TODO: free master_fd is accel is not working? */
367 }
368 }
369
370 device->master_fd = master_fd;
371 device->local_fd = fd;
372
373 if (tu_drm_get_gpu_id(device, &device->gpu_id)) {
374 if (instance->debug_flags & TU_DEBUG_STARTUP)
375 mesa_logi("Could not query the GPU ID");
376 result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
377 "could not get GPU ID");
378 goto fail;
379 }
380
381 if (tu_drm_get_gmem_size(device, &device->gmem_size)) {
382 if (instance->debug_flags & TU_DEBUG_STARTUP)
383 mesa_logi("Could not query the GMEM size");
384 result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
385 "could not get GMEM size");
386 goto fail;
387 }
388
389 if (tu_drm_get_gmem_base(device, &device->gmem_base)) {
390 if (instance->debug_flags & TU_DEBUG_STARTUP)
391 mesa_logi("Could not query the GMEM size");
392 result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
393 "could not get GMEM size");
394 goto fail;
395 }
396
397 return tu_physical_device_init(device, instance);
398
399 fail:
400 close(fd);
401 if (master_fd != -1)
402 close(master_fd);
403 return result;
404 }
405
406 VkResult
tu_enumerate_devices(struct tu_instance * instance)407 tu_enumerate_devices(struct tu_instance *instance)
408 {
409 /* TODO: Check for more devices ? */
410 drmDevicePtr devices[8];
411 VkResult result = VK_ERROR_INCOMPATIBLE_DRIVER;
412 int max_devices;
413
414 instance->physical_device_count = 0;
415
416 max_devices = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
417
418 if (instance->debug_flags & TU_DEBUG_STARTUP) {
419 if (max_devices < 0)
420 mesa_logi("drmGetDevices2 returned error: %s\n", strerror(max_devices));
421 else
422 mesa_logi("Found %d drm nodes", max_devices);
423 }
424
425 if (max_devices < 1)
426 return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
427 "No DRM devices found");
428
429 for (unsigned i = 0; i < (unsigned) max_devices; i++) {
430 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
431 devices[i]->bustype == DRM_BUS_PLATFORM) {
432
433 result = tu_drm_device_init(
434 instance->physical_devices + instance->physical_device_count,
435 instance, devices[i]);
436 if (result == VK_SUCCESS)
437 ++instance->physical_device_count;
438 else if (result != VK_ERROR_INCOMPATIBLE_DRIVER)
439 break;
440 }
441 }
442 drmFreeDevices(devices, max_devices);
443
444 return result;
445 }
446
447 static VkResult
sync_create(VkDevice _device,bool signaled,bool fence,const VkAllocationCallbacks * pAllocator,void ** p_sync)448 sync_create(VkDevice _device,
449 bool signaled,
450 bool fence,
451 const VkAllocationCallbacks *pAllocator,
452 void **p_sync)
453 {
454 TU_FROM_HANDLE(tu_device, device, _device);
455
456 struct tu_syncobj *sync =
457 vk_object_alloc(&device->vk, pAllocator, sizeof(*sync),
458 fence ? VK_OBJECT_TYPE_FENCE : VK_OBJECT_TYPE_SEMAPHORE);
459 if (!sync)
460 return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
461
462 struct drm_syncobj_create create = {};
463 if (signaled)
464 create.flags |= DRM_SYNCOBJ_CREATE_SIGNALED;
465
466 int ret = ioctl(device->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create);
467 if (ret) {
468 vk_free2(&device->vk.alloc, pAllocator, sync);
469 return VK_ERROR_OUT_OF_HOST_MEMORY;
470 }
471
472 sync->permanent = create.handle;
473 sync->temporary = 0;
474 *p_sync = sync;
475
476 return VK_SUCCESS;
477 }
478
479 static void
sync_set_temporary(struct tu_device * device,struct tu_syncobj * sync,uint32_t syncobj)480 sync_set_temporary(struct tu_device *device, struct tu_syncobj *sync, uint32_t syncobj)
481 {
482 if (sync->temporary) {
483 ioctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,
484 &(struct drm_syncobj_destroy) { .handle = sync->temporary });
485 }
486 sync->temporary = syncobj;
487 }
488
489 static void
sync_destroy(VkDevice _device,struct tu_syncobj * sync,const VkAllocationCallbacks * pAllocator)490 sync_destroy(VkDevice _device, struct tu_syncobj *sync, const VkAllocationCallbacks *pAllocator)
491 {
492 TU_FROM_HANDLE(tu_device, device, _device);
493
494 if (!sync)
495 return;
496
497 sync_set_temporary(device, sync, 0);
498 ioctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,
499 &(struct drm_syncobj_destroy) { .handle = sync->permanent });
500
501 vk_object_free(&device->vk, pAllocator, sync);
502 }
503
504 static VkResult
sync_import(VkDevice _device,struct tu_syncobj * sync,bool temporary,bool sync_fd,int fd)505 sync_import(VkDevice _device, struct tu_syncobj *sync, bool temporary, bool sync_fd, int fd)
506 {
507 TU_FROM_HANDLE(tu_device, device, _device);
508 int ret;
509
510 if (!sync_fd) {
511 uint32_t *dst = temporary ? &sync->temporary : &sync->permanent;
512
513 struct drm_syncobj_handle handle = { .fd = fd };
514 ret = ioctl(device->fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &handle);
515 if (ret)
516 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
517
518 if (*dst) {
519 ioctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,
520 &(struct drm_syncobj_destroy) { .handle = *dst });
521 }
522 *dst = handle.handle;
523 close(fd);
524 } else {
525 assert(temporary);
526
527 struct drm_syncobj_create create = {};
528
529 if (fd == -1)
530 create.flags |= DRM_SYNCOBJ_CREATE_SIGNALED;
531
532 ret = ioctl(device->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create);
533 if (ret)
534 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
535
536 if (fd != -1) {
537 ret = ioctl(device->fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &(struct drm_syncobj_handle) {
538 .fd = fd,
539 .handle = create.handle,
540 .flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE,
541 });
542 if (ret) {
543 ioctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,
544 &(struct drm_syncobj_destroy) { .handle = create.handle });
545 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
546 }
547 close(fd);
548 }
549
550 sync_set_temporary(device, sync, create.handle);
551 }
552
553 return VK_SUCCESS;
554 }
555
556 static VkResult
sync_export(VkDevice _device,struct tu_syncobj * sync,bool sync_fd,int * p_fd)557 sync_export(VkDevice _device, struct tu_syncobj *sync, bool sync_fd, int *p_fd)
558 {
559 TU_FROM_HANDLE(tu_device, device, _device);
560
561 struct drm_syncobj_handle handle = {
562 .handle = sync->temporary ?: sync->permanent,
563 .flags = COND(sync_fd, DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE),
564 .fd = -1,
565 };
566 int ret = ioctl(device->fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &handle);
567 if (ret)
568 return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
569
570 /* restore permanent payload on export */
571 sync_set_temporary(device, sync, 0);
572
573 *p_fd = handle.fd;
574 return VK_SUCCESS;
575 }
576
577 VkResult
tu_CreateSemaphore(VkDevice device,const VkSemaphoreCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkSemaphore * pSemaphore)578 tu_CreateSemaphore(VkDevice device,
579 const VkSemaphoreCreateInfo *pCreateInfo,
580 const VkAllocationCallbacks *pAllocator,
581 VkSemaphore *pSemaphore)
582 {
583 return sync_create(device, false, false, pAllocator, (void**) pSemaphore);
584 }
585
586 void
tu_DestroySemaphore(VkDevice device,VkSemaphore sem,const VkAllocationCallbacks * pAllocator)587 tu_DestroySemaphore(VkDevice device, VkSemaphore sem, const VkAllocationCallbacks *pAllocator)
588 {
589 TU_FROM_HANDLE(tu_syncobj, sync, sem);
590 sync_destroy(device, sync, pAllocator);
591 }
592
593 VkResult
tu_ImportSemaphoreFdKHR(VkDevice device,const VkImportSemaphoreFdInfoKHR * info)594 tu_ImportSemaphoreFdKHR(VkDevice device, const VkImportSemaphoreFdInfoKHR *info)
595 {
596 TU_FROM_HANDLE(tu_syncobj, sync, info->semaphore);
597 return sync_import(device, sync, info->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT,
598 info->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, info->fd);
599 }
600
601 VkResult
tu_GetSemaphoreFdKHR(VkDevice device,const VkSemaphoreGetFdInfoKHR * info,int * pFd)602 tu_GetSemaphoreFdKHR(VkDevice device, const VkSemaphoreGetFdInfoKHR *info, int *pFd)
603 {
604 TU_FROM_HANDLE(tu_syncobj, sync, info->semaphore);
605 return sync_export(device, sync,
606 info->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, pFd);
607 }
608
609 void
tu_GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice,const VkPhysicalDeviceExternalSemaphoreInfo * pExternalSemaphoreInfo,VkExternalSemaphoreProperties * pExternalSemaphoreProperties)610 tu_GetPhysicalDeviceExternalSemaphoreProperties(
611 VkPhysicalDevice physicalDevice,
612 const VkPhysicalDeviceExternalSemaphoreInfo *pExternalSemaphoreInfo,
613 VkExternalSemaphoreProperties *pExternalSemaphoreProperties)
614 {
615 if (pExternalSemaphoreInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT ||
616 pExternalSemaphoreInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) {
617 pExternalSemaphoreProperties->exportFromImportedHandleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT | VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
618 pExternalSemaphoreProperties->compatibleHandleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT | VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
619 pExternalSemaphoreProperties->externalSemaphoreFeatures = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
620 VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
621 } else {
622 pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
623 pExternalSemaphoreProperties->compatibleHandleTypes = 0;
624 pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
625 }
626 }
627
628 VkResult
tu_QueueSubmit(VkQueue _queue,uint32_t submitCount,const VkSubmitInfo * pSubmits,VkFence _fence)629 tu_QueueSubmit(VkQueue _queue,
630 uint32_t submitCount,
631 const VkSubmitInfo *pSubmits,
632 VkFence _fence)
633 {
634 TU_FROM_HANDLE(tu_queue, queue, _queue);
635 TU_FROM_HANDLE(tu_syncobj, fence, _fence);
636
637 for (uint32_t i = 0; i < submitCount; ++i) {
638 const VkSubmitInfo *submit = pSubmits + i;
639 const bool last_submit = (i == submitCount - 1);
640 uint32_t out_syncobjs_size = submit->signalSemaphoreCount;
641 if (last_submit && fence)
642 out_syncobjs_size += 1;
643 /* note: assuming there won't be any very large semaphore counts */
644 struct drm_msm_gem_submit_syncobj in_syncobjs[submit->waitSemaphoreCount];
645 struct drm_msm_gem_submit_syncobj out_syncobjs[out_syncobjs_size];
646 uint32_t nr_in_syncobjs = 0, nr_out_syncobjs = 0;
647
648 for (uint32_t i = 0; i < submit->waitSemaphoreCount; i++) {
649 TU_FROM_HANDLE(tu_syncobj, sem, submit->pWaitSemaphores[i]);
650 in_syncobjs[nr_in_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {
651 .handle = sem->temporary ?: sem->permanent,
652 .flags = MSM_SUBMIT_SYNCOBJ_RESET,
653 };
654 }
655
656 for (uint32_t i = 0; i < submit->signalSemaphoreCount; i++) {
657 TU_FROM_HANDLE(tu_syncobj, sem, submit->pSignalSemaphores[i]);
658 out_syncobjs[nr_out_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {
659 .handle = sem->temporary ?: sem->permanent,
660 .flags = 0,
661 };
662 }
663
664 if (last_submit && fence) {
665 out_syncobjs[nr_out_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {
666 .handle = fence->temporary ?: fence->permanent,
667 .flags = 0,
668 };
669 }
670
671 uint32_t entry_count = 0;
672 for (uint32_t j = 0; j < submit->commandBufferCount; ++j) {
673 TU_FROM_HANDLE(tu_cmd_buffer, cmdbuf, submit->pCommandBuffers[j]);
674 entry_count += cmdbuf->cs.entry_count;
675 }
676
677 mtx_lock(&queue->device->bo_mutex);
678
679 struct drm_msm_gem_submit_cmd cmds[entry_count];
680 uint32_t entry_idx = 0;
681 for (uint32_t j = 0; j < submit->commandBufferCount; ++j) {
682 TU_FROM_HANDLE(tu_cmd_buffer, cmdbuf, submit->pCommandBuffers[j]);
683 struct tu_cs *cs = &cmdbuf->cs;
684 for (unsigned i = 0; i < cs->entry_count; ++i, ++entry_idx) {
685 cmds[entry_idx].type = MSM_SUBMIT_CMD_BUF;
686 cmds[entry_idx].submit_idx =
687 queue->device->bo_idx[cs->entries[i].bo->gem_handle];
688 cmds[entry_idx].submit_offset = cs->entries[i].offset;
689 cmds[entry_idx].size = cs->entries[i].size;
690 cmds[entry_idx].pad = 0;
691 cmds[entry_idx].nr_relocs = 0;
692 cmds[entry_idx].relocs = 0;
693 }
694 }
695
696 uint32_t flags = MSM_PIPE_3D0;
697 if (nr_in_syncobjs) {
698 flags |= MSM_SUBMIT_SYNCOBJ_IN;
699 }
700 if (nr_out_syncobjs) {
701 flags |= MSM_SUBMIT_SYNCOBJ_OUT;
702 }
703 if (last_submit) {
704 flags |= MSM_SUBMIT_FENCE_FD_OUT;
705 }
706
707 struct drm_msm_gem_submit req = {
708 .flags = flags,
709 .queueid = queue->msm_queue_id,
710 .bos = (uint64_t)(uintptr_t) queue->device->bo_list,
711 .nr_bos = queue->device->bo_count,
712 .cmds = (uint64_t)(uintptr_t)cmds,
713 .nr_cmds = entry_count,
714 .in_syncobjs = (uint64_t)(uintptr_t)in_syncobjs,
715 .out_syncobjs = (uint64_t)(uintptr_t)out_syncobjs,
716 .nr_in_syncobjs = nr_in_syncobjs,
717 .nr_out_syncobjs = nr_out_syncobjs,
718 .syncobj_stride = sizeof(struct drm_msm_gem_submit_syncobj),
719 };
720
721 int ret = drmCommandWriteRead(queue->device->fd,
722 DRM_MSM_GEM_SUBMIT,
723 &req, sizeof(req));
724 mtx_unlock(&queue->device->bo_mutex);
725 if (ret) {
726 return tu_device_set_lost(queue->device, "submit failed: %s\n",
727 strerror(errno));
728 }
729
730 /* restore permanent payload on wait */
731 for (uint32_t i = 0; i < submit->waitSemaphoreCount; i++) {
732 TU_FROM_HANDLE(tu_syncobj, sem, submit->pWaitSemaphores[i]);
733 sync_set_temporary(queue->device, sem, 0);
734 }
735
736 if (last_submit) {
737 if (queue->fence >= 0)
738 close(queue->fence);
739 queue->fence = req.fence_fd;
740 }
741 }
742
743 if (!submitCount && fence) {
744 /* signal fence imemediately since we don't have a submit to do it */
745 ioctl(queue->device->fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &(struct drm_syncobj_array) {
746 .handles = (uintptr_t) (uint32_t[]) { fence->temporary ?: fence->permanent },
747 .count_handles = 1,
748 });
749 }
750
751 return VK_SUCCESS;
752 }
753
754 VkResult
tu_CreateFence(VkDevice device,const VkFenceCreateInfo * info,const VkAllocationCallbacks * pAllocator,VkFence * pFence)755 tu_CreateFence(VkDevice device,
756 const VkFenceCreateInfo *info,
757 const VkAllocationCallbacks *pAllocator,
758 VkFence *pFence)
759 {
760 return sync_create(device, info->flags & VK_FENCE_CREATE_SIGNALED_BIT, true,
761 pAllocator, (void**) pFence);
762 }
763
764 void
tu_DestroyFence(VkDevice device,VkFence fence,const VkAllocationCallbacks * pAllocator)765 tu_DestroyFence(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator)
766 {
767 TU_FROM_HANDLE(tu_syncobj, sync, fence);
768 sync_destroy(device, sync, pAllocator);
769 }
770
771 VkResult
tu_ImportFenceFdKHR(VkDevice device,const VkImportFenceFdInfoKHR * info)772 tu_ImportFenceFdKHR(VkDevice device, const VkImportFenceFdInfoKHR *info)
773 {
774 TU_FROM_HANDLE(tu_syncobj, sync, info->fence);
775 return sync_import(device, sync, info->flags & VK_FENCE_IMPORT_TEMPORARY_BIT,
776 info->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, info->fd);
777 }
778
779 VkResult
tu_GetFenceFdKHR(VkDevice device,const VkFenceGetFdInfoKHR * info,int * pFd)780 tu_GetFenceFdKHR(VkDevice device, const VkFenceGetFdInfoKHR *info, int *pFd)
781 {
782 TU_FROM_HANDLE(tu_syncobj, sync, info->fence);
783 return sync_export(device, sync,
784 info->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, pFd);
785 }
786
787 static VkResult
drm_syncobj_wait(struct tu_device * device,const uint32_t * handles,uint32_t count_handles,int64_t timeout_nsec,bool wait_all)788 drm_syncobj_wait(struct tu_device *device,
789 const uint32_t *handles, uint32_t count_handles,
790 int64_t timeout_nsec, bool wait_all)
791 {
792 int ret = ioctl(device->fd, DRM_IOCTL_SYNCOBJ_WAIT, &(struct drm_syncobj_wait) {
793 .handles = (uint64_t) (uintptr_t) handles,
794 .count_handles = count_handles,
795 .timeout_nsec = timeout_nsec,
796 .flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
797 COND(wait_all, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL)
798 });
799 if (ret) {
800 if (errno == ETIME)
801 return VK_TIMEOUT;
802
803 assert(0);
804 return VK_ERROR_DEVICE_LOST; /* TODO */
805 }
806 return VK_SUCCESS;
807 }
808
809 static uint64_t
gettime_ns(void)810 gettime_ns(void)
811 {
812 struct timespec current;
813 clock_gettime(CLOCK_MONOTONIC, ¤t);
814 return (uint64_t)current.tv_sec * 1000000000 + current.tv_nsec;
815 }
816
817 /* and the kernel converts it right back to relative timeout - very smart UAPI */
818 static uint64_t
absolute_timeout(uint64_t timeout)819 absolute_timeout(uint64_t timeout)
820 {
821 if (timeout == 0)
822 return 0;
823 uint64_t current_time = gettime_ns();
824 uint64_t max_timeout = (uint64_t) INT64_MAX - current_time;
825
826 timeout = MIN2(max_timeout, timeout);
827
828 return (current_time + timeout);
829 }
830
831 VkResult
tu_WaitForFences(VkDevice _device,uint32_t fenceCount,const VkFence * pFences,VkBool32 waitAll,uint64_t timeout)832 tu_WaitForFences(VkDevice _device,
833 uint32_t fenceCount,
834 const VkFence *pFences,
835 VkBool32 waitAll,
836 uint64_t timeout)
837 {
838 TU_FROM_HANDLE(tu_device, device, _device);
839
840 if (tu_device_is_lost(device))
841 return VK_ERROR_DEVICE_LOST;
842
843 uint32_t handles[fenceCount];
844 for (unsigned i = 0; i < fenceCount; ++i) {
845 TU_FROM_HANDLE(tu_syncobj, fence, pFences[i]);
846 handles[i] = fence->temporary ?: fence->permanent;
847 }
848
849 return drm_syncobj_wait(device, handles, fenceCount, absolute_timeout(timeout), waitAll);
850 }
851
852 VkResult
tu_ResetFences(VkDevice _device,uint32_t fenceCount,const VkFence * pFences)853 tu_ResetFences(VkDevice _device, uint32_t fenceCount, const VkFence *pFences)
854 {
855 TU_FROM_HANDLE(tu_device, device, _device);
856 int ret;
857
858 uint32_t handles[fenceCount];
859 for (unsigned i = 0; i < fenceCount; ++i) {
860 TU_FROM_HANDLE(tu_syncobj, fence, pFences[i]);
861 sync_set_temporary(device, fence, 0);
862 handles[i] = fence->permanent;
863 }
864
865 ret = ioctl(device->fd, DRM_IOCTL_SYNCOBJ_RESET, &(struct drm_syncobj_array) {
866 .handles = (uint64_t) (uintptr_t) handles,
867 .count_handles = fenceCount,
868 });
869 if (ret) {
870 tu_device_set_lost(device, "DRM_IOCTL_SYNCOBJ_RESET failure: %s",
871 strerror(errno));
872 }
873
874 return VK_SUCCESS;
875 }
876
877 VkResult
tu_GetFenceStatus(VkDevice _device,VkFence _fence)878 tu_GetFenceStatus(VkDevice _device, VkFence _fence)
879 {
880 TU_FROM_HANDLE(tu_device, device, _device);
881 TU_FROM_HANDLE(tu_syncobj, fence, _fence);
882 VkResult result;
883
884 result = drm_syncobj_wait(device, (uint32_t[]){fence->temporary ?: fence->permanent}, 1, 0, false);
885 if (result == VK_TIMEOUT)
886 result = VK_NOT_READY;
887 return result;
888 }
889
890 int
tu_signal_fences(struct tu_device * device,struct tu_syncobj * fence1,struct tu_syncobj * fence2)891 tu_signal_fences(struct tu_device *device, struct tu_syncobj *fence1, struct tu_syncobj *fence2)
892 {
893 uint32_t handles[2], count = 0;
894 if (fence1)
895 handles[count++] = fence1->temporary ?: fence1->permanent;
896
897 if (fence2)
898 handles[count++] = fence2->temporary ?: fence2->permanent;
899
900 if (!count)
901 return 0;
902
903 return ioctl(device->fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &(struct drm_syncobj_array) {
904 .handles = (uintptr_t) handles,
905 .count_handles = count
906 });
907 }
908
909 int
tu_syncobj_to_fd(struct tu_device * device,struct tu_syncobj * sync)910 tu_syncobj_to_fd(struct tu_device *device, struct tu_syncobj *sync)
911 {
912 struct drm_syncobj_handle handle = { .handle = sync->permanent };
913 int ret;
914
915 ret = ioctl(device->fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &handle);
916
917 return ret ? -1 : handle.fd;
918 }
919
920 #ifdef ANDROID
921 #include <libsync.h>
922
923 VkResult
tu_QueueSignalReleaseImageANDROID(VkQueue _queue,uint32_t waitSemaphoreCount,const VkSemaphore * pWaitSemaphores,VkImage image,int * pNativeFenceFd)924 tu_QueueSignalReleaseImageANDROID(VkQueue _queue,
925 uint32_t waitSemaphoreCount,
926 const VkSemaphore *pWaitSemaphores,
927 VkImage image,
928 int *pNativeFenceFd)
929 {
930 TU_FROM_HANDLE(tu_queue, queue, _queue);
931 VkResult result = VK_SUCCESS;
932
933 if (waitSemaphoreCount == 0) {
934 if (pNativeFenceFd)
935 *pNativeFenceFd = -1;
936 return VK_SUCCESS;
937 }
938
939 int fd = -1;
940
941 for (uint32_t i = 0; i < waitSemaphoreCount; ++i) {
942 int tmp_fd;
943 result = tu_GetSemaphoreFdKHR(
944 tu_device_to_handle(queue->device),
945 &(VkSemaphoreGetFdInfoKHR) {
946 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
947 .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
948 .semaphore = pWaitSemaphores[i],
949 },
950 &tmp_fd);
951 if (result != VK_SUCCESS) {
952 if (fd >= 0)
953 close(fd);
954 return result;
955 }
956
957 if (fd < 0)
958 fd = tmp_fd;
959 else if (tmp_fd >= 0) {
960 sync_accumulate("tu", &fd, tmp_fd);
961 close(tmp_fd);
962 }
963 }
964
965 if (pNativeFenceFd) {
966 *pNativeFenceFd = fd;
967 } else if (fd >= 0) {
968 close(fd);
969 /* We still need to do the exports, to reset the semaphores, but
970 * otherwise we don't wait on them. */
971 }
972 return VK_SUCCESS;
973 }
974 #endif
975