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