17ec681f3Smrg/*
27ec681f3Smrg * Copyright 2020 Google LLC
37ec681f3Smrg * SPDX-License-Identifier: MIT
47ec681f3Smrg */
57ec681f3Smrg
67ec681f3Smrg#include <errno.h>
77ec681f3Smrg#include <fcntl.h>
87ec681f3Smrg#include <poll.h>
97ec681f3Smrg#include <sys/mman.h>
107ec681f3Smrg#include <sys/stat.h>
117ec681f3Smrg#include <sys/types.h>
127ec681f3Smrg#include <unistd.h>
137ec681f3Smrg#include <xf86drm.h>
147ec681f3Smrg
157ec681f3Smrg#include "drm-uapi/virtgpu_drm.h"
167ec681f3Smrg#include "util/sparse_array.h"
177ec681f3Smrg#define VIRGL_RENDERER_UNSTABLE_APIS
187ec681f3Smrg#include "virtio-gpu/virglrenderer_hw.h"
197ec681f3Smrg
207ec681f3Smrg#include "vn_renderer.h"
217ec681f3Smrg
227ec681f3Smrg/* XXX WIP kernel uapi */
237ec681f3Smrg#ifndef VIRTGPU_PARAM_CONTEXT_INIT
247ec681f3Smrg#define VIRTGPU_PARAM_CONTEXT_INIT 6
257ec681f3Smrg#define VIRTGPU_CONTEXT_PARAM_CAPSET_ID 0x0001
267ec681f3Smrgstruct drm_virtgpu_context_set_param {
277ec681f3Smrg   __u64 param;
287ec681f3Smrg   __u64 value;
297ec681f3Smrg};
307ec681f3Smrgstruct drm_virtgpu_context_init {
317ec681f3Smrg   __u32 num_params;
327ec681f3Smrg   __u32 pad;
337ec681f3Smrg   __u64 ctx_set_params;
347ec681f3Smrg};
357ec681f3Smrg#define DRM_VIRTGPU_CONTEXT_INIT 0xb
367ec681f3Smrg#define DRM_IOCTL_VIRTGPU_CONTEXT_INIT                                       \
377ec681f3Smrg   DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_CONTEXT_INIT,                     \
387ec681f3Smrg            struct drm_virtgpu_context_init)
397ec681f3Smrg#endif /* VIRTGPU_PARAM_CONTEXT_INIT */
407ec681f3Smrg#ifndef VIRTGPU_PARAM_MAX_SYNC_QUEUE_COUNT
417ec681f3Smrg#define VIRTGPU_PARAM_MAX_SYNC_QUEUE_COUNT 100
427ec681f3Smrg#endif /* VIRTGPU_PARAM_MAX_SYNC_QUEUE_COUNT */
437ec681f3Smrg
447ec681f3Smrg/* XXX comment these out to really use kernel uapi */
457ec681f3Smrg#define SIMULATE_BO_SIZE_FIX 1
467ec681f3Smrg//#define SIMULATE_CONTEXT_INIT 1
477ec681f3Smrg#define SIMULATE_SYNCOBJ 1
487ec681f3Smrg#define SIMULATE_SUBMIT 1
497ec681f3Smrg
507ec681f3Smrg#define VIRTGPU_PCI_VENDOR_ID 0x1af4
517ec681f3Smrg#define VIRTGPU_PCI_DEVICE_ID 0x1050
527ec681f3Smrg
537ec681f3Smrgstruct virtgpu;
547ec681f3Smrg
557ec681f3Smrgstruct virtgpu_shmem {
567ec681f3Smrg   struct vn_renderer_shmem base;
577ec681f3Smrg   uint32_t gem_handle;
587ec681f3Smrg};
597ec681f3Smrg
607ec681f3Smrgstruct virtgpu_bo {
617ec681f3Smrg   struct vn_renderer_bo base;
627ec681f3Smrg   uint32_t gem_handle;
637ec681f3Smrg   uint32_t blob_flags;
647ec681f3Smrg};
657ec681f3Smrg
667ec681f3Smrgstruct virtgpu_sync {
677ec681f3Smrg   struct vn_renderer_sync base;
687ec681f3Smrg
697ec681f3Smrg   /*
707ec681f3Smrg    * drm_syncobj is in one of these states
717ec681f3Smrg    *
727ec681f3Smrg    *  - value N:      drm_syncobj has a signaled fence chain with seqno N
737ec681f3Smrg    *  - pending N->M: drm_syncobj has an unsignaled fence chain with seqno M
747ec681f3Smrg    *                  (which may point to another unsignaled fence chain with
757ec681f3Smrg    *                   seqno between N and M, and so on)
767ec681f3Smrg    *
777ec681f3Smrg    * TODO Do we want to use binary drm_syncobjs?  They would be
787ec681f3Smrg    *
797ec681f3Smrg    *  - value 0: drm_syncobj has no fence
807ec681f3Smrg    *  - value 1: drm_syncobj has a signaled fence with seqno 0
817ec681f3Smrg    *
827ec681f3Smrg    * They are cheaper but require special care.
837ec681f3Smrg    */
847ec681f3Smrg   uint32_t syncobj_handle;
857ec681f3Smrg};
867ec681f3Smrg
877ec681f3Smrgstruct virtgpu {
887ec681f3Smrg   struct vn_renderer base;
897ec681f3Smrg
907ec681f3Smrg   struct vn_instance *instance;
917ec681f3Smrg
927ec681f3Smrg   int fd;
937ec681f3Smrg   int version_minor;
947ec681f3Smrg   drmPciBusInfo bus_info;
957ec681f3Smrg
967ec681f3Smrg   uint32_t max_sync_queue_count;
977ec681f3Smrg
987ec681f3Smrg   struct {
997ec681f3Smrg      enum virgl_renderer_capset id;
1007ec681f3Smrg      uint32_t version;
1017ec681f3Smrg      struct virgl_renderer_capset_venus data;
1027ec681f3Smrg   } capset;
1037ec681f3Smrg
1047ec681f3Smrg   /* note that we use gem_handle instead of res_id to index because
1057ec681f3Smrg    * res_id is monotonically increasing by default (see
1067ec681f3Smrg    * virtio_gpu_resource_id_get)
1077ec681f3Smrg    */
1087ec681f3Smrg   struct util_sparse_array shmem_array;
1097ec681f3Smrg   struct util_sparse_array bo_array;
1107ec681f3Smrg
1117ec681f3Smrg   mtx_t dma_buf_import_mutex;
1127ec681f3Smrg};
1137ec681f3Smrg
1147ec681f3Smrg#ifdef SIMULATE_SYNCOBJ
1157ec681f3Smrg
1167ec681f3Smrg#include "util/hash_table.h"
1177ec681f3Smrg#include "util/u_idalloc.h"
1187ec681f3Smrg
1197ec681f3Smrgstatic struct {
1207ec681f3Smrg   mtx_t mutex;
1217ec681f3Smrg   struct hash_table *syncobjs;
1227ec681f3Smrg   struct util_idalloc ida;
1237ec681f3Smrg
1247ec681f3Smrg   int signaled_fd;
1257ec681f3Smrg} sim;
1267ec681f3Smrg
1277ec681f3Smrgstruct sim_syncobj {
1287ec681f3Smrg   mtx_t mutex;
1297ec681f3Smrg   uint64_t point;
1307ec681f3Smrg
1317ec681f3Smrg   int pending_fd;
1327ec681f3Smrg   uint64_t pending_point;
1337ec681f3Smrg   bool pending_cpu;
1347ec681f3Smrg};
1357ec681f3Smrg
1367ec681f3Smrgstatic uint32_t
1377ec681f3Smrgsim_syncobj_create(struct virtgpu *gpu, bool signaled)
1387ec681f3Smrg{
1397ec681f3Smrg   struct sim_syncobj *syncobj = calloc(1, sizeof(*syncobj));
1407ec681f3Smrg   if (!syncobj)
1417ec681f3Smrg      return 0;
1427ec681f3Smrg
1437ec681f3Smrg   mtx_init(&syncobj->mutex, mtx_plain);
1447ec681f3Smrg   syncobj->pending_fd = -1;
1457ec681f3Smrg
1467ec681f3Smrg   mtx_lock(&sim.mutex);
1477ec681f3Smrg
1487ec681f3Smrg   /* initialize lazily */
1497ec681f3Smrg   if (!sim.syncobjs) {
1507ec681f3Smrg      sim.syncobjs = _mesa_pointer_hash_table_create(NULL);
1517ec681f3Smrg      if (!sim.syncobjs) {
1527ec681f3Smrg         mtx_unlock(&sim.mutex);
1537ec681f3Smrg         return 0;
1547ec681f3Smrg      }
1557ec681f3Smrg
1567ec681f3Smrg      util_idalloc_init(&sim.ida, 32);
1577ec681f3Smrg
1587ec681f3Smrg      struct drm_virtgpu_execbuffer args = {
1597ec681f3Smrg         .flags = VIRTGPU_EXECBUF_FENCE_FD_OUT,
1607ec681f3Smrg      };
1617ec681f3Smrg      int ret = drmIoctl(gpu->fd, DRM_IOCTL_VIRTGPU_EXECBUFFER, &args);
1627ec681f3Smrg      if (ret || args.fence_fd < 0) {
1637ec681f3Smrg         _mesa_hash_table_destroy(sim.syncobjs, NULL);
1647ec681f3Smrg         sim.syncobjs = NULL;
1657ec681f3Smrg         mtx_unlock(&sim.mutex);
1667ec681f3Smrg         return 0;
1677ec681f3Smrg      }
1687ec681f3Smrg
1697ec681f3Smrg      sim.signaled_fd = args.fence_fd;
1707ec681f3Smrg   }
1717ec681f3Smrg
1727ec681f3Smrg   const unsigned syncobj_handle = util_idalloc_alloc(&sim.ida) + 1;
1737ec681f3Smrg   _mesa_hash_table_insert(sim.syncobjs,
1747ec681f3Smrg                           (const void *)(uintptr_t)syncobj_handle, syncobj);
1757ec681f3Smrg
1767ec681f3Smrg   mtx_unlock(&sim.mutex);
1777ec681f3Smrg
1787ec681f3Smrg   return syncobj_handle;
1797ec681f3Smrg}
1807ec681f3Smrg
1817ec681f3Smrgstatic void
1827ec681f3Smrgsim_syncobj_destroy(struct virtgpu *gpu, uint32_t syncobj_handle)
1837ec681f3Smrg{
1847ec681f3Smrg   struct sim_syncobj *syncobj = NULL;
1857ec681f3Smrg
1867ec681f3Smrg   mtx_lock(&sim.mutex);
1877ec681f3Smrg
1887ec681f3Smrg   struct hash_entry *entry = _mesa_hash_table_search(
1897ec681f3Smrg      sim.syncobjs, (const void *)(uintptr_t)syncobj_handle);
1907ec681f3Smrg   if (entry) {
1917ec681f3Smrg      syncobj = entry->data;
1927ec681f3Smrg      _mesa_hash_table_remove(sim.syncobjs, entry);
1937ec681f3Smrg      util_idalloc_free(&sim.ida, syncobj_handle - 1);
1947ec681f3Smrg   }
1957ec681f3Smrg
1967ec681f3Smrg   mtx_unlock(&sim.mutex);
1977ec681f3Smrg
1987ec681f3Smrg   if (syncobj) {
1997ec681f3Smrg      if (syncobj->pending_fd >= 0)
2007ec681f3Smrg         close(syncobj->pending_fd);
2017ec681f3Smrg      mtx_destroy(&syncobj->mutex);
2027ec681f3Smrg      free(syncobj);
2037ec681f3Smrg   }
2047ec681f3Smrg}
2057ec681f3Smrg
2067ec681f3Smrgstatic VkResult
2077ec681f3Smrgsim_syncobj_poll(int fd, int poll_timeout)
2087ec681f3Smrg{
2097ec681f3Smrg   struct pollfd pollfd = {
2107ec681f3Smrg      .fd = fd,
2117ec681f3Smrg      .events = POLLIN,
2127ec681f3Smrg   };
2137ec681f3Smrg   int ret;
2147ec681f3Smrg   do {
2157ec681f3Smrg      ret = poll(&pollfd, 1, poll_timeout);
2167ec681f3Smrg   } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
2177ec681f3Smrg
2187ec681f3Smrg   if (ret < 0 || (ret > 0 && !(pollfd.revents & POLLIN))) {
2197ec681f3Smrg      return (ret < 0 && errno == ENOMEM) ? VK_ERROR_OUT_OF_HOST_MEMORY
2207ec681f3Smrg                                          : VK_ERROR_DEVICE_LOST;
2217ec681f3Smrg   }
2227ec681f3Smrg
2237ec681f3Smrg   return ret ? VK_SUCCESS : VK_TIMEOUT;
2247ec681f3Smrg}
2257ec681f3Smrg
2267ec681f3Smrgstatic void
2277ec681f3Smrgsim_syncobj_set_point_locked(struct sim_syncobj *syncobj, uint64_t point)
2287ec681f3Smrg{
2297ec681f3Smrg   syncobj->point = point;
2307ec681f3Smrg
2317ec681f3Smrg   if (syncobj->pending_fd >= 0) {
2327ec681f3Smrg      close(syncobj->pending_fd);
2337ec681f3Smrg      syncobj->pending_fd = -1;
2347ec681f3Smrg      syncobj->pending_point = point;
2357ec681f3Smrg   }
2367ec681f3Smrg}
2377ec681f3Smrg
2387ec681f3Smrgstatic void
2397ec681f3Smrgsim_syncobj_update_point_locked(struct sim_syncobj *syncobj, int poll_timeout)
2407ec681f3Smrg{
2417ec681f3Smrg   if (syncobj->pending_fd >= 0) {
2427ec681f3Smrg      VkResult result;
2437ec681f3Smrg      if (syncobj->pending_cpu) {
2447ec681f3Smrg         if (poll_timeout == -1) {
2457ec681f3Smrg            const int max_cpu_timeout = 2000;
2467ec681f3Smrg            poll_timeout = max_cpu_timeout;
2477ec681f3Smrg            result = sim_syncobj_poll(syncobj->pending_fd, poll_timeout);
2487ec681f3Smrg            if (result == VK_TIMEOUT) {
2497ec681f3Smrg               vn_log(NULL, "cpu sync timed out after %dms; ignoring",
2507ec681f3Smrg                      poll_timeout);
2517ec681f3Smrg               result = VK_SUCCESS;
2527ec681f3Smrg            }
2537ec681f3Smrg         } else {
2547ec681f3Smrg            result = sim_syncobj_poll(syncobj->pending_fd, poll_timeout);
2557ec681f3Smrg         }
2567ec681f3Smrg      } else {
2577ec681f3Smrg         result = sim_syncobj_poll(syncobj->pending_fd, poll_timeout);
2587ec681f3Smrg      }
2597ec681f3Smrg      if (result == VK_SUCCESS) {
2607ec681f3Smrg         close(syncobj->pending_fd);
2617ec681f3Smrg         syncobj->pending_fd = -1;
2627ec681f3Smrg         syncobj->point = syncobj->pending_point;
2637ec681f3Smrg      }
2647ec681f3Smrg   }
2657ec681f3Smrg}
2667ec681f3Smrg
2677ec681f3Smrgstatic struct sim_syncobj *
2687ec681f3Smrgsim_syncobj_lookup(struct virtgpu *gpu, uint32_t syncobj_handle)
2697ec681f3Smrg{
2707ec681f3Smrg   struct sim_syncobj *syncobj = NULL;
2717ec681f3Smrg
2727ec681f3Smrg   mtx_lock(&sim.mutex);
2737ec681f3Smrg   struct hash_entry *entry = _mesa_hash_table_search(
2747ec681f3Smrg      sim.syncobjs, (const void *)(uintptr_t)syncobj_handle);
2757ec681f3Smrg   if (entry)
2767ec681f3Smrg      syncobj = entry->data;
2777ec681f3Smrg   mtx_unlock(&sim.mutex);
2787ec681f3Smrg
2797ec681f3Smrg   return syncobj;
2807ec681f3Smrg}
2817ec681f3Smrg
2827ec681f3Smrgstatic int
2837ec681f3Smrgsim_syncobj_reset(struct virtgpu *gpu, uint32_t syncobj_handle)
2847ec681f3Smrg{
2857ec681f3Smrg   struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
2867ec681f3Smrg   if (!syncobj)
2877ec681f3Smrg      return -1;
2887ec681f3Smrg
2897ec681f3Smrg   mtx_lock(&syncobj->mutex);
2907ec681f3Smrg   sim_syncobj_set_point_locked(syncobj, 0);
2917ec681f3Smrg   mtx_unlock(&syncobj->mutex);
2927ec681f3Smrg
2937ec681f3Smrg   return 0;
2947ec681f3Smrg}
2957ec681f3Smrg
2967ec681f3Smrgstatic int
2977ec681f3Smrgsim_syncobj_query(struct virtgpu *gpu,
2987ec681f3Smrg                  uint32_t syncobj_handle,
2997ec681f3Smrg                  uint64_t *point)
3007ec681f3Smrg{
3017ec681f3Smrg   struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
3027ec681f3Smrg   if (!syncobj)
3037ec681f3Smrg      return -1;
3047ec681f3Smrg
3057ec681f3Smrg   mtx_lock(&syncobj->mutex);
3067ec681f3Smrg   sim_syncobj_update_point_locked(syncobj, 0);
3077ec681f3Smrg   *point = syncobj->point;
3087ec681f3Smrg   mtx_unlock(&syncobj->mutex);
3097ec681f3Smrg
3107ec681f3Smrg   return 0;
3117ec681f3Smrg}
3127ec681f3Smrg
3137ec681f3Smrgstatic int
3147ec681f3Smrgsim_syncobj_signal(struct virtgpu *gpu,
3157ec681f3Smrg                   uint32_t syncobj_handle,
3167ec681f3Smrg                   uint64_t point)
3177ec681f3Smrg{
3187ec681f3Smrg   struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
3197ec681f3Smrg   if (!syncobj)
3207ec681f3Smrg      return -1;
3217ec681f3Smrg
3227ec681f3Smrg   mtx_lock(&syncobj->mutex);
3237ec681f3Smrg   sim_syncobj_set_point_locked(syncobj, point);
3247ec681f3Smrg   mtx_unlock(&syncobj->mutex);
3257ec681f3Smrg
3267ec681f3Smrg   return 0;
3277ec681f3Smrg}
3287ec681f3Smrg
3297ec681f3Smrgstatic int
3307ec681f3Smrgsim_syncobj_submit(struct virtgpu *gpu,
3317ec681f3Smrg                   uint32_t syncobj_handle,
3327ec681f3Smrg                   int sync_fd,
3337ec681f3Smrg                   uint64_t point,
3347ec681f3Smrg                   bool cpu)
3357ec681f3Smrg{
3367ec681f3Smrg   struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
3377ec681f3Smrg   if (!syncobj)
3387ec681f3Smrg      return -1;
3397ec681f3Smrg
3407ec681f3Smrg   int pending_fd = dup(sync_fd);
3417ec681f3Smrg   if (pending_fd < 0) {
3427ec681f3Smrg      vn_log(gpu->instance, "failed to dup sync fd");
3437ec681f3Smrg      return -1;
3447ec681f3Smrg   }
3457ec681f3Smrg
3467ec681f3Smrg   mtx_lock(&syncobj->mutex);
3477ec681f3Smrg
3487ec681f3Smrg   if (syncobj->pending_fd >= 0) {
3497ec681f3Smrg      mtx_unlock(&syncobj->mutex);
3507ec681f3Smrg
3517ec681f3Smrg      /* TODO */
3527ec681f3Smrg      vn_log(gpu->instance, "sorry, no simulated timeline semaphore");
3537ec681f3Smrg      close(pending_fd);
3547ec681f3Smrg      return -1;
3557ec681f3Smrg   }
3567ec681f3Smrg   if (syncobj->point >= point)
3577ec681f3Smrg      vn_log(gpu->instance, "non-monotonic signaling");
3587ec681f3Smrg
3597ec681f3Smrg   syncobj->pending_fd = pending_fd;
3607ec681f3Smrg   syncobj->pending_point = point;
3617ec681f3Smrg   syncobj->pending_cpu = cpu;
3627ec681f3Smrg
3637ec681f3Smrg   mtx_unlock(&syncobj->mutex);
3647ec681f3Smrg
3657ec681f3Smrg   return 0;
3667ec681f3Smrg}
3677ec681f3Smrg
3687ec681f3Smrgstatic int
3697ec681f3Smrgtimeout_to_poll_timeout(uint64_t timeout)
3707ec681f3Smrg{
3717ec681f3Smrg   const uint64_t ns_per_ms = 1000000;
3727ec681f3Smrg   const uint64_t ms = (timeout + ns_per_ms - 1) / ns_per_ms;
3737ec681f3Smrg   if (!ms && timeout)
3747ec681f3Smrg      return -1;
3757ec681f3Smrg   return ms <= INT_MAX ? ms : -1;
3767ec681f3Smrg}
3777ec681f3Smrg
3787ec681f3Smrgstatic int
3797ec681f3Smrgsim_syncobj_wait(struct virtgpu *gpu,
3807ec681f3Smrg                 const struct vn_renderer_wait *wait,
3817ec681f3Smrg                 bool wait_avail)
3827ec681f3Smrg{
3837ec681f3Smrg   if (wait_avail)
3847ec681f3Smrg      return -1;
3857ec681f3Smrg
3867ec681f3Smrg   const int poll_timeout = timeout_to_poll_timeout(wait->timeout);
3877ec681f3Smrg
3887ec681f3Smrg   /* TODO poll all fds at the same time */
3897ec681f3Smrg   for (uint32_t i = 0; i < wait->sync_count; i++) {
3907ec681f3Smrg      struct virtgpu_sync *sync = (struct virtgpu_sync *)wait->syncs[i];
3917ec681f3Smrg      const uint64_t point = wait->sync_values[i];
3927ec681f3Smrg
3937ec681f3Smrg      struct sim_syncobj *syncobj =
3947ec681f3Smrg         sim_syncobj_lookup(gpu, sync->syncobj_handle);
3957ec681f3Smrg      if (!syncobj)
3967ec681f3Smrg         return -1;
3977ec681f3Smrg
3987ec681f3Smrg      mtx_lock(&syncobj->mutex);
3997ec681f3Smrg
4007ec681f3Smrg      if (syncobj->point < point)
4017ec681f3Smrg         sim_syncobj_update_point_locked(syncobj, poll_timeout);
4027ec681f3Smrg
4037ec681f3Smrg      if (syncobj->point < point) {
4047ec681f3Smrg         if (wait->wait_any && i < wait->sync_count - 1 &&
4057ec681f3Smrg             syncobj->pending_fd < 0) {
4067ec681f3Smrg            mtx_unlock(&syncobj->mutex);
4077ec681f3Smrg            continue;
4087ec681f3Smrg         }
4097ec681f3Smrg         errno = ETIME;
4107ec681f3Smrg         mtx_unlock(&syncobj->mutex);
4117ec681f3Smrg         return -1;
4127ec681f3Smrg      }
4137ec681f3Smrg
4147ec681f3Smrg      mtx_unlock(&syncobj->mutex);
4157ec681f3Smrg
4167ec681f3Smrg      if (wait->wait_any)
4177ec681f3Smrg         break;
4187ec681f3Smrg
4197ec681f3Smrg      /* TODO adjust poll_timeout */
4207ec681f3Smrg   }
4217ec681f3Smrg
4227ec681f3Smrg   return 0;
4237ec681f3Smrg}
4247ec681f3Smrg
4257ec681f3Smrgstatic int
4267ec681f3Smrgsim_syncobj_export(struct virtgpu *gpu, uint32_t syncobj_handle)
4277ec681f3Smrg{
4287ec681f3Smrg   struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
4297ec681f3Smrg   if (!syncobj)
4307ec681f3Smrg      return -1;
4317ec681f3Smrg
4327ec681f3Smrg   int fd = -1;
4337ec681f3Smrg   mtx_lock(&syncobj->mutex);
4347ec681f3Smrg   if (syncobj->pending_fd >= 0)
4357ec681f3Smrg      fd = dup(syncobj->pending_fd);
4367ec681f3Smrg   else
4377ec681f3Smrg      fd = dup(sim.signaled_fd);
4387ec681f3Smrg   mtx_unlock(&syncobj->mutex);
4397ec681f3Smrg
4407ec681f3Smrg   return fd;
4417ec681f3Smrg}
4427ec681f3Smrg
4437ec681f3Smrgstatic uint32_t
4447ec681f3Smrgsim_syncobj_import(struct virtgpu *gpu, uint32_t syncobj_handle, int fd)
4457ec681f3Smrg{
4467ec681f3Smrg   struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
4477ec681f3Smrg   if (!syncobj)
4487ec681f3Smrg      return 0;
4497ec681f3Smrg
4507ec681f3Smrg   if (sim_syncobj_submit(gpu, syncobj_handle, fd, 1, false))
4517ec681f3Smrg      return 0;
4527ec681f3Smrg
4537ec681f3Smrg   return syncobj_handle;
4547ec681f3Smrg}
4557ec681f3Smrg
4567ec681f3Smrg#endif /* SIMULATE_SYNCOBJ */
4577ec681f3Smrg
4587ec681f3Smrg#ifdef SIMULATE_SUBMIT
4597ec681f3Smrg
4607ec681f3Smrgstatic int
4617ec681f3Smrgsim_submit_signal_syncs(struct virtgpu *gpu,
4627ec681f3Smrg                        int sync_fd,
4637ec681f3Smrg                        struct vn_renderer_sync *const *syncs,
4647ec681f3Smrg                        const uint64_t *sync_values,
4657ec681f3Smrg                        uint32_t sync_count,
4667ec681f3Smrg                        bool cpu)
4677ec681f3Smrg{
4687ec681f3Smrg   for (uint32_t i = 0; i < sync_count; i++) {
4697ec681f3Smrg      struct virtgpu_sync *sync = (struct virtgpu_sync *)syncs[i];
4707ec681f3Smrg      const uint64_t pending_point = sync_values[i];
4717ec681f3Smrg
4727ec681f3Smrg#ifdef SIMULATE_SYNCOBJ
4737ec681f3Smrg      int ret = sim_syncobj_submit(gpu, sync->syncobj_handle, sync_fd,
4747ec681f3Smrg                                   pending_point, cpu);
4757ec681f3Smrg      if (ret)
4767ec681f3Smrg         return ret;
4777ec681f3Smrg#else
4787ec681f3Smrg      /* we can in theory do a DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE followed by a
4797ec681f3Smrg       * DRM_IOCTL_SYNCOBJ_TRANSFER
4807ec681f3Smrg       */
4817ec681f3Smrg      return -1;
4827ec681f3Smrg#endif
4837ec681f3Smrg   }
4847ec681f3Smrg
4857ec681f3Smrg   return 0;
4867ec681f3Smrg}
4877ec681f3Smrg
4887ec681f3Smrgstatic uint32_t *
4897ec681f3Smrgsim_submit_alloc_gem_handles(struct vn_renderer_bo *const *bos,
4907ec681f3Smrg                             uint32_t bo_count)
4917ec681f3Smrg{
4927ec681f3Smrg   uint32_t *gem_handles = malloc(sizeof(*gem_handles) * bo_count);
4937ec681f3Smrg   if (!gem_handles)
4947ec681f3Smrg      return NULL;
4957ec681f3Smrg
4967ec681f3Smrg   for (uint32_t i = 0; i < bo_count; i++) {
4977ec681f3Smrg      struct virtgpu_bo *bo = (struct virtgpu_bo *)bos[i];
4987ec681f3Smrg      gem_handles[i] = bo->gem_handle;
4997ec681f3Smrg   }
5007ec681f3Smrg
5017ec681f3Smrg   return gem_handles;
5027ec681f3Smrg}
5037ec681f3Smrg
5047ec681f3Smrgstatic int
5057ec681f3Smrgsim_submit(struct virtgpu *gpu, const struct vn_renderer_submit *submit)
5067ec681f3Smrg{
5077ec681f3Smrg   /* TODO replace submit->bos by submit->gem_handles to avoid malloc/loop */
5087ec681f3Smrg   uint32_t *gem_handles = NULL;
5097ec681f3Smrg   if (submit->bo_count) {
5107ec681f3Smrg      gem_handles =
5117ec681f3Smrg         sim_submit_alloc_gem_handles(submit->bos, submit->bo_count);
5127ec681f3Smrg      if (!gem_handles)
5137ec681f3Smrg         return -1;
5147ec681f3Smrg   }
5157ec681f3Smrg
5167ec681f3Smrg   int ret = 0;
5177ec681f3Smrg   for (uint32_t i = 0; i < submit->batch_count; i++) {
5187ec681f3Smrg      const struct vn_renderer_submit_batch *batch = &submit->batches[i];
5197ec681f3Smrg
5207ec681f3Smrg      struct drm_virtgpu_execbuffer args = {
5217ec681f3Smrg         .flags = batch->sync_count ? VIRTGPU_EXECBUF_FENCE_FD_OUT : 0,
5227ec681f3Smrg         .size = batch->cs_size,
5237ec681f3Smrg         .command = (uintptr_t)batch->cs_data,
5247ec681f3Smrg         .bo_handles = (uintptr_t)gem_handles,
5257ec681f3Smrg         .num_bo_handles = submit->bo_count,
5267ec681f3Smrg      };
5277ec681f3Smrg
5287ec681f3Smrg      ret = drmIoctl(gpu->fd, DRM_IOCTL_VIRTGPU_EXECBUFFER, &args);
5297ec681f3Smrg      if (ret) {
5307ec681f3Smrg         vn_log(gpu->instance, "failed to execbuffer: %s", strerror(errno));
5317ec681f3Smrg         break;
5327ec681f3Smrg      }
5337ec681f3Smrg
5347ec681f3Smrg      if (batch->sync_count) {
5357ec681f3Smrg         ret = sim_submit_signal_syncs(gpu, args.fence_fd, batch->syncs,
5367ec681f3Smrg                                       batch->sync_values, batch->sync_count,
5377ec681f3Smrg                                       batch->sync_queue_cpu);
5387ec681f3Smrg         close(args.fence_fd);
5397ec681f3Smrg         if (ret)
5407ec681f3Smrg            break;
5417ec681f3Smrg      }
5427ec681f3Smrg   }
5437ec681f3Smrg
5447ec681f3Smrg   if (!submit->batch_count && submit->bo_count) {
5457ec681f3Smrg      struct drm_virtgpu_execbuffer args = {
5467ec681f3Smrg         .bo_handles = (uintptr_t)gem_handles,
5477ec681f3Smrg         .num_bo_handles = submit->bo_count,
5487ec681f3Smrg      };
5497ec681f3Smrg
5507ec681f3Smrg      ret = drmIoctl(gpu->fd, DRM_IOCTL_VIRTGPU_EXECBUFFER, &args);
5517ec681f3Smrg      if (ret)
5527ec681f3Smrg         vn_log(gpu->instance, "failed to execbuffer: %s", strerror(errno));
5537ec681f3Smrg   }
5547ec681f3Smrg
5557ec681f3Smrg   free(gem_handles);
5567ec681f3Smrg
5577ec681f3Smrg   return ret;
5587ec681f3Smrg}
5597ec681f3Smrg
5607ec681f3Smrg#endif /* SIMULATE_SUBMIT */
5617ec681f3Smrg
5627ec681f3Smrgstatic int
5637ec681f3Smrgvirtgpu_ioctl(struct virtgpu *gpu, unsigned long request, void *args)
5647ec681f3Smrg{
5657ec681f3Smrg   return drmIoctl(gpu->fd, request, args);
5667ec681f3Smrg}
5677ec681f3Smrg
5687ec681f3Smrgstatic uint64_t
5697ec681f3Smrgvirtgpu_ioctl_getparam(struct virtgpu *gpu, uint64_t param)
5707ec681f3Smrg{
5717ec681f3Smrg#ifdef SIMULATE_CONTEXT_INIT
5727ec681f3Smrg   if (param == VIRTGPU_PARAM_CONTEXT_INIT)
5737ec681f3Smrg      return 1;
5747ec681f3Smrg#endif
5757ec681f3Smrg#ifdef SIMULATE_SUBMIT
5767ec681f3Smrg   if (param == VIRTGPU_PARAM_MAX_SYNC_QUEUE_COUNT)
5777ec681f3Smrg      return 16;
5787ec681f3Smrg#endif
5797ec681f3Smrg
5807ec681f3Smrg   /* val must be zeroed because kernel only writes the lower 32 bits */
5817ec681f3Smrg   uint64_t val = 0;
5827ec681f3Smrg   struct drm_virtgpu_getparam args = {
5837ec681f3Smrg      .param = param,
5847ec681f3Smrg      .value = (uintptr_t)&val,
5857ec681f3Smrg   };
5867ec681f3Smrg
5877ec681f3Smrg   const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_GETPARAM, &args);
5887ec681f3Smrg   return ret ? 0 : val;
5897ec681f3Smrg}
5907ec681f3Smrg
5917ec681f3Smrgstatic int
5927ec681f3Smrgvirtgpu_ioctl_get_caps(struct virtgpu *gpu,
5937ec681f3Smrg                       enum virgl_renderer_capset id,
5947ec681f3Smrg                       uint32_t version,
5957ec681f3Smrg                       void *capset,
5967ec681f3Smrg                       size_t capset_size)
5977ec681f3Smrg{
5987ec681f3Smrg#ifdef SIMULATE_CONTEXT_INIT
5997ec681f3Smrg   if (id == VIRGL_RENDERER_CAPSET_VENUS && version == 0)
6007ec681f3Smrg      return 0;
6017ec681f3Smrg#endif
6027ec681f3Smrg
6037ec681f3Smrg   struct drm_virtgpu_get_caps args = {
6047ec681f3Smrg      .cap_set_id = id,
6057ec681f3Smrg      .cap_set_ver = version,
6067ec681f3Smrg      .addr = (uintptr_t)capset,
6077ec681f3Smrg      .size = capset_size,
6087ec681f3Smrg   };
6097ec681f3Smrg
6107ec681f3Smrg   return virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_GET_CAPS, &args);
6117ec681f3Smrg}
6127ec681f3Smrg
6137ec681f3Smrgstatic int
6147ec681f3Smrgvirtgpu_ioctl_context_init(struct virtgpu *gpu,
6157ec681f3Smrg                           enum virgl_renderer_capset capset_id)
6167ec681f3Smrg{
6177ec681f3Smrg#ifdef SIMULATE_CONTEXT_INIT
6187ec681f3Smrg   if (capset_id == VIRGL_RENDERER_CAPSET_VENUS)
6197ec681f3Smrg      return 0;
6207ec681f3Smrg#endif
6217ec681f3Smrg
6227ec681f3Smrg   struct drm_virtgpu_context_init args = {
6237ec681f3Smrg      .num_params = 1,
6247ec681f3Smrg      .ctx_set_params = (uintptr_t) &
6257ec681f3Smrg                        (struct drm_virtgpu_context_set_param){
6267ec681f3Smrg                           .param = VIRTGPU_CONTEXT_PARAM_CAPSET_ID,
6277ec681f3Smrg                           .value = capset_id,
6287ec681f3Smrg                        },
6297ec681f3Smrg   };
6307ec681f3Smrg
6317ec681f3Smrg   return virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_CONTEXT_INIT, &args);
6327ec681f3Smrg}
6337ec681f3Smrg
6347ec681f3Smrgstatic uint32_t
6357ec681f3Smrgvirtgpu_ioctl_resource_create_blob(struct virtgpu *gpu,
6367ec681f3Smrg                                   uint32_t blob_mem,
6377ec681f3Smrg                                   uint32_t blob_flags,
6387ec681f3Smrg                                   size_t blob_size,
6397ec681f3Smrg                                   uint64_t blob_id,
6407ec681f3Smrg                                   uint32_t *res_id)
6417ec681f3Smrg{
6427ec681f3Smrg#ifdef SIMULATE_BO_SIZE_FIX
6437ec681f3Smrg   blob_size = align64(blob_size, 4096);
6447ec681f3Smrg#endif
6457ec681f3Smrg
6467ec681f3Smrg   struct drm_virtgpu_resource_create_blob args = {
6477ec681f3Smrg      .blob_mem = blob_mem,
6487ec681f3Smrg      .blob_flags = blob_flags,
6497ec681f3Smrg      .size = blob_size,
6507ec681f3Smrg      .blob_id = blob_id,
6517ec681f3Smrg   };
6527ec681f3Smrg
6537ec681f3Smrg   if (virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_RESOURCE_CREATE_BLOB, &args))
6547ec681f3Smrg      return 0;
6557ec681f3Smrg
6567ec681f3Smrg   *res_id = args.res_handle;
6577ec681f3Smrg   return args.bo_handle;
6587ec681f3Smrg}
6597ec681f3Smrg
6607ec681f3Smrgstatic int
6617ec681f3Smrgvirtgpu_ioctl_resource_info(struct virtgpu *gpu,
6627ec681f3Smrg                            uint32_t gem_handle,
6637ec681f3Smrg                            struct drm_virtgpu_resource_info *info)
6647ec681f3Smrg{
6657ec681f3Smrg   *info = (struct drm_virtgpu_resource_info){
6667ec681f3Smrg      .bo_handle = gem_handle,
6677ec681f3Smrg   };
6687ec681f3Smrg
6697ec681f3Smrg   return virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_RESOURCE_INFO, info);
6707ec681f3Smrg}
6717ec681f3Smrg
6727ec681f3Smrgstatic void
6737ec681f3Smrgvirtgpu_ioctl_gem_close(struct virtgpu *gpu, uint32_t gem_handle)
6747ec681f3Smrg{
6757ec681f3Smrg   struct drm_gem_close args = {
6767ec681f3Smrg      .handle = gem_handle,
6777ec681f3Smrg   };
6787ec681f3Smrg
6797ec681f3Smrg   ASSERTED const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_GEM_CLOSE, &args);
6807ec681f3Smrg   assert(!ret);
6817ec681f3Smrg}
6827ec681f3Smrg
6837ec681f3Smrgstatic int
6847ec681f3Smrgvirtgpu_ioctl_prime_handle_to_fd(struct virtgpu *gpu,
6857ec681f3Smrg                                 uint32_t gem_handle,
6867ec681f3Smrg                                 bool mappable)
6877ec681f3Smrg{
6887ec681f3Smrg   struct drm_prime_handle args = {
6897ec681f3Smrg      .handle = gem_handle,
6907ec681f3Smrg      .flags = DRM_CLOEXEC | (mappable ? DRM_RDWR : 0),
6917ec681f3Smrg   };
6927ec681f3Smrg
6937ec681f3Smrg   const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args);
6947ec681f3Smrg   return ret ? -1 : args.fd;
6957ec681f3Smrg}
6967ec681f3Smrg
6977ec681f3Smrgstatic uint32_t
6987ec681f3Smrgvirtgpu_ioctl_prime_fd_to_handle(struct virtgpu *gpu, int fd)
6997ec681f3Smrg{
7007ec681f3Smrg   struct drm_prime_handle args = {
7017ec681f3Smrg      .fd = fd,
7027ec681f3Smrg   };
7037ec681f3Smrg
7047ec681f3Smrg   const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_PRIME_FD_TO_HANDLE, &args);
7057ec681f3Smrg   return ret ? 0 : args.handle;
7067ec681f3Smrg}
7077ec681f3Smrg
7087ec681f3Smrgstatic void *
7097ec681f3Smrgvirtgpu_ioctl_map(struct virtgpu *gpu, uint32_t gem_handle, size_t size)
7107ec681f3Smrg{
7117ec681f3Smrg   struct drm_virtgpu_map args = {
7127ec681f3Smrg      .handle = gem_handle,
7137ec681f3Smrg   };
7147ec681f3Smrg
7157ec681f3Smrg   if (virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_MAP, &args))
7167ec681f3Smrg      return NULL;
7177ec681f3Smrg
7187ec681f3Smrg   void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, gpu->fd,
7197ec681f3Smrg                    args.offset);
7207ec681f3Smrg   if (ptr == MAP_FAILED)
7217ec681f3Smrg      return NULL;
7227ec681f3Smrg
7237ec681f3Smrg   return ptr;
7247ec681f3Smrg}
7257ec681f3Smrg
7267ec681f3Smrgstatic uint32_t
7277ec681f3Smrgvirtgpu_ioctl_syncobj_create(struct virtgpu *gpu, bool signaled)
7287ec681f3Smrg{
7297ec681f3Smrg#ifdef SIMULATE_SYNCOBJ
7307ec681f3Smrg   return sim_syncobj_create(gpu, signaled);
7317ec681f3Smrg#endif
7327ec681f3Smrg
7337ec681f3Smrg   struct drm_syncobj_create args = {
7347ec681f3Smrg      .flags = signaled ? DRM_SYNCOBJ_CREATE_SIGNALED : 0,
7357ec681f3Smrg   };
7367ec681f3Smrg
7377ec681f3Smrg   const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_CREATE, &args);
7387ec681f3Smrg   return ret ? 0 : args.handle;
7397ec681f3Smrg}
7407ec681f3Smrg
7417ec681f3Smrgstatic void
7427ec681f3Smrgvirtgpu_ioctl_syncobj_destroy(struct virtgpu *gpu, uint32_t syncobj_handle)
7437ec681f3Smrg{
7447ec681f3Smrg#ifdef SIMULATE_SYNCOBJ
7457ec681f3Smrg   sim_syncobj_destroy(gpu, syncobj_handle);
7467ec681f3Smrg   return;
7477ec681f3Smrg#endif
7487ec681f3Smrg
7497ec681f3Smrg   struct drm_syncobj_destroy args = {
7507ec681f3Smrg      .handle = syncobj_handle,
7517ec681f3Smrg   };
7527ec681f3Smrg
7537ec681f3Smrg   ASSERTED const int ret =
7547ec681f3Smrg      virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_DESTROY, &args);
7557ec681f3Smrg   assert(!ret);
7567ec681f3Smrg}
7577ec681f3Smrg
7587ec681f3Smrgstatic int
7597ec681f3Smrgvirtgpu_ioctl_syncobj_handle_to_fd(struct virtgpu *gpu,
7607ec681f3Smrg                                   uint32_t syncobj_handle,
7617ec681f3Smrg                                   bool sync_file)
7627ec681f3Smrg{
7637ec681f3Smrg#ifdef SIMULATE_SYNCOBJ
7647ec681f3Smrg   return sync_file ? sim_syncobj_export(gpu, syncobj_handle) : -1;
7657ec681f3Smrg#endif
7667ec681f3Smrg
7677ec681f3Smrg   struct drm_syncobj_handle args = {
7687ec681f3Smrg      .handle = syncobj_handle,
7697ec681f3Smrg      .flags =
7707ec681f3Smrg         sync_file ? DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE : 0,
7717ec681f3Smrg   };
7727ec681f3Smrg
7737ec681f3Smrg   int ret = virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &args);
7747ec681f3Smrg   if (ret)
7757ec681f3Smrg      return -1;
7767ec681f3Smrg
7777ec681f3Smrg   return args.fd;
7787ec681f3Smrg}
7797ec681f3Smrg
7807ec681f3Smrgstatic uint32_t
7817ec681f3Smrgvirtgpu_ioctl_syncobj_fd_to_handle(struct virtgpu *gpu,
7827ec681f3Smrg                                   int fd,
7837ec681f3Smrg                                   uint32_t syncobj_handle)
7847ec681f3Smrg{
7857ec681f3Smrg#ifdef SIMULATE_SYNCOBJ
7867ec681f3Smrg   return syncobj_handle ? sim_syncobj_import(gpu, syncobj_handle, fd) : 0;
7877ec681f3Smrg#endif
7887ec681f3Smrg
7897ec681f3Smrg   struct drm_syncobj_handle args = {
7907ec681f3Smrg      .handle = syncobj_handle,
7917ec681f3Smrg      .flags =
7927ec681f3Smrg         syncobj_handle ? DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE : 0,
7937ec681f3Smrg      .fd = fd,
7947ec681f3Smrg   };
7957ec681f3Smrg
7967ec681f3Smrg   int ret = virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &args);
7977ec681f3Smrg   if (ret)
7987ec681f3Smrg      return 0;
7997ec681f3Smrg
8007ec681f3Smrg   return args.handle;
8017ec681f3Smrg}
8027ec681f3Smrg
8037ec681f3Smrgstatic int
8047ec681f3Smrgvirtgpu_ioctl_syncobj_reset(struct virtgpu *gpu, uint32_t syncobj_handle)
8057ec681f3Smrg{
8067ec681f3Smrg#ifdef SIMULATE_SYNCOBJ
8077ec681f3Smrg   return sim_syncobj_reset(gpu, syncobj_handle);
8087ec681f3Smrg#endif
8097ec681f3Smrg
8107ec681f3Smrg   struct drm_syncobj_array args = {
8117ec681f3Smrg      .handles = (uintptr_t)&syncobj_handle,
8127ec681f3Smrg      .count_handles = 1,
8137ec681f3Smrg   };
8147ec681f3Smrg
8157ec681f3Smrg   return virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_RESET, &args);
8167ec681f3Smrg}
8177ec681f3Smrg
8187ec681f3Smrgstatic int
8197ec681f3Smrgvirtgpu_ioctl_syncobj_query(struct virtgpu *gpu,
8207ec681f3Smrg                            uint32_t syncobj_handle,
8217ec681f3Smrg                            uint64_t *point)
8227ec681f3Smrg{
8237ec681f3Smrg#ifdef SIMULATE_SYNCOBJ
8247ec681f3Smrg   return sim_syncobj_query(gpu, syncobj_handle, point);
8257ec681f3Smrg#endif
8267ec681f3Smrg
8277ec681f3Smrg   struct drm_syncobj_timeline_array args = {
8287ec681f3Smrg      .handles = (uintptr_t)&syncobj_handle,
8297ec681f3Smrg      .points = (uintptr_t)point,
8307ec681f3Smrg      .count_handles = 1,
8317ec681f3Smrg   };
8327ec681f3Smrg
8337ec681f3Smrg   return virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_QUERY, &args);
8347ec681f3Smrg}
8357ec681f3Smrg
8367ec681f3Smrgstatic int
8377ec681f3Smrgvirtgpu_ioctl_syncobj_timeline_signal(struct virtgpu *gpu,
8387ec681f3Smrg                                      uint32_t syncobj_handle,
8397ec681f3Smrg                                      uint64_t point)
8407ec681f3Smrg{
8417ec681f3Smrg#ifdef SIMULATE_SYNCOBJ
8427ec681f3Smrg   return sim_syncobj_signal(gpu, syncobj_handle, point);
8437ec681f3Smrg#endif
8447ec681f3Smrg
8457ec681f3Smrg   struct drm_syncobj_timeline_array args = {
8467ec681f3Smrg      .handles = (uintptr_t)&syncobj_handle,
8477ec681f3Smrg      .points = (uintptr_t)&point,
8487ec681f3Smrg      .count_handles = 1,
8497ec681f3Smrg   };
8507ec681f3Smrg
8517ec681f3Smrg   return virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
8527ec681f3Smrg}
8537ec681f3Smrg
8547ec681f3Smrgstatic int
8557ec681f3Smrgvirtgpu_ioctl_syncobj_timeline_wait(struct virtgpu *gpu,
8567ec681f3Smrg                                    const struct vn_renderer_wait *wait,
8577ec681f3Smrg                                    bool wait_avail)
8587ec681f3Smrg{
8597ec681f3Smrg#ifdef SIMULATE_SYNCOBJ
8607ec681f3Smrg   return sim_syncobj_wait(gpu, wait, wait_avail);
8617ec681f3Smrg#endif
8627ec681f3Smrg
8637ec681f3Smrg   /* always enable wait-before-submit */
8647ec681f3Smrg   uint32_t flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
8657ec681f3Smrg   if (!wait->wait_any)
8667ec681f3Smrg      flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL;
8677ec681f3Smrg   /* wait for fences to appear instead of signaling */
8687ec681f3Smrg   if (wait_avail)
8697ec681f3Smrg      flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE;
8707ec681f3Smrg
8717ec681f3Smrg   /* TODO replace wait->syncs by wait->sync_handles to avoid malloc/loop */
8727ec681f3Smrg   uint32_t *syncobj_handles =
8737ec681f3Smrg      malloc(sizeof(*syncobj_handles) * wait->sync_count);
8747ec681f3Smrg   if (!syncobj_handles)
8757ec681f3Smrg      return -1;
8767ec681f3Smrg   for (uint32_t i = 0; i < wait->sync_count; i++) {
8777ec681f3Smrg      struct virtgpu_sync *sync = (struct virtgpu_sync *)wait->syncs[i];
8787ec681f3Smrg      syncobj_handles[i] = sync->syncobj_handle;
8797ec681f3Smrg   }
8807ec681f3Smrg
8817ec681f3Smrg   struct drm_syncobj_timeline_wait args = {
8827ec681f3Smrg      .handles = (uintptr_t)syncobj_handles,
8837ec681f3Smrg      .points = (uintptr_t)wait->sync_values,
8847ec681f3Smrg      .timeout_nsec = os_time_get_absolute_timeout(wait->timeout),
8857ec681f3Smrg      .count_handles = wait->sync_count,
8867ec681f3Smrg      .flags = flags,
8877ec681f3Smrg   };
8887ec681f3Smrg
8897ec681f3Smrg   const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &args);
8907ec681f3Smrg
8917ec681f3Smrg   free(syncobj_handles);
8927ec681f3Smrg
8937ec681f3Smrg   return ret;
8947ec681f3Smrg}
8957ec681f3Smrg
8967ec681f3Smrgstatic int
8977ec681f3Smrgvirtgpu_ioctl_submit(struct virtgpu *gpu,
8987ec681f3Smrg                     const struct vn_renderer_submit *submit)
8997ec681f3Smrg{
9007ec681f3Smrg#ifdef SIMULATE_SUBMIT
9017ec681f3Smrg   return sim_submit(gpu, submit);
9027ec681f3Smrg#endif
9037ec681f3Smrg   return -1;
9047ec681f3Smrg}
9057ec681f3Smrg
9067ec681f3Smrgstatic VkResult
9077ec681f3Smrgvirtgpu_sync_write(struct vn_renderer *renderer,
9087ec681f3Smrg                   struct vn_renderer_sync *_sync,
9097ec681f3Smrg                   uint64_t val)
9107ec681f3Smrg{
9117ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
9127ec681f3Smrg   struct virtgpu_sync *sync = (struct virtgpu_sync *)_sync;
9137ec681f3Smrg
9147ec681f3Smrg   const int ret =
9157ec681f3Smrg      virtgpu_ioctl_syncobj_timeline_signal(gpu, sync->syncobj_handle, val);
9167ec681f3Smrg
9177ec681f3Smrg   return ret ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS;
9187ec681f3Smrg}
9197ec681f3Smrg
9207ec681f3Smrgstatic VkResult
9217ec681f3Smrgvirtgpu_sync_read(struct vn_renderer *renderer,
9227ec681f3Smrg                  struct vn_renderer_sync *_sync,
9237ec681f3Smrg                  uint64_t *val)
9247ec681f3Smrg{
9257ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
9267ec681f3Smrg   struct virtgpu_sync *sync = (struct virtgpu_sync *)_sync;
9277ec681f3Smrg
9287ec681f3Smrg   const int ret =
9297ec681f3Smrg      virtgpu_ioctl_syncobj_query(gpu, sync->syncobj_handle, val);
9307ec681f3Smrg
9317ec681f3Smrg   return ret ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS;
9327ec681f3Smrg}
9337ec681f3Smrg
9347ec681f3Smrgstatic VkResult
9357ec681f3Smrgvirtgpu_sync_reset(struct vn_renderer *renderer,
9367ec681f3Smrg                   struct vn_renderer_sync *_sync,
9377ec681f3Smrg                   uint64_t initial_val)
9387ec681f3Smrg{
9397ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
9407ec681f3Smrg   struct virtgpu_sync *sync = (struct virtgpu_sync *)_sync;
9417ec681f3Smrg
9427ec681f3Smrg   int ret = virtgpu_ioctl_syncobj_reset(gpu, sync->syncobj_handle);
9437ec681f3Smrg   if (!ret) {
9447ec681f3Smrg      ret = virtgpu_ioctl_syncobj_timeline_signal(gpu, sync->syncobj_handle,
9457ec681f3Smrg                                                  initial_val);
9467ec681f3Smrg   }
9477ec681f3Smrg
9487ec681f3Smrg   return ret ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS;
9497ec681f3Smrg}
9507ec681f3Smrg
9517ec681f3Smrgstatic int
9527ec681f3Smrgvirtgpu_sync_export_syncobj(struct vn_renderer *renderer,
9537ec681f3Smrg                            struct vn_renderer_sync *_sync,
9547ec681f3Smrg                            bool sync_file)
9557ec681f3Smrg{
9567ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
9577ec681f3Smrg   struct virtgpu_sync *sync = (struct virtgpu_sync *)_sync;
9587ec681f3Smrg
9597ec681f3Smrg   return virtgpu_ioctl_syncobj_handle_to_fd(gpu, sync->syncobj_handle,
9607ec681f3Smrg                                             sync_file);
9617ec681f3Smrg}
9627ec681f3Smrg
9637ec681f3Smrgstatic void
9647ec681f3Smrgvirtgpu_sync_destroy(struct vn_renderer *renderer,
9657ec681f3Smrg                     struct vn_renderer_sync *_sync)
9667ec681f3Smrg{
9677ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
9687ec681f3Smrg   struct virtgpu_sync *sync = (struct virtgpu_sync *)_sync;
9697ec681f3Smrg
9707ec681f3Smrg   virtgpu_ioctl_syncobj_destroy(gpu, sync->syncobj_handle);
9717ec681f3Smrg
9727ec681f3Smrg   free(sync);
9737ec681f3Smrg}
9747ec681f3Smrg
9757ec681f3Smrgstatic VkResult
9767ec681f3Smrgvirtgpu_sync_create_from_syncobj(struct vn_renderer *renderer,
9777ec681f3Smrg                                 int fd,
9787ec681f3Smrg                                 bool sync_file,
9797ec681f3Smrg                                 struct vn_renderer_sync **out_sync)
9807ec681f3Smrg{
9817ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
9827ec681f3Smrg
9837ec681f3Smrg   uint32_t syncobj_handle;
9847ec681f3Smrg   if (sync_file) {
9857ec681f3Smrg      syncobj_handle = virtgpu_ioctl_syncobj_create(gpu, false);
9867ec681f3Smrg      if (!syncobj_handle)
9877ec681f3Smrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
9887ec681f3Smrg      if (!virtgpu_ioctl_syncobj_fd_to_handle(gpu, fd, syncobj_handle)) {
9897ec681f3Smrg         virtgpu_ioctl_syncobj_destroy(gpu, syncobj_handle);
9907ec681f3Smrg         return VK_ERROR_INVALID_EXTERNAL_HANDLE;
9917ec681f3Smrg      }
9927ec681f3Smrg   } else {
9937ec681f3Smrg      syncobj_handle = virtgpu_ioctl_syncobj_fd_to_handle(gpu, fd, 0);
9947ec681f3Smrg      if (!syncobj_handle)
9957ec681f3Smrg         return VK_ERROR_INVALID_EXTERNAL_HANDLE;
9967ec681f3Smrg   }
9977ec681f3Smrg
9987ec681f3Smrg   struct virtgpu_sync *sync = calloc(1, sizeof(*sync));
9997ec681f3Smrg   if (!sync) {
10007ec681f3Smrg      virtgpu_ioctl_syncobj_destroy(gpu, syncobj_handle);
10017ec681f3Smrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
10027ec681f3Smrg   }
10037ec681f3Smrg
10047ec681f3Smrg   sync->syncobj_handle = syncobj_handle;
10057ec681f3Smrg   sync->base.sync_id = 0; /* TODO */
10067ec681f3Smrg
10077ec681f3Smrg   *out_sync = &sync->base;
10087ec681f3Smrg
10097ec681f3Smrg   return VK_SUCCESS;
10107ec681f3Smrg}
10117ec681f3Smrg
10127ec681f3Smrgstatic VkResult
10137ec681f3Smrgvirtgpu_sync_create(struct vn_renderer *renderer,
10147ec681f3Smrg                    uint64_t initial_val,
10157ec681f3Smrg                    uint32_t flags,
10167ec681f3Smrg                    struct vn_renderer_sync **out_sync)
10177ec681f3Smrg{
10187ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
10197ec681f3Smrg
10207ec681f3Smrg   /* TODO */
10217ec681f3Smrg   if (flags & VN_RENDERER_SYNC_SHAREABLE)
10227ec681f3Smrg      return VK_ERROR_OUT_OF_DEVICE_MEMORY;
10237ec681f3Smrg
10247ec681f3Smrg   /* always false because we don't use binary drm_syncobjs */
10257ec681f3Smrg   const bool signaled = false;
10267ec681f3Smrg   const uint32_t syncobj_handle =
10277ec681f3Smrg      virtgpu_ioctl_syncobj_create(gpu, signaled);
10287ec681f3Smrg   if (!syncobj_handle)
10297ec681f3Smrg      return VK_ERROR_OUT_OF_DEVICE_MEMORY;
10307ec681f3Smrg
10317ec681f3Smrg   /* add a signaled fence chain with seqno initial_val */
10327ec681f3Smrg   const int ret =
10337ec681f3Smrg      virtgpu_ioctl_syncobj_timeline_signal(gpu, syncobj_handle, initial_val);
10347ec681f3Smrg   if (ret) {
10357ec681f3Smrg      virtgpu_ioctl_syncobj_destroy(gpu, syncobj_handle);
10367ec681f3Smrg      return VK_ERROR_OUT_OF_DEVICE_MEMORY;
10377ec681f3Smrg   }
10387ec681f3Smrg
10397ec681f3Smrg   struct virtgpu_sync *sync = calloc(1, sizeof(*sync));
10407ec681f3Smrg   if (!sync) {
10417ec681f3Smrg      virtgpu_ioctl_syncobj_destroy(gpu, syncobj_handle);
10427ec681f3Smrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
10437ec681f3Smrg   }
10447ec681f3Smrg
10457ec681f3Smrg   sync->syncobj_handle = syncobj_handle;
10467ec681f3Smrg   /* we will have a sync_id when shareable is true and virtio-gpu associates
10477ec681f3Smrg    * a host sync object with guest drm_syncobj
10487ec681f3Smrg    */
10497ec681f3Smrg   sync->base.sync_id = 0;
10507ec681f3Smrg
10517ec681f3Smrg   *out_sync = &sync->base;
10527ec681f3Smrg
10537ec681f3Smrg   return VK_SUCCESS;
10547ec681f3Smrg}
10557ec681f3Smrg
10567ec681f3Smrgstatic void
10577ec681f3Smrgvirtgpu_bo_invalidate(struct vn_renderer *renderer,
10587ec681f3Smrg                      struct vn_renderer_bo *bo,
10597ec681f3Smrg                      VkDeviceSize offset,
10607ec681f3Smrg                      VkDeviceSize size)
10617ec681f3Smrg{
10627ec681f3Smrg   /* nop because kernel makes every mapping coherent */
10637ec681f3Smrg}
10647ec681f3Smrg
10657ec681f3Smrgstatic void
10667ec681f3Smrgvirtgpu_bo_flush(struct vn_renderer *renderer,
10677ec681f3Smrg                 struct vn_renderer_bo *bo,
10687ec681f3Smrg                 VkDeviceSize offset,
10697ec681f3Smrg                 VkDeviceSize size)
10707ec681f3Smrg{
10717ec681f3Smrg   /* nop because kernel makes every mapping coherent */
10727ec681f3Smrg}
10737ec681f3Smrg
10747ec681f3Smrgstatic void *
10757ec681f3Smrgvirtgpu_bo_map(struct vn_renderer *renderer, struct vn_renderer_bo *_bo)
10767ec681f3Smrg{
10777ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
10787ec681f3Smrg   struct virtgpu_bo *bo = (struct virtgpu_bo *)_bo;
10797ec681f3Smrg   const bool mappable = bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE;
10807ec681f3Smrg
10817ec681f3Smrg   /* not thread-safe but is fine */
10827ec681f3Smrg   if (!bo->base.mmap_ptr && mappable) {
10837ec681f3Smrg      bo->base.mmap_ptr =
10847ec681f3Smrg         virtgpu_ioctl_map(gpu, bo->gem_handle, bo->base.mmap_size);
10857ec681f3Smrg   }
10867ec681f3Smrg
10877ec681f3Smrg   return bo->base.mmap_ptr;
10887ec681f3Smrg}
10897ec681f3Smrg
10907ec681f3Smrgstatic int
10917ec681f3Smrgvirtgpu_bo_export_dma_buf(struct vn_renderer *renderer,
10927ec681f3Smrg                          struct vn_renderer_bo *_bo)
10937ec681f3Smrg{
10947ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
10957ec681f3Smrg   struct virtgpu_bo *bo = (struct virtgpu_bo *)_bo;
10967ec681f3Smrg   const bool mappable = bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE;
10977ec681f3Smrg   const bool shareable = bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_SHAREABLE;
10987ec681f3Smrg
10997ec681f3Smrg   return shareable
11007ec681f3Smrg             ? virtgpu_ioctl_prime_handle_to_fd(gpu, bo->gem_handle, mappable)
11017ec681f3Smrg             : -1;
11027ec681f3Smrg}
11037ec681f3Smrg
11047ec681f3Smrgstatic bool
11057ec681f3Smrgvirtgpu_bo_destroy(struct vn_renderer *renderer, struct vn_renderer_bo *_bo)
11067ec681f3Smrg{
11077ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
11087ec681f3Smrg   struct virtgpu_bo *bo = (struct virtgpu_bo *)_bo;
11097ec681f3Smrg
11107ec681f3Smrg   mtx_lock(&gpu->dma_buf_import_mutex);
11117ec681f3Smrg
11127ec681f3Smrg   /* Check the refcount again after the import lock is grabbed.  Yes, we use
11137ec681f3Smrg    * the double-checked locking anti-pattern.
11147ec681f3Smrg    */
11157ec681f3Smrg   if (vn_refcount_is_valid(&bo->base.refcount)) {
11167ec681f3Smrg      mtx_unlock(&gpu->dma_buf_import_mutex);
11177ec681f3Smrg      return false;
11187ec681f3Smrg   }
11197ec681f3Smrg
11207ec681f3Smrg   if (bo->base.mmap_ptr)
11217ec681f3Smrg      munmap(bo->base.mmap_ptr, bo->base.mmap_size);
11227ec681f3Smrg   virtgpu_ioctl_gem_close(gpu, bo->gem_handle);
11237ec681f3Smrg
11247ec681f3Smrg   /* set gem_handle to 0 to indicate that the bo is invalid */
11257ec681f3Smrg   bo->gem_handle = 0;
11267ec681f3Smrg
11277ec681f3Smrg   mtx_unlock(&gpu->dma_buf_import_mutex);
11287ec681f3Smrg
11297ec681f3Smrg   return true;
11307ec681f3Smrg}
11317ec681f3Smrg
11327ec681f3Smrgstatic uint32_t
11337ec681f3Smrgvirtgpu_bo_blob_flags(VkMemoryPropertyFlags flags,
11347ec681f3Smrg                      VkExternalMemoryHandleTypeFlags external_handles)
11357ec681f3Smrg{
11367ec681f3Smrg   uint32_t blob_flags = 0;
11377ec681f3Smrg   if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
11387ec681f3Smrg      blob_flags |= VIRTGPU_BLOB_FLAG_USE_MAPPABLE;
11397ec681f3Smrg   if (external_handles)
11407ec681f3Smrg      blob_flags |= VIRTGPU_BLOB_FLAG_USE_SHAREABLE;
11417ec681f3Smrg   if (external_handles & VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT)
11427ec681f3Smrg      blob_flags |= VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE;
11437ec681f3Smrg
11447ec681f3Smrg   return blob_flags;
11457ec681f3Smrg}
11467ec681f3Smrg
11477ec681f3Smrgstatic VkResult
11487ec681f3Smrgvirtgpu_bo_create_from_dma_buf(struct vn_renderer *renderer,
11497ec681f3Smrg                               VkDeviceSize size,
11507ec681f3Smrg                               int fd,
11517ec681f3Smrg                               VkMemoryPropertyFlags flags,
11527ec681f3Smrg                               struct vn_renderer_bo **out_bo)
11537ec681f3Smrg{
11547ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
11557ec681f3Smrg   struct drm_virtgpu_resource_info info;
11567ec681f3Smrg   uint32_t gem_handle = 0;
11577ec681f3Smrg   struct virtgpu_bo *bo = NULL;
11587ec681f3Smrg
11597ec681f3Smrg   mtx_lock(&gpu->dma_buf_import_mutex);
11607ec681f3Smrg
11617ec681f3Smrg   gem_handle = virtgpu_ioctl_prime_fd_to_handle(gpu, fd);
11627ec681f3Smrg   if (!gem_handle)
11637ec681f3Smrg      goto fail;
11647ec681f3Smrg   bo = util_sparse_array_get(&gpu->bo_array, gem_handle);
11657ec681f3Smrg
11667ec681f3Smrg   if (virtgpu_ioctl_resource_info(gpu, gem_handle, &info))
11677ec681f3Smrg      goto fail;
11687ec681f3Smrg
11697ec681f3Smrg   uint32_t blob_flags;
11707ec681f3Smrg   size_t mmap_size;
11717ec681f3Smrg   if (info.blob_mem) {
11727ec681f3Smrg      /* must be VIRTGPU_BLOB_MEM_HOST3D */
11737ec681f3Smrg      if (info.blob_mem != VIRTGPU_BLOB_MEM_HOST3D)
11747ec681f3Smrg         goto fail;
11757ec681f3Smrg
11767ec681f3Smrg      /* blob_flags is not passed to the kernel and is only for internal use
11777ec681f3Smrg       * on imports.  Set it to what works best for us.
11787ec681f3Smrg       */
11797ec681f3Smrg      blob_flags = virtgpu_bo_blob_flags(flags, 0);
11807ec681f3Smrg      blob_flags |= VIRTGPU_BLOB_FLAG_USE_SHAREABLE;
11817ec681f3Smrg
11827ec681f3Smrg      /* mmap_size is only used when mappable */
11837ec681f3Smrg      mmap_size = 0;
11847ec681f3Smrg      if (blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE) {
11857ec681f3Smrg         if (info.size < size)
11867ec681f3Smrg            goto fail;
11877ec681f3Smrg
11887ec681f3Smrg         mmap_size = size;
11897ec681f3Smrg      }
11907ec681f3Smrg   } else {
11917ec681f3Smrg      /* must be classic resource here
11927ec681f3Smrg       * set blob_flags to 0 to fail virtgpu_bo_map
11937ec681f3Smrg       * set mmap_size to 0 since mapping is not allowed
11947ec681f3Smrg       */
11957ec681f3Smrg      blob_flags = 0;
11967ec681f3Smrg      mmap_size = 0;
11977ec681f3Smrg   }
11987ec681f3Smrg
11997ec681f3Smrg   /* we check bo->gem_handle instead of bo->refcount because bo->refcount
12007ec681f3Smrg    * might only be memset to 0 and is not considered initialized in theory
12017ec681f3Smrg    */
12027ec681f3Smrg   if (bo->gem_handle == gem_handle) {
12037ec681f3Smrg      if (bo->base.mmap_size < mmap_size)
12047ec681f3Smrg         goto fail;
12057ec681f3Smrg      if (blob_flags & ~bo->blob_flags)
12067ec681f3Smrg         goto fail;
12077ec681f3Smrg
12087ec681f3Smrg      /* we can't use vn_renderer_bo_ref as the refcount may drop to 0
12097ec681f3Smrg       * temporarily before virtgpu_bo_destroy grabs the lock
12107ec681f3Smrg       */
12117ec681f3Smrg      vn_refcount_fetch_add_relaxed(&bo->base.refcount, 1);
12127ec681f3Smrg   } else {
12137ec681f3Smrg      *bo = (struct virtgpu_bo){
12147ec681f3Smrg         .base = {
12157ec681f3Smrg            .refcount = VN_REFCOUNT_INIT(1),
12167ec681f3Smrg            .res_id = info.res_handle,
12177ec681f3Smrg            .mmap_size = mmap_size,
12187ec681f3Smrg         },
12197ec681f3Smrg         .gem_handle = gem_handle,
12207ec681f3Smrg         .blob_flags = blob_flags,
12217ec681f3Smrg      };
12227ec681f3Smrg   }
12237ec681f3Smrg
12247ec681f3Smrg   mtx_unlock(&gpu->dma_buf_import_mutex);
12257ec681f3Smrg
12267ec681f3Smrg   *out_bo = &bo->base;
12277ec681f3Smrg
12287ec681f3Smrg   return VK_SUCCESS;
12297ec681f3Smrg
12307ec681f3Smrgfail:
12317ec681f3Smrg   if (gem_handle && bo->gem_handle != gem_handle)
12327ec681f3Smrg      virtgpu_ioctl_gem_close(gpu, gem_handle);
12337ec681f3Smrg   mtx_unlock(&gpu->dma_buf_import_mutex);
12347ec681f3Smrg   return VK_ERROR_INVALID_EXTERNAL_HANDLE;
12357ec681f3Smrg}
12367ec681f3Smrg
12377ec681f3Smrgstatic VkResult
12387ec681f3Smrgvirtgpu_bo_create_from_device_memory(
12397ec681f3Smrg   struct vn_renderer *renderer,
12407ec681f3Smrg   VkDeviceSize size,
12417ec681f3Smrg   vn_object_id mem_id,
12427ec681f3Smrg   VkMemoryPropertyFlags flags,
12437ec681f3Smrg   VkExternalMemoryHandleTypeFlags external_handles,
12447ec681f3Smrg   struct vn_renderer_bo **out_bo)
12457ec681f3Smrg{
12467ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
12477ec681f3Smrg   const uint32_t blob_flags = virtgpu_bo_blob_flags(flags, external_handles);
12487ec681f3Smrg
12497ec681f3Smrg   uint32_t res_id;
12507ec681f3Smrg   uint32_t gem_handle = virtgpu_ioctl_resource_create_blob(
12517ec681f3Smrg      gpu, VIRTGPU_BLOB_MEM_HOST3D, blob_flags, size, mem_id, &res_id);
12527ec681f3Smrg   if (!gem_handle)
12537ec681f3Smrg      return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12547ec681f3Smrg
12557ec681f3Smrg   struct virtgpu_bo *bo = util_sparse_array_get(&gpu->bo_array, gem_handle);
12567ec681f3Smrg   *bo = (struct virtgpu_bo){
12577ec681f3Smrg      .base = {
12587ec681f3Smrg         .refcount = VN_REFCOUNT_INIT(1),
12597ec681f3Smrg         .res_id = res_id,
12607ec681f3Smrg         .mmap_size = size,
12617ec681f3Smrg      },
12627ec681f3Smrg      .gem_handle = gem_handle,
12637ec681f3Smrg      .blob_flags = blob_flags,
12647ec681f3Smrg   };
12657ec681f3Smrg
12667ec681f3Smrg   *out_bo = &bo->base;
12677ec681f3Smrg
12687ec681f3Smrg   return VK_SUCCESS;
12697ec681f3Smrg}
12707ec681f3Smrg
12717ec681f3Smrgstatic void
12727ec681f3Smrgvirtgpu_shmem_destroy(struct vn_renderer *renderer,
12737ec681f3Smrg                      struct vn_renderer_shmem *_shmem)
12747ec681f3Smrg{
12757ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
12767ec681f3Smrg   struct virtgpu_shmem *shmem = (struct virtgpu_shmem *)_shmem;
12777ec681f3Smrg
12787ec681f3Smrg   munmap(shmem->base.mmap_ptr, shmem->base.mmap_size);
12797ec681f3Smrg   virtgpu_ioctl_gem_close(gpu, shmem->gem_handle);
12807ec681f3Smrg}
12817ec681f3Smrg
12827ec681f3Smrgstatic struct vn_renderer_shmem *
12837ec681f3Smrgvirtgpu_shmem_create(struct vn_renderer *renderer, size_t size)
12847ec681f3Smrg{
12857ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
12867ec681f3Smrg
12877ec681f3Smrg   uint32_t res_id;
12887ec681f3Smrg   uint32_t gem_handle = virtgpu_ioctl_resource_create_blob(
12897ec681f3Smrg      gpu, VIRTGPU_BLOB_MEM_GUEST, VIRTGPU_BLOB_FLAG_USE_MAPPABLE, size, 0,
12907ec681f3Smrg      &res_id);
12917ec681f3Smrg   if (!gem_handle)
12927ec681f3Smrg      return NULL;
12937ec681f3Smrg
12947ec681f3Smrg   void *ptr = virtgpu_ioctl_map(gpu, gem_handle, size);
12957ec681f3Smrg   if (!ptr) {
12967ec681f3Smrg      virtgpu_ioctl_gem_close(gpu, gem_handle);
12977ec681f3Smrg      return NULL;
12987ec681f3Smrg   }
12997ec681f3Smrg
13007ec681f3Smrg   struct virtgpu_shmem *shmem =
13017ec681f3Smrg      util_sparse_array_get(&gpu->shmem_array, gem_handle);
13027ec681f3Smrg   *shmem = (struct virtgpu_shmem){
13037ec681f3Smrg      .base = {
13047ec681f3Smrg         .refcount = VN_REFCOUNT_INIT(1),
13057ec681f3Smrg         .res_id = res_id,
13067ec681f3Smrg         .mmap_size = size,
13077ec681f3Smrg         .mmap_ptr = ptr,
13087ec681f3Smrg      },
13097ec681f3Smrg      .gem_handle = gem_handle,
13107ec681f3Smrg   };
13117ec681f3Smrg
13127ec681f3Smrg   return &shmem->base;
13137ec681f3Smrg}
13147ec681f3Smrg
13157ec681f3Smrgstatic VkResult
13167ec681f3Smrgvirtgpu_wait(struct vn_renderer *renderer,
13177ec681f3Smrg             const struct vn_renderer_wait *wait)
13187ec681f3Smrg{
13197ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
13207ec681f3Smrg
13217ec681f3Smrg   const int ret = virtgpu_ioctl_syncobj_timeline_wait(gpu, wait, false);
13227ec681f3Smrg   if (ret && errno != ETIME)
13237ec681f3Smrg      return VK_ERROR_DEVICE_LOST;
13247ec681f3Smrg
13257ec681f3Smrg   return ret ? VK_TIMEOUT : VK_SUCCESS;
13267ec681f3Smrg}
13277ec681f3Smrg
13287ec681f3Smrgstatic VkResult
13297ec681f3Smrgvirtgpu_submit(struct vn_renderer *renderer,
13307ec681f3Smrg               const struct vn_renderer_submit *submit)
13317ec681f3Smrg{
13327ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
13337ec681f3Smrg
13347ec681f3Smrg   const int ret = virtgpu_ioctl_submit(gpu, submit);
13357ec681f3Smrg   return ret ? VK_ERROR_DEVICE_LOST : VK_SUCCESS;
13367ec681f3Smrg}
13377ec681f3Smrg
13387ec681f3Smrgstatic void
13397ec681f3Smrgvirtgpu_get_info(struct vn_renderer *renderer, struct vn_renderer_info *info)
13407ec681f3Smrg{
13417ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
13427ec681f3Smrg
13437ec681f3Smrg   memset(info, 0, sizeof(*info));
13447ec681f3Smrg
13457ec681f3Smrg   info->pci.vendor_id = VIRTGPU_PCI_VENDOR_ID;
13467ec681f3Smrg   info->pci.device_id = VIRTGPU_PCI_DEVICE_ID;
13477ec681f3Smrg
13487ec681f3Smrg   info->pci.has_bus_info = true;
13497ec681f3Smrg   info->pci.domain = gpu->bus_info.domain;
13507ec681f3Smrg   info->pci.bus = gpu->bus_info.bus;
13517ec681f3Smrg   info->pci.device = gpu->bus_info.dev;
13527ec681f3Smrg   info->pci.function = gpu->bus_info.func;
13537ec681f3Smrg
13547ec681f3Smrg   info->has_dma_buf_import = true;
13557ec681f3Smrg   /* Kernel makes every mapping coherent.  We are better off filtering
13567ec681f3Smrg    * incoherent memory types out than silently making them coherent.
13577ec681f3Smrg    */
13587ec681f3Smrg   info->has_cache_management = false;
13597ec681f3Smrg   /* TODO drm_syncobj */
13607ec681f3Smrg   info->has_external_sync = false;
13617ec681f3Smrg
13627ec681f3Smrg   info->has_implicit_fencing = false;
13637ec681f3Smrg
13647ec681f3Smrg   info->max_sync_queue_count = gpu->max_sync_queue_count;
13657ec681f3Smrg
13667ec681f3Smrg   const struct virgl_renderer_capset_venus *capset = &gpu->capset.data;
13677ec681f3Smrg   info->wire_format_version = capset->wire_format_version;
13687ec681f3Smrg   info->vk_xml_version = capset->vk_xml_version;
13697ec681f3Smrg   info->vk_ext_command_serialization_spec_version =
13707ec681f3Smrg      capset->vk_ext_command_serialization_spec_version;
13717ec681f3Smrg   info->vk_mesa_venus_protocol_spec_version =
13727ec681f3Smrg      capset->vk_mesa_venus_protocol_spec_version;
13737ec681f3Smrg}
13747ec681f3Smrg
13757ec681f3Smrgstatic void
13767ec681f3Smrgvirtgpu_destroy(struct vn_renderer *renderer,
13777ec681f3Smrg                const VkAllocationCallbacks *alloc)
13787ec681f3Smrg{
13797ec681f3Smrg   struct virtgpu *gpu = (struct virtgpu *)renderer;
13807ec681f3Smrg
13817ec681f3Smrg   if (gpu->fd >= 0)
13827ec681f3Smrg      close(gpu->fd);
13837ec681f3Smrg
13847ec681f3Smrg   mtx_destroy(&gpu->dma_buf_import_mutex);
13857ec681f3Smrg
13867ec681f3Smrg   util_sparse_array_finish(&gpu->shmem_array);
13877ec681f3Smrg   util_sparse_array_finish(&gpu->bo_array);
13887ec681f3Smrg
13897ec681f3Smrg   vk_free(alloc, gpu);
13907ec681f3Smrg}
13917ec681f3Smrg
13927ec681f3Smrgstatic VkResult
13937ec681f3Smrgvirtgpu_init_context(struct virtgpu *gpu)
13947ec681f3Smrg{
13957ec681f3Smrg   assert(!gpu->capset.version);
13967ec681f3Smrg   const int ret = virtgpu_ioctl_context_init(gpu, gpu->capset.id);
13977ec681f3Smrg   if (ret) {
13987ec681f3Smrg      if (VN_DEBUG(INIT)) {
13997ec681f3Smrg         vn_log(gpu->instance, "failed to initialize context: %s",
14007ec681f3Smrg                strerror(errno));
14017ec681f3Smrg      }
14027ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
14037ec681f3Smrg   }
14047ec681f3Smrg
14057ec681f3Smrg   return VK_SUCCESS;
14067ec681f3Smrg}
14077ec681f3Smrg
14087ec681f3Smrgstatic VkResult
14097ec681f3Smrgvirtgpu_init_capset(struct virtgpu *gpu)
14107ec681f3Smrg{
14117ec681f3Smrg   gpu->capset.id = VIRGL_RENDERER_CAPSET_VENUS;
14127ec681f3Smrg   gpu->capset.version = 0;
14137ec681f3Smrg
14147ec681f3Smrg   const int ret =
14157ec681f3Smrg      virtgpu_ioctl_get_caps(gpu, gpu->capset.id, gpu->capset.version,
14167ec681f3Smrg                             &gpu->capset.data, sizeof(gpu->capset.data));
14177ec681f3Smrg   if (ret) {
14187ec681f3Smrg      if (VN_DEBUG(INIT)) {
14197ec681f3Smrg         vn_log(gpu->instance, "failed to get venus v%d capset: %s",
14207ec681f3Smrg                gpu->capset.version, strerror(errno));
14217ec681f3Smrg      }
14227ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
14237ec681f3Smrg   }
14247ec681f3Smrg
14257ec681f3Smrg   return VK_SUCCESS;
14267ec681f3Smrg}
14277ec681f3Smrg
14287ec681f3Smrgstatic VkResult
14297ec681f3Smrgvirtgpu_init_params(struct virtgpu *gpu)
14307ec681f3Smrg{
14317ec681f3Smrg   const uint64_t required_params[] = {
14327ec681f3Smrg      VIRTGPU_PARAM_3D_FEATURES,   VIRTGPU_PARAM_CAPSET_QUERY_FIX,
14337ec681f3Smrg      VIRTGPU_PARAM_RESOURCE_BLOB, VIRTGPU_PARAM_HOST_VISIBLE,
14347ec681f3Smrg      VIRTGPU_PARAM_CROSS_DEVICE,  VIRTGPU_PARAM_CONTEXT_INIT,
14357ec681f3Smrg   };
14367ec681f3Smrg   uint64_t val;
14377ec681f3Smrg   for (uint32_t i = 0; i < ARRAY_SIZE(required_params); i++) {
14387ec681f3Smrg      val = virtgpu_ioctl_getparam(gpu, required_params[i]);
14397ec681f3Smrg      if (!val) {
14407ec681f3Smrg         if (VN_DEBUG(INIT)) {
14417ec681f3Smrg            vn_log(gpu->instance, "required kernel param %d is missing",
14427ec681f3Smrg                   (int)required_params[i]);
14437ec681f3Smrg         }
14447ec681f3Smrg         return VK_ERROR_INITIALIZATION_FAILED;
14457ec681f3Smrg      }
14467ec681f3Smrg   }
14477ec681f3Smrg
14487ec681f3Smrg   val = virtgpu_ioctl_getparam(gpu, VIRTGPU_PARAM_MAX_SYNC_QUEUE_COUNT);
14497ec681f3Smrg   if (!val) {
14507ec681f3Smrg      if (VN_DEBUG(INIT))
14517ec681f3Smrg         vn_log(gpu->instance, "no sync queue support");
14527ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
14537ec681f3Smrg   }
14547ec681f3Smrg   gpu->max_sync_queue_count = val;
14557ec681f3Smrg
14567ec681f3Smrg   return VK_SUCCESS;
14577ec681f3Smrg}
14587ec681f3Smrg
14597ec681f3Smrgstatic VkResult
14607ec681f3Smrgvirtgpu_open_device(struct virtgpu *gpu, const drmDevicePtr dev)
14617ec681f3Smrg{
14627ec681f3Smrg   /* skip unless the device has our PCI vendor/device id and a render node */
14637ec681f3Smrg   if (!(dev->available_nodes & (1 << DRM_NODE_RENDER)) ||
14647ec681f3Smrg       dev->bustype != DRM_BUS_PCI ||
14657ec681f3Smrg       dev->deviceinfo.pci->vendor_id != VIRTGPU_PCI_VENDOR_ID ||
14667ec681f3Smrg       dev->deviceinfo.pci->device_id != VIRTGPU_PCI_DEVICE_ID) {
14677ec681f3Smrg      if (VN_DEBUG(INIT)) {
14687ec681f3Smrg         const char *name = "unknown";
14697ec681f3Smrg         for (uint32_t i = 0; i < DRM_NODE_MAX; i++) {
14707ec681f3Smrg            if (dev->available_nodes & (1 << i)) {
14717ec681f3Smrg               name = dev->nodes[i];
14727ec681f3Smrg               break;
14737ec681f3Smrg            }
14747ec681f3Smrg         }
14757ec681f3Smrg         vn_log(gpu->instance, "skipping DRM device %s", name);
14767ec681f3Smrg      }
14777ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
14787ec681f3Smrg   }
14797ec681f3Smrg
14807ec681f3Smrg   const char *node_path = dev->nodes[DRM_NODE_RENDER];
14817ec681f3Smrg
14827ec681f3Smrg   int fd = open(node_path, O_RDWR | O_CLOEXEC);
14837ec681f3Smrg   if (fd < 0) {
14847ec681f3Smrg      if (VN_DEBUG(INIT))
14857ec681f3Smrg         vn_log(gpu->instance, "failed to open %s", node_path);
14867ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
14877ec681f3Smrg   }
14887ec681f3Smrg
14897ec681f3Smrg   drmVersionPtr version = drmGetVersion(fd);
14907ec681f3Smrg   if (!version || strcmp(version->name, "virtio_gpu") ||
14917ec681f3Smrg       version->version_major != 0) {
14927ec681f3Smrg      if (VN_DEBUG(INIT)) {
14937ec681f3Smrg         if (version) {
14947ec681f3Smrg            vn_log(gpu->instance, "unknown DRM driver %s version %d",
14957ec681f3Smrg                   version->name, version->version_major);
14967ec681f3Smrg         } else {
14977ec681f3Smrg            vn_log(gpu->instance, "failed to get DRM driver version");
14987ec681f3Smrg         }
14997ec681f3Smrg      }
15007ec681f3Smrg      if (version)
15017ec681f3Smrg         drmFreeVersion(version);
15027ec681f3Smrg      close(fd);
15037ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
15047ec681f3Smrg   }
15057ec681f3Smrg
15067ec681f3Smrg   gpu->fd = fd;
15077ec681f3Smrg   gpu->version_minor = version->version_minor;
15087ec681f3Smrg   gpu->bus_info = *dev->businfo.pci;
15097ec681f3Smrg
15107ec681f3Smrg   drmFreeVersion(version);
15117ec681f3Smrg
15127ec681f3Smrg   if (VN_DEBUG(INIT))
15137ec681f3Smrg      vn_log(gpu->instance, "using DRM device %s", node_path);
15147ec681f3Smrg
15157ec681f3Smrg   return VK_SUCCESS;
15167ec681f3Smrg}
15177ec681f3Smrg
15187ec681f3Smrgstatic VkResult
15197ec681f3Smrgvirtgpu_open(struct virtgpu *gpu)
15207ec681f3Smrg{
15217ec681f3Smrg   drmDevicePtr devs[8];
15227ec681f3Smrg   int count = drmGetDevices2(0, devs, ARRAY_SIZE(devs));
15237ec681f3Smrg   if (count < 0) {
15247ec681f3Smrg      if (VN_DEBUG(INIT))
15257ec681f3Smrg         vn_log(gpu->instance, "failed to enumerate DRM devices");
15267ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
15277ec681f3Smrg   }
15287ec681f3Smrg
15297ec681f3Smrg   VkResult result = VK_ERROR_INITIALIZATION_FAILED;
15307ec681f3Smrg   for (int i = 0; i < count; i++) {
15317ec681f3Smrg      result = virtgpu_open_device(gpu, devs[i]);
15327ec681f3Smrg      if (result == VK_SUCCESS)
15337ec681f3Smrg         break;
15347ec681f3Smrg   }
15357ec681f3Smrg
15367ec681f3Smrg   drmFreeDevices(devs, count);
15377ec681f3Smrg
15387ec681f3Smrg   return result;
15397ec681f3Smrg}
15407ec681f3Smrg
15417ec681f3Smrgstatic VkResult
15427ec681f3Smrgvirtgpu_init(struct virtgpu *gpu)
15437ec681f3Smrg{
15447ec681f3Smrg   util_sparse_array_init(&gpu->shmem_array, sizeof(struct virtgpu_shmem),
15457ec681f3Smrg                          1024);
15467ec681f3Smrg   util_sparse_array_init(&gpu->bo_array, sizeof(struct virtgpu_bo), 1024);
15477ec681f3Smrg
15487ec681f3Smrg   mtx_init(&gpu->dma_buf_import_mutex, mtx_plain);
15497ec681f3Smrg
15507ec681f3Smrg   VkResult result = virtgpu_open(gpu);
15517ec681f3Smrg   if (result == VK_SUCCESS)
15527ec681f3Smrg      result = virtgpu_init_params(gpu);
15537ec681f3Smrg   if (result == VK_SUCCESS)
15547ec681f3Smrg      result = virtgpu_init_capset(gpu);
15557ec681f3Smrg   if (result == VK_SUCCESS)
15567ec681f3Smrg      result = virtgpu_init_context(gpu);
15577ec681f3Smrg   if (result != VK_SUCCESS)
15587ec681f3Smrg      return result;
15597ec681f3Smrg
15607ec681f3Smrg   gpu->base.ops.destroy = virtgpu_destroy;
15617ec681f3Smrg   gpu->base.ops.get_info = virtgpu_get_info;
15627ec681f3Smrg   gpu->base.ops.submit = virtgpu_submit;
15637ec681f3Smrg   gpu->base.ops.wait = virtgpu_wait;
15647ec681f3Smrg
15657ec681f3Smrg   gpu->base.shmem_ops.create = virtgpu_shmem_create;
15667ec681f3Smrg   gpu->base.shmem_ops.destroy = virtgpu_shmem_destroy;
15677ec681f3Smrg
15687ec681f3Smrg   gpu->base.bo_ops.create_from_device_memory =
15697ec681f3Smrg      virtgpu_bo_create_from_device_memory;
15707ec681f3Smrg   gpu->base.bo_ops.create_from_dma_buf = virtgpu_bo_create_from_dma_buf;
15717ec681f3Smrg   gpu->base.bo_ops.destroy = virtgpu_bo_destroy;
15727ec681f3Smrg   gpu->base.bo_ops.export_dma_buf = virtgpu_bo_export_dma_buf;
15737ec681f3Smrg   gpu->base.bo_ops.map = virtgpu_bo_map;
15747ec681f3Smrg   gpu->base.bo_ops.flush = virtgpu_bo_flush;
15757ec681f3Smrg   gpu->base.bo_ops.invalidate = virtgpu_bo_invalidate;
15767ec681f3Smrg
15777ec681f3Smrg   gpu->base.sync_ops.create = virtgpu_sync_create;
15787ec681f3Smrg   gpu->base.sync_ops.create_from_syncobj = virtgpu_sync_create_from_syncobj;
15797ec681f3Smrg   gpu->base.sync_ops.destroy = virtgpu_sync_destroy;
15807ec681f3Smrg   gpu->base.sync_ops.export_syncobj = virtgpu_sync_export_syncobj;
15817ec681f3Smrg   gpu->base.sync_ops.reset = virtgpu_sync_reset;
15827ec681f3Smrg   gpu->base.sync_ops.read = virtgpu_sync_read;
15837ec681f3Smrg   gpu->base.sync_ops.write = virtgpu_sync_write;
15847ec681f3Smrg
15857ec681f3Smrg   return VK_SUCCESS;
15867ec681f3Smrg}
15877ec681f3Smrg
15887ec681f3SmrgVkResult
15897ec681f3Smrgvn_renderer_create_virtgpu(struct vn_instance *instance,
15907ec681f3Smrg                           const VkAllocationCallbacks *alloc,
15917ec681f3Smrg                           struct vn_renderer **renderer)
15927ec681f3Smrg{
15937ec681f3Smrg   struct virtgpu *gpu = vk_zalloc(alloc, sizeof(*gpu), VN_DEFAULT_ALIGN,
15947ec681f3Smrg                                   VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
15957ec681f3Smrg   if (!gpu)
15967ec681f3Smrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
15977ec681f3Smrg
15987ec681f3Smrg   gpu->instance = instance;
15997ec681f3Smrg   gpu->fd = -1;
16007ec681f3Smrg
16017ec681f3Smrg   VkResult result = virtgpu_init(gpu);
16027ec681f3Smrg   if (result != VK_SUCCESS) {
16037ec681f3Smrg      virtgpu_destroy(&gpu->base, alloc);
16047ec681f3Smrg      return result;
16057ec681f3Smrg   }
16067ec681f3Smrg
16077ec681f3Smrg   *renderer = &gpu->base;
16087ec681f3Smrg
16097ec681f3Smrg   return VK_SUCCESS;
16107ec681f3Smrg}
1611