• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 Google LLC
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <poll.h>
9 #include <sys/mman.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13 #include <xf86drm.h>
14 
15 #include "drm-uapi/virtgpu_drm.h"
16 #include "util/sparse_array.h"
17 #define VIRGL_RENDERER_UNSTABLE_APIS
18 #include "virtio-gpu/virglrenderer_hw.h"
19 
20 #include "vn_renderer.h"
21 
22 /* XXX WIP kernel uapi */
23 #ifndef VIRTGPU_PARAM_CONTEXT_INIT
24 #define VIRTGPU_PARAM_CONTEXT_INIT 6
25 #define VIRTGPU_CONTEXT_PARAM_CAPSET_ID 0x0001
26 struct drm_virtgpu_context_set_param {
27    __u64 param;
28    __u64 value;
29 };
30 struct drm_virtgpu_context_init {
31    __u32 num_params;
32    __u32 pad;
33    __u64 ctx_set_params;
34 };
35 #define DRM_VIRTGPU_CONTEXT_INIT 0xb
36 #define DRM_IOCTL_VIRTGPU_CONTEXT_INIT                                       \
37    DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_CONTEXT_INIT,                     \
38             struct drm_virtgpu_context_init)
39 #endif /* VIRTGPU_PARAM_CONTEXT_INIT */
40 #ifndef VIRTGPU_PARAM_MAX_SYNC_QUEUE_COUNT
41 #define VIRTGPU_PARAM_MAX_SYNC_QUEUE_COUNT 100
42 #endif /* VIRTGPU_PARAM_MAX_SYNC_QUEUE_COUNT */
43 
44 /* XXX comment these out to really use kernel uapi */
45 #define SIMULATE_BO_SIZE_FIX 1
46 //#define SIMULATE_CONTEXT_INIT 1
47 #define SIMULATE_SYNCOBJ 1
48 #define SIMULATE_SUBMIT 1
49 
50 #define VIRTGPU_PCI_VENDOR_ID 0x1af4
51 #define VIRTGPU_PCI_DEVICE_ID 0x1050
52 
53 struct virtgpu;
54 
55 struct virtgpu_shmem {
56    struct vn_renderer_shmem base;
57    uint32_t gem_handle;
58 };
59 
60 struct virtgpu_bo {
61    struct vn_renderer_bo base;
62    uint32_t gem_handle;
63    uint32_t blob_flags;
64 };
65 
66 struct virtgpu_sync {
67    struct vn_renderer_sync base;
68 
69    /*
70     * drm_syncobj is in one of these states
71     *
72     *  - value N:      drm_syncobj has a signaled fence chain with seqno N
73     *  - pending N->M: drm_syncobj has an unsignaled fence chain with seqno M
74     *                  (which may point to another unsignaled fence chain with
75     *                   seqno between N and M, and so on)
76     *
77     * TODO Do we want to use binary drm_syncobjs?  They would be
78     *
79     *  - value 0: drm_syncobj has no fence
80     *  - value 1: drm_syncobj has a signaled fence with seqno 0
81     *
82     * They are cheaper but require special care.
83     */
84    uint32_t syncobj_handle;
85 };
86 
87 struct virtgpu {
88    struct vn_renderer base;
89 
90    struct vn_instance *instance;
91 
92    int fd;
93    int version_minor;
94    drmPciBusInfo bus_info;
95 
96    uint32_t max_sync_queue_count;
97 
98    struct {
99       enum virgl_renderer_capset id;
100       uint32_t version;
101       struct virgl_renderer_capset_venus data;
102    } capset;
103 
104    /* note that we use gem_handle instead of res_id to index because
105     * res_id is monotonically increasing by default (see
106     * virtio_gpu_resource_id_get)
107     */
108    struct util_sparse_array shmem_array;
109    struct util_sparse_array bo_array;
110 
111    mtx_t dma_buf_import_mutex;
112 };
113 
114 #ifdef SIMULATE_SYNCOBJ
115 
116 #include "util/hash_table.h"
117 #include "util/u_idalloc.h"
118 
119 static struct {
120    mtx_t mutex;
121    struct hash_table *syncobjs;
122    struct util_idalloc ida;
123 
124    int signaled_fd;
125 } sim;
126 
127 struct sim_syncobj {
128    mtx_t mutex;
129    uint64_t point;
130 
131    int pending_fd;
132    uint64_t pending_point;
133    bool pending_cpu;
134 };
135 
136 static uint32_t
sim_syncobj_create(struct virtgpu * gpu,bool signaled)137 sim_syncobj_create(struct virtgpu *gpu, bool signaled)
138 {
139    struct sim_syncobj *syncobj = calloc(1, sizeof(*syncobj));
140    if (!syncobj)
141       return 0;
142 
143    mtx_init(&syncobj->mutex, mtx_plain);
144    syncobj->pending_fd = -1;
145 
146    mtx_lock(&sim.mutex);
147 
148    /* initialize lazily */
149    if (!sim.syncobjs) {
150       sim.syncobjs = _mesa_pointer_hash_table_create(NULL);
151       if (!sim.syncobjs) {
152          mtx_unlock(&sim.mutex);
153          return 0;
154       }
155 
156       util_idalloc_init(&sim.ida, 32);
157 
158       struct drm_virtgpu_execbuffer args = {
159          .flags = VIRTGPU_EXECBUF_FENCE_FD_OUT,
160       };
161       int ret = drmIoctl(gpu->fd, DRM_IOCTL_VIRTGPU_EXECBUFFER, &args);
162       if (ret || args.fence_fd < 0) {
163          _mesa_hash_table_destroy(sim.syncobjs, NULL);
164          sim.syncobjs = NULL;
165          mtx_unlock(&sim.mutex);
166          return 0;
167       }
168 
169       sim.signaled_fd = args.fence_fd;
170    }
171 
172    const unsigned syncobj_handle = util_idalloc_alloc(&sim.ida) + 1;
173    _mesa_hash_table_insert(sim.syncobjs,
174                            (const void *)(uintptr_t)syncobj_handle, syncobj);
175 
176    mtx_unlock(&sim.mutex);
177 
178    return syncobj_handle;
179 }
180 
181 static void
sim_syncobj_destroy(struct virtgpu * gpu,uint32_t syncobj_handle)182 sim_syncobj_destroy(struct virtgpu *gpu, uint32_t syncobj_handle)
183 {
184    struct sim_syncobj *syncobj = NULL;
185 
186    mtx_lock(&sim.mutex);
187 
188    struct hash_entry *entry = _mesa_hash_table_search(
189       sim.syncobjs, (const void *)(uintptr_t)syncobj_handle);
190    if (entry) {
191       syncobj = entry->data;
192       _mesa_hash_table_remove(sim.syncobjs, entry);
193       util_idalloc_free(&sim.ida, syncobj_handle - 1);
194    }
195 
196    mtx_unlock(&sim.mutex);
197 
198    if (syncobj) {
199       if (syncobj->pending_fd >= 0)
200          close(syncobj->pending_fd);
201       mtx_destroy(&syncobj->mutex);
202       free(syncobj);
203    }
204 }
205 
206 static VkResult
sim_syncobj_poll(int fd,int poll_timeout)207 sim_syncobj_poll(int fd, int poll_timeout)
208 {
209    struct pollfd pollfd = {
210       .fd = fd,
211       .events = POLLIN,
212    };
213    int ret;
214    do {
215       ret = poll(&pollfd, 1, poll_timeout);
216    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
217 
218    if (ret < 0 || (ret > 0 && !(pollfd.revents & POLLIN))) {
219       return (ret < 0 && errno == ENOMEM) ? VK_ERROR_OUT_OF_HOST_MEMORY
220                                           : VK_ERROR_DEVICE_LOST;
221    }
222 
223    return ret ? VK_SUCCESS : VK_TIMEOUT;
224 }
225 
226 static void
sim_syncobj_set_point_locked(struct sim_syncobj * syncobj,uint64_t point)227 sim_syncobj_set_point_locked(struct sim_syncobj *syncobj, uint64_t point)
228 {
229    syncobj->point = point;
230 
231    if (syncobj->pending_fd >= 0) {
232       close(syncobj->pending_fd);
233       syncobj->pending_fd = -1;
234       syncobj->pending_point = point;
235    }
236 }
237 
238 static void
sim_syncobj_update_point_locked(struct sim_syncobj * syncobj,int poll_timeout)239 sim_syncobj_update_point_locked(struct sim_syncobj *syncobj, int poll_timeout)
240 {
241    if (syncobj->pending_fd >= 0) {
242       VkResult result;
243       if (syncobj->pending_cpu) {
244          if (poll_timeout == -1) {
245             const int max_cpu_timeout = 2000;
246             poll_timeout = max_cpu_timeout;
247             result = sim_syncobj_poll(syncobj->pending_fd, poll_timeout);
248             if (result == VK_TIMEOUT) {
249                vn_log(NULL, "cpu sync timed out after %dms; ignoring",
250                       poll_timeout);
251                result = VK_SUCCESS;
252             }
253          } else {
254             result = sim_syncobj_poll(syncobj->pending_fd, poll_timeout);
255          }
256       } else {
257          result = sim_syncobj_poll(syncobj->pending_fd, poll_timeout);
258       }
259       if (result == VK_SUCCESS) {
260          close(syncobj->pending_fd);
261          syncobj->pending_fd = -1;
262          syncobj->point = syncobj->pending_point;
263       }
264    }
265 }
266 
267 static struct sim_syncobj *
sim_syncobj_lookup(struct virtgpu * gpu,uint32_t syncobj_handle)268 sim_syncobj_lookup(struct virtgpu *gpu, uint32_t syncobj_handle)
269 {
270    struct sim_syncobj *syncobj = NULL;
271 
272    mtx_lock(&sim.mutex);
273    struct hash_entry *entry = _mesa_hash_table_search(
274       sim.syncobjs, (const void *)(uintptr_t)syncobj_handle);
275    if (entry)
276       syncobj = entry->data;
277    mtx_unlock(&sim.mutex);
278 
279    return syncobj;
280 }
281 
282 static int
sim_syncobj_reset(struct virtgpu * gpu,uint32_t syncobj_handle)283 sim_syncobj_reset(struct virtgpu *gpu, uint32_t syncobj_handle)
284 {
285    struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
286    if (!syncobj)
287       return -1;
288 
289    mtx_lock(&syncobj->mutex);
290    sim_syncobj_set_point_locked(syncobj, 0);
291    mtx_unlock(&syncobj->mutex);
292 
293    return 0;
294 }
295 
296 static int
sim_syncobj_query(struct virtgpu * gpu,uint32_t syncobj_handle,uint64_t * point)297 sim_syncobj_query(struct virtgpu *gpu,
298                   uint32_t syncobj_handle,
299                   uint64_t *point)
300 {
301    struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
302    if (!syncobj)
303       return -1;
304 
305    mtx_lock(&syncobj->mutex);
306    sim_syncobj_update_point_locked(syncobj, 0);
307    *point = syncobj->point;
308    mtx_unlock(&syncobj->mutex);
309 
310    return 0;
311 }
312 
313 static int
sim_syncobj_signal(struct virtgpu * gpu,uint32_t syncobj_handle,uint64_t point)314 sim_syncobj_signal(struct virtgpu *gpu,
315                    uint32_t syncobj_handle,
316                    uint64_t point)
317 {
318    struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
319    if (!syncobj)
320       return -1;
321 
322    mtx_lock(&syncobj->mutex);
323    sim_syncobj_set_point_locked(syncobj, point);
324    mtx_unlock(&syncobj->mutex);
325 
326    return 0;
327 }
328 
329 static int
sim_syncobj_submit(struct virtgpu * gpu,uint32_t syncobj_handle,int sync_fd,uint64_t point,bool cpu)330 sim_syncobj_submit(struct virtgpu *gpu,
331                    uint32_t syncobj_handle,
332                    int sync_fd,
333                    uint64_t point,
334                    bool cpu)
335 {
336    struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
337    if (!syncobj)
338       return -1;
339 
340    int pending_fd = dup(sync_fd);
341    if (pending_fd < 0) {
342       vn_log(gpu->instance, "failed to dup sync fd");
343       return -1;
344    }
345 
346    mtx_lock(&syncobj->mutex);
347 
348    if (syncobj->pending_fd >= 0) {
349       mtx_unlock(&syncobj->mutex);
350 
351       /* TODO */
352       vn_log(gpu->instance, "sorry, no simulated timeline semaphore");
353       close(pending_fd);
354       return -1;
355    }
356    if (syncobj->point >= point)
357       vn_log(gpu->instance, "non-monotonic signaling");
358 
359    syncobj->pending_fd = pending_fd;
360    syncobj->pending_point = point;
361    syncobj->pending_cpu = cpu;
362 
363    mtx_unlock(&syncobj->mutex);
364 
365    return 0;
366 }
367 
368 static int
timeout_to_poll_timeout(uint64_t timeout)369 timeout_to_poll_timeout(uint64_t timeout)
370 {
371    const uint64_t ns_per_ms = 1000000;
372    const uint64_t ms = (timeout + ns_per_ms - 1) / ns_per_ms;
373    if (!ms && timeout)
374       return -1;
375    return ms <= INT_MAX ? ms : -1;
376 }
377 
378 static int
sim_syncobj_wait(struct virtgpu * gpu,const struct vn_renderer_wait * wait,bool wait_avail)379 sim_syncobj_wait(struct virtgpu *gpu,
380                  const struct vn_renderer_wait *wait,
381                  bool wait_avail)
382 {
383    if (wait_avail)
384       return -1;
385 
386    const int poll_timeout = timeout_to_poll_timeout(wait->timeout);
387 
388    /* TODO poll all fds at the same time */
389    for (uint32_t i = 0; i < wait->sync_count; i++) {
390       struct virtgpu_sync *sync = (struct virtgpu_sync *)wait->syncs[i];
391       const uint64_t point = wait->sync_values[i];
392 
393       struct sim_syncobj *syncobj =
394          sim_syncobj_lookup(gpu, sync->syncobj_handle);
395       if (!syncobj)
396          return -1;
397 
398       mtx_lock(&syncobj->mutex);
399 
400       if (syncobj->point < point)
401          sim_syncobj_update_point_locked(syncobj, poll_timeout);
402 
403       if (syncobj->point < point) {
404          if (wait->wait_any && i < wait->sync_count - 1 &&
405              syncobj->pending_fd < 0) {
406             mtx_unlock(&syncobj->mutex);
407             continue;
408          }
409          errno = ETIME;
410          mtx_unlock(&syncobj->mutex);
411          return -1;
412       }
413 
414       mtx_unlock(&syncobj->mutex);
415 
416       if (wait->wait_any)
417          break;
418 
419       /* TODO adjust poll_timeout */
420    }
421 
422    return 0;
423 }
424 
425 static int
sim_syncobj_export(struct virtgpu * gpu,uint32_t syncobj_handle)426 sim_syncobj_export(struct virtgpu *gpu, uint32_t syncobj_handle)
427 {
428    struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
429    if (!syncobj)
430       return -1;
431 
432    int fd = -1;
433    mtx_lock(&syncobj->mutex);
434    if (syncobj->pending_fd >= 0)
435       fd = dup(syncobj->pending_fd);
436    else
437       fd = dup(sim.signaled_fd);
438    mtx_unlock(&syncobj->mutex);
439 
440    return fd;
441 }
442 
443 static uint32_t
sim_syncobj_import(struct virtgpu * gpu,uint32_t syncobj_handle,int fd)444 sim_syncobj_import(struct virtgpu *gpu, uint32_t syncobj_handle, int fd)
445 {
446    struct sim_syncobj *syncobj = sim_syncobj_lookup(gpu, syncobj_handle);
447    if (!syncobj)
448       return 0;
449 
450    if (sim_syncobj_submit(gpu, syncobj_handle, fd, 1, false))
451       return 0;
452 
453    return syncobj_handle;
454 }
455 
456 #endif /* SIMULATE_SYNCOBJ */
457 
458 #ifdef SIMULATE_SUBMIT
459 
460 static int
sim_submit_signal_syncs(struct virtgpu * gpu,int sync_fd,struct vn_renderer_sync * const * syncs,const uint64_t * sync_values,uint32_t sync_count,bool cpu)461 sim_submit_signal_syncs(struct virtgpu *gpu,
462                         int sync_fd,
463                         struct vn_renderer_sync *const *syncs,
464                         const uint64_t *sync_values,
465                         uint32_t sync_count,
466                         bool cpu)
467 {
468    for (uint32_t i = 0; i < sync_count; i++) {
469       struct virtgpu_sync *sync = (struct virtgpu_sync *)syncs[i];
470       const uint64_t pending_point = sync_values[i];
471 
472 #ifdef SIMULATE_SYNCOBJ
473       int ret = sim_syncobj_submit(gpu, sync->syncobj_handle, sync_fd,
474                                    pending_point, cpu);
475       if (ret)
476          return ret;
477 #else
478       /* we can in theory do a DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE followed by a
479        * DRM_IOCTL_SYNCOBJ_TRANSFER
480        */
481       return -1;
482 #endif
483    }
484 
485    return 0;
486 }
487 
488 static uint32_t *
sim_submit_alloc_gem_handles(struct vn_renderer_bo * const * bos,uint32_t bo_count)489 sim_submit_alloc_gem_handles(struct vn_renderer_bo *const *bos,
490                              uint32_t bo_count)
491 {
492    uint32_t *gem_handles = malloc(sizeof(*gem_handles) * bo_count);
493    if (!gem_handles)
494       return NULL;
495 
496    for (uint32_t i = 0; i < bo_count; i++) {
497       struct virtgpu_bo *bo = (struct virtgpu_bo *)bos[i];
498       gem_handles[i] = bo->gem_handle;
499    }
500 
501    return gem_handles;
502 }
503 
504 static int
sim_submit(struct virtgpu * gpu,const struct vn_renderer_submit * submit)505 sim_submit(struct virtgpu *gpu, const struct vn_renderer_submit *submit)
506 {
507    /* TODO replace submit->bos by submit->gem_handles to avoid malloc/loop */
508    uint32_t *gem_handles = NULL;
509    if (submit->bo_count) {
510       gem_handles =
511          sim_submit_alloc_gem_handles(submit->bos, submit->bo_count);
512       if (!gem_handles)
513          return -1;
514    }
515 
516    int ret = 0;
517    for (uint32_t i = 0; i < submit->batch_count; i++) {
518       const struct vn_renderer_submit_batch *batch = &submit->batches[i];
519 
520       struct drm_virtgpu_execbuffer args = {
521          .flags = batch->sync_count ? VIRTGPU_EXECBUF_FENCE_FD_OUT : 0,
522          .size = batch->cs_size,
523          .command = (uintptr_t)batch->cs_data,
524          .bo_handles = (uintptr_t)gem_handles,
525          .num_bo_handles = submit->bo_count,
526       };
527 
528       ret = drmIoctl(gpu->fd, DRM_IOCTL_VIRTGPU_EXECBUFFER, &args);
529       if (ret) {
530          vn_log(gpu->instance, "failed to execbuffer: %s", strerror(errno));
531          break;
532       }
533 
534       if (batch->sync_count) {
535          ret = sim_submit_signal_syncs(gpu, args.fence_fd, batch->syncs,
536                                        batch->sync_values, batch->sync_count,
537                                        batch->sync_queue_cpu);
538          close(args.fence_fd);
539          if (ret)
540             break;
541       }
542    }
543 
544    if (!submit->batch_count && submit->bo_count) {
545       struct drm_virtgpu_execbuffer args = {
546          .bo_handles = (uintptr_t)gem_handles,
547          .num_bo_handles = submit->bo_count,
548       };
549 
550       ret = drmIoctl(gpu->fd, DRM_IOCTL_VIRTGPU_EXECBUFFER, &args);
551       if (ret)
552          vn_log(gpu->instance, "failed to execbuffer: %s", strerror(errno));
553    }
554 
555    free(gem_handles);
556 
557    return ret;
558 }
559 
560 #endif /* SIMULATE_SUBMIT */
561 
562 static int
virtgpu_ioctl(struct virtgpu * gpu,unsigned long request,void * args)563 virtgpu_ioctl(struct virtgpu *gpu, unsigned long request, void *args)
564 {
565    return drmIoctl(gpu->fd, request, args);
566 }
567 
568 static uint64_t
virtgpu_ioctl_getparam(struct virtgpu * gpu,uint64_t param)569 virtgpu_ioctl_getparam(struct virtgpu *gpu, uint64_t param)
570 {
571 #ifdef SIMULATE_CONTEXT_INIT
572    if (param == VIRTGPU_PARAM_CONTEXT_INIT)
573       return 1;
574 #endif
575 #ifdef SIMULATE_SUBMIT
576    if (param == VIRTGPU_PARAM_MAX_SYNC_QUEUE_COUNT)
577       return 16;
578 #endif
579 
580    /* val must be zeroed because kernel only writes the lower 32 bits */
581    uint64_t val = 0;
582    struct drm_virtgpu_getparam args = {
583       .param = param,
584       .value = (uintptr_t)&val,
585    };
586 
587    const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_GETPARAM, &args);
588    return ret ? 0 : val;
589 }
590 
591 static int
virtgpu_ioctl_get_caps(struct virtgpu * gpu,enum virgl_renderer_capset id,uint32_t version,void * capset,size_t capset_size)592 virtgpu_ioctl_get_caps(struct virtgpu *gpu,
593                        enum virgl_renderer_capset id,
594                        uint32_t version,
595                        void *capset,
596                        size_t capset_size)
597 {
598 #ifdef SIMULATE_CONTEXT_INIT
599    if (id == VIRGL_RENDERER_CAPSET_VENUS && version == 0)
600       return 0;
601 #endif
602 
603    struct drm_virtgpu_get_caps args = {
604       .cap_set_id = id,
605       .cap_set_ver = version,
606       .addr = (uintptr_t)capset,
607       .size = capset_size,
608    };
609 
610    return virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_GET_CAPS, &args);
611 }
612 
613 static int
virtgpu_ioctl_context_init(struct virtgpu * gpu,enum virgl_renderer_capset capset_id)614 virtgpu_ioctl_context_init(struct virtgpu *gpu,
615                            enum virgl_renderer_capset capset_id)
616 {
617 #ifdef SIMULATE_CONTEXT_INIT
618    if (capset_id == VIRGL_RENDERER_CAPSET_VENUS)
619       return 0;
620 #endif
621 
622    struct drm_virtgpu_context_init args = {
623       .num_params = 1,
624       .ctx_set_params = (uintptr_t) &
625                         (struct drm_virtgpu_context_set_param){
626                            .param = VIRTGPU_CONTEXT_PARAM_CAPSET_ID,
627                            .value = capset_id,
628                         },
629    };
630 
631    return virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_CONTEXT_INIT, &args);
632 }
633 
634 static uint32_t
virtgpu_ioctl_resource_create_blob(struct virtgpu * gpu,uint32_t blob_mem,uint32_t blob_flags,size_t blob_size,uint64_t blob_id,uint32_t * res_id)635 virtgpu_ioctl_resource_create_blob(struct virtgpu *gpu,
636                                    uint32_t blob_mem,
637                                    uint32_t blob_flags,
638                                    size_t blob_size,
639                                    uint64_t blob_id,
640                                    uint32_t *res_id)
641 {
642 #ifdef SIMULATE_BO_SIZE_FIX
643    blob_size = align64(blob_size, 4096);
644 #endif
645 
646    struct drm_virtgpu_resource_create_blob args = {
647       .blob_mem = blob_mem,
648       .blob_flags = blob_flags,
649       .size = blob_size,
650       .blob_id = blob_id,
651    };
652 
653    if (virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_RESOURCE_CREATE_BLOB, &args))
654       return 0;
655 
656    *res_id = args.res_handle;
657    return args.bo_handle;
658 }
659 
660 static int
virtgpu_ioctl_resource_info(struct virtgpu * gpu,uint32_t gem_handle,struct drm_virtgpu_resource_info * info)661 virtgpu_ioctl_resource_info(struct virtgpu *gpu,
662                             uint32_t gem_handle,
663                             struct drm_virtgpu_resource_info *info)
664 {
665    *info = (struct drm_virtgpu_resource_info){
666       .bo_handle = gem_handle,
667    };
668 
669    return virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_RESOURCE_INFO, info);
670 }
671 
672 static void
virtgpu_ioctl_gem_close(struct virtgpu * gpu,uint32_t gem_handle)673 virtgpu_ioctl_gem_close(struct virtgpu *gpu, uint32_t gem_handle)
674 {
675    struct drm_gem_close args = {
676       .handle = gem_handle,
677    };
678 
679    ASSERTED const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_GEM_CLOSE, &args);
680    assert(!ret);
681 }
682 
683 static int
virtgpu_ioctl_prime_handle_to_fd(struct virtgpu * gpu,uint32_t gem_handle,bool mappable)684 virtgpu_ioctl_prime_handle_to_fd(struct virtgpu *gpu,
685                                  uint32_t gem_handle,
686                                  bool mappable)
687 {
688    struct drm_prime_handle args = {
689       .handle = gem_handle,
690       .flags = DRM_CLOEXEC | (mappable ? DRM_RDWR : 0),
691    };
692 
693    const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args);
694    return ret ? -1 : args.fd;
695 }
696 
697 static uint32_t
virtgpu_ioctl_prime_fd_to_handle(struct virtgpu * gpu,int fd)698 virtgpu_ioctl_prime_fd_to_handle(struct virtgpu *gpu, int fd)
699 {
700    struct drm_prime_handle args = {
701       .fd = fd,
702    };
703 
704    const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_PRIME_FD_TO_HANDLE, &args);
705    return ret ? 0 : args.handle;
706 }
707 
708 static void *
virtgpu_ioctl_map(struct virtgpu * gpu,uint32_t gem_handle,size_t size)709 virtgpu_ioctl_map(struct virtgpu *gpu, uint32_t gem_handle, size_t size)
710 {
711    struct drm_virtgpu_map args = {
712       .handle = gem_handle,
713    };
714 
715    if (virtgpu_ioctl(gpu, DRM_IOCTL_VIRTGPU_MAP, &args))
716       return NULL;
717 
718    void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, gpu->fd,
719                     args.offset);
720    if (ptr == MAP_FAILED)
721       return NULL;
722 
723    return ptr;
724 }
725 
726 static uint32_t
virtgpu_ioctl_syncobj_create(struct virtgpu * gpu,bool signaled)727 virtgpu_ioctl_syncobj_create(struct virtgpu *gpu, bool signaled)
728 {
729 #ifdef SIMULATE_SYNCOBJ
730    return sim_syncobj_create(gpu, signaled);
731 #endif
732 
733    struct drm_syncobj_create args = {
734       .flags = signaled ? DRM_SYNCOBJ_CREATE_SIGNALED : 0,
735    };
736 
737    const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_CREATE, &args);
738    return ret ? 0 : args.handle;
739 }
740 
741 static void
virtgpu_ioctl_syncobj_destroy(struct virtgpu * gpu,uint32_t syncobj_handle)742 virtgpu_ioctl_syncobj_destroy(struct virtgpu *gpu, uint32_t syncobj_handle)
743 {
744 #ifdef SIMULATE_SYNCOBJ
745    sim_syncobj_destroy(gpu, syncobj_handle);
746    return;
747 #endif
748 
749    struct drm_syncobj_destroy args = {
750       .handle = syncobj_handle,
751    };
752 
753    ASSERTED const int ret =
754       virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_DESTROY, &args);
755    assert(!ret);
756 }
757 
758 static int
virtgpu_ioctl_syncobj_handle_to_fd(struct virtgpu * gpu,uint32_t syncobj_handle,bool sync_file)759 virtgpu_ioctl_syncobj_handle_to_fd(struct virtgpu *gpu,
760                                    uint32_t syncobj_handle,
761                                    bool sync_file)
762 {
763 #ifdef SIMULATE_SYNCOBJ
764    return sync_file ? sim_syncobj_export(gpu, syncobj_handle) : -1;
765 #endif
766 
767    struct drm_syncobj_handle args = {
768       .handle = syncobj_handle,
769       .flags =
770          sync_file ? DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE : 0,
771    };
772 
773    int ret = virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &args);
774    if (ret)
775       return -1;
776 
777    return args.fd;
778 }
779 
780 static uint32_t
virtgpu_ioctl_syncobj_fd_to_handle(struct virtgpu * gpu,int fd,uint32_t syncobj_handle)781 virtgpu_ioctl_syncobj_fd_to_handle(struct virtgpu *gpu,
782                                    int fd,
783                                    uint32_t syncobj_handle)
784 {
785 #ifdef SIMULATE_SYNCOBJ
786    return syncobj_handle ? sim_syncobj_import(gpu, syncobj_handle, fd) : 0;
787 #endif
788 
789    struct drm_syncobj_handle args = {
790       .handle = syncobj_handle,
791       .flags =
792          syncobj_handle ? DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE : 0,
793       .fd = fd,
794    };
795 
796    int ret = virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &args);
797    if (ret)
798       return 0;
799 
800    return args.handle;
801 }
802 
803 static int
virtgpu_ioctl_syncobj_reset(struct virtgpu * gpu,uint32_t syncobj_handle)804 virtgpu_ioctl_syncobj_reset(struct virtgpu *gpu, uint32_t syncobj_handle)
805 {
806 #ifdef SIMULATE_SYNCOBJ
807    return sim_syncobj_reset(gpu, syncobj_handle);
808 #endif
809 
810    struct drm_syncobj_array args = {
811       .handles = (uintptr_t)&syncobj_handle,
812       .count_handles = 1,
813    };
814 
815    return virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_RESET, &args);
816 }
817 
818 static int
virtgpu_ioctl_syncobj_query(struct virtgpu * gpu,uint32_t syncobj_handle,uint64_t * point)819 virtgpu_ioctl_syncobj_query(struct virtgpu *gpu,
820                             uint32_t syncobj_handle,
821                             uint64_t *point)
822 {
823 #ifdef SIMULATE_SYNCOBJ
824    return sim_syncobj_query(gpu, syncobj_handle, point);
825 #endif
826 
827    struct drm_syncobj_timeline_array args = {
828       .handles = (uintptr_t)&syncobj_handle,
829       .points = (uintptr_t)point,
830       .count_handles = 1,
831    };
832 
833    return virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_QUERY, &args);
834 }
835 
836 static int
virtgpu_ioctl_syncobj_timeline_signal(struct virtgpu * gpu,uint32_t syncobj_handle,uint64_t point)837 virtgpu_ioctl_syncobj_timeline_signal(struct virtgpu *gpu,
838                                       uint32_t syncobj_handle,
839                                       uint64_t point)
840 {
841 #ifdef SIMULATE_SYNCOBJ
842    return sim_syncobj_signal(gpu, syncobj_handle, point);
843 #endif
844 
845    struct drm_syncobj_timeline_array args = {
846       .handles = (uintptr_t)&syncobj_handle,
847       .points = (uintptr_t)&point,
848       .count_handles = 1,
849    };
850 
851    return virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
852 }
853 
854 static int
virtgpu_ioctl_syncobj_timeline_wait(struct virtgpu * gpu,const struct vn_renderer_wait * wait,bool wait_avail)855 virtgpu_ioctl_syncobj_timeline_wait(struct virtgpu *gpu,
856                                     const struct vn_renderer_wait *wait,
857                                     bool wait_avail)
858 {
859 #ifdef SIMULATE_SYNCOBJ
860    return sim_syncobj_wait(gpu, wait, wait_avail);
861 #endif
862 
863    /* always enable wait-before-submit */
864    uint32_t flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
865    if (!wait->wait_any)
866       flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL;
867    /* wait for fences to appear instead of signaling */
868    if (wait_avail)
869       flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE;
870 
871    /* TODO replace wait->syncs by wait->sync_handles to avoid malloc/loop */
872    uint32_t *syncobj_handles =
873       malloc(sizeof(*syncobj_handles) * wait->sync_count);
874    if (!syncobj_handles)
875       return -1;
876    for (uint32_t i = 0; i < wait->sync_count; i++) {
877       struct virtgpu_sync *sync = (struct virtgpu_sync *)wait->syncs[i];
878       syncobj_handles[i] = sync->syncobj_handle;
879    }
880 
881    struct drm_syncobj_timeline_wait args = {
882       .handles = (uintptr_t)syncobj_handles,
883       .points = (uintptr_t)wait->sync_values,
884       .timeout_nsec = os_time_get_absolute_timeout(wait->timeout),
885       .count_handles = wait->sync_count,
886       .flags = flags,
887    };
888 
889    const int ret = virtgpu_ioctl(gpu, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &args);
890 
891    free(syncobj_handles);
892 
893    return ret;
894 }
895 
896 static int
virtgpu_ioctl_submit(struct virtgpu * gpu,const struct vn_renderer_submit * submit)897 virtgpu_ioctl_submit(struct virtgpu *gpu,
898                      const struct vn_renderer_submit *submit)
899 {
900 #ifdef SIMULATE_SUBMIT
901    return sim_submit(gpu, submit);
902 #endif
903    return -1;
904 }
905 
906 static VkResult
virtgpu_sync_write(struct vn_renderer * renderer,struct vn_renderer_sync * _sync,uint64_t val)907 virtgpu_sync_write(struct vn_renderer *renderer,
908                    struct vn_renderer_sync *_sync,
909                    uint64_t val)
910 {
911    struct virtgpu *gpu = (struct virtgpu *)renderer;
912    struct virtgpu_sync *sync = (struct virtgpu_sync *)_sync;
913 
914    const int ret =
915       virtgpu_ioctl_syncobj_timeline_signal(gpu, sync->syncobj_handle, val);
916 
917    return ret ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS;
918 }
919 
920 static VkResult
virtgpu_sync_read(struct vn_renderer * renderer,struct vn_renderer_sync * _sync,uint64_t * val)921 virtgpu_sync_read(struct vn_renderer *renderer,
922                   struct vn_renderer_sync *_sync,
923                   uint64_t *val)
924 {
925    struct virtgpu *gpu = (struct virtgpu *)renderer;
926    struct virtgpu_sync *sync = (struct virtgpu_sync *)_sync;
927 
928    const int ret =
929       virtgpu_ioctl_syncobj_query(gpu, sync->syncobj_handle, val);
930 
931    return ret ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS;
932 }
933 
934 static VkResult
virtgpu_sync_reset(struct vn_renderer * renderer,struct vn_renderer_sync * _sync,uint64_t initial_val)935 virtgpu_sync_reset(struct vn_renderer *renderer,
936                    struct vn_renderer_sync *_sync,
937                    uint64_t initial_val)
938 {
939    struct virtgpu *gpu = (struct virtgpu *)renderer;
940    struct virtgpu_sync *sync = (struct virtgpu_sync *)_sync;
941 
942    int ret = virtgpu_ioctl_syncobj_reset(gpu, sync->syncobj_handle);
943    if (!ret) {
944       ret = virtgpu_ioctl_syncobj_timeline_signal(gpu, sync->syncobj_handle,
945                                                   initial_val);
946    }
947 
948    return ret ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS;
949 }
950 
951 static int
virtgpu_sync_export_syncobj(struct vn_renderer * renderer,struct vn_renderer_sync * _sync,bool sync_file)952 virtgpu_sync_export_syncobj(struct vn_renderer *renderer,
953                             struct vn_renderer_sync *_sync,
954                             bool sync_file)
955 {
956    struct virtgpu *gpu = (struct virtgpu *)renderer;
957    struct virtgpu_sync *sync = (struct virtgpu_sync *)_sync;
958 
959    return virtgpu_ioctl_syncobj_handle_to_fd(gpu, sync->syncobj_handle,
960                                              sync_file);
961 }
962 
963 static void
virtgpu_sync_destroy(struct vn_renderer * renderer,struct vn_renderer_sync * _sync)964 virtgpu_sync_destroy(struct vn_renderer *renderer,
965                      struct vn_renderer_sync *_sync)
966 {
967    struct virtgpu *gpu = (struct virtgpu *)renderer;
968    struct virtgpu_sync *sync = (struct virtgpu_sync *)_sync;
969 
970    virtgpu_ioctl_syncobj_destroy(gpu, sync->syncobj_handle);
971 
972    free(sync);
973 }
974 
975 static VkResult
virtgpu_sync_create_from_syncobj(struct vn_renderer * renderer,int fd,bool sync_file,struct vn_renderer_sync ** out_sync)976 virtgpu_sync_create_from_syncobj(struct vn_renderer *renderer,
977                                  int fd,
978                                  bool sync_file,
979                                  struct vn_renderer_sync **out_sync)
980 {
981    struct virtgpu *gpu = (struct virtgpu *)renderer;
982 
983    uint32_t syncobj_handle;
984    if (sync_file) {
985       syncobj_handle = virtgpu_ioctl_syncobj_create(gpu, false);
986       if (!syncobj_handle)
987          return VK_ERROR_OUT_OF_HOST_MEMORY;
988       if (!virtgpu_ioctl_syncobj_fd_to_handle(gpu, fd, syncobj_handle)) {
989          virtgpu_ioctl_syncobj_destroy(gpu, syncobj_handle);
990          return VK_ERROR_INVALID_EXTERNAL_HANDLE;
991       }
992    } else {
993       syncobj_handle = virtgpu_ioctl_syncobj_fd_to_handle(gpu, fd, 0);
994       if (!syncobj_handle)
995          return VK_ERROR_INVALID_EXTERNAL_HANDLE;
996    }
997 
998    struct virtgpu_sync *sync = calloc(1, sizeof(*sync));
999    if (!sync) {
1000       virtgpu_ioctl_syncobj_destroy(gpu, syncobj_handle);
1001       return VK_ERROR_OUT_OF_HOST_MEMORY;
1002    }
1003 
1004    sync->syncobj_handle = syncobj_handle;
1005    sync->base.sync_id = 0; /* TODO */
1006 
1007    *out_sync = &sync->base;
1008 
1009    return VK_SUCCESS;
1010 }
1011 
1012 static VkResult
virtgpu_sync_create(struct vn_renderer * renderer,uint64_t initial_val,uint32_t flags,struct vn_renderer_sync ** out_sync)1013 virtgpu_sync_create(struct vn_renderer *renderer,
1014                     uint64_t initial_val,
1015                     uint32_t flags,
1016                     struct vn_renderer_sync **out_sync)
1017 {
1018    struct virtgpu *gpu = (struct virtgpu *)renderer;
1019 
1020    /* TODO */
1021    if (flags & VN_RENDERER_SYNC_SHAREABLE)
1022       return VK_ERROR_OUT_OF_DEVICE_MEMORY;
1023 
1024    /* always false because we don't use binary drm_syncobjs */
1025    const bool signaled = false;
1026    const uint32_t syncobj_handle =
1027       virtgpu_ioctl_syncobj_create(gpu, signaled);
1028    if (!syncobj_handle)
1029       return VK_ERROR_OUT_OF_DEVICE_MEMORY;
1030 
1031    /* add a signaled fence chain with seqno initial_val */
1032    const int ret =
1033       virtgpu_ioctl_syncobj_timeline_signal(gpu, syncobj_handle, initial_val);
1034    if (ret) {
1035       virtgpu_ioctl_syncobj_destroy(gpu, syncobj_handle);
1036       return VK_ERROR_OUT_OF_DEVICE_MEMORY;
1037    }
1038 
1039    struct virtgpu_sync *sync = calloc(1, sizeof(*sync));
1040    if (!sync) {
1041       virtgpu_ioctl_syncobj_destroy(gpu, syncobj_handle);
1042       return VK_ERROR_OUT_OF_HOST_MEMORY;
1043    }
1044 
1045    sync->syncobj_handle = syncobj_handle;
1046    /* we will have a sync_id when shareable is true and virtio-gpu associates
1047     * a host sync object with guest drm_syncobj
1048     */
1049    sync->base.sync_id = 0;
1050 
1051    *out_sync = &sync->base;
1052 
1053    return VK_SUCCESS;
1054 }
1055 
1056 static void
virtgpu_bo_invalidate(struct vn_renderer * renderer,struct vn_renderer_bo * bo,VkDeviceSize offset,VkDeviceSize size)1057 virtgpu_bo_invalidate(struct vn_renderer *renderer,
1058                       struct vn_renderer_bo *bo,
1059                       VkDeviceSize offset,
1060                       VkDeviceSize size)
1061 {
1062    /* nop because kernel makes every mapping coherent */
1063 }
1064 
1065 static void
virtgpu_bo_flush(struct vn_renderer * renderer,struct vn_renderer_bo * bo,VkDeviceSize offset,VkDeviceSize size)1066 virtgpu_bo_flush(struct vn_renderer *renderer,
1067                  struct vn_renderer_bo *bo,
1068                  VkDeviceSize offset,
1069                  VkDeviceSize size)
1070 {
1071    /* nop because kernel makes every mapping coherent */
1072 }
1073 
1074 static void *
virtgpu_bo_map(struct vn_renderer * renderer,struct vn_renderer_bo * _bo)1075 virtgpu_bo_map(struct vn_renderer *renderer, struct vn_renderer_bo *_bo)
1076 {
1077    struct virtgpu *gpu = (struct virtgpu *)renderer;
1078    struct virtgpu_bo *bo = (struct virtgpu_bo *)_bo;
1079    const bool mappable = bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE;
1080 
1081    /* not thread-safe but is fine */
1082    if (!bo->base.mmap_ptr && mappable) {
1083       bo->base.mmap_ptr =
1084          virtgpu_ioctl_map(gpu, bo->gem_handle, bo->base.mmap_size);
1085    }
1086 
1087    return bo->base.mmap_ptr;
1088 }
1089 
1090 static int
virtgpu_bo_export_dma_buf(struct vn_renderer * renderer,struct vn_renderer_bo * _bo)1091 virtgpu_bo_export_dma_buf(struct vn_renderer *renderer,
1092                           struct vn_renderer_bo *_bo)
1093 {
1094    struct virtgpu *gpu = (struct virtgpu *)renderer;
1095    struct virtgpu_bo *bo = (struct virtgpu_bo *)_bo;
1096    const bool mappable = bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE;
1097    const bool shareable = bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_SHAREABLE;
1098 
1099    return shareable
1100              ? virtgpu_ioctl_prime_handle_to_fd(gpu, bo->gem_handle, mappable)
1101              : -1;
1102 }
1103 
1104 static bool
virtgpu_bo_destroy(struct vn_renderer * renderer,struct vn_renderer_bo * _bo)1105 virtgpu_bo_destroy(struct vn_renderer *renderer, struct vn_renderer_bo *_bo)
1106 {
1107    struct virtgpu *gpu = (struct virtgpu *)renderer;
1108    struct virtgpu_bo *bo = (struct virtgpu_bo *)_bo;
1109 
1110    mtx_lock(&gpu->dma_buf_import_mutex);
1111 
1112    /* Check the refcount again after the import lock is grabbed.  Yes, we use
1113     * the double-checked locking anti-pattern.
1114     */
1115    if (vn_refcount_is_valid(&bo->base.refcount)) {
1116       mtx_unlock(&gpu->dma_buf_import_mutex);
1117       return false;
1118    }
1119 
1120    if (bo->base.mmap_ptr)
1121       munmap(bo->base.mmap_ptr, bo->base.mmap_size);
1122    virtgpu_ioctl_gem_close(gpu, bo->gem_handle);
1123 
1124    /* set gem_handle to 0 to indicate that the bo is invalid */
1125    bo->gem_handle = 0;
1126 
1127    mtx_unlock(&gpu->dma_buf_import_mutex);
1128 
1129    return true;
1130 }
1131 
1132 static uint32_t
virtgpu_bo_blob_flags(VkMemoryPropertyFlags flags,VkExternalMemoryHandleTypeFlags external_handles)1133 virtgpu_bo_blob_flags(VkMemoryPropertyFlags flags,
1134                       VkExternalMemoryHandleTypeFlags external_handles)
1135 {
1136    uint32_t blob_flags = 0;
1137    if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
1138       blob_flags |= VIRTGPU_BLOB_FLAG_USE_MAPPABLE;
1139    if (external_handles)
1140       blob_flags |= VIRTGPU_BLOB_FLAG_USE_SHAREABLE;
1141    if (external_handles & VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT)
1142       blob_flags |= VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE;
1143 
1144    return blob_flags;
1145 }
1146 
1147 static VkResult
virtgpu_bo_create_from_dma_buf(struct vn_renderer * renderer,VkDeviceSize size,int fd,VkMemoryPropertyFlags flags,struct vn_renderer_bo ** out_bo)1148 virtgpu_bo_create_from_dma_buf(struct vn_renderer *renderer,
1149                                VkDeviceSize size,
1150                                int fd,
1151                                VkMemoryPropertyFlags flags,
1152                                struct vn_renderer_bo **out_bo)
1153 {
1154    struct virtgpu *gpu = (struct virtgpu *)renderer;
1155    struct drm_virtgpu_resource_info info;
1156    uint32_t gem_handle = 0;
1157    struct virtgpu_bo *bo = NULL;
1158 
1159    mtx_lock(&gpu->dma_buf_import_mutex);
1160 
1161    gem_handle = virtgpu_ioctl_prime_fd_to_handle(gpu, fd);
1162    if (!gem_handle)
1163       goto fail;
1164    bo = util_sparse_array_get(&gpu->bo_array, gem_handle);
1165 
1166    if (virtgpu_ioctl_resource_info(gpu, gem_handle, &info))
1167       goto fail;
1168 
1169    uint32_t blob_flags;
1170    size_t mmap_size;
1171    if (info.blob_mem) {
1172       /* must be VIRTGPU_BLOB_MEM_HOST3D */
1173       if (info.blob_mem != VIRTGPU_BLOB_MEM_HOST3D)
1174          goto fail;
1175 
1176       /* blob_flags is not passed to the kernel and is only for internal use
1177        * on imports.  Set it to what works best for us.
1178        */
1179       blob_flags = virtgpu_bo_blob_flags(flags, 0);
1180       blob_flags |= VIRTGPU_BLOB_FLAG_USE_SHAREABLE;
1181 
1182       /* mmap_size is only used when mappable */
1183       mmap_size = 0;
1184       if (blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE) {
1185          if (info.size < size)
1186             goto fail;
1187 
1188          mmap_size = size;
1189       }
1190    } else {
1191       /* must be classic resource here
1192        * set blob_flags to 0 to fail virtgpu_bo_map
1193        * set mmap_size to 0 since mapping is not allowed
1194        */
1195       blob_flags = 0;
1196       mmap_size = 0;
1197    }
1198 
1199    /* we check bo->gem_handle instead of bo->refcount because bo->refcount
1200     * might only be memset to 0 and is not considered initialized in theory
1201     */
1202    if (bo->gem_handle == gem_handle) {
1203       if (bo->base.mmap_size < mmap_size)
1204          goto fail;
1205       if (blob_flags & ~bo->blob_flags)
1206          goto fail;
1207 
1208       /* we can't use vn_renderer_bo_ref as the refcount may drop to 0
1209        * temporarily before virtgpu_bo_destroy grabs the lock
1210        */
1211       vn_refcount_fetch_add_relaxed(&bo->base.refcount, 1);
1212    } else {
1213       *bo = (struct virtgpu_bo){
1214          .base = {
1215             .refcount = VN_REFCOUNT_INIT(1),
1216             .res_id = info.res_handle,
1217             .mmap_size = mmap_size,
1218          },
1219          .gem_handle = gem_handle,
1220          .blob_flags = blob_flags,
1221       };
1222    }
1223 
1224    mtx_unlock(&gpu->dma_buf_import_mutex);
1225 
1226    *out_bo = &bo->base;
1227 
1228    return VK_SUCCESS;
1229 
1230 fail:
1231    if (gem_handle && bo->gem_handle != gem_handle)
1232       virtgpu_ioctl_gem_close(gpu, gem_handle);
1233    mtx_unlock(&gpu->dma_buf_import_mutex);
1234    return VK_ERROR_INVALID_EXTERNAL_HANDLE;
1235 }
1236 
1237 static VkResult
virtgpu_bo_create_from_device_memory(struct vn_renderer * renderer,VkDeviceSize size,vn_object_id mem_id,VkMemoryPropertyFlags flags,VkExternalMemoryHandleTypeFlags external_handles,struct vn_renderer_bo ** out_bo)1238 virtgpu_bo_create_from_device_memory(
1239    struct vn_renderer *renderer,
1240    VkDeviceSize size,
1241    vn_object_id mem_id,
1242    VkMemoryPropertyFlags flags,
1243    VkExternalMemoryHandleTypeFlags external_handles,
1244    struct vn_renderer_bo **out_bo)
1245 {
1246    struct virtgpu *gpu = (struct virtgpu *)renderer;
1247    const uint32_t blob_flags = virtgpu_bo_blob_flags(flags, external_handles);
1248 
1249    uint32_t res_id;
1250    uint32_t gem_handle = virtgpu_ioctl_resource_create_blob(
1251       gpu, VIRTGPU_BLOB_MEM_HOST3D, blob_flags, size, mem_id, &res_id);
1252    if (!gem_handle)
1253       return VK_ERROR_OUT_OF_DEVICE_MEMORY;
1254 
1255    struct virtgpu_bo *bo = util_sparse_array_get(&gpu->bo_array, gem_handle);
1256    *bo = (struct virtgpu_bo){
1257       .base = {
1258          .refcount = VN_REFCOUNT_INIT(1),
1259          .res_id = res_id,
1260          .mmap_size = size,
1261       },
1262       .gem_handle = gem_handle,
1263       .blob_flags = blob_flags,
1264    };
1265 
1266    *out_bo = &bo->base;
1267 
1268    return VK_SUCCESS;
1269 }
1270 
1271 static void
virtgpu_shmem_destroy(struct vn_renderer * renderer,struct vn_renderer_shmem * _shmem)1272 virtgpu_shmem_destroy(struct vn_renderer *renderer,
1273                       struct vn_renderer_shmem *_shmem)
1274 {
1275    struct virtgpu *gpu = (struct virtgpu *)renderer;
1276    struct virtgpu_shmem *shmem = (struct virtgpu_shmem *)_shmem;
1277 
1278    munmap(shmem->base.mmap_ptr, shmem->base.mmap_size);
1279    virtgpu_ioctl_gem_close(gpu, shmem->gem_handle);
1280 }
1281 
1282 static struct vn_renderer_shmem *
virtgpu_shmem_create(struct vn_renderer * renderer,size_t size)1283 virtgpu_shmem_create(struct vn_renderer *renderer, size_t size)
1284 {
1285    struct virtgpu *gpu = (struct virtgpu *)renderer;
1286 
1287    uint32_t res_id;
1288    uint32_t gem_handle = virtgpu_ioctl_resource_create_blob(
1289       gpu, VIRTGPU_BLOB_MEM_GUEST, VIRTGPU_BLOB_FLAG_USE_MAPPABLE, size, 0,
1290       &res_id);
1291    if (!gem_handle)
1292       return NULL;
1293 
1294    void *ptr = virtgpu_ioctl_map(gpu, gem_handle, size);
1295    if (!ptr) {
1296       virtgpu_ioctl_gem_close(gpu, gem_handle);
1297       return NULL;
1298    }
1299 
1300    struct virtgpu_shmem *shmem =
1301       util_sparse_array_get(&gpu->shmem_array, gem_handle);
1302    *shmem = (struct virtgpu_shmem){
1303       .base = {
1304          .refcount = VN_REFCOUNT_INIT(1),
1305          .res_id = res_id,
1306          .mmap_size = size,
1307          .mmap_ptr = ptr,
1308       },
1309       .gem_handle = gem_handle,
1310    };
1311 
1312    return &shmem->base;
1313 }
1314 
1315 static VkResult
virtgpu_wait(struct vn_renderer * renderer,const struct vn_renderer_wait * wait)1316 virtgpu_wait(struct vn_renderer *renderer,
1317              const struct vn_renderer_wait *wait)
1318 {
1319    struct virtgpu *gpu = (struct virtgpu *)renderer;
1320 
1321    const int ret = virtgpu_ioctl_syncobj_timeline_wait(gpu, wait, false);
1322    if (ret && errno != ETIME)
1323       return VK_ERROR_DEVICE_LOST;
1324 
1325    return ret ? VK_TIMEOUT : VK_SUCCESS;
1326 }
1327 
1328 static VkResult
virtgpu_submit(struct vn_renderer * renderer,const struct vn_renderer_submit * submit)1329 virtgpu_submit(struct vn_renderer *renderer,
1330                const struct vn_renderer_submit *submit)
1331 {
1332    struct virtgpu *gpu = (struct virtgpu *)renderer;
1333 
1334    const int ret = virtgpu_ioctl_submit(gpu, submit);
1335    return ret ? VK_ERROR_DEVICE_LOST : VK_SUCCESS;
1336 }
1337 
1338 static void
virtgpu_get_info(struct vn_renderer * renderer,struct vn_renderer_info * info)1339 virtgpu_get_info(struct vn_renderer *renderer, struct vn_renderer_info *info)
1340 {
1341    struct virtgpu *gpu = (struct virtgpu *)renderer;
1342 
1343    memset(info, 0, sizeof(*info));
1344 
1345    info->pci.vendor_id = VIRTGPU_PCI_VENDOR_ID;
1346    info->pci.device_id = VIRTGPU_PCI_DEVICE_ID;
1347 
1348    info->pci.has_bus_info = true;
1349    info->pci.domain = gpu->bus_info.domain;
1350    info->pci.bus = gpu->bus_info.bus;
1351    info->pci.device = gpu->bus_info.dev;
1352    info->pci.function = gpu->bus_info.func;
1353 
1354    info->has_dma_buf_import = true;
1355    /* Kernel makes every mapping coherent.  We are better off filtering
1356     * incoherent memory types out than silently making them coherent.
1357     */
1358    info->has_cache_management = false;
1359    /* TODO drm_syncobj */
1360    info->has_external_sync = false;
1361 
1362    info->has_implicit_fencing = false;
1363 
1364    info->max_sync_queue_count = gpu->max_sync_queue_count;
1365 
1366    const struct virgl_renderer_capset_venus *capset = &gpu->capset.data;
1367    info->wire_format_version = capset->wire_format_version;
1368    info->vk_xml_version = capset->vk_xml_version;
1369    info->vk_ext_command_serialization_spec_version =
1370       capset->vk_ext_command_serialization_spec_version;
1371    info->vk_mesa_venus_protocol_spec_version =
1372       capset->vk_mesa_venus_protocol_spec_version;
1373 }
1374 
1375 static void
virtgpu_destroy(struct vn_renderer * renderer,const VkAllocationCallbacks * alloc)1376 virtgpu_destroy(struct vn_renderer *renderer,
1377                 const VkAllocationCallbacks *alloc)
1378 {
1379    struct virtgpu *gpu = (struct virtgpu *)renderer;
1380 
1381    if (gpu->fd >= 0)
1382       close(gpu->fd);
1383 
1384    mtx_destroy(&gpu->dma_buf_import_mutex);
1385 
1386    util_sparse_array_finish(&gpu->shmem_array);
1387    util_sparse_array_finish(&gpu->bo_array);
1388 
1389    vk_free(alloc, gpu);
1390 }
1391 
1392 static VkResult
virtgpu_init_context(struct virtgpu * gpu)1393 virtgpu_init_context(struct virtgpu *gpu)
1394 {
1395    assert(!gpu->capset.version);
1396    const int ret = virtgpu_ioctl_context_init(gpu, gpu->capset.id);
1397    if (ret) {
1398       if (VN_DEBUG(INIT)) {
1399          vn_log(gpu->instance, "failed to initialize context: %s",
1400                 strerror(errno));
1401       }
1402       return VK_ERROR_INITIALIZATION_FAILED;
1403    }
1404 
1405    return VK_SUCCESS;
1406 }
1407 
1408 static VkResult
virtgpu_init_capset(struct virtgpu * gpu)1409 virtgpu_init_capset(struct virtgpu *gpu)
1410 {
1411    gpu->capset.id = VIRGL_RENDERER_CAPSET_VENUS;
1412    gpu->capset.version = 0;
1413 
1414    const int ret =
1415       virtgpu_ioctl_get_caps(gpu, gpu->capset.id, gpu->capset.version,
1416                              &gpu->capset.data, sizeof(gpu->capset.data));
1417    if (ret) {
1418       if (VN_DEBUG(INIT)) {
1419          vn_log(gpu->instance, "failed to get venus v%d capset: %s",
1420                 gpu->capset.version, strerror(errno));
1421       }
1422       return VK_ERROR_INITIALIZATION_FAILED;
1423    }
1424 
1425    return VK_SUCCESS;
1426 }
1427 
1428 static VkResult
virtgpu_init_params(struct virtgpu * gpu)1429 virtgpu_init_params(struct virtgpu *gpu)
1430 {
1431    const uint64_t required_params[] = {
1432       VIRTGPU_PARAM_3D_FEATURES,   VIRTGPU_PARAM_CAPSET_QUERY_FIX,
1433       VIRTGPU_PARAM_RESOURCE_BLOB, VIRTGPU_PARAM_HOST_VISIBLE,
1434       VIRTGPU_PARAM_CROSS_DEVICE,  VIRTGPU_PARAM_CONTEXT_INIT,
1435    };
1436    uint64_t val;
1437    for (uint32_t i = 0; i < ARRAY_SIZE(required_params); i++) {
1438       val = virtgpu_ioctl_getparam(gpu, required_params[i]);
1439       if (!val) {
1440          if (VN_DEBUG(INIT)) {
1441             vn_log(gpu->instance, "required kernel param %d is missing",
1442                    (int)required_params[i]);
1443          }
1444          return VK_ERROR_INITIALIZATION_FAILED;
1445       }
1446    }
1447 
1448    val = virtgpu_ioctl_getparam(gpu, VIRTGPU_PARAM_MAX_SYNC_QUEUE_COUNT);
1449    if (!val) {
1450       if (VN_DEBUG(INIT))
1451          vn_log(gpu->instance, "no sync queue support");
1452       return VK_ERROR_INITIALIZATION_FAILED;
1453    }
1454    gpu->max_sync_queue_count = val;
1455 
1456    return VK_SUCCESS;
1457 }
1458 
1459 static VkResult
virtgpu_open_device(struct virtgpu * gpu,const drmDevicePtr dev)1460 virtgpu_open_device(struct virtgpu *gpu, const drmDevicePtr dev)
1461 {
1462    /* skip unless the device has our PCI vendor/device id and a render node */
1463    if (!(dev->available_nodes & (1 << DRM_NODE_RENDER)) ||
1464        dev->bustype != DRM_BUS_PCI ||
1465        dev->deviceinfo.pci->vendor_id != VIRTGPU_PCI_VENDOR_ID ||
1466        dev->deviceinfo.pci->device_id != VIRTGPU_PCI_DEVICE_ID) {
1467       if (VN_DEBUG(INIT)) {
1468          const char *name = "unknown";
1469          for (uint32_t i = 0; i < DRM_NODE_MAX; i++) {
1470             if (dev->available_nodes & (1 << i)) {
1471                name = dev->nodes[i];
1472                break;
1473             }
1474          }
1475          vn_log(gpu->instance, "skipping DRM device %s", name);
1476       }
1477       return VK_ERROR_INITIALIZATION_FAILED;
1478    }
1479 
1480    const char *node_path = dev->nodes[DRM_NODE_RENDER];
1481 
1482    int fd = open(node_path, O_RDWR | O_CLOEXEC);
1483    if (fd < 0) {
1484       if (VN_DEBUG(INIT))
1485          vn_log(gpu->instance, "failed to open %s", node_path);
1486       return VK_ERROR_INITIALIZATION_FAILED;
1487    }
1488 
1489    drmVersionPtr version = drmGetVersion(fd);
1490    if (!version || strcmp(version->name, "virtio_gpu") ||
1491        version->version_major != 0) {
1492       if (VN_DEBUG(INIT)) {
1493          if (version) {
1494             vn_log(gpu->instance, "unknown DRM driver %s version %d",
1495                    version->name, version->version_major);
1496          } else {
1497             vn_log(gpu->instance, "failed to get DRM driver version");
1498          }
1499       }
1500       if (version)
1501          drmFreeVersion(version);
1502       close(fd);
1503       return VK_ERROR_INITIALIZATION_FAILED;
1504    }
1505 
1506    gpu->fd = fd;
1507    gpu->version_minor = version->version_minor;
1508    gpu->bus_info = *dev->businfo.pci;
1509 
1510    drmFreeVersion(version);
1511 
1512    if (VN_DEBUG(INIT))
1513       vn_log(gpu->instance, "using DRM device %s", node_path);
1514 
1515    return VK_SUCCESS;
1516 }
1517 
1518 static VkResult
virtgpu_open(struct virtgpu * gpu)1519 virtgpu_open(struct virtgpu *gpu)
1520 {
1521    drmDevicePtr devs[8];
1522    int count = drmGetDevices2(0, devs, ARRAY_SIZE(devs));
1523    if (count < 0) {
1524       if (VN_DEBUG(INIT))
1525          vn_log(gpu->instance, "failed to enumerate DRM devices");
1526       return VK_ERROR_INITIALIZATION_FAILED;
1527    }
1528 
1529    VkResult result = VK_ERROR_INITIALIZATION_FAILED;
1530    for (int i = 0; i < count; i++) {
1531       result = virtgpu_open_device(gpu, devs[i]);
1532       if (result == VK_SUCCESS)
1533          break;
1534    }
1535 
1536    drmFreeDevices(devs, count);
1537 
1538    return result;
1539 }
1540 
1541 static VkResult
virtgpu_init(struct virtgpu * gpu)1542 virtgpu_init(struct virtgpu *gpu)
1543 {
1544    util_sparse_array_init(&gpu->shmem_array, sizeof(struct virtgpu_shmem),
1545                           1024);
1546    util_sparse_array_init(&gpu->bo_array, sizeof(struct virtgpu_bo), 1024);
1547 
1548    mtx_init(&gpu->dma_buf_import_mutex, mtx_plain);
1549 
1550    VkResult result = virtgpu_open(gpu);
1551    if (result == VK_SUCCESS)
1552       result = virtgpu_init_params(gpu);
1553    if (result == VK_SUCCESS)
1554       result = virtgpu_init_capset(gpu);
1555    if (result == VK_SUCCESS)
1556       result = virtgpu_init_context(gpu);
1557    if (result != VK_SUCCESS)
1558       return result;
1559 
1560    gpu->base.ops.destroy = virtgpu_destroy;
1561    gpu->base.ops.get_info = virtgpu_get_info;
1562    gpu->base.ops.submit = virtgpu_submit;
1563    gpu->base.ops.wait = virtgpu_wait;
1564 
1565    gpu->base.shmem_ops.create = virtgpu_shmem_create;
1566    gpu->base.shmem_ops.destroy = virtgpu_shmem_destroy;
1567 
1568    gpu->base.bo_ops.create_from_device_memory =
1569       virtgpu_bo_create_from_device_memory;
1570    gpu->base.bo_ops.create_from_dma_buf = virtgpu_bo_create_from_dma_buf;
1571    gpu->base.bo_ops.destroy = virtgpu_bo_destroy;
1572    gpu->base.bo_ops.export_dma_buf = virtgpu_bo_export_dma_buf;
1573    gpu->base.bo_ops.map = virtgpu_bo_map;
1574    gpu->base.bo_ops.flush = virtgpu_bo_flush;
1575    gpu->base.bo_ops.invalidate = virtgpu_bo_invalidate;
1576 
1577    gpu->base.sync_ops.create = virtgpu_sync_create;
1578    gpu->base.sync_ops.create_from_syncobj = virtgpu_sync_create_from_syncobj;
1579    gpu->base.sync_ops.destroy = virtgpu_sync_destroy;
1580    gpu->base.sync_ops.export_syncobj = virtgpu_sync_export_syncobj;
1581    gpu->base.sync_ops.reset = virtgpu_sync_reset;
1582    gpu->base.sync_ops.read = virtgpu_sync_read;
1583    gpu->base.sync_ops.write = virtgpu_sync_write;
1584 
1585    return VK_SUCCESS;
1586 }
1587 
1588 VkResult
vn_renderer_create_virtgpu(struct vn_instance * instance,const VkAllocationCallbacks * alloc,struct vn_renderer ** renderer)1589 vn_renderer_create_virtgpu(struct vn_instance *instance,
1590                            const VkAllocationCallbacks *alloc,
1591                            struct vn_renderer **renderer)
1592 {
1593    struct virtgpu *gpu = vk_zalloc(alloc, sizeof(*gpu), VN_DEFAULT_ALIGN,
1594                                    VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
1595    if (!gpu)
1596       return VK_ERROR_OUT_OF_HOST_MEMORY;
1597 
1598    gpu->instance = instance;
1599    gpu->fd = -1;
1600 
1601    VkResult result = virtgpu_init(gpu);
1602    if (result != VK_SUCCESS) {
1603       virtgpu_destroy(&gpu->base, alloc);
1604       return result;
1605    }
1606 
1607    *renderer = &gpu->base;
1608 
1609    return VK_SUCCESS;
1610 }
1611