Merge tag 'powerpc-6.6-6' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[platform/kernel/linux-starfive.git] / drivers / gpu / drm / virtio / virtgpu_submit.c
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright (C) 2015 Red Hat, Inc.
4  * All Rights Reserved.
5  *
6  * Authors:
7  *    Dave Airlie
8  *    Alon Levy
9  */
10
11 #include <linux/dma-fence-unwrap.h>
12 #include <linux/file.h>
13 #include <linux/sync_file.h>
14 #include <linux/uaccess.h>
15
16 #include <drm/drm_file.h>
17 #include <drm/drm_syncobj.h>
18 #include <drm/virtgpu_drm.h>
19
20 #include "virtgpu_drv.h"
21
22 struct virtio_gpu_submit_post_dep {
23         struct drm_syncobj *syncobj;
24         struct dma_fence_chain *chain;
25         u64 point;
26 };
27
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
35         struct virtio_gpu_object_array *buflist;
36         struct drm_virtgpu_execbuffer *exbuf;
37         struct virtio_gpu_fence *out_fence;
38         struct virtio_gpu_fpriv *vfpriv;
39         struct virtio_gpu_device *vgdev;
40         struct sync_file *sync_file;
41         struct drm_file *file;
42         int out_fence_fd;
43         u64 fence_ctx;
44         u32 ring_idx;
45         void *buf;
46 };
47
48 static int virtio_gpu_do_fence_wait(struct virtio_gpu_submit *submit,
49                                     struct dma_fence *in_fence)
50 {
51         u32 context = submit->fence_ctx + submit->ring_idx;
52
53         if (dma_fence_match_context(in_fence, context))
54                 return 0;
55
56         return dma_fence_wait(in_fence, true);
57 }
58
59 static int virtio_gpu_dma_fence_wait(struct virtio_gpu_submit *submit,
60                                      struct dma_fence *fence)
61 {
62         struct dma_fence_unwrap itr;
63         struct dma_fence *f;
64         int err;
65
66         dma_fence_unwrap_for_each(f, &itr, fence) {
67                 err = virtio_gpu_do_fence_wait(submit, f);
68                 if (err)
69                         return err;
70         }
71
72         return 0;
73 }
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
272 static int virtio_gpu_fence_event_create(struct drm_device *dev,
273                                          struct drm_file *file,
274                                          struct virtio_gpu_fence *fence,
275                                          u32 ring_idx)
276 {
277         struct virtio_gpu_fence_event *e = NULL;
278         int ret;
279
280         e = kzalloc(sizeof(*e), GFP_KERNEL);
281         if (!e)
282                 return -ENOMEM;
283
284         e->event.type = VIRTGPU_EVENT_FENCE_SIGNALED;
285         e->event.length = sizeof(e->event);
286
287         ret = drm_event_reserve_init(dev, file, &e->base, &e->event);
288         if (ret) {
289                 kfree(e);
290                 return ret;
291         }
292
293         fence->e = e;
294
295         return 0;
296 }
297
298 static int virtio_gpu_init_submit_buflist(struct virtio_gpu_submit *submit)
299 {
300         struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
301         u32 *bo_handles;
302
303         if (!exbuf->num_bo_handles)
304                 return 0;
305
306         bo_handles = kvmalloc_array(exbuf->num_bo_handles, sizeof(*bo_handles),
307                                     GFP_KERNEL);
308         if (!bo_handles)
309                 return -ENOMEM;
310
311         if (copy_from_user(bo_handles, u64_to_user_ptr(exbuf->bo_handles),
312                            exbuf->num_bo_handles * sizeof(*bo_handles))) {
313                 kvfree(bo_handles);
314                 return -EFAULT;
315         }
316
317         submit->buflist = virtio_gpu_array_from_handles(submit->file, bo_handles,
318                                                         exbuf->num_bo_handles);
319         if (!submit->buflist) {
320                 kvfree(bo_handles);
321                 return -ENOENT;
322         }
323
324         kvfree(bo_handles);
325
326         return 0;
327 }
328
329 static void virtio_gpu_cleanup_submit(struct virtio_gpu_submit *submit)
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
335         if (!IS_ERR(submit->buf))
336                 kvfree(submit->buf);
337
338         if (submit->buflist)
339                 virtio_gpu_array_put_free(submit->buflist);
340
341         if (submit->out_fence_fd >= 0)
342                 put_unused_fd(submit->out_fence_fd);
343
344         if (submit->out_fence)
345                 dma_fence_put(&submit->out_fence->f);
346
347         if (submit->sync_file)
348                 fput(submit->sync_file->file);
349 }
350
351 static void virtio_gpu_submit(struct virtio_gpu_submit *submit)
352 {
353         virtio_gpu_cmd_submit(submit->vgdev, submit->buf, submit->exbuf->size,
354                               submit->vfpriv->ctx_id, submit->buflist,
355                               submit->out_fence);
356         virtio_gpu_notify(submit->vgdev);
357 }
358
359 static void virtio_gpu_complete_submit(struct virtio_gpu_submit *submit)
360 {
361         submit->buf = NULL;
362         submit->buflist = NULL;
363         submit->sync_file = NULL;
364         submit->out_fence_fd = -1;
365 }
366
367 static int virtio_gpu_init_submit(struct virtio_gpu_submit *submit,
368                                   struct drm_virtgpu_execbuffer *exbuf,
369                                   struct drm_device *dev,
370                                   struct drm_file *file,
371                                   u64 fence_ctx, u32 ring_idx)
372 {
373         struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
374         struct virtio_gpu_device *vgdev = dev->dev_private;
375         struct virtio_gpu_fence *out_fence;
376         bool drm_fence_event;
377         int err;
378
379         memset(submit, 0, sizeof(*submit));
380
381         if ((exbuf->flags & VIRTGPU_EXECBUF_RING_IDX) &&
382             (vfpriv->ring_idx_mask & BIT_ULL(ring_idx)))
383                 drm_fence_event = true;
384         else
385                 drm_fence_event = false;
386
387         if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) ||
388             exbuf->num_out_syncobjs ||
389             exbuf->num_bo_handles ||
390             drm_fence_event)
391                 out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx);
392         else
393                 out_fence = NULL;
394
395         if (drm_fence_event) {
396                 err = virtio_gpu_fence_event_create(dev, file, out_fence, ring_idx);
397                 if (err) {
398                         dma_fence_put(&out_fence->f);
399                         return err;
400                 }
401         }
402
403         submit->out_fence = out_fence;
404         submit->fence_ctx = fence_ctx;
405         submit->ring_idx = ring_idx;
406         submit->out_fence_fd = -1;
407         submit->vfpriv = vfpriv;
408         submit->vgdev = vgdev;
409         submit->exbuf = exbuf;
410         submit->file = file;
411
412         err = virtio_gpu_init_submit_buflist(submit);
413         if (err)
414                 return err;
415
416         submit->buf = vmemdup_user(u64_to_user_ptr(exbuf->command), exbuf->size);
417         if (IS_ERR(submit->buf))
418                 return PTR_ERR(submit->buf);
419
420         if (exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) {
421                 err = get_unused_fd_flags(O_CLOEXEC);
422                 if (err < 0)
423                         return err;
424
425                 submit->out_fence_fd = err;
426
427                 submit->sync_file = sync_file_create(&out_fence->f);
428                 if (!submit->sync_file)
429                         return -ENOMEM;
430         }
431
432         return 0;
433 }
434
435 static int virtio_gpu_wait_in_fence(struct virtio_gpu_submit *submit)
436 {
437         int ret = 0;
438
439         if (submit->exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_IN) {
440                 struct dma_fence *in_fence =
441                                 sync_file_get_fence(submit->exbuf->fence_fd);
442                 if (!in_fence)
443                         return -EINVAL;
444
445                 /*
446                  * Wait if the fence is from a foreign context, or if the
447                  * fence array contains any fence from a foreign context.
448                  */
449                 ret = virtio_gpu_dma_fence_wait(submit, in_fence);
450
451                 dma_fence_put(in_fence);
452         }
453
454         return ret;
455 }
456
457 static void virtio_gpu_install_out_fence_fd(struct virtio_gpu_submit *submit)
458 {
459         if (submit->sync_file) {
460                 submit->exbuf->fence_fd = submit->out_fence_fd;
461                 fd_install(submit->out_fence_fd, submit->sync_file->file);
462         }
463 }
464
465 static int virtio_gpu_lock_buflist(struct virtio_gpu_submit *submit)
466 {
467         if (submit->buflist)
468                 return virtio_gpu_array_lock_resv(submit->buflist);
469
470         return 0;
471 }
472
473 int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
474                                 struct drm_file *file)
475 {
476         struct virtio_gpu_device *vgdev = dev->dev_private;
477         struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
478         u64 fence_ctx = vgdev->fence_drv.context;
479         struct drm_virtgpu_execbuffer *exbuf = data;
480         struct virtio_gpu_submit submit;
481         u32 ring_idx = 0;
482         int ret = -EINVAL;
483
484         if (!vgdev->has_virgl_3d)
485                 return -ENOSYS;
486
487         if (exbuf->flags & ~VIRTGPU_EXECBUF_FLAGS)
488                 return ret;
489
490         if (exbuf->flags & VIRTGPU_EXECBUF_RING_IDX) {
491                 if (exbuf->ring_idx >= vfpriv->num_rings)
492                         return ret;
493
494                 if (!vfpriv->base_fence_ctx)
495                         return ret;
496
497                 fence_ctx = vfpriv->base_fence_ctx;
498                 ring_idx = exbuf->ring_idx;
499         }
500
501         virtio_gpu_create_context(dev, file);
502
503         ret = virtio_gpu_init_submit(&submit, exbuf, dev, file,
504                                      fence_ctx, ring_idx);
505         if (ret)
506                 goto cleanup;
507
508         ret = virtio_gpu_parse_post_deps(&submit);
509         if (ret)
510                 goto cleanup;
511
512         ret = virtio_gpu_parse_deps(&submit);
513         if (ret)
514                 goto cleanup;
515
516         /*
517          * Await in-fences in the end of the job submission path to
518          * optimize the path by proceeding directly to the submission
519          * to virtio after the waits.
520          */
521         ret = virtio_gpu_wait_in_fence(&submit);
522         if (ret)
523                 goto cleanup;
524
525         ret = virtio_gpu_lock_buflist(&submit);
526         if (ret)
527                 goto cleanup;
528
529         virtio_gpu_submit(&submit);
530
531         /*
532          * Set up usr-out data after submitting the job to optimize
533          * the job submission path.
534          */
535         virtio_gpu_install_out_fence_fd(&submit);
536         virtio_gpu_process_post_deps(&submit);
537         virtio_gpu_complete_submit(&submit);
538 cleanup:
539         virtio_gpu_cleanup_submit(&submit);
540
541         return ret;
542 }