• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &current);
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