| @@ -14,11 +14,24 @@ · 13 unmodified lines |
| 14 | #include <linux/uaccess.h> | 14 | #include <linux/uaccess.h> |
| 15 | | 15 | |
| 16 | #include <drm/drm_file.h> | 16 | #include <drm/drm_file.h> |
| | 17 | #include <drm/drm_syncobj.h> |
| 17 | #include <drm/virtgpu_drm.h> | 18 | #include <drm/virtgpu_drm.h> |
| 18 | | 19 | |
| 19 | #include "virtgpu_drv.h" | 20 | #include "virtgpu_drv.h" |
| 20 | | 21 | |
| | 22 | struct virtio_gpu_submit_post_dep { |
| | 23 | struct drm_syncobj *syncobj; |
| | 24 | struct dma_fence_chain *chain; |
| | 25 | u64 point; |
| | 26 | }; |
| | 27 | |
| 21 | struct virtio_gpu_submit { | 28 | struct virtio_gpu_submit { |
| | 29 | struct virtio_gpu_submit_post_dep *post_deps; |
| | 30 | unsigned int num_out_syncobjs; |
| | 31 | |
| | 32 | struct drm_syncobj **in_syncobjs; |
| | 33 | unsigned int num_in_syncobjs; |
| | 34 | |
| 22 | struct virtio_gpu_object_array *buflist; | 35 | struct virtio_gpu_object_array *buflist; |
| 23 | struct drm_virtgpu_execbuffer *exbuf; | 36 | struct drm_virtgpu_execbuffer *exbuf; |
| 24 | struct virtio_gpu_fence *out_fence; | 37 | struct virtio_gpu_fence *out_fence; |
| @@ -59,6 +72,203 @@ static int virtio_gpu_dma_fence_wait(struct virtio_gpu_submit *submit, · 34 unmodified lines |
| 59 | return 0; | 72 | return 0; |
| 60 | } | 73 | } |
| 61 | | 74 | |
| | 75 | static void virtio_gpu_free_syncobjs(struct drm_syncobj **syncobjs, |
| | 76 | u32 nr_syncobjs) |
| | 77 | { |
| | 78 | u32 i = nr_syncobjs; |
| | 79 | |
| | 80 | while (i--) { |
| | 81 | if (syncobjs[i]) |
| | 82 | drm_syncobj_put(syncobjs[i]); |
| | 83 | } |
| | 84 | |
| | 85 | kvfree(syncobjs); |
| | 86 | } |
| | 87 | |
| | 88 | static int |
| | 89 | virtio_gpu_parse_deps(struct virtio_gpu_submit *submit) |
| | 90 | { |
| | 91 | struct drm_virtgpu_execbuffer *exbuf = submit->exbuf; |
| | 92 | struct drm_virtgpu_execbuffer_syncobj syncobj_desc; |
| | 93 | size_t syncobj_stride = exbuf->syncobj_stride; |
| | 94 | u32 num_in_syncobjs = exbuf->num_in_syncobjs; |
| | 95 | struct drm_syncobj **syncobjs; |
| | 96 | int ret = 0, i; |
| | 97 | |
| | 98 | if (!num_in_syncobjs) |
| | 99 | return 0; |
| | 100 | |
| | 101 | /* |
| | 102 | * kvalloc at first tries to allocate memory using kmalloc and |
| | 103 | * falls back to vmalloc only on failure. It also uses __GFP_NOWARN |
| | 104 | * internally for allocations larger than a page size, preventing |
| | 105 | * storm of KMSG warnings. |
| | 106 | */ |
| | 107 | syncobjs = kvcalloc(num_in_syncobjs, sizeof(*syncobjs), GFP_KERNEL); |
| | 108 | if (!syncobjs) |
| | 109 | return -ENOMEM; |
| | 110 | |
| | 111 | for (i = 0; i < num_in_syncobjs; i++) { |
| | 112 | u64 address = exbuf->in_syncobjs + i * syncobj_stride; |
| | 113 | struct dma_fence *fence; |
| | 114 | |
| | 115 | memset(&syncobj_desc, 0, sizeof(syncobj_desc)); |
| | 116 | |
| | 117 | if (copy_from_user(&syncobj_desc, |
| | 118 | u64_to_user_ptr(address), |
| | 119 | min(syncobj_stride, sizeof(syncobj_desc)))) { |
| | 120 | ret = -EFAULT; |
| | 121 | break; |
| | 122 | } |
| | 123 | |
| | 124 | if (syncobj_desc.flags & ~VIRTGPU_EXECBUF_SYNCOBJ_FLAGS) { |
| | 125 | ret = -EINVAL; |
| | 126 | break; |
| | 127 | } |
| | 128 | |
| | 129 | ret = drm_syncobj_find_fence(submit->file, syncobj_desc.handle, |
| | 130 | syncobj_desc.point, 0, &fence); |
| | 131 | if (ret) |
| | 132 | break; |
| | 133 | |
| | 134 | ret = virtio_gpu_dma_fence_wait(submit, fence); |
| | 135 | |
| | 136 | dma_fence_put(fence); |
| | 137 | if (ret) |
| | 138 | break; |
| | 139 | |
| | 140 | if (syncobj_desc.flags & VIRTGPU_EXECBUF_SYNCOBJ_RESET) { |
| | 141 | syncobjs[i] = drm_syncobj_find(submit->file, |
| | 142 | syncobj_desc.handle); |
| | 143 | if (!syncobjs[i]) { |
| | 144 | ret = -EINVAL; |
| | 145 | break; |
| | 146 | } |
| | 147 | } |
| | 148 | } |
| | 149 | |
| | 150 | if (ret) { |
| | 151 | virtio_gpu_free_syncobjs(syncobjs, i); |
| | 152 | return ret; |
| | 153 | } |
| | 154 | |
| | 155 | submit->num_in_syncobjs = num_in_syncobjs; |
| | 156 | submit->in_syncobjs = syncobjs; |
| | 157 | |
| | 158 | return ret; |
| | 159 | } |
| | 160 | |
| | 161 | static void virtio_gpu_reset_syncobjs(struct drm_syncobj **syncobjs, |
| | 162 | u32 nr_syncobjs) |
| | 163 | { |
| | 164 | u32 i; |
| | 165 | |
| | 166 | for (i = 0; i < nr_syncobjs; i++) { |
| | 167 | if (syncobjs[i]) |
| | 168 | drm_syncobj_replace_fence(syncobjs[i], NULL); |
| | 169 | } |
| | 170 | } |
| | 171 | |
| | 172 | static void |
| | 173 | virtio_gpu_free_post_deps(struct virtio_gpu_submit_post_dep *post_deps, |
| | 174 | u32 nr_syncobjs) |
| | 175 | { |
| | 176 | u32 i = nr_syncobjs; |
| | 177 | |
| | 178 | while (i--) { |
| | 179 | kfree(post_deps[i].chain); |
| | 180 | drm_syncobj_put(post_deps[i].syncobj); |
| | 181 | } |
| | 182 | |
| | 183 | kvfree(post_deps); |
| | 184 | } |
| | 185 | |
| | 186 | static int virtio_gpu_parse_post_deps(struct virtio_gpu_submit *submit) |
| | 187 | { |
| | 188 | struct drm_virtgpu_execbuffer *exbuf = submit->exbuf; |
| | 189 | struct drm_virtgpu_execbuffer_syncobj syncobj_desc; |
| | 190 | struct virtio_gpu_submit_post_dep *post_deps; |
| | 191 | u32 num_out_syncobjs = exbuf->num_out_syncobjs; |
| | 192 | size_t syncobj_stride = exbuf->syncobj_stride; |
| | 193 | int ret = 0, i; |
| | 194 | |
| | 195 | if (!num_out_syncobjs) |
| | 196 | return 0; |
| | 197 | |
| | 198 | post_deps = kvcalloc(num_out_syncobjs, sizeof(*post_deps), GFP_KERNEL); |
| | 199 | if (!post_deps) |
| | 200 | return -ENOMEM; |
| | 201 | |
| | 202 | for (i = 0; i < num_out_syncobjs; i++) { |
| | 203 | u64 address = exbuf->out_syncobjs + i * syncobj_stride; |
| | 204 | |
| | 205 | memset(&syncobj_desc, 0, sizeof(syncobj_desc)); |
| | 206 | |
| | 207 | if (copy_from_user(&syncobj_desc, |
| | 208 | u64_to_user_ptr(address), |
| | 209 | min(syncobj_stride, sizeof(syncobj_desc)))) { |
| | 210 | ret = -EFAULT; |
| | 211 | break; |
| | 212 | } |
| | 213 | |
| | 214 | post_deps[i].point = syncobj_desc.point; |
| | 215 | |
| | 216 | if (syncobj_desc.flags) { |
| | 217 | ret = -EINVAL; |
| | 218 | break; |
| | 219 | } |
| | 220 | |
| | 221 | if (syncobj_desc.point) { |
| | 222 | post_deps[i].chain = dma_fence_chain_alloc(); |
| | 223 | if (!post_deps[i].chain) { |
| | 224 | ret = -ENOMEM; |
| | 225 | break; |
| | 226 | } |
| | 227 | } |
| | 228 | |
| | 229 | post_deps[i].syncobj = drm_syncobj_find(submit->file, |
| | 230 | syncobj_desc.handle); |
| | 231 | if (!post_deps[i].syncobj) { |
| | 232 | kfree(post_deps[i].chain); |
| | 233 | ret = -EINVAL; |
| | 234 | break; |
| | 235 | } |
| | 236 | } |
| | 237 | |
| | 238 | if (ret) { |
| | 239 | virtio_gpu_free_post_deps(post_deps, i); |
| | 240 | return ret; |
| | 241 | } |
| | 242 | |
| | 243 | submit->num_out_syncobjs = num_out_syncobjs; |
| | 244 | submit->post_deps = post_deps; |
| | 245 | |
| | 246 | return 0; |
| | 247 | } |
| | 248 | |
| | 249 | static void |
| | 250 | virtio_gpu_process_post_deps(struct virtio_gpu_submit *submit) |
| | 251 | { |
| | 252 | struct virtio_gpu_submit_post_dep *post_deps = submit->post_deps; |
| | 253 | |
| | 254 | if (post_deps) { |
| | 255 | struct dma_fence *fence = &submit->out_fence->f; |
| | 256 | u32 i; |
| | 257 | |
| | 258 | for (i = 0; i < submit->num_out_syncobjs; i++) { |
| | 259 | if (post_deps[i].chain) { |
| | 260 | drm_syncobj_add_point(post_deps[i].syncobj, |
| | 261 | post_deps[i].chain, |
| | 262 | fence, post_deps[i].point); |
| | 263 | post_deps[i].chain = NULL; |
| | 264 | } else { |
| | 265 | drm_syncobj_replace_fence(post_deps[i].syncobj, |
| | 266 | fence); |
| | 267 | } |
| | 268 | } |
| | 269 | } |
| | 270 | } |
| | 271 | |
| 62 | static int virtio_gpu_fence_event_create(struct drm_device *dev, | 272 | static int virtio_gpu_fence_event_create(struct drm_device *dev, |
| 63 | struct drm_file *file, | 273 | struct drm_file *file, |
| 64 | struct virtio_gpu_fence *fence, | 274 | struct virtio_gpu_fence *fence, |
| @@ -118,6 +328,10 @@ static int virtio_gpu_init_submit_buflist(struct virtio_gpu_submit *submit) · 53 unmodified lines |
| 118 | | 328 | |
| 119 | static void virtio_gpu_cleanup_submit(struct virtio_gpu_submit *submit) | 329 | static void virtio_gpu_cleanup_submit(struct virtio_gpu_submit *submit) |
| 120 | { | 330 | { |
| | 331 | virtio_gpu_reset_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs); |
| | 332 | virtio_gpu_free_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs); |
| | 333 | virtio_gpu_free_post_deps(submit->post_deps, submit->num_out_syncobjs); |
| | 334 | |
| 121 | if (!IS_ERR(submit->buf)) | 335 | if (!IS_ERR(submit->buf)) |
| 122 | kvfree(submit->buf); | 336 | kvfree(submit->buf); |
| 123 | | 337 | |
| @@ -172,6 +386,7 @@ static int virtio_gpu_init_submit(struct virtio_gpu_submit *submit, · 48 unmodified lines |
| 172 | drm_fence_event = false; | 386 | drm_fence_event = false; |
| 173 | | 387 | |
| 174 | if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) || | 388 | if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) || |
| | 389 | exbuf->num_out_syncobjs || |
| 175 | exbuf->num_bo_handles || | 390 | exbuf->num_bo_handles || |
| 176 | drm_fence_event) | 391 | drm_fence_event) |
| 177 | out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx); | 392 | out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx); |
| @@ -291,6 +506,14 @@ int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, · 113 unmodified lines |
| 291 | if (ret) | 506 | if (ret) |
| 292 | goto cleanup; | 507 | goto cleanup; |
| 293 | | 508 | |
| | 509 | ret = virtio_gpu_parse_post_deps(&submit); |
| | 510 | if (ret) |
| | 511 | goto cleanup; |
| | 512 | |
| | 513 | ret = virtio_gpu_parse_deps(&submit); |
| | 514 | if (ret) |
| | 515 | goto cleanup; |
| | 516 | |
| 294 | /* | 517 | /* |
| 295 | * Await in-fences in the end of the job submission path to | 518 | * Await in-fences in the end of the job submission path to |
| 296 | * optimize the path by proceeding directly to the submission | 519 | * optimize the path by proceeding directly to the submission |
| @@ -311,6 +534,7 @@ int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, · 14 unmodified lines |
| 311 | * the job submission path. | 534 | * the job submission path. |
| 312 | */ | 535 | */ |
| 313 | virtio_gpu_install_out_fence_fd(&submit); | 536 | virtio_gpu_install_out_fence_fd(&submit); |
| | 537 | virtio_gpu_process_post_deps(&submit); |
| 314 | virtio_gpu_complete_submit(&submit); | 538 | virtio_gpu_complete_submit(&submit); |
| 315 | cleanup: | 539 | cleanup: |
| 316 | virtio_gpu_cleanup_submit(&submit); | 540 | virtio_gpu_cleanup_submit(&submit); |