1/* 2 * Copyright © 2022 Google, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24#include <assert.h> 25#include <inttypes.h> 26#include <pthread.h> 27 28#include "util/libsync.h" 29#include "util/os_file.h" 30 31#include "drm/freedreno_ringbuffer_sp.h" 32#include "virtio_priv.h" 33 34static void 35retire_execute(void *job, void *gdata, int thread_index) 36{ 37 struct fd_submit_sp *fd_submit = job; 38 39 sync_wait(fd_submit->out_fence_fd, -1); 40 close(fd_submit->out_fence_fd); 41} 42 43static void 44retire_cleanup(void *job, void *gdata, int thread_index) 45{ 46 struct fd_submit_sp *fd_submit = job; 47 fd_submit_del(&fd_submit->base); 48} 49 50static int 51flush_submit_list(struct list_head *submit_list) 52{ 53 struct fd_submit_sp *fd_submit = to_fd_submit_sp(last_submit(submit_list)); 54 struct virtio_pipe *virtio_pipe = to_virtio_pipe(fd_submit->base.pipe); 55 struct fd_device *dev = virtio_pipe->base.dev; 56 57 unsigned nr_cmds = 0; 58 59 /* Determine the number of extra cmds's from deferred submits that 60 * we will be merging in: 61 */ 62 foreach_submit (submit, submit_list) { 63 assert(submit->pipe == &virtio_pipe->base); 64 nr_cmds += to_fd_ringbuffer_sp(submit->primary)->u.nr_cmds; 65 } 66 67 /* TODO we can get rid of the extra copy into the req by just 68 * assuming the max amount that nr->bos will grow is by the 69 * nr_cmds, and just over-allocate a bit. 70 */ 71 72 struct drm_msm_gem_submit_cmd cmds[nr_cmds]; 73 74 unsigned cmd_idx = 0; 75 76 /* Build up the table of cmds, and for all but the last submit in the 77 * list, merge their bo tables into the last submit. 78 */ 79 foreach_submit_safe (submit, submit_list) { 80 struct fd_ringbuffer_sp *deferred_primary = 81 to_fd_ringbuffer_sp(submit->primary); 82 83 for (unsigned i = 0; i < deferred_primary->u.nr_cmds; i++) { 84 cmds[cmd_idx].type = MSM_SUBMIT_CMD_BUF; 85 cmds[cmd_idx].submit_idx = 86 fd_submit_append_bo(fd_submit, deferred_primary->u.cmds[i].ring_bo); 87 cmds[cmd_idx].submit_offset = deferred_primary->offset; 88 cmds[cmd_idx].size = deferred_primary->u.cmds[i].size; 89 cmds[cmd_idx].pad = 0; 90 cmds[cmd_idx].nr_relocs = 0; 91 92 cmd_idx++; 93 } 94 95 /* We are merging all the submits in the list into the last submit, 96 * so the remainder of the loop body doesn't apply to the last submit 97 */ 98 if (submit == last_submit(submit_list)) { 99 DEBUG_MSG("merged %u submits", cmd_idx); 100 break; 101 } 102 103 struct fd_submit_sp *fd_deferred_submit = to_fd_submit_sp(submit); 104 for (unsigned i = 0; i < fd_deferred_submit->nr_bos; i++) { 105 /* Note: if bo is used in both the current submit and the deferred 106 * submit being merged, we expect to hit the fast-path as we add it 107 * to the current submit: 108 */ 109 fd_submit_append_bo(fd_submit, fd_deferred_submit->bos[i]); 110 } 111 112 /* Now that the cmds/bos have been transfered over to the current submit, 113 * we can remove the deferred submit from the list and drop it's reference 114 */ 115 list_del(&submit->node); 116 fd_submit_del(submit); 117 } 118 119 /* Needs to be after get_cmd() as that could create bos/cmds table: 120 * 121 * NOTE allocate on-stack in the common case, but with an upper- 122 * bound to limit on-stack allocation to 4k: 123 */ 124 const unsigned bo_limit = 4096 / sizeof(struct drm_msm_gem_submit_bo); 125 bool bos_on_stack = fd_submit->nr_bos < bo_limit; 126 struct drm_msm_gem_submit_bo 127 _submit_bos[bos_on_stack ? fd_submit->nr_bos : 0]; 128 struct drm_msm_gem_submit_bo *submit_bos; 129 uint32_t _guest_handles[bos_on_stack ? fd_submit->nr_bos : 0]; 130 uint32_t *guest_handles; 131 if (bos_on_stack) { 132 submit_bos = _submit_bos; 133 guest_handles = _guest_handles; 134 } else { 135 submit_bos = malloc(fd_submit->nr_bos * sizeof(submit_bos[0])); 136 guest_handles = malloc(fd_submit->nr_bos * sizeof(guest_handles[0])); 137 } 138 139 for (unsigned i = 0; i < fd_submit->nr_bos; i++) { 140 struct virtio_bo *virtio_bo = to_virtio_bo(fd_submit->bos[i]); 141 142 guest_handles[i] = virtio_bo->base.handle; 143 144 submit_bos[i].flags = fd_submit->bos[i]->reloc_flags; 145 submit_bos[i].handle = virtio_bo->res_id; 146 submit_bos[i].presumed = 0; 147 } 148 149 if (virtio_pipe->next_submit_fence <= 0) 150 virtio_pipe->next_submit_fence = 1; 151 152 uint32_t kfence = virtio_pipe->next_submit_fence++; 153 154 /* TODO avoid extra memcpy, and populate bo's and cmds directly 155 * into the req msg 156 */ 157 unsigned bos_len = fd_submit->nr_bos * sizeof(struct drm_msm_gem_submit_bo); 158 unsigned cmd_len = nr_cmds * sizeof(struct drm_msm_gem_submit_cmd); 159 unsigned req_len = sizeof(struct msm_ccmd_gem_submit_req) + bos_len + cmd_len; 160 struct msm_ccmd_gem_submit_req *req = malloc(req_len); 161 162 req->hdr = MSM_CCMD(GEM_SUBMIT, req_len); 163 req->flags = virtio_pipe->pipe; 164 req->queue_id = virtio_pipe->queue_id; 165 req->nr_bos = fd_submit->nr_bos; 166 req->nr_cmds = nr_cmds; 167 req->fence = kfence; 168 169 memcpy(req->payload, submit_bos, bos_len); 170 memcpy(req->payload + bos_len, cmds, cmd_len); 171 172 struct fd_submit_fence *out_fence = fd_submit->out_fence; 173 int *out_fence_fd = NULL; 174 175 if (out_fence) { 176 out_fence->fence.kfence = kfence; 177 out_fence->fence.ufence = fd_submit->base.fence; 178 /* Even if gallium driver hasn't requested a fence-fd, request one. 179 * This way, if we have to block waiting for the fence, we can do 180 * it in the guest, rather than in the single-threaded host. 181 */ 182 out_fence->use_fence_fd = true; 183 out_fence_fd = &out_fence->fence_fd; 184 } else { 185 /* we are using retire_queue, so we need an out-fence for each 186 * submit.. we can just re-use fd_submit->out_fence_fd for temporary 187 * storage. 188 */ 189 out_fence_fd = &fd_submit->out_fence_fd; 190 } 191 192 if (fd_submit->in_fence_fd != -1) { 193 virtio_pipe->no_implicit_sync = true; 194 } 195 196 if (virtio_pipe->no_implicit_sync) { 197 req->flags |= MSM_SUBMIT_NO_IMPLICIT; 198 } 199 200 virtio_execbuf_fenced(dev, &req->hdr, guest_handles, req->nr_bos, 201 fd_submit->in_fence_fd, out_fence_fd, 202 virtio_pipe->ring_idx); 203 204 free(req); 205 206 if (!bos_on_stack) { 207 free(submit_bos); 208 free(guest_handles); 209 } 210 211 if (fd_submit->in_fence_fd != -1) 212 close(fd_submit->in_fence_fd); 213 214 if (out_fence_fd != &fd_submit->out_fence_fd) 215 fd_submit->out_fence_fd = os_dupfd_cloexec(*out_fence_fd); 216 217 fd_submit_ref(&fd_submit->base); 218 219 util_queue_fence_init(&fd_submit->retire_fence); 220 221 util_queue_add_job(&virtio_pipe->retire_queue, 222 fd_submit, &fd_submit->retire_fence, 223 retire_execute, 224 retire_cleanup, 225 0); 226 227 return 0; 228} 229 230struct fd_submit * 231virtio_submit_new(struct fd_pipe *pipe) 232{ 233 /* We don't do any translation from internal FD_RELOC flags to MSM flags. */ 234 STATIC_ASSERT(FD_RELOC_READ == MSM_SUBMIT_BO_READ); 235 STATIC_ASSERT(FD_RELOC_WRITE == MSM_SUBMIT_BO_WRITE); 236 STATIC_ASSERT(FD_RELOC_DUMP == MSM_SUBMIT_BO_DUMP); 237 238 return fd_submit_sp_new(pipe, flush_submit_list); 239} 240